From c5e4c2cfc092317c1e3ceb0f654f880f6a8573f4 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Tue, 16 Dec 2003 07:07:39 +0000 Subject: [PATCH 001/482] Initial revision --- .cvsignore | 5 + .cvsnotify | 1 + INSTALL | 72 + Makefile.in | 70 + arp.c | 56 + bounce.c | 90 + cli.c | 944 ++++++ cluster.c | 85 + cluster.h | 18 + cluster_master.c | 480 +++ cluster_slave.c | 249 ++ config.h.in | 213 ++ configure | 7102 ++++++++++++++++++++++++++++++++++++++++ configure.in | 48 + conform.cfg | 137 + constants.c | 136 + constants.h | 27 + control.c | 70 + control.h | 18 + etc/.cvsignore | 2 + etc/ip_pool.default | 2 + etc/l2tpns.cfg.default | 43 + etc/l2tpns.logrotate | 8 + etc/users.default | 1 + garden.c | 172 + install-sh | 238 ++ l2tpns.c | 2988 +++++++++++++++++ l2tpns.h | 393 +++ ll.c | 141 + ll.h | 28 + machines.cfg | 39 + md5.c | 349 ++ md5.h | 72 + nsctl.c | 138 + plugin.h | 116 + ppp.c | 725 ++++ radius.c | 614 ++++ rl.c | 158 + stamp-h | 1 + throttle.c | 73 + util.c | 16 + util.h | 1 + 42 files changed, 16139 insertions(+) create mode 100644 .cvsignore create mode 100644 .cvsnotify create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 arp.c create mode 100644 bounce.c create mode 100644 cli.c create mode 100644 cluster.c create mode 100644 cluster.h create mode 100644 cluster_master.c create mode 100644 cluster_slave.c create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 conform.cfg create mode 100644 constants.c create mode 100644 constants.h create mode 100644 control.c create mode 100644 control.h create mode 100644 etc/.cvsignore create mode 100644 etc/ip_pool.default create mode 100644 etc/l2tpns.cfg.default create mode 100644 etc/l2tpns.logrotate create mode 100644 etc/users.default create mode 100644 garden.c create mode 100755 install-sh create mode 100644 l2tpns.c create mode 100644 l2tpns.h create mode 100644 ll.c create mode 100644 ll.h create mode 100644 machines.cfg create mode 100644 md5.c create mode 100644 md5.h create mode 100644 nsctl.c create mode 100644 plugin.h create mode 100644 ppp.c create mode 100644 radius.c create mode 100644 rl.c create mode 100644 stamp-h create mode 100644 throttle.c create mode 100644 util.c create mode 100644 util.h diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..9a84440 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,5 @@ +*.o +l2tpns +state.dump +*.swp +cluster_master diff --git a/.cvsnotify b/.cvsnotify new file mode 100644 index 0000000..589d7f1 --- /dev/null +++ b/.cvsnotify @@ -0,0 +1 @@ +david.parrish@optusnet.com.au diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4ccf61f --- /dev/null +++ b/INSTALL @@ -0,0 +1,72 @@ +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. + + * A kernel with iptables support + + * If you want to use throttling, you must have a kernel and a tc (iproute) which supports HTB. + + +2. Compile + +./configure --prefix=/usr --sysconfdir=/etc/l2tpns +make + + +3. Install + + * make install. This does: + * Install the binaries into /usr/bin (l2tpns, cluster_master and nsctl) + * Create config dir /etc/l2tpns and create default config files + * Ensures that /dev/net/tun exists + + * Modify config file. You probably need to change most of the config options. + + * Set up basic firewall rules. This should be done in an init script. + + iptables -t nat -N l2tpns + iptables -t nat -A PREROUTING -j l2tpns + iptables -t mangle -N l2tpns + iptables -t mangle -A PREROUTING -j l2tpns + + * Set up walled garden firewall rules. This should be done in an init + script. This is not required unless you are using the garden plugin. + + iptables -t nat -N garden >/dev/null 2>&1 + iptables -t nat -F garden + iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 80 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 110 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p tcp -m tcp --dport 443 -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p icmp -m icmp --icmp-type echo-request -j DNAT --to 192.168.1.1 + iptables -t nat -A garden -p icmp -j ACCEPT + iptables -t nat -A garden -j DROP + + * Set up IP address pools in /etc/l2tpns/ip_pool + + * Set up clustering + + * Run cluster_master on a separate machine + * Set the "cluster master" and "bind address" parameters in /etc/l2tpns/l2tpns.cfg + + * Make l2tpns run on startup + + * Test it out + + + + +This software is quite stable and is being used in a production +environment at a quite large ISP. However, you may have problems +setting it up, and if so, I would appreciate it if you would file +useful bug reports on the Source Forge page: + +http://sourceforge.net/projects/l2tpns/ + +-- David Parrish diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..404a715 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,70 @@ +subdirs = @subdirs@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +infodir = @infodir@ +etcdir = @sysconfdir@ +libdir = @prefix@/lib/l2tpns + +CC = @CC@ +CFLAGS=-Wall @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +INSTALL = @INSTALL@ +DEFS = @DEFS@ + +OBJS= md5.o \ + cli.o \ + l2tpns.o \ + ppp.o \ + radius.o \ + throttle.o \ + rl.o \ + ll.o \ + cluster.o \ + cluster_slave.o \ + arp.o \ + constants.o \ + ll.o \ + control.o \ + util.o \ + +PLUGINS=garden.so + +all: l2tpns cluster_master nsctl $(PLUGINS) + +l2tpns: $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(DEFS) + +cluster_master: cluster_master.o ll.o cluster.o util.o + $(CC) $(CFLAGS) -o $@ $^ $(DEFS) + +nsctl: nsctl.o control.o + $(CC) $(CFLAGS) -o $@ $^ $(DEFS) + +clean: + /bin/rm -f *.o *.so l2tpns cluster_master nsctl + +distclean: + /bin/rm -f Makefile config.h config.status config.cache config.log + +install: all + $(INSTALL) -D -o root -g root -m 0755 l2tpns $(bindir)/l2tpns + $(INSTALL) -D -o root -g root -m 0755 cluster_master $(bindir)/cluster_master + $(INSTALL) -D -o root -g root -m 0755 nsctl $(bindir)/nsctl + $(INSTALL) -D -o root -g root -m 0600 etc/l2tpns.cfg.default $(etcdir)/l2tpns.cfg + $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool + $(INSTALL) -D -o root -g root -m 0600 etc/users.default $(etcdir)/l2tpns.users + for PLUGIN in $(PLUGINS); do + $(INSTALL) -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN) + done + if [ ! -e /dev/net/tun ]; then + mkdir /dev/net + mknod /dev/net/tun c 10 200 + fi + +%.so: %.c + $(CC) -fPIC -shared -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBPATH) + diff --git a/arp.c b/arp.c new file mode 100644 index 0000000..122880a --- /dev/null +++ b/arp.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +#include "l2tpns.h" + +/* Most of this code is based on keepalived:vrrp_arp.c */ + +struct arp_buf { + struct ether_header eth; + struct arphdr arp; + + /* Data bit - variably sized, so not present in |struct arphdr| */ + unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address */ + ipt ar_sip; /* Sender IP address. */ + unsigned char ar_tha[ETH_ALEN]; /* Target hardware address */ + ipt ar_tip; /* Target ip */ +} __attribute__((packed)); + +void sendarp(int ifr_idx, const unsigned char* mac, ipt ip) +{ + int fd; + struct sockaddr_ll sll; + struct arp_buf buf; + + /* Ethernet */ + memset(buf.eth.ether_dhost, 0xFF, ETH_ALEN); + memcpy(buf.eth.ether_shost, mac, ETH_ALEN); + buf.eth.ether_type = htons(ETHERTYPE_ARP); + + /* ARP */ + buf.arp.ar_hrd = htons(ARPHRD_ETHER); + buf.arp.ar_pro = htons(ETHERTYPE_IP); + buf.arp.ar_hln = ETH_ALEN; + buf.arp.ar_pln = 4; //IPPROTO_ADDR_LEN; + buf.arp.ar_op = htons(ARPOP_REQUEST); + + /* Data */ + memcpy(buf.ar_sha, mac, ETH_ALEN); + 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 */ + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = ifr_idx; + + sendto(fd, &buf, sizeof(buf), 0, (struct sockaddr*)&sll, sizeof(sll)); +} diff --git a/bounce.c b/bounce.c new file mode 100644 index 0000000..9d3f334 --- /dev/null +++ b/bounce.c @@ -0,0 +1,90 @@ +#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 new file mode 100644 index 0000000..3aafb10 --- /dev/null +++ b/cli.c @@ -0,0 +1,944 @@ +// L2TPNS Command Line Interface +// $Id: cli.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ +// vim: sw=4 ts=8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "util.h" +#include "config.h" + +#ifdef HAVE_LIBCLI + +extern tunnelt *tunnel; +extern sessiont *session; +extern radiust *radius; +extern ippoolt *ip_address_pool; +extern struct Tstats *_statistics; +extern int cli_pid; +struct cli_def *cli = NULL; +int cli_quit = 0; +extern int clifd, udpfd, tapfd, snoopfd, radfd, ifrfd, cluster_sockfd; +extern sessionidt *cli_session_kill; +extern tunnelidt *cli_tunnel_kill; +extern tbft *filter_buckets; +#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 *debug_levels[] = { + "CRIT", + "ERROR", + "WARN", + "INFO", + "CALL", + "DATA", +}; + +struct +{ + char critical; + char error; + char warning; + char info; + char calls; + char data; +} debug_flags; + +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); + +void init_cli() +{ + FILE *f; + char buf[4096]; + struct cli_command *c; + int on = 1; + struct sockaddr_in addr; + + 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, "banana", cmd_show_banana, "Show a banana"); + cli_register_command(cli, c, "pool", cmd_show_pool, NULL); + +#ifdef STATISTICS + cli_register_command(cli, c, "counters", cmd_show_counters, NULL); + + c = cli_register_command(cli, NULL, "clear", NULL, NULL); + cli_register_command(cli, c, "counters", cmd_clear_counters, NULL); +#endif + + cli_register_command(cli, NULL, "snoop", cmd_snoop, NULL); + cli_register_command(cli, NULL, "throttle", cmd_throttle, NULL); + + 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); + + 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, NULL, "debug", cmd_debug, "Specify a debugging level"); + + 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, "tunnel", cmd_watch_tunnel, "Dump logs for a tunnel"); + + // Enable regular processing + cli_regular(cli, regular_stuff); + + if (!(f = fopen(CLIUSERS, "r"))) + { + log(0, 0, 0, 0, "WARNING! No users specified. Command-line access is open to all\n"); + } + else + { + while (fgets(buf, 4096, f)) + { + char *p; + if (*buf == '#') continue; + if ((p = strchr(buf, '\r'))) *p = 0; + if ((p = strchr(buf, '\n'))) *p = 0; + if (!*buf) continue; + if (!(p = strchr((char *)buf, ':'))) continue; + *p++ = 0; + cli_allow_user(cli, buf, p); + log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf); + } + fclose(f); + } + + memset(&addr, 0, sizeof(addr)); + clifd = socket(PF_INET, SOCK_STREAM, 6); + setsockopt(clifd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + { + int flags; + // Set cli fd as non-blocking + flags = fcntl(clifd, F_GETFL, 0); + fcntl(clifd, F_SETFL, flags | O_NONBLOCK); + } + addr.sin_family = AF_INET; + addr.sin_port = htons(23); + if (bind(clifd, (void *) &addr, sizeof(addr)) < 0) + { + log(0, 0, 0, 0, "Error listening on cli port 23: %s\n", strerror(errno)); + return; + } + listen(clifd, 10); +} + +void cli_do(int sockfd) +{ + if ((cli_pid = fork())) return; + + // Close sockets + if (udpfd) close(udpfd); udpfd = 0; + if (tapfd) close(tapfd); tapfd = 0; + if (snoopfd) close(snoopfd); snoopfd = 0; + if (radfd) close(radfd); radfd = 0; + if (ifrfd) close(ifrfd); ifrfd = 0; + if (cluster_sockfd) close(cluster_sockfd); cluster_sockfd = 0; + if (clifd) close(clifd); clifd = 0; + + signal(SIGPIPE, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGKILL, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + log(3, 0, 0, 0, "Accepted connection to CLI\n"); + + debug_session = 0; + debug_tunnel = 0; +#ifdef RINGBUFFER + debug_rb_tail = ringbuffer->tail; +#endif + memset(&debug_flags, 0, sizeof(debug_flags)); + debug_flags.critical = 1; + + cli_loop(cli, sockfd, "l2tpns> "); + + 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) +{ + int i; + time_t time_now; + + time(&time_now); + if (argc > 0) + { + // Show individual session + for (i = 0; i < argc; i++) + { + unsigned int s; + s = atoi(argv[i]); + if (!s || s > MAXSESSION) + { + fprintf(w, "Invalid session id \"%s\"\r\n", 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"); + } + return CLI_OK; + } + + // Show Summary + fprintf(w, " %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s\r\n", + "SID", + "TID", + "Username", + "IP", + "I", + "T", + "S", + "opened", + "downloaded", + "uploaded", + "idle", + "LAC", + "CLI"); + for (i = 0; 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", + 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, + abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), + tunnelip, + session[i].calling[0] ? session[i].calling : "*"); + if (userip) free(userip); + if (tunnelip) free(tunnelip); + } + return CLI_OK; +} + +int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i, x; + time_t time_now; + + time(&time_now); + if (argc > 0) + { + // Show individual tunnel + for (i = 0; i < argc; i++) + { + char s[65535] = {0}; + unsigned int t; + t = atoi(argv[i]); + if (!t || t > MAXTUNNEL) + { + fprintf(w, "Invalid tunnel id \"%s\"\r\n", argv[i]); + continue; + } + 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; + } + + // Show tunnel summary + fprintf(w, "%s %s %s %s\r\n", + "TID", + "Hostname", + "IP", + "Sessions"); + for (i = 0; i < MAXTUNNEL; i++) + { + int sessions = 0; + if (!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", + i, + tunnel[i].hostname, + inet_toa(htonl(tunnel[i].ip)), + sessions); + } + return CLI_OK; +} + +int cmd_show_users(struct cli_def *cli, FILE *w, 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", + session[i].user); + } + return CLI_OK; +} + +int cmd_show_counters(struct cli_def *cli, FILE *w, 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", + GET_STAT(tap_rx_bytes), + GET_STAT(tap_rx_packets), + GET_STAT(tap_rx_errors)); + fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "TX", + GET_STAT(tap_tx_bytes), + GET_STAT(tap_tx_packets), + GET_STAT(tap_tx_errors)); + fprintf(w, "\r\n"); + + fprintf(w, "%-10s %-8s %-10s %-8s %-8s\r\n", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); + fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "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", + 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)); + +#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)); +#endif + return CLI_OK; +} + +int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fprintf(w, "L2TPNS %s\r\n", VERSION); + fprintf(w, "ID: %s\r\n", rcs_id); + return CLI_OK; +} +int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + int used = 0, free = 0; + + fprintf(w, "%-15s %4s %8s %s\r\n", "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) + { + used++; + s = sessionbyip(ip_address_pool[i].address); + } + else + { + free++; + } + + fprintf(w, "%-15s %4s %8d %s\r\n", + inet_toa(ip_address_pool[i].address), + (s) ? "Y" : "N", + s, + session[s].user); + } + fprintf(w, "\r\nFree: %d\r\nUsed: %d\r\n", 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); + + return CLI_OK; +} + +int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + fprintf(w, "Counters cleared\r\n"); + 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 i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + + if (session[s].ip && session[s].opened && !session[s].die) + { + int x; + + fprintf(w, "Dropping user %s\r\n", session[s].user); + for (x = 0; x < MAXSESSION; x++) + { + if (!cli_session_kill[x]) + { + cli_session_kill[x] = s; + break; + } + } + } + } + + return CLI_OK; +} + +int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + tunnelidt tid; + + if (!argc) + { + fprintf(w, "Specify a tunnel to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "tunnel_id ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + int x; + + if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL)) + { + fprintf(w, "Invalid tunnel ID (%d - %d)\r\n", 0, MAXTUNNEL); + continue; + } + + if (!tunnel[tid].ip) + { + fprintf(w, "Tunnel %d is not connected\r\n", tid); + continue; + } + + if (tunnel[tid].die) + { + fprintf(w, "Tunnel %d is already being shut down\r\n", tid); + continue; + } + + for (x = 0; x < MAXTUNNEL; x++) + { + if (!cli_tunnel_kill[x]) + { + cli_tunnel_kill[x] = tid; + fprintf(w, "Tunnel %d shut down (%s)\r\n", tid, tunnel[tid].hostname); + break; + } + } + } + + return CLI_OK; +} + +int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a session id to drop\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "session_id ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION)) + { + fprintf(w, "Invalid session ID (%d - %d)\r\n", 0, MAXSESSION); + continue; + } + + if (session[s].ip && session[s].opened && !session[s].die) + { + int x; + for (x = 0; x < MAXSESSION; x++) + { + if (!cli_session_kill[x]) + { + cli_session_kill[x] = s; + break; + } + } + fprintf(w, "Dropping session %d\r\n", s); + } + else + { + fprintf(w, "Session %d is not active.\r\n", s); + } + } + + return CLI_OK; +} + +int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + session[s].snoop = 1; + + fprintf(w, "Snooping user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + session[s].snoop = 0; + + fprintf(w, "Not snooping user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + throttle_session(s, 1); + + fprintf(w, "throttling user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + sessionidt s; + + if (!argc) + { + fprintf(w, "Specify a user\r\n"); + return CLI_OK; + } + for (i = 0; i < argc; i++) + { + if (strchr(argv[i], '?')) + { + fprintf(w, "username ..."); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (!(s = sessionbyuser(argv[i]))) + { + fprintf(w, "User %s is not connected\r\n", argv[i]); + continue; + } + throttle_session(s, 0); + + fprintf(w, "unthrottling user %s\r\n", argv[i]); + } + return CLI_OK; +} + +int cmd_debug(struct cli_def *cli, FILE *w, 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"); + return CLI_OK; + } + + for (i = 0; i < argc; i++) + { + 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"); + return CLI_OK; + } + } + + for (i = 0; i < argc; i++) + { + if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 1; + if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 1; + if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 1; + if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 1; + if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 1; + if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 1; + if (strcasecmp(argv[i], "all") == 0) + { + memset(&debug_flags, 1, sizeof(debug_flags)); + debug_flags.data = 0; + } + } + + return CLI_OK; +} + +int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + int i; + + for (i = 0; i < argc; i++) + { + if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 0; + if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 0; + if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 0; + if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 0; + if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 0; + if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 0; + if (strcasecmp(argv[i], "all") == 0) memset(&debug_flags, 0, sizeof(debug_flags)); + } + + return CLI_OK; +} + +int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + sessionidt s; + + if (argc != 1) + { + fprintf(w, "Specify a single session to debug (0 to disable)\r\n"); + return CLI_OK; + } + s = atoi(argv[0]); + + if (debug_session) + fprintf(w, "No longer debugging session %d\r\n", debug_session); + + if (s) fprintf(w, "Debugging session %d.\r\n", s); + debug_session = s; + + return CLI_OK; +} + +int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +{ + tunnelidt s; + + if (argc != 1) + { + fprintf(w, "Specify a single tunnel to debug (0 to disable)\r\n"); + return CLI_OK; + } + s = atoi(argv[0]); + + if (debug_tunnel) + fprintf(w, "No longer debugging tunnel %d\r\n", debug_tunnel); + + if (s) fprintf(w, "Debugging tunnel %d.\r\n", s); + debug_tunnel = s; + + return CLI_OK; +} + +int regular_stuff(struct cli_def *cli, FILE *w) +{ + int i = debug_rb_tail; + +#ifdef RINGBUFFER + while (i != ringbuffer->tail) + { + int show_message = 0; + + if (*ringbuffer->buffer[i].message) + { + // Always show messages if we are doing general debug + if (ringbuffer->buffer[i].level == 0 && debug_flags.critical) show_message = 1; + if (ringbuffer->buffer[i].level == 1 && debug_flags.error) show_message = 1; + if (ringbuffer->buffer[i].level == 2 && debug_flags.warning) show_message = 1; + if (ringbuffer->buffer[i].level == 3 && debug_flags.info) show_message = 1; + if (ringbuffer->buffer[i].level == 4 && debug_flags.calls) show_message = 1; + if (ringbuffer->buffer[i].level == 5 && debug_flags.data) show_message = 1; + } + + if (show_message) + { + ipt address = ntohl(ringbuffer->buffer[i].address); + char *ipaddr; + struct in_addr addr; + + memcpy(&addr, &address, sizeof(ringbuffer->buffer[i].address)); + ipaddr = inet_ntoa(addr); + + fprintf(w, "%s-%s-%u-%u %s\r", + debug_levels[(int)ringbuffer->buffer[i].level], + ipaddr, + ringbuffer->buffer[i].tunnel, + ringbuffer->buffer[i].session, + ringbuffer->buffer[i].message); + } + + if (++i == ringbuffer->tail) break; + if (i == RINGBUFFER_SIZE) i = 0; + } + + debug_rb_tail = ringbuffer->tail; +#endif + return CLI_OK; +} + +#endif diff --git a/cluster.c b/cluster.c new file mode 100644 index 0000000..5ad3ff3 --- /dev/null +++ b/cluster.c @@ -0,0 +1,85 @@ +// L2TPNS Clustering Stuff +// $Id: cluster.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ #include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cluster.h" + +int cluster_sockfd = 0; +int cluster_server = 0; +uint32_t vip_address; +extern int debug; +void _log_hex(int level, const char *title, const char *data, int maxsize); +#define log_hex(a,b,c,d) +#ifndef log_hex +#define log_hex(a,b,c,d) do{if (a > debug) _log_hex(a,b,c,d);}while (0) +#endif + +// Create a listening socket +int cluster_init(uint32_t bind_address, int server) +{ + struct sockaddr_in addr; + + vip_address = bind_address; + cluster_server = !!server; + + cluster_sockfd = socket(AF_INET, SOCK_DGRAM, UDP); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cluster_server ? CLUSTERPORT : CLUSTERCLIENTPORT); + addr.sin_addr.s_addr = INADDR_ANY; + setsockopt(cluster_sockfd, SOL_SOCKET, SO_REUSEADDR, &addr, sizeof(addr)); + if (bind(cluster_sockfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + exit(-1); + } + + return cluster_sockfd; +} + +int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen) +{ + size_t l = 1 + sizeof(uint32_t) + datalen; + char *buf = NULL; + struct sockaddr_in addr = {0}; + + if (!cluster_sockfd) return -1; + if (!ip_address) return 0; + + buf = calloc(l, 1); + *(uint32_t *)(buf) = htonl(vip); + *(char *)(buf+sizeof(uint32_t)) = type; + + if (data && datalen > 0) + memcpy((char *)(buf + sizeof(uint32_t) + 1), data, datalen); + + addr.sin_addr.s_addr = ip_address; + addr.sin_port = htons(cluster_server ? CLUSTERCLIENTPORT : CLUSTERPORT); + addr.sin_family = AF_INET; + + log_hex(4, "Cluster send", buf, l); + + if (sendto(cluster_sockfd, buf, l, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) + { + perror("sendto"); + free(buf); + return -1; + } + free(buf); + + return 0; +} diff --git a/cluster.h b/cluster.h new file mode 100644 index 0000000..57759fc --- /dev/null +++ b/cluster.h @@ -0,0 +1,18 @@ +// L2TPNS Clustering Stuff +// $Id: cluster.h,v 1.1 2003-12-16 07:07:39 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 CLUSTERPORT 32792 +#define CLUSTERCLIENTPORT 32793 +#define UDP 17 +#define TIMEOUT 20 +#define IL sizeof(int) + +int cluster_init(uint32_t bind_address, int server); +int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen); +int processcluster(char *buf, int l); diff --git a/cluster_master.c b/cluster_master.c new file mode 100644 index 0000000..0cc43ee --- /dev/null +++ b/cluster_master.c @@ -0,0 +1,480 @@ +// L2TPNS Cluster Master +// $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cluster.h" +#include "ll.h" +#include "util.h" +#include "config.h" + +#define L2TPNS BINDIR "/l2tpns" + +typedef struct +{ + char *hostname; + unsigned long last_message; + uint32_t ip_address; + uint32_t slave_address; + int remove_from_cluster; + int down; + int tunnel_len; + int session_len; + pid_t pid; + + int num_tunnels; + char *tunnels[1000]; + int num_sessions; + char *sessions[13000]; +} slave; + +uint32_t master_address; +linked_list *slaves; +extern int cluster_sockfd; +int debug = 4; + +int processmsg(char *buf, int l, struct sockaddr_in *src_addr); +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 backup_up(slave *s); +int backup_down(slave *s); +int return_state(slave *s); +slave *find_slave(uint32_t address); +#define log _log +void _log(int level, const char *format, ...); +void log_hex(int level, const char *title, const char *data, int maxsize); + +/* Catch our forked processes exiting */ +void sigchild_handler(int signal) +{ + int status; + int pid; + + pid = wait(&status); + /* TODO: catch errors and respawn? */ +} + +int main(int argc, char *argv[]) +{ + slave *s; + char buf[4096]; + struct timeval to; + + if (argc != 2) { + log(0, "Usage: %s
\n", argv[0]); + exit(-1); + } + + master_address = inet_addr(argv[1]); + if (master_address == INADDR_NONE) { + log(0, "Invalid ip %s\n", argv[1]); + exit(-1); + } + + cluster_init(master_address, 1); + slaves = ll_init(); + + 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"); + + to.tv_sec = 1; + to.tv_usec = 0; + while (1) + { + fd_set r; + int n; + + FD_ZERO(&r); + FD_SET(cluster_sockfd, &r); + n = select(cluster_sockfd + 1, &r, 0, 0, &to); + if (n < 0) + { + if (errno != EINTR) + { + perror("select"); + exit(-1); + } + continue; + } + else if (n) + { + struct sockaddr_in addr; + int alen = sizeof(addr); + + memset(buf, 0, sizeof(buf)); + if (FD_ISSET(cluster_sockfd, &r)) + processmsg(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); + continue; + } + + // Handle slaves timing out + { + time_t now = time(NULL); + ll_reset(slaves); + while ((s = ll_next(slaves))) + { + if (s->down) continue; + if (s->last_message < (now - TIMEOUT)) + { + log(4, "Slave \"%s\" s->last_message is %lu (timeout is %lu)\n", s->hostname, s->last_message, (now - TIMEOUT)); + if (s->remove_from_cluster) + { + // Remove them from the cluster + ll_delete(slaves, s); + if (s->hostname) free(s->hostname); + free(s); + ll_reset(slaves); + continue; + } + backup_up(s); + } + } + } + + to.tv_sec = 1; + to.tv_usec = 0; + } + + return 0; +} + +int processmsg(char *buf, int l, struct sockaddr_in *src_addr) +{ + char mtype; + uint32_t addr; + + log_hex(4, "Received", buf, l); + if (!buf || l <= sizeof(uint32_t)) return 0; + + addr = ntohl(*(uint32_t*)buf); + buf += sizeof(uint32_t); + l -= sizeof(uint32_t); + + mtype = *buf; buf++; l--; + + switch (mtype) + { + case C_HELLO: + handle_hello(buf, l, src_addr, addr); + break; + case C_PING: + if (!find_slave(addr)) + handle_hello(buf, l, src_addr, addr); + else + handle_ping(buf, l, addr); + break; + case C_TUNNEL: + if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); + handle_tunnel(buf, l, addr); + break; + case C_SESSION: + if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); + handle_session(buf, l, addr); + break; + } + return mtype; +} + +int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr) +{ + slave *s; + char *hostname; + + hostname = calloc(l + 1, 1); + memcpy(hostname, buf, l); + + // Is this a slave we have state information for? + if ((s = find_slave(addr))) + { + if (src_addr->sin_addr.s_addr == master_address) + { + log(1, "Got hello from \"%s\", local backup for %s.\n", hostname, inet_toa(s->ip_address)); + } + else if (s->down) + { + log(1, "Slave \"%s\" (for %s) has come back.\n", hostname, inet_toa(s->ip_address)); + backup_down(s); + } + else + { + log(1, "Slave \"%s\" said hello and we didn't know it was down.\n", s->hostname); + } + + /* Reset the hostname if needed */ + free(s->hostname); + s->hostname = hostname; + } else { + // No state information, it's a new slave + s = calloc(sizeof(slave), 1); + s->ip_address = addr; + ll_push(slaves, s); + s->hostname = hostname; + log(1, "New slave added to cluster \"%s\"\n", s->hostname); + } + + s->slave_address = src_addr->sin_addr.s_addr; + + // Send state information back + return_state(s); + + s->last_message = time(NULL); + + return 0; +} + +int handle_tunnel(char *buf, int l, uint32_t addr) +{ + int tid; + slave *s; + if (!(s = find_slave(addr))) + { + log(0, "handle_tunnel() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + // Skip hostname + tid = *(char *)buf; + buf += (tid + 1); + l -= (tid + 1); + + // Grab tunnel ID + tid = *(int *)buf; + buf += sizeof(int); + l -= sizeof(int); + + log(3, "Received tunnel %d from \"%s\" (%d bytes long)\n", tid, s->hostname, l); + + // Allocate memory for it if it's not already + if (!s->tunnels[tid]) + { + s->tunnels[tid] = malloc(l); + s->num_tunnels++; + s->tunnel_len = l; + } + + memcpy(s->tunnels[tid], buf, l); + return l; +} + +int handle_session(char *buf, int l, uint32_t addr) +{ + slave *s; + int sid; + char hostname[4096] = {0}; + if (!(s = find_slave(addr))) + { + log(0, "handle_session() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + // Skip hostname + sid = *(char *)buf; + memcpy(hostname, (char *)(buf + 1), sid); + buf += (sid + 1); + l -= (sid + 1); + log(0, "Hostname is %s\n", hostname); + + // Grab session ID + sid = *(int *)buf; + buf += sizeof(int); + l -= sizeof(int); + + log(3, "Received session %d from \"%s\" (%d bytes long)\n", sid, s->hostname, l); + + // Allocate memory for it if it's not already + if (!s->sessions[sid]) + { + s->sessions[sid] = malloc(l); + s->num_sessions++; + s->session_len = l; + } + + memcpy(s->sessions[sid], buf, l); + return l; +} + +int handle_ping(char *buf, int l, uint32_t addr) +{ + slave *s; + if (!(s = find_slave(addr))) + { + log(0, "handle_ping() called with no valid slave\n"); + return 0; + } + s->last_message = time(NULL); + + return 0; +} + +int return_state(slave *s) +{ + char *packet; + int i; + int num_tunnels = 0, num_sessions = 0; + int pktlen; + + log(3, "Sending state information to \"%s\"\n", s->hostname); + + for (i = 0; i < 1000; i++) + if (s->tunnels[i]) num_tunnels++; + + for (i = 0; i < 13000; i++) + if (s->sessions[i]) num_sessions++; + + if (!num_sessions && !num_tunnels) return 0; + + packet = calloc(IL * 4, 1); + *(int *)(packet + IL * 0) = num_tunnels; + *(int *)(packet + IL * 1) = num_sessions; + *(int *)(packet + IL * 2) = s->tunnel_len; + *(int *)(packet + IL * 3) = s->session_len; + cluster_send_message(s->slave_address, s->ip_address, C_HELLO_RESPONSE, packet, IL * 4); + free(packet); + + // Send tunnels one-by-one, in order + log(0, "Sending %d tunnels of %d bytes each\n", num_tunnels, s->tunnel_len); + pktlen = s->tunnel_len + sizeof(int); + packet = malloc(pktlen); + for (i = 0; i < 1000; i++) + { + if (s->tunnels[i]) + { + *(int *)packet = i; + memcpy((char *)(packet + sizeof(int)), s->tunnels[i], s->tunnel_len); + log(0, "Sending tunnel %d\n", i); + cluster_send_message(s->slave_address, s->ip_address, C_TUNNEL, packet, pktlen); + } + } + free(packet); + + // Send sessions one-by-one, in order + log(0, "Sending %d sessions of %d bytes each\n", num_sessions, s->session_len); + pktlen = s->session_len + sizeof(int); + packet = malloc(pktlen); + for (i = 0; i < 13000; i++) + { + if (s->sessions[i]) + { + *(int *)packet = i; + memcpy((char *)(packet + sizeof(int)), s->sessions[i], s->session_len); + log(0, "Sending session %d\n", i); + cluster_send_message(s->slave_address, s->ip_address, C_SESSION, packet, pktlen); + } + } + free(packet); + + return 0; +} + +slave *find_slave(uint32_t address) +{ + slave *s; + + ll_reset(slaves); + while ((s = ll_next(slaves))) + { + if (s->ip_address == address) + { + return s; + } + } + return NULL; +} + +void _log(int level, const char *format, ...) +{ + va_list ap; + if (debug < level) return; + + va_start(ap, format); + vfprintf(stderr, format, ap); +} + +void log_hex(int level, const char *title, const char *data, int maxsize) +{ + unsigned int i, j; + unsigned const char *d = (unsigned const char *)data; + + if (debug < level) return; + log(level, "%s (%d bytes):\n", title, maxsize); + setvbuf(stderr, NULL, _IOFBF, 16384); + for (i = 0; i < maxsize; ) + { + fprintf(stderr, "%4X: ", i); + for (j = i; j < maxsize && j < (i + 16); j++) + { + fprintf(stderr, "%02X ", d[j]); + if (j == i + 7) + fputs(": ", stderr); + } + + for (; j < i + 16; j++) + { + fputs(" ", stderr); + if (j == i + 7) + fputs(": ", stderr); + } + + fputs(" ", stderr); + for (j = i; j < maxsize && j < (i + 16); j++) + { + if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) + fputc(d[j], stderr); + else + fputc('.', stderr); + + if (j == i + 7) + fputs(" ", stderr); + } + + i = j; + fputs("\n", stderr); + } + fflush(stderr); + setbuf(stderr, NULL); +} + +int backup_up(slave *s) +{ + log(2, "Becoming backup for \"%s\" (%s).\n", s->hostname, inet_toa(s->ip_address)); + s->pid = fork(); + if (!s->pid) + { + if (execl(L2TPNS, L2TPNS, "-a", inet_toa(s->ip_address), NULL) < 0) + log(0, "Error execing backup " L2TPNS ": %s\n", strerror(errno)); + exit(0); + } + s->down = 1; + return 0; +} + +int backup_down(slave *s) +{ + log(2, "Not being backup for \"%s\" (%s) anymore.\n", s->hostname, inet_toa(s->ip_address)); + s->down = 0; + if (s->pid) { + kill(s->pid, SIGTERM); + sleep(2); + kill(s->pid, SIGKILL); + } + return 0; +} + diff --git a/cluster_slave.c b/cluster_slave.c new file mode 100644 index 0000000..71e2eac --- /dev/null +++ b/cluster_slave.c @@ -0,0 +1,249 @@ +// L2TPNS Cluster Master +// $Id: cluster_slave.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "cluster.h" +#include "ll.h" +#include "util.h" + +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; + +int handle_tunnel(char *buf, int l); +int handle_session(char *buf, int l); +int handle_hello_response(char *buf, int l); + +int processcluster(char *buf, int l) +{ + char mtype; + uint32_t addr; + + log_hex(4, "Cluster receive", buf, l); + if (!buf || l <= sizeof(uint32_t)) return 0; + + addr = ntohl(*(uint32_t*)buf); + buf += sizeof(uint32_t); + l -= sizeof(uint32_t); + + if (addr != vip_address) + { + log(0, 0, 0, 0, "Received cluster message for VIP %s, which isn't ours\n", inet_toa(addr)); + } + + mtype = *buf; buf++; l--; + + switch (mtype) + { + case C_HELLO: + break; + case C_HELLO_RESPONSE: + handle_hello_response(buf, l); + break; + case C_PING: + break; + case C_TUNNEL: + handle_tunnel(buf, l); + break; + case C_SESSION: + handle_session(buf, l); + break; + } + return mtype; + + return 0; +} + +int handle_tunnel(char *buf, int l) +{ + int t; + + 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); + + if (t > MAXTUNNEL) + { + log(0, 0, 0, t, "Cluster master tried to send tunnel %d, which is bigger than MAXTUNNEL (%d)\n", t, MAXTUNNEL); + return 0; + } + + if (l != sizeof(tunnelt)) + { + log(1, 0, 0, t, "Discarding bogus tunnel message (%d bytes instead of %d).\n", l, sizeof(tunnelt)); + 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); + + tunnel[t].controlc = 0; + tunnel[t].controls = NULL; + tunnel[t].controle = NULL; + return 0; +} + +int handle_session(char *buf, int l) +{ + int s; + + 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); + + if (s > MAXSESSION) + { + log(0, 0, s, 0, "Cluster master tried to send session %d, which is bigger than MAXSESSION (%d)\n", s, MAXSESSION); + return 0; + } + + if (l != sizeof(sessiont)) + { + log(1, 0, s, 0, "Discarding short session message (%d bytes instead of %d).\n", l, sizeof(sessiont)); + return 0; + } + + if (s > 1) + { + session[s-1].next = session[s].next; + } + + if (sessionfree == s) + { + sessionfree = session[s].next; + } + + memcpy(&session[s], buf, l); + session[s].tbf = 0; + session[s].throttle = 0; + if (session[s].opened) + { + log(2, 0, s, session[s].tunnel, "Cluster master sent active session for user %s\n", session[s].user); + sessionsetup(session[s].tunnel, s, 0); + if (session[s].ip && session[s].ip != 0xFFFFFFFE) + { + int x; + for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++) + { + if (ip_address_pool[x].address == session[s].ip) + { + ip_address_pool[x].assigned = 1; + break; + } + } + } + } + return 0; +} + +int handle_hello_response(char *buf, int l) +{ + int numtunnels, numsessions; + + /* The cluster master has downed the address, so send another garp */ + send_garp(vip_address); + + if (!l) return 0; + + if (l < (4 * IL)) + { + log(1, 0, 0, 0, "Cluster master sent invalid hello response: %d bytes instead of %d\n", l, (4 * IL)); + return 0; + } + numtunnels = *(int *)(buf + IL * 0); + numsessions = *(int *)(buf + IL * 1); + if (numtunnels == 0 && numsessions == 0) + { + log(2, 0, 0, 0, "Cluster master has no state information for us.\n"); + return 0; + } + log(2, 0, 0, 0, "The cluster master will send %d tunnels and %d sessions.\n", numtunnels, numsessions); + return 0; +} + +int cluster_send_session(int s) +{ + char *packet; + int len = 0; + + if (!cluster_sockfd) return 1; + + packet = malloc(4096); + + // Hostname + len = strlen(hostname); + *(char *)packet = len; + memcpy((char *)(packet + 1), hostname, len); + len++; + + // Session ID + *(int *)(packet + len) = s; + len += sizeof(int); + + // Session data + memcpy((char *)(packet + len), &session[s], sizeof(sessiont)); + len += sizeof(sessiont); + + cluster_send_message(cluster_address, vip_address, C_SESSION, packet, len); + free(packet); + + return 1; +} + +int cluster_send_tunnel(int t) +{ + char *packet; + int len = 0; + + packet = malloc(4096); + + // Hostname + len = strlen(hostname); + *(char *)packet = len; + memcpy((char *)(packet + 1), hostname, len); + len++; + + // Tunnel ID + *(int *)(packet + len) = t; + len += sizeof(int); + + // Tunnel data + memcpy((char *)(packet + len), &tunnel[t], sizeof(tunnelt)); + len += sizeof(tunnelt); + + cluster_send_message(cluster_address, vip_address, C_TUNNEL, packet, len); + free(packet); + + return 1; +} + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..16b6b08 --- /dev/null +++ b/config.h.in @@ -0,0 +1,213 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `gethostbyname' function. */ +#undef HAVE_GETHOSTBYNAME + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `inet_ntoa' function. */ +#undef HAVE_INET_NTOA + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the `pow' function. */ +#undef HAVE_POW + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `socket' function. */ +#undef HAVE_SOCKET + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strrchr' function. */ +#undef HAVE_STRRCHR + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the C compiler supports function prototypes. */ +#undef PROTOTYPES + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to the type of arg 1 for `select'. */ +#undef SELECT_TYPE_ARG1 + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#undef SELECT_TYPE_ARG234 + +/* Define to the type of arg 5 for `select'. */ +#undef SELECT_TYPE_ARG5 + +/* Define to 1 if the `setvbuf' function takes the buffering type as its + second argument and the buffer pointer as the third, as on System V before + release 3. */ +#undef SETVBUF_REVERSED + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define like PROTOTYPES; this can be used by system headers. */ +#undef __PROTOTYPES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork + +#undef HAVE_LIBCLI +#undef HAVE_LIBM +#undef HAVE_LIBDL + +#undef LIBDIR +#undef ETCDIR +#undef BINDIR + diff --git a/configure b/configure new file mode 100755 index 0000000..52c95f2 --- /dev/null +++ b/configure @@ -0,0 +1,7102 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.58 for l2tpns 1.0.0. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='l2tpns' +PACKAGE_TARNAME='l2tpns' +PACKAGE_VERSION='1.0.0' +PACKAGE_STRING='l2tpns 1.0.0' +PACKAGE_BUGREPORT='fred_nerk@sourceforge.net' + +ac_unique_file="ll.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_STAT_H +# include +#endif +#if STDC_HEADERS +# include +# include +#else +# if HAVE_STDLIB_H +# include +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif +#if HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures l2tpns 1.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of l2tpns 1.0.0:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd "$ac_popdir" + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +l2tpns configure 1.0.0 +generated by GNU Autoconf 2.58 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by l2tpns $as_me 1.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers config.h" + + +# Checks for programs. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + +# Checks for header files. + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 +echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 +if test "${ac_cv_header_sys_wait_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_sys_wait_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_sys_wait_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 +if test $ac_cv_header_sys_wait_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SYS_WAIT_H 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + + + +for ac_header in arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + + +# Checks for library functions. + + +for ac_header in unistd.h vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6 +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* By Ruediger Kuhlmann. */ + #include + #if HAVE_UNISTD_H + # include + #endif + /* Some systems only have a dummy stub for fork() */ + int main () + { + if (fork() < 0) + exit (1); + exit (0); + } +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6 + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +# include +#endif +#if HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if STDC_HEADERS || HAVE_STDLIB_H +# include +#else +char *malloc (); +#endif + +int +main () +{ +exit (malloc (0) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case $LIBOBJS in + "malloc.$ac_objext" | \ + *" malloc.$ac_objext" | \ + "malloc.$ac_objext "* | \ + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + +echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + exit (1); + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + exit (1); + } + exit (0); + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 +test $ac_cv_func_memcmp_working = no && case $LIBOBJS in + "memcmp.$ac_objext" | \ + *" memcmp.$ac_objext" | \ + "memcmp.$ac_objext "* | \ + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; +esac + + + + +for ac_header in stdlib.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in getpagesize +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +echo "$as_me:$LINENO: checking for working mmap" >&5 +echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_mmap_fixed_mapped=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !STDC_HEADERS && !HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#if !HAVE_GETPAGESIZE +/* Assume that all systems that can run configure have sys/param.h. */ +# if !HAVE_SYS_PARAM_H +# define HAVE_SYS_PARAM_H 1 +# endif + +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# if HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + int i, pagesize; + int fd; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + exit (1); + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + exit (1); + if (write (fd, data, pagesize) != pagesize) + exit (1); + close (fd); + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + exit (1); + data2 = (char *) malloc (2 * pagesize); + if (!data2) + exit (1); + data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + exit (1); + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + exit (1); + if (read (fd, data3, pagesize) != pagesize) + exit (1); + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + exit (1); + close (fd); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_mmap_fixed_mapped=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 +echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MMAP 1 +_ACEOF + +fi +rm -f conftest.mmap + + + +for ac_header in sys/select.h sys/socket.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to fred_nerk@sourceforge.net ## +## ---------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +echo "$as_me:$LINENO: checking types of arguments for select" >&5 +echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 +if test "${ac_cv_func_select_args+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + for ac_arg234 in 'fd_set *' 'int *' 'void *'; do + for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do + for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#if HAVE_SYS_SELECT_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif + +int +main () +{ +extern int select ($ac_arg1, + $ac_arg234, $ac_arg234, $ac_arg234, + $ac_arg5); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + done + done +done +# Provide a safe default value. +: ${ac_cv_func_select_args='int,int *,struct timeval *'} + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 +echo "${ECHO_T}$ac_cv_func_select_args" >&6 +ac_save_IFS=$IFS; IFS=',' +set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` +IFS=$ac_save_IFS +shift + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG1 $1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG234 ($2) +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define SELECT_TYPE_ARG5 ($3) +_ACEOF + +rm -f conftest* + +echo "$as_me:$LINENO: checking for function prototypes" >&5 +echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6 +if test "$ac_cv_prog_cc_stdc" != no; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define PROTOTYPES 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define __PROTOTYPES 1 +_ACEOF + +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +echo "$as_me:$LINENO: checking whether setvbuf arguments are reversed" >&5 +echo $ECHO_N "checking whether setvbuf arguments are reversed... $ECHO_C" >&6 +if test "${ac_cv_func_setvbuf_reversed+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_func_setvbuf_reversed=no + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +# if PROTOTYPES + int (setvbuf) (FILE *, int, char *, size_t); +# endif +int +main () +{ +char buf; return setvbuf (stdout, _IOLBF, &buf, 1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +# if PROTOTYPES + int (setvbuf) (FILE *, int, char *, size_t); +# endif +int +main () +{ +char buf; return setvbuf (stdout, &buf, _IOLBF, 1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It compiles and links either way, so it must not be declared + # with a prototype and most likely this is a K&R C compiler. + # Try running it. + if test "$cross_compiling" = yes; then + : # Assume setvbuf is not reversed when cross-compiling. +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +/* This call has the arguments reversed. + A reversed system may check and see that the address of buf + is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */ + char buf; + if (setvbuf (stdout, _IOLBF, &buf, 1) != 0) + exit (1); + putchar ('\r'); + exit (0); /* Non-reversed systems SEGV here. */ + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_setvbuf_reversed=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +rm -f core *.core +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + ac_cv_func_setvbuf_reversed=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_setvbuf_reversed" >&5 +echo "${ECHO_T}$ac_cv_func_setvbuf_reversed" >&6 +if test $ac_cv_func_setvbuf_reversed = yes; then + +cat >>confdefs.h <<\_ACEOF +#define SETVBUF_REVERSED 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#ifdef signal +# undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case $LIBOBJS in + "lstat.$ac_objext" | \ + *" lstat.$ac_objext" | \ + "lstat.$ac_objext "* | \ + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; +esac + +fi + +echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 +echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 +if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_stat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + exit (stat ("", &sbuf) ? 1 : 0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_stat_empty_string_bug=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_stat_empty_string_bug=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6 +if test $ac_cv_func_stat_empty_string_bug = yes; then + case $LIBOBJS in + "stat.$ac_objext" | \ + *" stat.$ac_objext" | \ + "stat.$ac_objext "* | \ + *" stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_STAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + + +for ac_func in strftime +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +else + # strftime is in -lintl on SCO UNIX. +echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 +echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 +if test "${ac_cv_lib_intl_strftime+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lintl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime (); +int +main () +{ +strftime (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_intl_strftime=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_intl_strftime=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 +echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 +if test $ac_cv_lib_intl_strftime = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_STRFTIME 1 +_ACEOF + +LIBS="-lintl $LIBS" +fi + +fi +done + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + + + + + + + + + + + + + + +for ac_func in alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Checks for libraries. + +echo "$as_me:$LINENO: checking for cli_init in -lcli" >&5 +echo $ECHO_N "checking for cli_init in -lcli... $ECHO_C" >&6 +if test "${ac_cv_lib_cli_cli_init+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcli $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char cli_init (); +int +main () +{ +cli_init (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_cli_cli_init=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_cli_cli_init=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_cli_cli_init" >&5 +echo "${ECHO_T}$ac_cv_lib_cli_cli_init" >&6 +if test $ac_cv_lib_cli_cli_init = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCLI 1 +_ACEOF + + LIBS="-lcli $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBDL 1 +_ACEOF + + LIBS="-ldl $LIBS" + +fi + + +echo "$as_me:$LINENO: checking for pow in -lm" >&5 +echo $ECHO_N "checking for pow in -lm... $ECHO_C" >&6 +if test "${ac_cv_lib_m_pow+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pow (); +int +main () +{ +pow (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_m_pow=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_m_pow=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5 +echo "${ECHO_T}$ac_cv_lib_m_pow" >&6 +if test $ac_cv_lib_m_pow = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + + +cat >>confdefs.h <<_ACEOF +#define LIBDIR "$prefix/lib/l2tpns" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define ETCDIR "$sysconfdir" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define BINDIR "$prefix/bin" +_ACEOF + + + ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by l2tpns $as_me 1.0.0, which was +generated by GNU Autoconf 2.58. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +l2tpns config.status 1.0.0 +configured by $0, generated by GNU Autoconf 2.58, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..f7b8b81 --- /dev/null +++ b/configure.in @@ -0,0 +1,48 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.58) +AC_INIT([l2tpns], [1.0.0], [fred_nerk@sourceforge.net]) +AC_CONFIG_SRCDIR([ll.c]) +AC_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL + +# Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +# Checks for library functions. +AC_FUNC_FORK +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_SETVBUF_REVERSED +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr]) + +# Checks for libraries. +AC_CHECK_LIB([cli], [cli_init]) +AC_CHECK_LIB([dl], [dlopen]) +AC_CHECK_LIB([m], [pow]) + +AC_DEFINE_UNQUOTED(LIBDIR, ["$prefix/lib/l2tpns"]) +AC_DEFINE_UNQUOTED(ETCDIR, ["$sysconfdir"]) +AC_DEFINE_UNQUOTED(BINDIR, ["$prefix/bin"]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/conform.cfg b/conform.cfg new file mode 100644 index 0000000..fabea05 --- /dev/null +++ b/conform.cfg @@ -0,0 +1,137 @@ +#!/usr/bin/perl -w +# vim:ft=perl + +die "l2tpns requires RedHat 7.3 or above" if i_isa("SOE_linux_rh6"); + +my $restart = 0; + +my %conf = (); +for my $c (i_isa_fetchall('L2tpns_config')) { + foreach my $opt (keys %$c) { + if (ref $conf{$opt} and ref $conf{$opt} eq 'ARRAY') { + $c->{$opt} = [ $c->{$opt} ] unless ref $c->{$opt}; + push @{$conf{$opt}}, @{$c->{$opt}}; + } elsif (ref $c->{$opt} and ref $c->{$opt} eq 'ARRAY') { + # Make sure to copy to avoid changing /etc/machine + $conf{$opt} = [ $conf{$opt} ] if $conf{$opt}; + $conf{$opt} ||= []; + push @{$conf{$opt}}, @{$c->{$opt}}; + } else { + $conf{$opt} = $c->{$opt}; + } + } +} + +$conf{Address_pool} ||= i_isa("Address_pool"); # backwards compat + +unless (i_isa("No_throttle")) { + chomp(my $kernel = `uname -r`); + print "WARN: l2tpns requires kernel 2.4.18-187OIE1. This is included in $_path/rpm/kernel-2.4.18-187OIE1.i386.rpm\n" + unless ($kernel eq '2.4.18-187OIE1' || $kernel =~ /^2\.4\.2\d/); +} + +# Recompile the server if needed +if ((stat("src/l2tpns.c"))[9] > (stat("src/l2tpns"))[9]) { + chdir("src"); + command("make clean"); + command("make"); + chdir(".."); + $restart++; +} + +command("mkdir /dev/net") and ++$restart unless -d "/dev/net"; +command("mknod /dev/net/tun c 10 200") and ++$restart unless -c "/dev/net/tun"; + +my $cluster = i_isa('Gateway_cluster'); +my $cluster_name = $cluster->{master} || $cluster->{slave} || die 'Not a master or a slave' if $cluster; +my $master = $cluster && $cluster->{master}; +my $command = $master ? "cluster_master" : "l2tpns"; +push @{$m{$_class}->{Monitor}->{process}->{tests}}, $command; + + +if ($cluster) { + $conf{'save state'} ||= 'no'; + if (!$master && !$cluster->{bind_address}) { + die 'No bind address for cluster slave'; + } + $conf{'bind address'} ||= $cluster->{bind_address} unless $master; + my $cluster_master; + my @cluster_slaves = (); + my @cluster_slave_addresses = (); + foreach my $host (type_list('Gateway_cluster')) { + my $host_conf = OIE::Conform::i_isa(\%m, $host, 'Gateway_cluster'); + if ($host_conf->{master} eq $cluster_name) { + $cluster_master = $host; + } elsif ($host_conf->{slave} eq $cluster_name) { + push @cluster_slaves, $host; + push @{$conf{Address_pool}}, map { "$host_conf->{bind_address}:$_" } @{$m{$host}->{L2tpns_config}->{Address_pool}} if $master; + push @cluster_slave_addresses, $m{$host}->{int_eth0}->{ip}; + } + } + + if ($master) { + push @{$m{$_class}->{inittab_include}}, + "$_path/src/cluster_master $m{$iam}->{int_eth0}->{ip}"; + push @{$m{$_class}->{inittab_disable}}, + "$_path/src/l2tpns"; + $m{$_class}->{Firewall}->{$_} = '32792:udp' + foreach @cluster_slave_addresses; + } + $conf{'cluster master'} ||= $m{$cluster_master}->{int_eth0}->{ip}; +} + +# Build up address pool +my $pool = $conf{Address_pool}; +if ($pool) { + my $address_pool = ""; + + foreach (@$pool) { + $address_pool .= "$_\n"; + } + + text_install("$_path/etc/ip_pool.txt", $address_pool) and $restart++; +} else { + print "WARN: No Address_pool defined in machines.\n"; +} +delete $conf{"Address_pool"}; # Don't add it to the conf file + +my $servicenet = $conf{"servicenet"}; +if ($servicenet) { + $conf{'servicenet'} = 'yes'; + push @{$conf{plugin}}, 'servicenet' unless grep /^servicenet$/, @{$conf{plugin}}; + file_install("/etc/rc.d/rc.firewall.INPUT.servicenet", "$_path/etc/rc.firewall.INPUT.servicenet", undef, undef, + "s/#SERVICENET#/$servicenet/g") + and queue_command("/etc/rc.d/rc.firewall"); +} else { + $conf{'servicenet'} = 'no'; + # Uninstall + if (-f "/etc/rc.d/rc.firewall.INPUT.servicenet") { + unlink "/etc/rc.d/rc.firewall.INPUT.servicenet"; + command("iptables -F snet"); + } +} + +# Note that we don't file_install the config file, but instead modify it +# in place + +my $config = slurp_file("$_path/etc/l2tpns.cfg"); + +# plugins need to go first, else they won't pick up params +foreach my $p (@{$conf{plugin}}) { + $config =~ s/^#?\s*plugin\s+=\s+\Q$p\E$/plugin = $p/mg or + $config = "plugin = $p\n\n$config"; +} +delete $conf{plugin}; + +foreach my $c (keys %conf) { + $config =~ s/^#?\s*\Q$c\E\s+=\s+.*$/$c = $conf{$c}/mg or + $config .= "$c = $conf{$c}\n\n"; +} + +file_install("/etc/rc.d/rc.firewall.INPUT.l2tpns", "$_path/etc/rc.firewall.INPUT.l2tpns") + and queue_command("/etc/rc.d/rc.firewall"); + +text_install("$_path/etc/l2tpns.cfg", $config) and $restart++; + +queue_command("killall $command") if $restart; + diff --git a/constants.c b/constants.c new file mode 100644 index 0000000..46258b1 --- /dev/null +++ b/constants.c @@ -0,0 +1,136 @@ +#include "constants.h" +#include + +const char *lcp_types[MAX_LCP_TYPE+1] = { + "Reserved", + "Maximum-Receive-Unit", + "Reserved 2", + "Authentication-Protocol", + "Quality-Protocol", + "Magic-Number", + "Reserved 6", + "Protocol-Field-Compression", + "Address-and-Control-Field-Compression", +}; + +const char *avpnames[MAX_AVPNAME+1] = { + "Message Type", // 0 + "Result Code", // 1 + "Protocol Version", // 2 + "Framing Capabilities", // 3 + "Bearer Capabilities", // 4 + "Tie Breaker", // 5 + "Firmware Revision", // 6 + "Host Name", // 7 + "Vendor Name", // 8 + "Assigned Tunnel ID", // 9 + "Receive Window Size", // 10 + "Challenge", // 11 + "Q.931 Cause Code", // 12 + "Challenge Response", // 13 + "Assigned Session ID", // 14 + "Call Serial Number", // 15 + "Minimum BPS", // 16 + "Maximum BPS", // 17 + "Bearer Type", // 18 (2 = Analog, 1 = Digital) + "Framing Type", // 19 (2 = Async, 1 = Sync) + "Reserved 20", // 20 + "Called Number", // 21 + "Calling Number", // 22 + "Sub Address", // 23 + "Tx Connect Speed", // 24 + "Physical Channel ID", // 25 + "Initial Received LCP CONFREQ", // 26 + "Last Sent LCP CONFREQ", // 27 + "Last Received LCP CONFREQ", // 28 + "Proxy Authen Type", // 29 + "Proxy Authen Name", // 30 + "Proxy Authen Challenge", // 31 + "Proxy Authen ID", // 32 + "Proxy Authen Response", // 33 + "Call Errors", // 34 + "ACCM", // 35 + "Random Vector", // 36 + "Private Group ID", // 37 + "Rx Connect Speed", // 38 + "Sequencing Required", // 39 +}; + +const char *stopccn_result_codes[MAX_STOPCCN_RESULT_CODE+1] = { + "Reserved", + "General request to clear control connection", + "General error--Error Code indicates the problem", + "Control channel already exists", + "Requester is not authorized to establish a control channel", + "The protocol version of the requester is not supported", + "Requester is being shut down", + "Finite State Machine error", +}; + +const char *cdn_result_codes[MAX_CDN_RESULT_CODE+1] = { + "Reserved", + "Call disconnected due to loss of carrier", + "Call disconnected for the reason indicated in error code", + "Call disconnected for administrative reasons", + "Call failed due to lack of appropriate facilities being available (temporary condition)", + "Call failed due to lack of appropriate facilities being available (permanent condition)", + "Invalid destination", + "Call failed due to no carrier detected", + "Call failed due to detection of a busy signal", + "Call failed due to lack of a dial tone", + "Call was not established within time allotted by LAC", + "Call was connected but no appropriate framing was detected", +}; + +const char *error_codes[MAX_ERROR_CODE+1] = { + "No general error", + "No control connection exists yet for this LAC-LNS pair", + "Length is wrong", + "One of the field values was out of range or reserved field was non-zero", + "Insufficient resources to handle this operation now", + "The Session ID is invalid in this context", + "A generic vendor-specific error occurred in the LAC", + "Try another LNS", + "Session or tunnel was shutdown due to receipt of an unknown AVP with the M-bit set", +}; + +const char *authtypes[MAX_AUTHTYPE+1] = { + "Reserved", + "Textual username/password exchange", + "PPP CHAP", + "PPP PAP", + "No Authentication", + "Microsoft CHAP Version 1 (MSCHAPv1)", +}; + +const char *radius_states[MAX_RADIUS_STATE+1] = { + "RADIUSNULL", + "RADIUSCHAP", + "RADIUSAUTH", + "RADIUSIPCP", + "RADIUSSTART", + "RADIUSSTOP", + "RADIUSWAIT", + NULL +}; + +const char *l2tp_message_types[MAX_L2TP_MESSAGE_TYPE+1] = { + "reserved", + "SCCRQ", + "SCCRP", + "SCCCN", + "StopCCN", // 4 + "reserved", + "HELLO", + "OCRQ", + "OCRP", + "OCCN", + "ICRQ", // 10 + "ICRP", + "ICCN", + "reserved", + "CDN", + "WEN", // 15 + "SLI", +}; + diff --git a/constants.h b/constants.h new file mode 100644 index 0000000..3ac8983 --- /dev/null +++ b/constants.h @@ -0,0 +1,27 @@ + +// enum these ? + +#define MAX_LCP_TYPE 8 +extern const char *lcp_types[MAX_LCP_TYPE+1]; + +#define MAX_AVPNAME 40 +extern const char *avpnames[MAX_AVPNAME+1]; + +#define MAX_STOPCCN_RESULT_CODE 7 +extern const char *stopccn_result_codes[MAX_STOPCCN_RESULT_CODE+1]; + +#define MAX_CDN_RESULT_CODE 11 +extern const char *cdn_result_codes[MAX_CDN_RESULT_CODE+1]; + +#define MAX_ERROR_CODE 8 +extern const char *error_codes[MAX_ERROR_CODE+1]; + +#define MAX_AUTHTYPE 5 +extern const char *authtypes[MAX_AUTHTYPE+1]; + +// Can remove the NULL end now +#define MAX_RADIUS_STATE 8 +extern const char *radius_states[MAX_RADIUS_STATE+1]; + +#define MAX_L2TP_MESSAGE_TYPE 16 +extern const char *l2tp_message_types[MAX_L2TP_MESSAGE_TYPE+1]; diff --git a/control.c b/control.c new file mode 100644 index 0000000..9d83522 --- /dev/null +++ b/control.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "control.h" + +int new_packet(short type, char *packet) +{ + int id = (time(NULL) ^ (rand() * 1024*1024)); + + *(short *)(packet + 0) = ntohs(0x9012); + *(short *)(packet + 2) = ntohs(type); + *(int *)(packet + 6) = ntohl(id); + + return 10; +} + +int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len) +{ + struct sockaddr_in addr; + + *(short *)(packet + 4) = ntohs(len); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + *(int*)&addr.sin_addr = htonl(dest_ip); + addr.sin_port = htons(dest_port); + if (sendto(sockfd, packet, len, 0, (void *) &addr, sizeof(addr)) < 0) + { + perror("sendto"); + return 0; + } + return 1; +} + +int read_packet(int sockfd, char *packet) +{ + struct sockaddr_in addr; + int alen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + return recvfrom(sockfd, packet, 1400, 0, (void *) &addr, &alen); +} + +void dump_packet(char *packet, FILE *stream) +{ + if (htons(*(short *)(packet + 0)) != 0x9012) + { + fprintf(stream, "Invalid packet identifier %x\n", htons(*(short *)(packet + 0))); + return; + } + fprintf(stream, "Control packet:\n"); + fprintf(stream, " Type: %d\n", htons(*(short *)(packet + 2))); + fprintf(stream, " Length: %d\n", htons(*(short *)(packet + 4))); + fprintf(stream, " Identifier: %x\n", htonl(*(int *)(packet + 6))); + fprintf(stream, "\n"); +} + + diff --git a/control.h b/control.h new file mode 100644 index 0000000..041d3ce --- /dev/null +++ b/control.h @@ -0,0 +1,18 @@ +#ifndef __CONTROL_H__ +#define __CONTROL_H__ + +#define PKT_RESP_OK 1 +#define PKT_RESP_ERROR 2 + +#define PKT_LOAD_PLUGIN 5 +#define PKT_UNLOAD_PLUGIN 6 + +#define PKT_GARDEN 1000 +#define PKT_UNGARDEN 1001 + +int new_packet(short type, char *packet); +int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len); +void dump_packet(char *packet, FILE *stream); +int read_packet(int sockfd, char *packet); + +#endif diff --git a/etc/.cvsignore b/etc/.cvsignore new file mode 100644 index 0000000..050550f --- /dev/null +++ b/etc/.cvsignore @@ -0,0 +1,2 @@ +ip_pool.txt +l2tpns.cfg diff --git a/etc/ip_pool.default b/etc/ip_pool.default new file mode 100644 index 0000000..9ae05e9 --- /dev/null +++ b/etc/ip_pool.default @@ -0,0 +1,2 @@ +10.10.10.0/24 +10.13.10.0/24 diff --git a/etc/l2tpns.cfg.default b/etc/l2tpns.cfg.default new file mode 100644 index 0000000..2620ce0 --- /dev/null +++ b/etc/l2tpns.cfg.default @@ -0,0 +1,43 @@ +# Comment out to log to stderr +log file = /var/log/l2tpns + +# This must be defined, or it won't work +# It's only used if the LAC requests tunnel authentication +l2tp secret = foobar + +# You can have multiple radius server entries, but ony one radius secret +radius server = radius.yourdomain.com.au +radius secret = barfoo + +# Turn on or off Radius Accounting (START and STOP records) +radius accounting = 0 + +# Only 2 dns server entries are allowed +dns server = 192.168.1.1 +dns server = 192.168.1.2 + +# Set this to 0 to disable throttling +throttle rate = 0 + +# This can be from 1 to 5 +# At 5, all packets are logged and your system will run +# very slowly +# 2 will show errors only +debug = 2 + +# Save / load state on restart +save state = no + +# Cluster Management +#cluster master = 192.168.1.15 + +# Where accounting information will be dumped. Comment out to disable. +accounting dir = /var/run/l2tpns/acct/ + +# You need to set this to the IP address of the tun interface +# if you want to use clustering +#bind address = 127.0.0.1 + +# Uncomment this if you wish to use the walled garden plugin +#plugin = garden + diff --git a/etc/l2tpns.logrotate b/etc/l2tpns.logrotate new file mode 100644 index 0000000..c6cf5e2 --- /dev/null +++ b/etc/l2tpns.logrotate @@ -0,0 +1,8 @@ +/var/log/l2tpns { + daily + missingok + rotate 14 + postrotate + /usr/bin/killall -HUP l2tpns + endscript +} diff --git a/etc/users.default b/etc/users.default new file mode 100644 index 0000000..dd67351 --- /dev/null +++ b/etc/users.default @@ -0,0 +1 @@ +# List username:password combinations here for cli users diff --git a/garden.c b/garden.c new file mode 100644 index 0000000..064bea2 --- /dev/null +++ b/garden.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "plugin.h" +#include "control.h" + +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 -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", + NULL +}; + +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"); + data->auth_allowed = 1; + data->s->walled_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); + return PLUGIN_RET_OK; +} + +int plugin_kill_session(struct param_new_session *data) +{ + if (data->s->walled_garden) garden_session(data->s, 0); + return PLUGIN_RET_OK; +} + +int plugin_control(struct param_control *data) +{ + sessiont *s; + sessionidt session; + + 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; + + data->send_response = 1; + s = p.get_session_by_id(session); + if (!s || !s->ip) + { + char *errormsg = "Session not connected"; + *(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); + sprintf((data->response + data->response_length), "%s", errormsg); + data->response_length += strlen(errormsg) + 1; + + p.log(3, 0, 0, 0, "Unknown session %s\n", session); + return PLUGIN_RET_STOP; + } + *(short *)(data->response + 2) = ntohs(PKT_RESP_OK); + + if (!(garden_session(s, (data->type == PKT_GARDEN)))) + { + char *errormsg = "User not connected"; + *(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); + sprintf((data->response + data->response_length), "%s", errormsg); + data->response_length += strlen(errormsg) + 1; + } + 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]; + + if (!s) return 0; + if (!s->opened) return 0; + + if (flag == 1) + { + 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; + } + else + { + sessionidt other; + int count = 10; + + // 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"); + } + /* Clean up counters */ + s->cin = s->cout = 0; + s->pin = s->pout = 0; + + snprintf(cmd, 2048, "iptables -t nat -D garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip))); + p.log(3, 0, 0, s->tunnel, "%s\n", cmd); + while (--count) + { + int status = system(cmd); + if (WEXITSTATUS(status) != 0) break; + } + + s->walled_garden = 0; + + if (!s->die) { + /* OK, we're up! */ + u8 r = p.radiusnew(p.get_id_by_session(s)); + p.radiussend(r, RADIUSSTART); + } + } + s->walled_garden = flag; + return 1; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + int i; + + 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]); + system(init_commands[i]); + } + + return 1; +} + +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]); + system(done_commands[i]); + } +} + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..ab74c88 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/l2tpns.c b/l2tpns.c new file mode 100644 index 0000000..40f4eed --- /dev/null +++ b/l2tpns.c @@ -0,0 +1,2988 @@ +// L2TP Network Server +// Adrian Kennard 2002 +// (c) Copyrigth 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) +// vim: sw=8 ts=8 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBCLI +#include +#endif +#include "md5.h" +#include "l2tpns.h" +#include "cluster.h" +#include "plugin.h" +#include "ll.h" +#include "constants.h" +#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; +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]; + +char *plugin_functions[] = { + NULL, + "plugin_pre_auth", + "plugin_post_auth", + "plugin_packet_rx", + "plugin_packet_tx", + "plugin_timer", + "plugin_config", + "plugin_new_session", + "plugin_kill_session", + "plugin_control", + "plugin_radius_response", +}; +#define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) + +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 +struct Tringbuffer *ringbuffer = NULL; +#endif +tbft *filter_buckets = NULL; + +void sigalrm_handler(int); +void sighup_handler(int); +void sigterm_handler(int); +void sigquit_handler(int); +void sigchild_handler(int); +void sigsegv_handler(int); +void read_config_file(); +void read_state(); +void dump_state(); + +// return internal time (10ths since run) +clockt now(void) +{ + struct timeval t; + gettimeofday(&t, 0); + return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1; +} + +// work out a retry time based on try number +clockt backoff(u8 try) +{ + if (try > 5) try = 5; // max backoff + return now() + 10 * (1 << try); +} + +void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) +{ + va_list ap; + +#ifdef RINGBUFFER + if (ringbuffer) + { + if (++ringbuffer->tail >= RINGBUFFER_SIZE) + ringbuffer->tail = 0; + if (ringbuffer->tail == ringbuffer->head) + if (++ringbuffer->head >= RINGBUFFER_SIZE) + ringbuffer->head = 0; + + ringbuffer->buffer[ringbuffer->tail].level = level; + ringbuffer->buffer[ringbuffer->tail].address = address; + ringbuffer->buffer[ringbuffer->tail].session = s; + ringbuffer->buffer[ringbuffer->tail].tunnel = t; + va_start(ap, format); + vsnprintf(ringbuffer->buffer[ringbuffer->tail].message, 4095, format, ap); + va_end(ap); + } +#endif + + if (debug < level) return; + + if (!log_stream && log_filename) + { + if ((log_stream = fopen(log_filename, "a"))) + fseek(log_stream, 0, SEEK_END); + setbuf(log_stream, NULL); + } + if (!log_stream) + { + log_stream = stderr; + setbuf(log_stream, NULL); + } + + 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; + 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; ) + { + 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); + } + + for (; j < i + 16; j++) + { + 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); + } + fflush(log_stream); + setbuf(log_stream, NULL); +} + + +// Add a route +void routeset(ipt ip, ipt mask, ipt gw, u8 add) +{ + struct rtentry r; + memset(&r, 0, sizeof(r)); + r.rt_dev = 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); + r.rt_flags = (RTF_UP | RTF_STATIC); + if (gw) + r.rt_flags |= RTF_GATEWAY; + else + r.rt_flags |= RTF_HOST; + if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) perror("routeset"); + log(1, ip, 0, 0, "Route %s %u.%u.%u.%u/%u.%u.%u.%u %u.%u.%u.%u\n", add ? "Add" : "Del", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, mask >> 24, mask >> 16 & 255, mask >> 8 & 255, mask & 255, gw >> 24, gw >> 16 & 255, gw >> 8 & 255, gw & 255); +} + +// Set up TAP interface +void inittap(void) +{ + struct ifreq ifr; + struct sockaddr_in sin = {0}; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN; + + tapfd = open(TAPDEVICE, O_RDWR); + if (tapfd < 0) + { // fatal + log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); + exit(-1); + } + if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) + { + 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); + 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 + memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); + + if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) + { + perror("set tap addr"); + exit( -1); + } + ifr.ifr_flags = IFF_UP; + if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) + { + perror("set tap flags"); + exit( -1); + } + if (ioctl(ifrfd, SIOCGIFHWADDR, (void *) &ifr) < 0) + { + perror("get tap hwaddr"); + exit( -1); + } + memcpy(&tapmac, 2 + (u8 *) & ifr.ifr_hwaddr, 6); + if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) + { + perror("get tap ifindex"); + exit( -1); + } + tapidx = ifr.ifr_ifindex; +} + +// set up UDP port +void initudp(void) +{ + int on = 1; + struct sockaddr_in addr; + + // Tunnel + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(L2TPPORT); + addr.sin_addr.s_addr = 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"); + exit( -1); + } + snoopfd = socket(AF_INET, SOCK_DGRAM, UDP); + + // Control + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(1702); + controlfd = socket(AF_INET, SOCK_DGRAM, 17); + setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + exit(-1); + } +} + +// Find session by IP, 0 for not found +sessionidt sessionbyip(ipt ip) +{ + unsigned char *a = (char *)&ip; + char **d = (char **) ip_hash; + +#ifdef STAT_CALLS + STAT(call_sessionbyip); +#endif + + if (!(d = (char **) d[(size_t) *a++])) return 0; + if (!(d = (char **) d[(size_t) *a++])) return 0; + if (!(d = (char **) d[(size_t) *a++])) return 0; + + return (ipt) d[(size_t) *a]; +} + +void cache_sessionid(ipt ip, sessionidt s) +{ + unsigned char *a = (char *) &ip; + char **d = (char **) ip_hash; + int i; + + for (i = 0; i < 3; i++) + { + if (!d[(size_t) a[i]]) + { + if (!(d[(size_t) a[i]] = calloc(256, sizeof (void *)))) + return; + } + + d = (char **) d[(size_t) a[i]]; + } + + log(4, ip, s, session[s].tunnel, "Caching session ID %d for ip address\n", s); + d[(size_t) a[3]] = (char *)((int)s); +} + +void uncache_sessionid(ipt ip) +{ + unsigned char *a = (char *) &ip; + char **d = (char **) ip_hash; + int i; + + for (i = 0; i < 3; i++) + { + if (!d[(size_t) a[i]]) return; + d = (char **) d[(size_t) a[i]]; + } + d[(size_t) a[3]] = NULL; +} + +// Find session by username, 0 for not found +// walled garden'd users aren't authenticated, so the username is +// reasonably useless. Ignore them to avoid incorrect actions +sessionidt sessionbyuser(char *username) +{ + int s; +#ifdef STAT_CALLS + STAT(call_sessionbyuser); +#endif + for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++); + if (s < MAXSESSION) + return s; + return 0; +} + +void send_garp(ipt ip) +{ + int s; + struct ifreq ifr; + unsigned char mac[6]; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + perror("socket"); + exit(-1); + } + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, "eth0"); + if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) + { + perror("get eth0 hwaddr"); + exit(-1); + } + memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) + { + perror("get eth0 ifindex"); + exit(-1); + } + close(s); + sendarp(ifr.ifr_ifindex, mac, ip); +} + +// Find session by username, 0 for not found +sessiont *sessiontbysessionidt(sessionidt s) +{ + if (!s || s > MAXSESSION) return NULL; + return &session[s]; +} + +sessionidt sessionidtbysessiont(sessiont *s) +{ + sessionidt val = s-session; + if (s < session || val > MAXSESSION) return 0; + return val; +} + +// send gratuitous ARP to set ARP table for newly allocated IP +void sessionsendarp(sessionidt s) +{ + unsigned char mac[6]; +#ifdef STAT_CALLS + STAT(call_sendarp); +#endif + *(u16 *) (mac + 0) = htons(tapmac[0]); // set source address + *(u16 *) (mac + 2) = htons(tapmac[1]); + *(u16 *) (mac + 4) = htons(tapmac[2]); + sendarp(tapidx, mac, session[s].ip); + STAT(arp_sent); +} + +// Handle ARP requests +void processarp(u8 * buf, int len) +{ + ipt ip; + sessionidt s; + +#ifdef STAT_CALLS + STAT(call_processarp); +#endif + STAT(arp_recv); + if (len != 46) + { + log(0, 0, 0, 0, "Unexpected length ARP %d bytes\n", len); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 16) != htons(PKTARP)) + { + log(0, 0, 0, 0, "Unexpected ARP type %04X\n", ntohs(*(u16 *) (buf + 16))); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 18) != htons(0x0001)) + { + log(0, 0, 0, 0, "Unexpected ARP hard type %04X\n", ntohs(*(u16 *) (buf + 18))); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 20) != htons(PKTIP)) + { + log(0, 0, 0, 0, "Unexpected ARP prot type %04X\n", ntohs(*(u16 *) (buf + 20))); + STAT(arp_errors); + return; + } + if (buf[22] != 6) + { + log(0, 0, 0, 0, "Unexpected ARP hard len %d\n", buf[22]); + STAT(arp_errors); + return; + } + if (buf[23] != 4) + { + log(0, 0, 0, 0, "Unexpected ARP prot len %d\n", buf[23]); + STAT(arp_errors); + return; + } + if (*(u16 *) (buf + 24) != htons(0x0001)) + { + log(0, 0, 0, 0, "Unexpected ARP op %04X\n", ntohs(*(u16 *) (buf + 24))); + STAT(arp_errors); + return; + } + ip = ntohl(*(u32 *) (buf + 42)); + // look up session + s = sessionbyip(htonl(ip)); + if (s) + { + log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, session[s].tunnel, s); + memcpy(buf + 4, buf + 10, 6); // set destination as source + *(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address + *(u16 *) (buf + 12) = htons(tapmac[1]); + *(u16 *) (buf + 14) = htons(tapmac[2]); + *(u16 *) (buf + 24) = htons(0x0002); // ARP reply + memcpy(buf + 26, buf + 10, 6); // sender ethernet + memcpy(buf + 36, buf + 4, 6); // target ethernet + *(u32 *) (buf + 42) = *(u32 *) (buf + 32); // target IP + *(u32 *) (buf + 32) = htonl(ip); // sender IP + write(tapfd, buf, len); + STAT(arp_replies); + } + else + { + log(3, ip, 0, 0, "ARP request for unknown IP %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255); + STAT(arp_discarded); + } +} + +// actually send a control message for a specific tunnel +void tunnelsend(u8 * buf, u16 l, tunnelidt t) +{ + struct sockaddr_in addr; + +#ifdef STAT_CALLS + STAT(call_tunnelsend); +#endif + if (!tunnel[t].ip) + { + log(1, 0, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); + STAT(tunnel_tx_errors); + return; + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + *(u32 *) & addr.sin_addr = htonl(tunnel[t].ip); + addr.sin_port = htons(tunnel[t].port); + + // sequence expected, if sequence in message + if (*buf & 0x08) *(u16 *) (buf + ((*buf & 0x40) ? 10 : 8)) = htons(tunnel[t].nr); + + // If this is a control message, deal with retries + if (*buf & 0x80) + { + tunnel[t].last = time_now; // control message sent + tunnel[t].retry = backoff(tunnel[t].try); // when to resend + if (tunnel[t].try > 1) + { + STAT(tunnel_retries); + log(3, tunnel[t].ip, 0, t, "Control message resend try %d\n", tunnel[t].try); + } + } + + if (sendto(udpfd, buf, l, 0, (void *) &addr, sizeof(addr)) < 0) + { + log(0, tunnel[t].ip, ntohs((*(u16 *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n", + strerror(errno), udpfd, buf, l, inet_ntoa(addr.sin_addr)); + STAT(tunnel_tx_errors); + return; + } + + log_hex(5, "Send Tunnel Data", buf, l); + STAT(tunnel_tx_packets); + INC_STAT(tunnel_tx_bytes, l); +} + +// process outgoing (to tunnel) IP +void processipout(u8 * buf, int len) +{ + sessionidt s; + sessiont *sp; + tunnelidt t; + ipt ip; + u8 b[MAXETHER]; +#ifdef STAT_CALLS + STAT(call_processipout); +#endif + if (len < 38) + { + log(1, 0, 0, 0, "Short IP, %d bytes\n", len); + STAT(tunnel_tx_errors); + return; + } + + // Skip the tun header + buf += 4; + len -= 4; + + // Got an IP header now + if (*(u8 *)(buf) >> 4 != 4) + { + log(1, 0, 0, 0, "IP: Don't understand anything except IPv4\n"); + return; + } + + 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)); + return; + } + t = session[s].tunnel; + sp = &session[s]; + + // Snooping this session, send it to ASIO + if (sp->snoop) snoop_send_packet(buf, len); + + log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + + // Plugin hook + { + struct param_packet_rx packet = { &tunnel[t], &session[s], buf, len }; + run_plugins(PLUGIN_PACKET_RX, &packet); + } + + // Add on L2TP header + { + u8 *p = makeppp(b, buf, len, t, s, PPPIP); + tunnelsend(b, len + (p-b), t); // send it... + sp->cout += len; // byte count + sp->pout++; + udp_tx += len; + } +} + +// add an AVP (16 bit) +void control16(controlt * c, u16 avp, u16 val, u8 m) +{ + u16 l = (m ? 0x8008 : 0x0008); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + *(u16 *) (c->buf + c->length + 6) = htons(val); + c->length += 8; +} + +// add an AVP (32 bit) +void control32(controlt * c, u16 avp, u32 val, u8 m) +{ + u16 l = (m ? 0x800A : 0x000A); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + *(u32 *) (c->buf + c->length + 6) = htonl(val); + c->length += 10; +} + +// add an AVP (32 bit) +void controls(controlt * c, u16 avp, char *val, u8 m) +{ + u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + memcpy(c->buf + c->length + 6, val, strlen(val)); + c->length += 6 + strlen(val); +} + +// add a binary AVP +void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m) +{ + u16 l = ((m ? 0x8000 : 0) + len + 6); + *(u16 *) (c->buf + c->length + 0) = htons(l); + *(u16 *) (c->buf + c->length + 2) = htons(0); + *(u16 *) (c->buf + c->length + 4) = htons(avp); + memcpy(c->buf + c->length + 6, val, len); + c->length += 6 + len; +} + +// new control connection +controlt *controlnew(u16 mtype) +{ + controlt *c; + if (!controlfree) + c = malloc(sizeof(controlt)); + else + { + c = controlfree; + controlfree = c->next; + } + assert(c); + c->next = 0; + *(u16 *) (c->buf + 0) = htons(0xC802); // flags/ver + c->length = 12; + control16(c, 0, mtype, 1); + return c; +} + +// send zero block if nothing is waiting +void controlnull(tunnelidt t) +{ + u8 buf[12]; + if (tunnel[t].controlc) + return; + *(u16 *) (buf + 0) = htons(0xC802); // flags/ver + *(u16 *) (buf + 2) = htons(12); // length + *(u16 *) (buf + 4) = htons(tunnel[t].far); // tunnel + *(u16 *) (buf + 6) = htons(0); // session + *(u16 *) (buf + 8) = htons(tunnel[t].ns); // sequence + *(u16 *) (buf + 10) = htons(tunnel[t].nr); // sequence + tunnelsend(buf, 12, t); +} + +// add a control message to a tunnel, and send if within window +void controladd(controlt * c, tunnelidt t, sessionidt s) +{ + *(u16 *) (c->buf + 2) = htons(c->length); // length + *(u16 *) (c->buf + 4) = htons(tunnel[t].far); // tunnel + *(u16 *) (c->buf + 6) = htons(s ? session[s].far : 0); // session + *(u16 *) (c->buf + 8) = htons(tunnel[t].ns); // sequence + tunnel[t].ns++; // advance sequence + // link in message in to queue + if (tunnel[t].controlc) + tunnel[t].controle->next = c; + else + tunnel[t].controls = c; + tunnel[t].controle = c; + tunnel[t].controlc++; + // send now if space in window + if (tunnel[t].controlc <= tunnel[t].window) + { + tunnel[t].try = 0; // first send + tunnelsend(c->buf, c->length, t); + } +} + +// start tidy shutdown of session +void sessionshutdown(sessionidt s, char *reason) +{ + int dead = session[s].die; + int walled_garden = session[s].walled_garden; + +#ifdef STAT_CALLS + STAT(call_sessionshutdown); +#endif + if (!session[s].tunnel) + return; // not a live session + + if (!session[s].die) + log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); + + session[s].die = now() + 150; // Clean up in 15 seconds + + { + struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] }; + run_plugins(PLUGIN_KILL_SESSION, &data); + } + + // RADIUS Stop message + if (session[s].opened && !walled_garden && !dead) { + u8 r = session[s].radius; + if (!r) + { + if (!radiusfree) + { + log(1, 0, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); + STAT(radius_overflow); + } else { + int n; + r = radiusnew(s); + for (n = 0; n < 15; n++) + radius[r].auth[n] = rand(); + } + } + if (r && radius[r].state != RADIUSSTOP) + radiussend(r, RADIUSSTOP); // stop, if not already trying + } + + if (session[s].ip) + { // IP allocated, clear and unroute + u8 r; + if (session[s].route[0].ip) + { + routeset(session[s].ip, 0, 0, 0); + for (r = 0; r < MAXROUTE; r++) + { + if (session[s].route[r].ip) + { + routeset(session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 0); + session[s].route[r].ip = 0; + } + } + } + if (session[s].throttle) throttle_session(s, 0); session[s].throttle = 0; + free_ip_address(session[s].ip); + session[s].ip = 0; + } + { // Send CDN + controlt *c = controlnew(14); // sending CDN + control16(c, 1, 3, 1); // result code (admin reasons - TBA make error, general error, add message + control16(c, 14, s, 1); // assigned session (our end) + controladd(c, session[s].tunnel, s); // send the message + } + cluster_send_session(s); +} + +void sendipcp(tunnelidt t, sessionidt s) +{ + u8 buf[MAXCONTROL]; + u8 r = session[s].radius; + u8 *q; +#ifdef STAT_CALLS + STAT(call_sendipcp); +#endif + if (!r) + r = radiusnew(s); + if (radius[r].state != RADIUSIPCP) + { + radius[r].state = RADIUSIPCP; + radius[r].try = 0; + } + radius[r].retry = backoff(radius[r].try++); + if (radius[r].try > 10) + { + sessionshutdown(s, "No reply on IPCP"); + return; + } + q = makeppp(buf, 0, 0, t, s, PPPIPCP); + *q = ConfigReq; + q[1] = r; // ID, dont care, we only send one type of request + *(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) + tunnelsend(buf, 10 + (q - buf), t); // send it +} + +// kill a session now +void sessionkill(sessionidt s, char *reason) +{ +#ifdef STAT_CALLS + STAT(call_sessionkill); +#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 + memset(&session[s], 0, sizeof(session[s])); + session[s].next = sessionfree; + sessionfree = s; + log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason); + cluster_send_session(s); +} + +// kill a tunnel now +void tunnelkill(tunnelidt t, char *reason) +{ + sessionidt s; + controlt *c; +#ifdef STAT_CALLS + STAT(call_tunnelkill); +#endif + // free control messages + while ((c = tunnel[t].controls)) + { + controlt * n = c->next; + tunnel[t].controls = n; + tunnel[t].controlc--; + c->next = controlfree; + controlfree = c; + } + // kill sessions + for (s = 0; s < MAXSESSION; s++) + if (session[s].tunnel == t) + sessionkill(s, reason); + // free tunnel + memset(&tunnel[t], 0, sizeof(tunnel[t])); + tunnel[t].next = tunnelfree; + cluster_send_tunnel(t); + log(1, 0, 0, t, "Kill tunnel %d: %s\n", t, reason); + tunnelfree = t; +} + +// shut down a tunnel +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 + 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++) + if (session[s].tunnel == t) + sessionkill(s, reason); + tunnel[t].die = now() + 700; // Clean up in 70 seconds + cluster_send_tunnel(t); + // TBA - should we wait for sessions to stop? + { // Send StopCCN + controlt *c = controlnew(4); // sending StopCCN + control16(c, 1, 1, 1); // result code (admin reasons - TBA make error, general error, add message + control16(c, 9, t, 1); // assigned tunnel (our end) + controladd(c, t, 0); // send the message + } +} + +// read and process packet on tunnel (UDP) +void processudp(u8 * buf, int len, struct sockaddr_in *addr) +{ + char *chapresponse = NULL; + u16 l = len, t = 0, s = 0, ns = 0, nr = 0; + u8 *p = buf + 2; + +#ifdef STAT_CALLS + STAT(call_processudp); +#endif + udp_rx += len; + udp_rx_pkt++; + log_hex(5, "UDP Data", buf, len); + STAT(tunnel_rx_packets); + INC_STAT(tunnel_rx_bytes, len); + if (len < 6) + { + log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "Short UDP, %d bytes\n", len); + STAT(tunnel_rx_errors); + return; + } + if ((buf[1] & 0x0F) != 2) + { + log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "Bad L2TP ver %d\n", (buf[1] & 0x0F) != 2); + STAT(tunnel_rx_errors); + return; + } + if (*buf & 0x40) + { // length + l = ntohs(*(u16 *) p); + p += 2; + } + t = ntohs(*(u16 *) p); + p += 2; + s = ntohs(*(u16 *) p); + p += 2; + if (s >= MAXSESSION) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Received UDP packet with invalid session ID\n"); + STAT(tunnel_rx_errors); + return; + } + if (t >= MAXTUNNEL) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Received UDP packet with invalid tunnel ID\n"); + STAT(tunnel_rx_errors); + return; + } + if (s && !session[s].tunnel) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "UDP packet contains session %d but no session[%d].tunnel exists (LAC said tunnel = %d). Dropping packet.\n", s, s, t); + STAT(tunnel_rx_errors); + return; + } + if (*buf & 0x08) + { // ns/nr + ns = ntohs(*(u16 *) p); + p += 2; + nr = ntohs(*(u16 *) p); + p += 2; + } + if (*buf & 0x02) + { // offset + u16 o = ntohs(*(u16 *) p); + p += o + 2; + } + if ((p - buf) > l) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Bad length %d>%d\n", (p - buf), l); + STAT(tunnel_rx_errors); + return; + } + l -= (p - buf); + if (t) tunnel[t].last = time_now; + if (*buf & 0x80) + { // control + u16 message = 0xFFFF; // message type + u8 fatal = 0; + u8 mandatorymessage = 0; + u8 chap = 0; // if CHAP being used + u16 asession = 0; // assigned session + u32 amagic = 0; // magic number + u8 aflags = 0; // flags from last LCF + u16 version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case) + int requestchap = 0; // do we request PAP instead of original CHAP request? + char called[MAXTEL] = ""; // called number + char calling[MAXTEL] = ""; // calling number + if ((*buf & 0xCA) != 0xC8) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Bad control header %02X\n", *buf); + STAT(tunnel_rx_errors); + return; + } + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): %d ns %d nr %d ns %d nr %d\n", + l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); + // 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) + { + 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)); + 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 + int skip = tunnel[t].window; // track how many in-window packets are still in queue + if (tunnel[t].controlc) + { // some to clear maybe + while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) + { + controlt *c = tunnel[t].controls; + tunnel[t].controls = c->next; + tunnel[t].controlc--; + c->next = controlfree; + controlfree = c; + skip--; + tunnel[t].try = 0; // we have progress + } + } + if (tunnel[t].nr < ns && tunnel[t].nr != 0) + { + // is this the sequence we were expecting? + log(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d not %d)\n", t, ns, tunnel[t].nr); + STAT(tunnel_rx_errors); +// controlnull(t); + return; + } + if (l) + tunnel[t].nr++; // receiver advance (do here so quoted correctly in any sends below) + if (skip < 0) + skip = 0; + if (skip < tunnel[t].controlc) + { // 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) + { + c = c->next; + skip--; + } + while (c && tosend) + { + tunnel[t].try = 0; // first send + tunnelsend(c->buf, c->length, t); + c = c->next; + tosend--; + } + } + if (!tunnel[t].controlc) + tunnel[t].retry = 0; // caught up + } + if (l) + { // if not a null message + // process AVPs + while (l && !(fatal & 0x80)) + { + u16 n = (ntohs(*(u16 *) p) & 0x3FF); + u8 *b = p; + u8 flags = *p; + u16 mtype; + p += n; // next + if (l < n) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Invalid length in AVP\n"); + STAT(tunnel_rx_errors); + fatal = flags; + return; + } + l -= n; + if (flags & 0x40) + { + // handle hidden AVPs + if (!l2tpsecret) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no L2TP secret.\n"); + fatal = flags; + continue; + } + if (!session[s].random_vector_length) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no random vector.\n"); + fatal = flags; + continue; + } + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n"); + } + if (*b & 0x3C) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unrecognised AVP flags %02X\n", *b); + fatal = flags; + continue; // next + } + b += 2; + if (*(u16 *) (b)) + { + log(2, ntohl(addr->sin_addr.s_addr), s, t, "Unknown AVP vendor %d\n", ntohs(*(u16 *) (b))); + fatal = flags; + continue; // next + } + b += 2; + mtype = ntohs(*(u16 *) (b)); + b += 2; + n -= 6; + + log(4, ntohl(addr->sin_addr.s_addr), s, t, " AVP %d (%s) len %d\n", mtype, avpnames[mtype], n); + switch (mtype) + { + case 0: // message type + message = ntohs(*(u16 *) b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Message type = %d (%s)\n", *b, + l2tp_message_types[message]); + mandatorymessage = flags; + break; + case 1: // result code + { + u16 rescode = ntohs(*(u16 *)(b)); + const char* resdesc = "(unknown)"; + if (message == 4) { /* StopCCN */ + if (rescode <= MAX_STOPCCN_RESULT_CODE) + resdesc = stopccn_result_codes[rescode]; + } else if (message == 14) { /* CDN */ + if (rescode <= MAX_CDN_RESULT_CODE) + resdesc = cdn_result_codes[rescode]; + } + + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Result Code %d: %s\n", + rescode, resdesc); + if (n >= 4) { + u16 errcode = ntohs(*(u16 *)(b + 2)); + const char* errdesc = "(unknown)"; + if (errcode <= MAX_ERROR_CODE) + errdesc = error_codes[errcode]; + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error Code %d: %s\n", + errcode, errdesc); + } + if (n > 4) { + /* %*s doesn't work?? */ + char buf[n-4+2]; + memcpy(buf, b+4, n-4); + buf[n-4+1] = '\0'; + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error String: %s\n", + buf); + } + break; + } + break; + case 2: // protocol version + { + version = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Protocol version = %d\n", version); + if (version && version != 0x0100) + { // allow 0.0 and 1.0 + log(1, ntohl(addr->sin_addr.s_addr), s, t, " Bad protocol version %04X\n", + version); + fatal = flags; + continue; // next + } + } + break; + case 3: // framing capabilities +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Framing capabilities\n"); + break; + case 4: // bearer capabilities +// 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 +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Tie breaker\n"); + continue; + case 6: // firmware revision +// log(4, ntohl(addr->sin_addr.s_addr), s, t, "Firmware revision\n"); + break; + case 7: // host name + memset(tunnel[t].hostname, 0, 128); + memcpy(tunnel[t].hostname, b, (n >= 127) ? 127 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Tunnel hostname = \"%s\"\n", tunnel[t].hostname); + // TBA - to send to RADIUS + break; + case 8: // vendor name + memset(tunnel[t].vendor, 0, 128); + memcpy(tunnel[t].vendor, b, (n >= 127) ? 127 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor); + break; + case 9: // assigned tunnel + tunnel[t].far = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Remote tunnel id = %d\n", tunnel[t].far); + break; + case 10: // rx window + tunnel[t].window = ntohs(*(u16 *) (b)); + if (!tunnel[t].window) + tunnel[t].window = 1; // window of 0 is silly + log(4, ntohl(addr->sin_addr.s_addr), s, t, " rx window = %d\n", tunnel[t].window); + break; + case 11: // Challenge + { + log(4, ntohl(addr->sin_addr.s_addr), s, t, " LAC requested CHAP authentication for tunnel\n"); + build_chap_response(b, 2, n, &chapresponse); + } + break; + case 14: // assigned session + asession = session[s].far = ntohs(*(u16 *) (b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " assigned session = %d\n", asession); + break; + case 15: // call serial number + log(4, ntohl(addr->sin_addr.s_addr), s, t, " call serial number = %d\n", ntohl(*(u32 *)b)); + break; + case 18: // bearer type + log(4, ntohl(addr->sin_addr.s_addr), s, t, " bearer type = %d\n", ntohl(*(u32 *)b)); + // TBA - for RADIUS + break; + case 19: // framing type + log(4, ntohl(addr->sin_addr.s_addr), s, t, " framing type = %d\n", ntohl(*(u32 *)b)); + // TBA + break; + case 21: // called number + memset(called, 0, MAXTEL); + memcpy(called, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Called <%s>\n", called); + break; + case 22: // calling number + memset(calling, 0, MAXTEL); + memcpy(calling, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Calling <%s>\n", calling); + break; + case 24: // tx connect speed + if (n == 4) + { + session[s].tx_connect_speed = ntohl(*(u32 *)b); + } + else + { + // AS5300s send connect speed as a string + char tmp[30] = {0}; + memcpy(tmp, b, (n >= 30) ? 30 : n); + session[s].tx_connect_speed = atol(tmp); + } + log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%d>\n", + session[s].tx_connect_speed); + break; + case 38: // rx connect speed + if (n == 4) + { + session[s].rx_connect_speed = ntohl(*(u32 *)b); + } + else + { + // AS5300s send connect speed as a string + char tmp[30] = {0}; + memcpy(tmp, b, (n >= 30) ? 30 : n); + session[s].rx_connect_speed = atol(tmp); + } + log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%d>\n", + session[s].rx_connect_speed); + break; + case 25: // Physical Channel ID + { + u32 tmp = ntohl(*(u32 *)b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Physical Channel ID <%X>\n", tmp); + break; + } + case 29: // Proxy Authentication Type + { + u16 authtype = ntohs(*(u16 *)b); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Type %d (%s)\n", + authtype, authtypes[authtype]); + requestchap = (authtype == 2); + break; + } + case 30: // Proxy Authentication Name + { + char authname[64] = {0}; + memcpy(authname, b, (n > 63) ? 63 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Name (%s)\n", + authname); + break; + } + case 31: // Proxy Authentication Challenge + { + memcpy(radius[session[s].radius].auth, b, 16); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge (%X)\n", radius[session[s].radius].auth); + break; + } + case 32: // Proxy Authentication ID + { + u16 authid = ntohs(*(u16 *)(b)); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth ID (%d)\n", + authid); + if (session[s].radius) + radius[session[s].radius].id = authid; + break; + } + case 33: // Proxy Authentication Response + { + char authresp[64] = {0}; + memcpy(authresp, b, (n > 63) ? 63 : n); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Response\n"); + break; + } + case 27: // last send lcp + { // find magic number + u8 *p = b, *e = p + n; + while (p < e && p[1]) + { + if (*p == 5 && p[1] == 6) + amagic = ntohl(*(u32 *) (p + 2)); + else if (*p == 3 && p[1] == 5 && *(u16 *) (p + 2) == htons(PPPCHAP) && p[4] == 5) + chap = 1; + else if (*p == 7) + aflags |= SESSIONPFC; + else if (*p == 8) + aflags |= SESSIONACFC; + p += p[1]; + } + + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + } + } + break; + case 28: // last recv lcp confreq + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + break; + } + case 26: // Initial Received LCP CONFREQ + { + char tmp[500] = {0}; + tmp[0] = ConfigReq; + memcpy((tmp + 1), b, n); + } + break; + case 39: // seq required - we control it as an LNS anyway... + break; + case 36: // Random Vector + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Random Vector received. Enabled AVP Hiding.\n"); + memset(session[s].random_vector, 0, sizeof(session[s].random_vector)); + memcpy(session[s].random_vector, b, n); + session[s].random_vector_length = n; + break; + default: + log(2, ntohl(addr->sin_addr.s_addr), s, t, " Unknown AVP type %d\n", mtype); + fatal = flags; + continue; // next + } + } + // process message + if (fatal & 0x80) + tunnelshutdown(t, "Unknown Mandatory AVP"); + else + switch (message) + { + case 1: // SCCRQ - Start Control Connection Request + { + controlt *c = controlnew(2); // sending SCCRP + control16(c, 2, version, 1); // protocol version + control32(c, 3, 3, 1); // framing + controls(c, 7, tunnel[t].hostname, 1); // host name (TBA) + if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response + control16(c, 9, t, 1); // assigned tunnel + controladd(c, t, s); // send the resply + } + break; + case 2: // SCCRP + // TBA + break; + case 3: // SCCN + controlnull(t); // ack + break; + case 4: // StopCCN + controlnull(t); // ack + tunnelshutdown(t, "Stopped"); // Shut down cleanly + tunnelkill(t, "Stopped"); // Immediately force everything dead + break; + case 6: // HELLO + controlnull(t); // simply ACK + break; + case 7: // OCRQ + // TBA + break; + case 8: // OCRO + // TBA + break; + case 9: // OCCN + // TBA + break; + case 10: // ICRQ + if (!sessionfree) + { + STAT(session_overflow); + tunnelshutdown(t, "No free sessions"); + } + else + { + u8 r; + controlt *c; + + // make a RADIUS session + if (!radiusfree) + { + STAT(radius_overflow); + log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); + 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; + 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); + STAT(session_created); + } + break; + case 11: // ICRP + // TBA + break; + case 12: // ICCN + session[s].magic = amagic; // set magic number + session[s].flags = aflags; // set flags received + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Magic %X Flags %X\n", amagic, aflags); + controlnull(t); // ack + // In CHAP state, request PAP instead + if (requestchap) + initlcp(t, s); + break; + case 14: // CDN + controlnull(t); // ack + sessionshutdown(s, "Closed (Received CDN)"); + break; + case 0xFFFF: + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Missing message type\n"); + break; + default: + STAT(tunnel_rx_errors); + if (mandatorymessage & 0x80) + tunnelshutdown(t, "Unknown message"); + else + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unknown message type %d\n", message); + break; + } + if (chapresponse) free(chapresponse); + cluster_send_tunnel(t); + } + else + { + log(4, 0, s, t, " Got a ZLB ack\n"); + } + } + else + { // data + u16 prot; + + log_hex(5, "Receive Tunnel Data", p, l); + if (session[s].die) + { + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Session %d is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + if (l > 2 && p[0] == 0xFF && p[1] == 0x03) + { // HDLC address header, discard + p += 2; + l -= 2; + } + if (l < 2) + { + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Short ppp length %d\n", l); + STAT(tunnel_rx_errors); + return; + } + if (*p & 1) + { + prot = *p++; + l--; + } + else + { + prot = ntohs(*(u16 *) p); + p += 2; + l -= 2; + } + if (prot == PPPPAP) + { + session[s].last_packet = time_now; + processpap(t, s, p, l); + } + else if (prot == PPPCHAP) + { + session[s].last_packet = time_now; + processchap(t, s, p, l); + } + else if (prot == PPPLCP) + { + session[s].last_packet = time_now; + processlcp(t, s, p, l); + } + else if (prot == PPPIPCP) + { + session[s].last_packet = time_now; + processipcp(t, s, p, l); + } + else if (prot == PPPCCP) + { + session[s].last_packet = time_now; + processccp(t, s, p, l); + } + else if (prot == PPPIP) + { + session[s].last_packet = time_now; + processipin(t, s, p, l); + } + else + { + STAT(tunnel_rx_errors); + log(1, ntohl(addr->sin_addr.s_addr), s, t, "Unknown PPP protocol %04X\n", prot); + } + } +} + +// read and process packet on tap +void processtap(u8 * buf, int len) +{ + log_hex(5, "Receive TAP Data", buf, len); + STAT(tap_rx_packets); + INC_STAT(tap_rx_bytes, len); +#ifdef STAT_CALLS + STAT(call_processtap); +#endif + eth_rx_pkt++; + eth_rx += len; + if (len < 22) + { + log(1, 0, 0, 0, "Short tap packet %d bytes\n", len); + STAT(tap_rx_errors); + return; + } + if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP + processarp(buf, len); + else if (*(u16 *) (buf + 2) == htons(PKTIP)) // ARP + processipout(buf, len); + else + { + log(1, 0, 0, 0, "Unexpected tap packet %04X, %d bytes\n", ntohs(*(u16 *) (buf + 2)), len); + } +} + +// main loop - gets packets on tap or udp and processes them +void mainloop(void) +{ + fd_set cr; + int cn; + u8 buf[65536]; + struct timeval to; + clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc + clockt next_acct = slow + ACCT_TIME; + clockt next_cluster_ping = slow + 50; + to.tv_sec = 1; + to.tv_usec = 0; + log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, radfd=%d, cluster_sockfd=%d, controlfd=%d\n", + udpfd, tapfd, radfd, cluster_sockfd, controlfd); + + FD_ZERO(&cr); + FD_SET(udpfd, &cr); + 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; + memcpy(&r, &cr, sizeof(fd_set)); + n = select(n + 1, &r, 0, 0, &to); + if (n < 0) + { + if (errno != EINTR) + { + perror("select"); + exit( -1); + } + } + else if (n) + { + struct sockaddr_in addr; + int alen = sizeof(addr); + if (FD_ISSET(udpfd, &r)) + processudp(buf, recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen), &addr); + else if (FD_ISSET(tapfd, &r)) + processtap(buf, read(tapfd, buf, sizeof(buf))); + else if (FD_ISSET(radfd, &r)) + processrad(buf, recv(radfd, buf, sizeof(buf), 0)); + else if (FD_ISSET(cluster_sockfd, &r)) + 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; + int sockfd; + int len = sizeof(addr); + + if ((sockfd = accept(clifd, (struct sockaddr *)&addr, &len)) <= 0) + { + log(0, 0, 0, 0, "accept error: %s\n", strerror(errno)); + continue; + } + else + { + cli_do(sockfd); + close(sockfd); + } + } +#endif + else + { + log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n); + continue; + } + } + else if (n == 0) { // handle timeouts + clockt when = now(); + clockt best = when + 100; // default timeout + sessionidt s; + tunnelidt t; + u8 r; + for (r = 0; r < MAXRADIUS; r++) + if (radius[r].state && radius[r].retry) + { + if (radius[r].retry <= when) + radiusretry(r); + if (radius[r].retry && radius[r].retry < best) + best = radius[r].retry; + } + for (t = 0; t < MAXTUNNEL; t++) + { + // check for expired tunnels + if (tunnel[t].die && tunnel[t].die <= when) + { + STAT(tunnel_timeout); + tunnelkill(t, "Expired"); + continue; + } + // check for message resend + if (tunnel[t].retry && tunnel[t].controlc) + { + // resend pending messages as timeout on reply + if (tunnel[t].retry <= when) + { + controlt *c = tunnel[t].controls; + u8 w = tunnel[t].window; + tunnel[t].try++; // another try + if (tunnel[t].try > 5) + tunnelkill(t, "Timeout on control message"); // game over + else + while (c && w--) + { + tunnelsend(c->buf, c->length, t); + c = c->next; + } + } + if (tunnel[t].retry && tunnel[t].retry < best) + best = tunnel[t].retry; + } + // Send hello + if (tunnel[t].ip && !tunnel[t].die && tunnel[t].last < when + 600 && !tunnel[t].controlc) + { + controlt *c = controlnew(6); // sending HELLO + controladd(c, t, 0); // send the message + log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n"); + } + } + +#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++) + { + log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); + sessionshutdown(cli_session_kill[i], "Requested by CLI"); + cli_session_kill[i] = 0; + } + } + // Check for tunnels that have been killed from the CLI + if (cli_tunnel_kill[0]) + { + int i; + for (i = 0; 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"); + cli_tunnel_kill[i] = 0; + } + } +#endif + + for (s = 0; 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)) + { + sessionkill(s, "No response to LCP ECHO requests"); + continue; + } + + // No data in IDLE_TIMEOUT seconds, send LCP ECHO + if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) + { + u8 b[MAXCONTROL] = {0}; + u8 *q = makeppp(b, 0, 0, session[s].tunnel, s, PPPLCP); + + *q = EchoReq; + *(u8 *)(q + 1) = (time_now % 255); // ID + *(u16 *)(q + 2) = htons(8); // Length + *(u32 *)(q + 4) = 0; // Magic Number (not supported) + + log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", + time_now - session[s].last_packet); + tunnelsend(b, 24, session[s].tunnel); // send it + continue; + } + } + if (accounting_dir && next_acct <= when) + { + // Dump accounting data + next_acct = when + ACCT_TIME; + dump_acct_info(); + } + + if (cluster_sockfd && next_cluster_ping <= when) + { + // Dump accounting data + next_cluster_ping = when + 50; + cluster_send_message(cluster_address, bind_address, C_PING, hostname, strlen(hostname)); + } + + if (best <= when) + best = when + 1; // should not really happen + to.tv_sec = (best - when) / 10; + to.tv_usec = 100000 * ((best - when) % 10); + log(5, 0, 0, 0, "Next time check in %d.%d seconds\n", (best - when) / 10, ((best - when) % 10)); + } + } +} + +// Init data structures +void initdata(void) +{ + int i; + + _statistics = mmap(NULL, sizeof(struct Tstats), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (_statistics <= 0) + { + log(0, 0, 0, 0, "Error doing mmap for _statistics: %s\n", strerror(errno)); + exit(1); + } + tunnel = mmap(NULL, sizeof(tunnelt) * MAXTUNNEL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (tunnel <= 0) + { + 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) + { + 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) + { + 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) + { + 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) + { + log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + exit(1); + } + 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) + { + 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) + { + log(0, 0, 0, 0, "Error doing mmap for cli tunnel kill: %s\n", strerror(errno)); + exit(1); + } + 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) + { + 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) + { + // Grab my hostname unless it's been specified + gethostname(hostname, sizeof(hostname)); + { + struct hostent *h = gethostbyname(hostname); + if (h) + myip = ntohl(*(u32 *) h->h_addr); + } + } + _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) +{ + /* Flush the tables here so that we have a clean slate */ + system("iptables -t nat -F l2tpns"); + system("iptables -t mangle -F l2tpns"); +} + +ipt assign_ip_address() +{ + int c = 0; +#ifdef STAT_CALLS + STAT(call_assign_ip_address); +#endif + ip_pool_index++; + while (1) + { + if (ip_pool_index >= ip_pool_size) + { + if (++c == 2) + return 0; + ip_pool_index = 0; + } + if (!ip_address_pool[ip_pool_index].assigned && ip_address_pool[ip_pool_index].address) + { + 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); + } + ip_pool_index++; + } + return 0; +} + +void free_ip_address(ipt address) +{ + int i; + ipt a; +#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)); +} + +// Initialize the IP address pool +void initippool() +{ + FILE *f; + char *buf, *p; + int pi = 0; + memset(ip_address_pool, 0, sizeof(ip_address_pool)); + + if (!(f = fopen(IPPOOLFILE, "r"))) + { + log(0, 0, 0, 0, "Can't load pool file " IPPOOLFILE ": %s\n", strerror(errno)); + exit(-1); + } + + buf = (char *)malloc(4096); + + while (pi < MAXIPPOOL && fgets(buf, 4096, f)) + { + char* pool = buf; + if (*buf == '#' || *buf == '\n') + continue; // Skip comments / blank lines + if ((p = (char *)strrchr(buf, '\n'))) *p = 0; + if ((p = (char *)strchr(buf, ':'))) + { + ipt src; + *p = '\0'; + src = inet_addr(buf); + if (src == INADDR_NONE) + { + log(0, 0, 0, 0, "Invalid address pool IP %s", buf); + exit(-1); + } + // This entry is for a specific IP only + if (src != bind_address) + continue; + *p = ':'; + pool = p+1; + } + if ((p = (char *)strchr(pool, '/'))) + { + // It's a range + int numbits = 0; + unsigned long start = 0, end = 0, mask = 0, ip; + struct rtentry r; + + log(2, 0, 0, 0, "Adding IP address range %s\n", buf); + *p++ = 0; + if (!*p || !(numbits = atoi(p))) + { + log(0, 0, 0, 0, "Invalid pool range %s/\n", buf, p); + continue; + } + start = end = ntohl(inet_addr(pool)); + mask = (unsigned long)(pow(2, numbits) - 1) << (32 - numbits); + start &= mask; + end = start + (int)(pow(2, (32 - numbits))) - 1; + for (ip = (start + 1); ip < end && pi < MAXIPPOOL; ip++) + { + if ((ip & 0xFF) == 0 || (ip & 0xFF) == 255) + continue; + ip_address_pool[pi++].address = htonl(ip); + } + + // 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_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; + *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask); + r.rt_flags = (RTF_UP | RTF_STATIC); + if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0) + { + log(0, 0, 0, 0, "Error adding ip address pool route %s/%d: %s\n", inet_toa(start), mask, strerror(errno)); + } + } + else + { + // It's a single ip address + ip_address_pool[pi++].address = inet_addr(pool); + } + } + + free(buf); + fclose(f); + log(1, 0, 0, 0, "IP address pool is %d addresses\n", pi); + ip_pool_size = pi; +} + +void snoop_send_packet(char *packet, u16 size) +{ + if (!snoop_addr.sin_port || snoopfd <= 0 || size <= 0 || !packet) + return; + + 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); +} + +void dump_acct_info() +{ + char filename[1024]; + char timestr[64]; + time_t t = time(NULL); + int i; + FILE *f = NULL; + +#ifdef STAT_CALLS + 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); + + for (i = 0; i < MAXSESSION; i++) + { + if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) + continue; + if (!f) + { + time_t now = time(NULL); + if (!(f = fopen(filename, "w"))) + { + log(0, 0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno)); + return; + } + log(3, 0, 0, 0, "Dumping accounting information to %s\n", filename); + fprintf(f, "# dslwatch.pl dump file V1.01\n" + "# host: %s\n" + "# time: %ld\n" + "# uptime: %ld\n" + "# format: username ip qos uptxoctets downrxoctets\n", + hostname, + now, + now - basetime); + } + + log(4, 0, 0, 0, "Dumping accounting information for %s\n", session[i].user); + fprintf(f, "%s %s %d %lu %lu\n", + session[i].user, // username + inet_toa(htonl(session[i].ip)), // ip + (session[i].throttle) ? 2 : 1, // qos + (unsigned long)session[i].cin, // uptxoctets + (unsigned long)session[i].cout); // downrxoctets + + session[i].pin = session[i].cin = 0; + session[i].pout = session[i].cout = 0; + } + + if (f) fclose(f); +} + +// Main program +int main(int argc, char *argv[]) +{ + int o; + + _program_name = strdup(argv[0]); + + { + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + // Remove the maximum core size + setrlimit(RLIMIT_CORE, &rlim); + // Make core dumps go to /tmp + chdir("/tmp"); + } + + time(&basetime); // start clock + // scan args + + while ((o = getopt(argc, argv, "vc:f:h:a:")) >= 0) + { + switch (o) + { + case 'v': + debug++; + break; + case 'c': + config_file = strdup(optarg); + break; + case 'f': + log_filename = strdup(optarg); + break; + case 'h': + strncpy(hostname, optarg, 1000); + break; + case 'a': + myip = inet_addr(optarg); + if (myip == INADDR_NONE) { + log(0, 0, 0, 0, "Invalid ip %s\n", optarg); + exit(-1); + } + 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"); + return (0); + break; + } + } + + 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"); + + /* 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)); + + inittap(); + log(1, 0, 0, 0, "Set up on interface %s\n", tapdevice); + + initudp(); + initrad(); + initippool(); + init_rl(); + if (handle_interface) { + send_garp(bind_address); + } + read_state(); + +#ifdef HAVE_LIBCLI + init_cli(); +#endif + + signal(SIGALRM, sigalrm_handler); + signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGINT, sigterm_handler); + 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); + + 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) + fclose(log_stream); + + 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; + } + + // Update the internal time counter + time(&time_now); + strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + alarm(1); + + { + // Run timer hooks + struct param_timer p = { time_now }; + run_plugins(PLUGIN_TIMER, &p); + } + +} + +void sigterm_handler(int junk) +{ + log(1, 0, 0, 0, "Shutting down cleanly\n"); + if (config_save_state) + dump_state(); + main_quit++; +} + +void sigquit_handler(int junk) +{ + int i; + log(1, 0, 0, 0, "Shutting down without saving sessions\n"); + for (i = 0; i < MAXSESSION; i++) + { + if (session[i].opened) + sessionkill(i, "L2TPNS Closing"); + } + for (i = 0; i < MAXTUNNEL; i++) + { + if (tunnel[i].ip) + tunnelshutdown(i, "L2TPNS Closing"); + } + 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 +} + +void *backtrace_buffer[30] = {0}; + +void sigsegv_handler(int signal) +{ + log(0, 0, 0, 0, "----------------------------------------------\n"); + log(0, 0, 0, 0, "- SEGFAULT! -\n"); + log(0, 0, 0, 0, "----------------------------------------------\n"); + _exit(0); +} + +void read_state() +{ + struct stat sb; + FILE *f; + + if (!config_save_state) return; + + if (stat(STATEFILE, &sb) < 0) + 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 (!(f = fopen(STATEFILE, "r"))) + { + log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno)); + unlink(STATEFILE); + return; + } + fseek(f, 0, 0); + + log(1, 0, 0, 0, "Reading state information\n"); + { + 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++; + } + { + 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; + } + fclose(f); + log(0, 0, 0, 0, "Loaded saved state information\n"); + unlink(STATEFILE); +} + +void dump_state() +{ + FILE *f; + + if (!config_save_state) return; + + if ((f = fopen(STATEFILE, "w"))) + { + u32 i; + log(1, 0, 0, 0, "Dumping state information\n"); + + i = MAXTUNNEL; + fwrite(&i, sizeof(i), 1, f); // Number of tunnels + + log(2, 0, 0, 0, "Dumping %lu tunnels\n", i); + fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f); + + 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); + + fclose(f); + } + else + { + log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno)); + } + return; +} + +void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response) +{ + MD5_CTX ctx; + *challenge_response = NULL; + + if (!l2tpsecret || !*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, challenge, challenge_length); + MD5Final(*challenge_response, &ctx); + + return; +} + +void read_config_file() +{ + 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; + } + + if (radiussecret) + { + free(radiussecret); + radiussecret = NULL; + } + + if (l2tpsecret) + { + free(l2tpsecret); + l2tpsecret = NULL; + } + + if (log_filename) + { + free(log_filename); + log_filename = NULL; + } + + if (snoop_destination_host) + { + 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) + { + log(1, 0, 0, 0, "Disabled throttling.\n"); + } + else + { + log(1, 0, 0, 0, "Enabled throttling (rate is %lu kbits/s)\n", rl_rate); + } + } 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) + { + 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; + } + } 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); + } + } + + if (snoop_destination_host) + { + if (inet_aton(snoop_destination_host, &snoop_addr.sin_addr)) + { + snoop_addr.sin_port = htons(snoop_destination_port); + snoop_addr.sin_family = AF_INET; + } + else + { + log(0, 0, 0, 0, "Can't find address for snoop host %s\n", snoop_destination_host); + } + } + + free(buf); + fclose(f); + log(2, 0, 0, 0, "Done reading config file\n"); +} + +int sessionsetup(tunnelidt t, sessionidt s, u8 routes) +{ + // A session now exists, set it up + ipt ip; + char *user; + sessionidt i; +#ifdef STAT_CALLS + STAT(call_sessionsetup); +#endif + log(3, session[s].ip, s, t, "Doing session setup for session\n"); + if (!session[s].ip) { + 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 + // from someone who doesn't need to know the password + ip = session[s].ip; + user = session[s].user; + for (i = 0; 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) + sessionkill(i, "Duplicate session for user"); + } + + if (routes) + { + if (session[s].route[routes].ip && session[s].route[routes].mask) + { + log(2, session[s].ip, s, t, "Routing session\n"); + routeset(session[s].ip, 0, 0, 1); + while (routes--) + routeset(session[s].route[routes].ip, session[s].route[routes].mask, + session[s].ip, 1); + } + } + sessionsendarp(s); + if (!session[s].sid) + sendipcp(t, s); + + // Force throttling on or off + // This has the advantage of cleaning up after another throttled user who may have left + // firewall rules lying around + throttle_session(s, session[s].throttle); + + { + struct param_new_session data = { &tunnel[t], &session[s] }; + run_plugins(PLUGIN_NEW_SESSION, &data); + } + + session[s].sid = ++last_sid; + cache_sessionid(htonl(session[s].ip), s); + + cluster_send_session(s); + session[s].last_packet = time_now; + { + char *sessionip, *tunnelip; + sessionip = strdup(inet_toa(ntohl(session[s].ip))); + tunnelip = strdup(inet_toa(ntohl(tunnel[t].ip))); + log(2, session[s].ip, s, t, "Login by %s at %s from %s (%s)\n", + session[s].user, sessionip, tunnelip, tunnel[t].hostname); + if (sessionip) free(sessionip); + if (tunnelip) free(tunnelip); + } + + return 1; // RADIUS OK and IP allocated, done... +} + +#ifdef RINGBUFFER +void ringbuffer_dump(FILE *stream) +{ + int i = ringbuffer->head; + + while (i != ringbuffer->tail) + { + if (*ringbuffer->buffer[i].message) + fprintf(stream, "%d-%s", ringbuffer->buffer[i].level, ringbuffer->buffer[i].message); + if (++i == ringbuffer->tail) break; + if (i == RINGBUFFER_SIZE) i = 0; + } +} +#endif + +void initplugins() +{ + int i; + + loaded_plugins = ll_init(); + // Initialize the plugins to nothing + for (i = 0; i < MAX_PLUGIN_TYPES; i++) + plugins[i] = ll_init(); +} + +void add_plugin(char *plugin_name) +{ + void *p; + int (*initfunc)(struct pluginfuncs *); + char path[256] = {0}; + int i; + struct pluginfuncs funcs; + + funcs._log = _log; + funcs._log_hex = _log_hex; + funcs.inet_toa = inet_toa; + funcs.get_session_by_username = sessionbyuser; + funcs.get_session_by_id = sessiontbysessionidt; + funcs.get_id_by_session = sessionidtbysessiont; + funcs.sessionkill = sessionkill; + funcs.radiusnew = radiusnew; + funcs.radiussend = radiussend; + + snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); + + log(2, 0, 0, 0, "Loading plugin from %s\n", path); + p = dlopen(path, RTLD_NOW); + if (!p) + { + log(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror()); + return; + } + + if (ll_contains(loaded_plugins, p)) + { + dlclose(p); + return; + } + + { + int *v = dlsym(p, "__plugin_api_version"); + if (!v || *v != PLUGIN_API_VERSION) + { + log(1, 0, 0, 0, " Plugin load failed: API version mismatch\n", dlerror()); + dlclose(p); + return; + } + } + + initfunc = dlsym(p, "plugin_init"); + if (!initfunc) + { + log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist.\n", dlerror()); + dlclose(p); + return; + } + + if (!initfunc(&funcs)) + { + log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE.\n", dlerror()); + dlclose(p); + return; + } + + for (i = 0; i < max_plugin_functions; i++) + { + void *x; + if (!plugin_functions[i]) continue; + if ((x = dlsym(p, plugin_functions[i]))) + { + log(3, 0, 0, 0, " Supports function \"%s\"\n", plugin_functions[i]); + ll_push(plugins[i], x); + } + } + log(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name); +} + +void remove_plugin(char *plugin_name) +{ + void *p; + int (*donefunc)(); + char path[256] = {0}; + int i; + + snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); + + log(2, 0, 0, 0, "Removing plugin %s\n", plugin_name); + // Get the existing pointer + p = dlopen(path, RTLD_LAZY); + if (!p) return; + + for (i = 0; i < max_plugin_functions; i++) + { + void *x; + if (!plugin_functions[i]) continue; + if ((x = dlsym(p, plugin_functions[i]))) ll_delete(plugins[i], x); + } + + if (ll_contains(loaded_plugins, p)) + { + ll_delete(loaded_plugins, p); + + donefunc = dlsym(p, "plugin_done"); + if (donefunc) donefunc(); + } + + dlclose(p); + dlclose(p); + log(2, 0, 0, 0, "Removed plugin %s\n", plugin_name); +} + +int run_plugins(int plugin_type, void *data) +{ + int (*func)(void *data); + if (!plugins[plugin_type] || plugin_type > max_plugin_functions) return 1; + + ll_reset(plugins[plugin_type]); + while ((func = ll_next(plugins[plugin_type]))) + { + int rc; + rc = func(data); + if (rc == PLUGIN_RET_STOP) return 1; + if (rc == PLUGIN_RET_ERROR) return 0; + } + return 1; +} + +void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) +{ + char *resp; + int l; + 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); + + resp = calloc(1400, 1); + l = new_packet(PKT_RESP_ERROR, resp); + *(int *)(resp + 6) = *(int *)(buf + 6); + + param.type = ntohs(*(short *)(buf + 2)); + param.id = ntohl(*(int *)(buf + 6)); + param.data_length = ntohs(*(short *)(buf + 4)) - 10; + param.data = (param.data_length > 0) ? (char *)(buf + 10) : NULL; + param.response = resp; + param.response_length = l; + + if (param.type == PKT_LOAD_PLUGIN && param.data_length) + { + add_plugin(param.data); + } + else if (param.type == PKT_UNLOAD_PLUGIN && param.data_length) + { + remove_plugin(param.data); + } + else + { + run_plugins(PLUGIN_CONTROL, ¶m); + } + + if (param.send_response) + { + send_packet(controlfd, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), param.response, param.response_length); + log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent Control packet response\n"); + } + + free(resp); +} + diff --git a/l2tpns.h b/l2tpns.h new file mode 100644 index 0000000..afbfe3f --- /dev/null +++ b/l2tpns.h @@ -0,0 +1,393 @@ +// L2TPNS Global Stuff +// $Id: l2tpns.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include + +#include "config.h" + +#define VERSION "1.0" + +// Limits +#define MAXTUNNEL 500 // could be up to 65535 +#define MAXSESSION 50000 // could be up to 65535 +#define MAXRADIUS 255 +#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 MAXRADSERVER 10 // max radius servers +#define MAXROUTE 10 // max static routes per session +#define MAXIPPOOL 131072 // max number of ip addresses in pool +#define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate +#define MAX_LOG_LENGTH 512 // Maximum size of log message +#define ECHO_TIMEOUT 60 // Time between last packet sent and LCP ECHO generation +#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation + +// Constants +#define STATISTICS +#define STAT_CALLS +#define RINGBUFFER +#define UDP 17 +#define TAPDEVICE "/dev/net/tun" +#define CLIUSERS ETCDIR "l2tpns.users" // CLI Users file +#define CONFIGFILE ETCDIR "l2tpns.cfg" // Configuration file +#define IPPOOLFILE ETCDIR "l2tpns.ip_pool" // Address pool configuration +#define STATEFILE "/tmp/l2tpns.dump" // State dump file + +#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... +#define RADAPORT 1646 // old radius accounting port +#define PKTARP 0x0806 // ARP packet type +#define PKTIP 0x0800 // IP packet type +#define PSEUDOMAC 0x0200 // pseudo MAC prefix (local significant MAC) +#define PPPPAP 0xC023 +#define PPPCHAP 0xC223 +#define PPPLCP 0xC021 +#define PPPIPCP 0x8021 +#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 + +// Types +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned char u8; +typedef u32 ipt; +typedef u16 portt; +typedef u16 sessionidt; +typedef u16 tunnelidt; +typedef u32 clockt; +typedef u8 hasht[16]; + +// structures +typedef struct routes // route +{ + ipt ip; + ipt mask; +} +routet; + +typedef struct controls // control message +{ + struct controls *next; // next in queue + u16 length; // length + u8 buf[MAXCONTROL]; +} +controlt; + +typedef struct stbft +{ + struct stbft *next; + char handle[10]; + char in_use; + int mark; +} tbft; + + +// 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 + 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 + unsigned long tx_connect_speed; + unsigned long rx_connect_speed; +} +sessiont; + +#define SESSIONPFC 1 // PFC negotiated flags +#define SESSIONACFC 2 // ACFC negotiated flags + +// 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 + 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 + char hostname[128]; // tunnel hostname + char vendor[128]; // LAC vendor + u8 try; // number of retrys on a control message + u16 controlc; // outstaind messages in queue + controlt *controls; // oldest message + controlt *controle; // newest message +} +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 + char calling[MAXTEL]; // calling number + char pass[129]; // password + u8 id; // ID for PPP response + u8 try; // which try we are on + u8 state; // state of radius requests + u8 chap; // set if CHAP used (is CHAP identifier) +} +radiust; + +typedef struct +{ + ipt address; + char assigned; // 1 if assigned, 0 if free +} +ippoolt; + +#ifdef RINGBUFFER +struct Tringbuffer +{ + struct { + char level; + sessionidt session; + tunnelidt tunnel; + ipt address; + char message[MAX_LOG_LENGTH]; + } buffer[RINGBUFFER_SIZE]; + int head; + int tail; +}; +#endif + +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 +{ + time_t start_time; + time_t last_reset; + + unsigned long tap_rx_packets; + unsigned long tap_tx_packets; + unsigned long tap_rx_bytes; + unsigned long tap_tx_bytes; + unsigned long tap_rx_errors; + unsigned long tap_tx_errors; + + unsigned long tunnel_rx_packets; + unsigned long tunnel_tx_packets; + unsigned long tunnel_rx_bytes; + unsigned long tunnel_tx_bytes; + unsigned long tunnel_rx_errors; + unsigned long tunnel_tx_errors; + + unsigned long tunnel_retries; + unsigned long radius_retries; + + unsigned long arp_errors; + unsigned long arp_replies; + unsigned long arp_discarded; + unsigned long arp_sent; + unsigned long arp_recv; + + unsigned long packets_snooped; + + unsigned long tunnel_created; + unsigned long session_created; + unsigned long tunnel_timeout; + unsigned long session_timeout; + unsigned long radius_timeout; + unsigned long radius_overflow; + unsigned long tunnel_overflow; + unsigned long session_overflow; + + unsigned long ip_allocated; + unsigned long ip_freed; +#ifdef STAT_CALLS + unsigned long call_processtap; + unsigned long call_processarp; + unsigned long call_processipout; + unsigned long call_processudp; + unsigned long call_sessionbyip; + unsigned long call_sessionbyuser; + unsigned long call_sendarp; + unsigned long call_sendipcp; + unsigned long call_tunnelsend; + unsigned long call_sessionkill; + unsigned long call_sessionshutdown; + unsigned long call_tunnelkill; + unsigned long call_tunnelshutdown; + unsigned long call_assign_ip_address; + unsigned long call_free_ip_address; + unsigned long call_dump_acct_info; + unsigned long call_sessionsetup; + unsigned long call_processpap; + unsigned long call_processchap; + unsigned long call_processlcp; + unsigned long call_processipcp; + unsigned long call_processipin; + unsigned long call_processccp; + unsigned long call_sendchap; + unsigned long call_processrad; + unsigned long call_radiussend; + unsigned long call_radiusretry; +#endif +}; + +#ifdef STATISTICS +#define STAT(x) _statistics->x++ +#define INC_STAT(x,y) _statistics->x += y +#define GET_STAT(x) _statistics->x +#define SET_STAT(x, y) _statistics->x = y +#else +#define STAT(x) +#define INC_STAT(x,y) +#define GET_STAT(x) 0 +#define SET_STAT(x, y) +#endif + +// arp.c +void sendarp(int ifr_idx, const unsigned char* mac, ipt ip); + + +// ppp.c +void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l); +void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l); +void sendchap(tunnelidt t, sessionidt s); +u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype); +u8 *findppp(u8 * b, u8 mtype); +void initlcp(tunnelidt t, sessionidt s); +void dumplcp(char *p, int l); + + +// radius.c +void initrad(void); +void radiussend(u8 r, u8 state); +void processrad(u8 *buf, int len); +void radiusretry(u8 r); +u8 radiusnew(sessionidt s); + +// throttle.c +int throttle_session(sessionidt s, int throttle); + + +// rl.c +void init_rl(); +u16 rl_create_tbf(); +u16 rl_get_tbf(); +void rl_done_tbf(u16 t); +void rl_destroy_tbf(u16 t); + + +// l2tpns.c +clockt now(void); +clockt backoff(u8 try); +void routeset(ipt ip, ipt mask, ipt gw, u8 add); +void inittap(void); +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); +void send_garp(ipt ip); +void sessionkill(sessionidt s, char *reason); +void control16(controlt * c, u16 avp, u16 val, u8 m); +void control32(controlt * c, u16 avp, u32 val, u8 m); +void controls(controlt * c, u16 avp, char *val, u8 m); +void controlb(controlt * c, u16 avp, char *val, unsigned int len, u8 m); +controlt *controlnew(u16 mtype); +void controlnull(tunnelidt t); +void controladd(controlt * c, tunnelidt t, sessionidt s); +void tunnelsend(u8 * buf, u16 l, tunnelidt t); +void tunnelkill(tunnelidt t, char *reason); +void tunnelshutdown(tunnelidt t, char *reason); +void sendipcp(tunnelidt t, sessionidt s); +void processipout(u8 * buf, int len); +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); +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) +#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); +void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response); +int sessionsetup(tunnelidt t, sessionidt s, u8 routes); +int cluster_send_session(int s); +int cluster_send_tunnel(int t); +#ifdef HAVE_LIBCLI +void init_cli(); +void cli_do(int sockfd); +#endif +#ifdef RINGBUFFER +void ringbuffer_dump(FILE *stream); +#endif +void initplugins(); +int run_plugins(int plugin_type, void *data); +void add_plugin(char *plugin_name); +void remove_plugin(char *plugin_name); diff --git a/ll.c b/ll.c new file mode 100644 index 0000000..a4aad8b --- /dev/null +++ b/ll.c @@ -0,0 +1,141 @@ +// L2TPNS Linked List Stuff +// $Id: ll.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ll.h" + +linked_list *ll_init() +{ + return (linked_list *)calloc(sizeof(linked_list), 1); +} + +void ll_done(linked_list *l) +{ + li *i = l->head, *n; + + while (i) + { + n = i->next; + free(i); + i = n; + } + + free(l); +} + +li *ll_push(linked_list *l, void *data) +{ + li *i; + + if (!l) return NULL; + if (!(i = (li *)calloc(sizeof(li), 1))) return NULL; + + i->data = data; + i->next = NULL; + if (l->end) + l->end->next = i; + else + l->head = i; + l->end = i; + + return i; +} + +void *ll_pop(linked_list *l) +{ + li *i; + void *data; + + if (!l) return NULL; + if (!l->head) + return NULL; + + data = l->head->data; + i = l->head->next; + free(l->head); + l->head = i; + return data; +} + +void ll_iterate(linked_list *l, int(*func)(void *)) +{ + li *i; + if (!l || !func) return; + + for (i = l->head; i; i = i->next) + { + if (i->data) + if (!func(i)) + break; + } +} + +void ll_reset(linked_list *l) +{ + if (!l) return; + l->current = NULL; +} + +void *ll_next(linked_list *l) +{ + if (!l) return NULL; + if (!l->current) + l->current = l->head; + else + l->current = l->current->next; + if (!l->current) + return NULL; + return l->current->data; +} + +void ll_delete(linked_list *l, void *data) +{ + li *i = l->head, *p = NULL; + + while (i) + { + if (i->data == data) + { + if (l->head == i) l->head = i->next; + if (l->end == i) l->end = i->next; + if (p) p->next = i->next; + free(i); + l->current = NULL; + return; + } + p = i; + i = i->next; + } +} + +int ll_size(linked_list *l) +{ + int count = 0; + li *i; + + if (!l) return 0; + + for (i = l->head; i; i = i->next) + if (i->data) count++; + + return count; +} + +int ll_contains(linked_list *l, void *search) +{ + li *i; + for (i = l->head; i; i = i->next) + if (i->data == search) + return 1; + return 0; +} + diff --git a/ll.h b/ll.h new file mode 100644 index 0000000..ad4d30c --- /dev/null +++ b/ll.h @@ -0,0 +1,28 @@ +#ifndef __LL_H__ +#define __LL_H__ + +typedef struct s_li +{ + void *data; + struct s_li *next; +} li; + +typedef struct s_ll +{ + li *head; + li *end; + li *current; +} linked_list; + +linked_list *ll_init(); +void ll_done(linked_list *l); +li *ll_push(linked_list *l, void *data); +void ll_delete(linked_list *l, void *data); +void *ll_pop(linked_list *l); +void ll_iterate(linked_list *l, int(*func)(void *)); +void ll_reset(linked_list *l); +void *ll_next(linked_list *l); +int ll_size(linked_list *l); +int ll_contains(linked_list *l, void *search); + +#endif diff --git a/machines.cfg b/machines.cfg new file mode 100644 index 0000000..a10a694 --- /dev/null +++ b/machines.cfg @@ -0,0 +1,39 @@ +#!/usr/bin/perl -w +# vim:ft=perl + +$m{$_class} = { + IPfilter => "iptables", + + File_append => [ + [ "/etc/modules.conf", "alias char-major-10-200 tun\n", "char-major-10-200", "depmod -a" ], + ], + inittab_include => [ + "$_path/src/l2tpns", + ], + rpm_check => [ + "$_path/rpm/libcli-1.2.0-1.i386.rpm", + 'iproute', + 'perl-Compress-Zlib', + 'perl-MLDBM', + 'perl-Storable', + ], + Firewall => { + 'all' => '1701:udp', + }, + F_Firewall => { + all => 'all', + }, + Sysctl => { + 'net.ipv4.ip_forward' => 1, + 'net.ipv4.conf.all.proxy_arp' => 1, + 'net.core.rmem_max' => 8388608, + 'net.core.wmem_max' => 8388608, + 'net.core.rmem_default' => 8388608, + 'net.core.wmem_default' => 8388608, + 'net.ipv4.tcp_rmem' => '4096 65530 128388607', + 'net.ipv4.tcp_wmem' => '4096 65530 128388607', + }, + File_install => [ + [ "/etc/logrotate.d/l2tpns", "$_path/etc/l2tpns.logrotate", undef, { mode => 0755 } ], + ], +}; diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..e0691c8 --- /dev/null +++ b/md5.c @@ -0,0 +1,349 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST((UINT4[4], unsigned char[64])); +static void Encode PROTO_LIST((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(context) + MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(context, input, inputLen) + MD5_CTX *context; /* context */ + unsigned char *input; /* input block */ + unsigned int inputLen; /* length of input block */ +{ + unsigned int i, + index, + partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4) inputLen << 3)) < ((UINT4) inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4) inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) + { + MD5_memcpy((POINTER) & context->buffer[index], (POINTER) input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER) & context->buffer[index], (POINTER) & input[i], inputLen - i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(digest, context) + unsigned char digest[16]; /* message digest */ + MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, + padLen; + + /* Save number of bits */ + Encode(bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. + */ + MD5_memset((POINTER) context, 0, sizeof(*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform(state, block) + UINT4 state[4]; + unsigned char block[64]; +{ + UINT4 a = state[0], + b = state[1], + c = state[2], + d = state[3], + x[16]; + + Decode(x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + + */ + MD5_memset((POINTER) x, 0, sizeof(x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(output, input, len) + unsigned char *output; + UINT4 *input; + unsigned int len; +{ + unsigned int i, + j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output[j] = (unsigned char) (input[i] & 0xff); + output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff); + output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff); + output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(output, input, len) + UINT4 *output; + unsigned char *input; + unsigned int len; +{ + unsigned int i, + j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) | (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void +MD5_memcpy(output, input, len) + POINTER output; + POINTER input; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void +MD5_memset(output, value, len) + POINTER output; + int value; + unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *) output)[i] = (char) value; +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..53ecf51 --- /dev/null +++ b/md5.h @@ -0,0 +1,72 @@ +/* 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 + + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +typedef unsigned long int UINT4; + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + diff --git a/nsctl.c b/nsctl.c new file mode 100644 index 0000000..8a8aee0 --- /dev/null +++ b/nsctl.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "control.h" + +struct { char *command; int pkt_type; int params; } commands[] = { + { "load_plugin", PKT_LOAD_PLUGIN, 1 }, + { "unload_plugin", PKT_UNLOAD_PLUGIN, 1 }, + { "garden", PKT_GARDEN, 1 }, + { "ungarden", PKT_UNGARDEN, 1 }, +}; + +char *dest_host = NULL; +unsigned int dest_port = 1702; +int udpfd; + +int main(int argc, char *argv[]) +{ + int len = 0; + int dest_ip = 0; + int pkt_type = 0; + char *packet = NULL; + int i; + + setbuf(stdout, NULL); + + if (argc < 3) + { + printf("Usage: %s [args...]\n", argv[0]); + return 1; + } + + dest_host = strdup(argv[1]); + + { + // Init socket + int on = 1; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(1703); + udpfd = socket(AF_INET, SOCK_DGRAM, 17); + setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + return(1); + } + } + + { + struct hostent *h = gethostbyname(dest_host); + if (h) dest_ip = ntohl(*(unsigned int *)h->h_addr); + if (!dest_ip) dest_ip = ntohl(inet_addr(dest_host)); + if (!dest_ip) + { + printf("Can't resolve \"%s\"\n", dest_host); + return 0; + } + } + + if (!(packet = calloc(1400, 1))) + { + perror("calloc"); + return(1); + } + + srand(time(NULL)); + + // Deal with command & params + for (i = 0; i < (sizeof(commands) / sizeof(commands[0])); i++) + { + if (strcasecmp(commands[i].command, argv[2]) == 0) + { + int p; + pkt_type = commands[i].pkt_type; + len = new_packet(pkt_type, packet); + if (argc < (commands[i].params + 3)) + { + printf("Not enough parameters for %s\n", argv[2]); + return 1; + } + for (p = 0; p < commands[i].params; p++) + { + strncpy((packet + len), argv[p + 3], 1400 - len); + len += strlen(argv[p + 3]) + 1; + } + break; + } + } + if (!pkt_type) + { + printf("Unknown command\n"); + return 1; + } + + send_packet(udpfd, dest_ip, dest_port, packet, len); + + { + int n; + fd_set r; + struct timeval timeout; + + FD_ZERO(&r); + FD_SET(udpfd, &r); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + n = select(udpfd + 1, &r, 0, 0, &timeout); + if (n <= 0) + { + printf("Timeout waiting for packet\n"); + return 0; + } + } + if ((len = read_packet(udpfd, packet))) + { + printf("Received "); + dump_packet(packet, stdout); + } + + return 0; +} + diff --git a/plugin.h b/plugin.h new file mode 100644 index 0000000..28855e1 --- /dev/null +++ b/plugin.h @@ -0,0 +1,116 @@ +#ifndef __PLUGIN_H__ +#define __PLUGIN_H__ + +#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 + +#define PLUGIN_RET_ERROR 0 +#define PLUGIN_RET_OK 1 +#define PLUGIN_RET_STOP 2 + +struct pluginfuncs +{ + 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); + char *(*inet_toa)(unsigned long addr); + sessionidt (*get_session_by_username)(char *username); + sessiont *(*get_session_by_id)(sessionidt s); + sessionidt (*get_id_by_session)(sessiont *s); + void (*sessionkill)(sessionidt s, char *reason); + u8 (*radiusnew)(sessionidt s); + void (*radiussend)(u8 r, u8 state); +}; + +struct param_pre_auth +{ + tunnelt *t; + sessiont *s; + char *username; + char *password; + int protocol; + int continue_auth; +}; + +struct param_post_auth +{ + tunnelt *t; + sessiont *s; + char *username; + short auth_allowed; + int protocol; +}; + +struct param_packet_rx +{ + tunnelt *t; + sessiont *s; + char *buf; + int len; +}; + +struct param_packet_tx +{ + tunnelt *t; + sessiont *s; + char *buf; + int len; +}; + +struct param_timer +{ + time_t time_now; +}; + +struct param_config +{ + char *key; + char *value; +}; + +struct param_control +{ + char *buf; + int l; + unsigned int source_ip; + unsigned short source_port; + char *response; + int response_length; + int send_response; + short type; + int id; + char *data; + int data_length; +}; + +struct param_new_session +{ + tunnelt *t; + sessiont *s; +}; + +struct param_kill_session +{ + tunnelt *t; + sessiont *s; +}; + +struct param_radius_response +{ + tunnelt *t; + sessiont *s; + char *key; + char *value; +}; + +#endif diff --git a/ppp.c b/ppp.c new file mode 100644 index 0000000..cf9122d --- /dev/null +++ b/ppp.c @@ -0,0 +1,725 @@ +// L2TPNS PPP Stuff +// $Id: ppp.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "constants.h" +#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; + +// Process PAP messages +void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + char user[129]; + char pass[129]; + +#ifdef STAT_CALLS + STAT(call_processpap); +#endif + log_hex(5, "PAP", p, l); + if (l < 4) + { + log(1, 0, s, t, "Short PAP %u bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p != 1) + { + log(1, 0, s, t, "Unexpected PAP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + if (ntohs(*(u16 *) (p + 2)) > l) + { + log(1, 0, s, t, "Length mismatch PAP %d/%d\n", ntohs(*(u16 *) (p + 2)), l); + STAT(tunnel_rx_errors); + return ; + } + { + u8 *b = p; + b += 4; + if (*b && *b < sizeof(user)) + memcpy(user, b + 1, *b); + user[*b] = 0; + b += 1 + *b; + if (*b && *b < sizeof(pass)) + memcpy(pass, b + 1, *b); + pass[*b] = 0; + log(3, 0, s, t, "PAP login %s/%s\n", user, pass); + } + if (session[s].ip || !session[s].radius) + { + // respond now, either no RADIUS available or already authenticated + u8 b[MAXCONTROL]; + u8 id = p[1]; + u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + if (session[s].ip) + *p = 2; // ACK + else + *p = 3; // cant authorise + p[1] = id; + *(u16 *) (p + 2) = htons(5); // length + 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); + } + else + { + log(1, 0, s, t, "No radius session available to authenticate session...\n"); + } + log(3, 0, s, t, "Fallback response to PAP (%s)\n", (session[s].ip) ? "ACK" : "NAK"); + tunnelsend(b, 5 + (p - b), t); // send it + } + else + { // set up RADIUS request + u8 r = session[s].radius; + + // Run PRE_AUTH plugins + struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 }; + run_plugins(PLUGIN_PRE_AUTH, &packet); + if (!packet.continue_auth) + { + log(3, 0, s, t, "A plugin rejected PRE_AUTH\n"); + if (packet.username) free(packet.username); + if (packet.password) free(packet.password); + return; + } + + strncpy(session[s].user, packet.username, sizeof(session[s].user)); + strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass)); + + free(packet.username); + free(packet.password); + + radius[r].id = p[1]; + log(3, 0, s, t, "Sending login for %s/%s to radius\n", user, pass); + radiussend(r, RADIUSAUTH); + } +} + +// Process CHAP messages +void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + u8 r; + u16 len; + +#ifdef STAT_CALLS + STAT(call_processchap); +#endif + log_hex(5, "CHAP", p, l); + r = session[s].radius; + if (!r) + { + log(1, 0, s, t, "Unexpected CHAP message\n"); + STAT(tunnel_rx_errors); + return; + } + if (*p != 2) + { + log(1, 0, s, t, "Unexpected CHAP response code %d\n", *p); + STAT(tunnel_rx_errors); + return; + } + if (p[1] != radius[r].id) + { + log(1, 0, s, t, "Wrong CHAP response ID %d (should be %d) (%d)\n", p[1], radius[r].id, r); + STAT(tunnel_rx_errors); + return ; + } + len = ntohs(*(u16 *) (p + 2)); + if (len > l) + { + log(1, 0, s, t, "Bad CHAP length %d\n", len); + STAT(tunnel_rx_errors); + return ; + } + if (p[4] != 16) + { + log(1, 0, s, t, "Bad CHAP response length %d\n", p[4]); + STAT(tunnel_rx_errors); + return ; + } + if (len - 21 >= sizeof(session[s].user)) + { + log(1, 0, s, t, "CHAP user too long %d\n", len - 21); + STAT(tunnel_rx_errors); + return ; + } + + // Run PRE_AUTH plugins + { + struct param_pre_auth packet = { &tunnel[t], &session[s], NULL, NULL, PPPCHAP, 1 }; + + packet.username = calloc(len-20, 1); + packet.password = calloc(16, 1); + memcpy(packet.username, p + 21, len - 21); + memcpy(packet.password, p + 5, 16); + + run_plugins(PLUGIN_PRE_AUTH, &packet); + if (!packet.continue_auth) + { + log(3, 0, s, t, "A plugin rejected PRE_AUTH\n"); + if (packet.username) free(packet.username); + if (packet.password) free(packet.password); + return; + } + + strncpy(session[s].user, packet.username, sizeof(session[s].user)); + memcpy(radius[r].pass, packet.password, 16); + + free(packet.username); + free(packet.password); + } + + radius[r].chap = 1; + radiussend(r, RADIUSAUTH); + log(3, 0, s, t, "CHAP login %s\n", session[s].user); +} + +char *ppp_lcp_types[] = { + NULL, + "ConfigReq", + "ConfigAck", + "ConfigNak", + "ConfigRej", + "TerminateReq", + "TerminateAck", + "CodeRej", + "ProtocolRej", + "EchoReq", + "EchoReply", + "DiscardRequest", +}; + +void dumplcp(char *p, int l) +{ + signed int x = l - 3; + char *o = (p + 3); + + log_hex(5, "PPP LCP Packet", p, l); + log(4, 0, 0, 0, "PPP LCP Packet type %d (%s)\n", *p, ppp_lcp_types[(int)*p]); + log(4, 0, 0, 0, "Length: %d\n", l); + if (*p != ConfigReq && *p != ConfigRej && *p != ConfigAck) + return; + + while (x > 2) + { + int type = *(u8 *)(o); + int length = *(u8 *)(o + 1); + if (length == 0) + { + log(4, 0, 0, 0, " Option length is 0...\n"); + break; + } + if (type == 0) + { + log(4, 0, 0, 0, " Option type is 0...\n"); + x -= length; + o += length; + continue; + } + switch (type) + { + case 1: // Maximum-Receive-Unit + log(4, 0, 0, 0, " %s %d\n", lcp_types[type], ntohs(*(u16 *)(o + 2))); + break; + case 3: // Authentication-Protocol + { + int proto = ntohs(*(u16 *)(o + 2)); + log(4, 0, 0, 0, " %s %s\n", lcp_types[type], + proto == 0xC223 ? "CHAP" : "PAP"); + break; + } + case 5: // Magic-Number + { + u32 magicno = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], magicno); + break; + } + case 4: // Quality-Protocol + { + u32 qp = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], qp); + break; + } + case 7: // Protocol-Field-Compression + { + u32 pfc = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], pfc); + break; + } + case 8: // Address-And-Control-Field-Compression + { + u32 afc = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], afc); + break; + } + default: + log(2, 0, 0, 0, " Unknown PPP LCP Option type %d\n", type); + break; + } + x -= length; + o += length; + } +} + +// Process LCP messages +void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ + u8 b[MAXCONTROL]; + u8 *q = NULL; + +#ifdef STAT_CALLS + STAT(call_processlcp); +#endif + log_hex(5, "LCP", p, l); + if (l < 4) + { + log(1, session[s].ip, s, t, "Short LCP %d bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p == ConfigAck) + { + log(3, session[s].ip, s, t, "LCP: Discarding ConfigAck\n"); + } + else if (*p == ConfigReq) + { + signed int x = l - 1; + char *o = (p + 1); + + log(3, session[s].ip, s, t, "LCP: ConfigReq (%d bytes)...\n", l); + + while (x > 2) + { + int type = *(u8 *)(o); + int length = *(u8 *)(o + 1); + if (length == 0 || type == 0) break; + switch (type) + { + case 1: // Maximum-Receive-Unit + session[s].mru = ntohs(*(u16 *)(o + 2)); + break; + case 3: // Authentication-Protocol + { + int proto = ntohs(*(u16 *)(o + 2)); + if (proto == 0xC223) + { + log(2, session[s].ip, s, t, " Remote end is trying to do CHAP. Rejecting it.\n"); + + if (!q) + { + q = makeppp(b, p, l, t, s, PPPLCP); + *q++ = ConfigNak; + } + memcpy(q, o, length); + *(u16 *)(q += 2) = htons(0xC023); // NAK -> Use PAP instead + q += length; + } + break; + } + case 5: // Magic-Number + { +// u32 magicno = ntohl(*(u32 *)(o + 2)); + break; + } + case 4: // Quality-Protocol + { +// u32 qp = ntohl(*(u32 *)(o + 2)); + break; + } + case 7: // Protocol-Field-Compression + { +// u32 pfc = ntohl(*(u32 *)(o + 2)); + break; + } + case 8: // Address-And-Control-Field-Compression + { +// u32 afc = ntohl(*(u32 *)(o + 2)); + break; + } + default: + log(2, session[s].ip, s, t, " Unknown PPP LCP Option type %d\n", type); + break; + } + x -= length; + o += length; + } + + if (!q) + { + // Send back a ConfigAck + log(3, session[s].ip, s, t, "ConfigReq accepted, sending as Ack\n"); + q = makeppp(b, p, l, t, s, PPPLCP); + *q = ConfigAck; + tunnelsend(b, l + (q - b), t); + } + else + { + // Already built a ConfigNak... send it + log(3, session[s].ip, s, t, "Sending ConfigNak\n"); + tunnelsend(b, l + (q - b), t); + + log(3, session[s].ip, s, t, "Sending ConfigReq, requesting PAP login\n"); + q = makeppp(b, NULL, 0, t, s, PPPLCP); + *q++ = ConfigReq; + *(u8 *)(q++) = 3; + *(u8 *)(q++) = 4; + *(u16 *)(q += 2) = htons(0xC023); + tunnelsend(b, l + (q - b), t); + } + } + else if (*p == ConfigNak) + { + log(1, session[s].ip, s, t, "Remote end sent a ConfigNak. Ignoring\n"); + dumplcp(p, l); + return ; + } + else if (*p == TerminateReq) + { + *p = TerminateAck; // close + q = makeppp(b, p, l, t, s, PPPLCP); + log(3, session[s].ip, s, t, "LCP: Received TerminateReq. Sending TerminateAck\n"); + sessionshutdown(s, "Remote end closed connection."); + tunnelsend(b, l + (q - b), t); // send it + } + else if (*p == TerminateReq) + { + sessionshutdown(s, "Remote end closed connection."); + } + else if (*p == EchoReq) + { + *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"); + tunnelsend(b, l + (q - b), t); // send it + } + else if (*p == EchoReply) + { + // Ignore it, last_packet time is set earlier than this. + } + else + { + log(1, session[s].ip, s, t, "Unexpected LCP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } +} + +// Process IPCP messages +void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processipcp); +#endif + log_hex(5, "IPCP", p, l); + if (l < 5) + { + log(1, 0, s, t, "Short IPCP %d bytes", l); + STAT(tunnel_rx_errors); + return ; + } + if (*p == ConfigAck) + { // happy with our IPCP + u8 r = session[s].radius; + if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) + if (!r) + r = radiusnew(s); + if (r) + radiussend(r, RADIUSSTART); // send radius start, having got IPCP at last + return ; // done + } + if (*p != ConfigReq) + { + log(1, 0, s, t, "Unexpected IPCP code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + if (ntohs(*(u16 *) (p + 2)) > l) + { + log(1, 0, s, t, "Length mismatch IPCP %d/%d\n", ntohs(*(u16 *) (p + 2)), l); + STAT(tunnel_rx_errors); + return ; + } + if (!session[s].ip) + { + log(3, 0, s, t, "Waiting on radius reply\n"); + return ; // have to wait on RADIUS eply + } + // form a config reply quoting the IP in the session + { + u8 b[MAXCONTROL]; + u8 *i, + *q; + + q = p + 4; + i = p + l; + while (q < i && q[1]) + { + if (*q != 0x81 && *q != 0x83 && *q != 3) + break; + q += q[1]; + } + if (q < i) + { // reject + u16 n = 4; + i = p + l; + q = makeppp(b, p, l, t, s, PPPIPCP); + *q = ConfigRej; + p += 4; + while (p < i && p[1]) + { + if (*p != 0x81 && *p != 0x83 && *p != 3) + { + log(2, 0, s, t, "IPCP reject %d\n", *p); + memcpy(q + n, p, p[1]); + n += p[1]; + } + p += p[1]; + } + *(u16 *) (q + 2) = htons(n); + tunnelsend(b, n + (q - b), t); // send it + } + else + { + *p = ConfigAck; + i = findppp(p, 0x81); // Primary DNS address + if (i) + { + if (*(u32 *) (i + 2) != htonl(session[s].dns1)) + { + *(u32 *) (i + 2) = htonl(session[s].dns1); + *p = ConfigNak; + } + } + i = findppp(p, 0x83); // Secondary DNS address (TBA, is it) + if (i) + { + if (*(u32 *) (i + 2) != htonl(session[s].dns2)) + { + *(u32 *) (i + 2) = htonl(session[s].dns2); + *p = ConfigNak; + } + } + i = findppp(p, 3); // IP address + if (!i || i[1] != 6) + { + log(1, 0, s, t, "No IP in IPCP request\n"); + STAT(tunnel_rx_errors); + return ; + } + if (*(u32 *) (i + 2) != htonl(session[s].ip)) + { + *(u32 *) (i + 2) = htonl(session[s].ip); + *p = ConfigNak; + } + q = makeppp(b, p, l, t, s, PPPIPCP); + tunnelsend(b, l + (q - b), t); // send it + } + } +} + +// process IP packet received +void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processipin); +#endif + log_hex(5, "IP", p, l); + + if (l > MAXETHER) + { + log(1, *(u32 *)(p + 12), s, t, "IP packet too long %d\n", l); + STAT(tunnel_rx_errors); + return ; + } + + session[s].cin += l; + session[s].pin++; + eth_tx += l; + + // Add on the tun header + p -= 4; + *(u32 *)p = htonl(0x00000800); + l += 4; + + // Plugin hook + { + struct param_packet_rx packet = { &tunnel[t], &session[s], p, l }; + run_plugins(PLUGIN_PACKET_TX, &packet); + } + + // send to ethernet + if (write(tapfd, p, l) < 0) + { + STAT(tap_tx_errors); + log(0, 0, s, t, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", + l, strerror(errno), tapfd, p); + } + + if (session[s].snoop) + { + // Snooping this session, send it to ASIO + snoop_send_packet(p, l); + } + STAT(tap_tx_packets); + INC_STAT(tap_tx_bytes, l); +} + +// Process LCP messages +void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) +{ +#ifdef STAT_CALLS + STAT(call_processccp); +#endif + log_hex(5, "CCP", p, l); + if (l < 2 || (*p != ConfigReq && *p != TerminateReq)) + { + log(1, 0, s, t, "Unexpecetd CCP request code %d\n", *p); + STAT(tunnel_rx_errors); + return ; + } + // reject + { + u8 b[MAXCONTROL]; + u8 *q; + if (*p == ConfigReq) + { + if (l < 6) + { + *p = ConfigAck; // accept no compression + } + else + { + *p = ConfigRej; // reject + } + } + else + *p = TerminateAck; // close + q = makeppp(b, p, l, t, s, PPPCCP); + tunnelsend(b, l + (q - b), t); // send it + } +} + +// send a CHAP PP packet +void sendchap(tunnelidt t, sessionidt s) +{ + u8 b[MAXCONTROL]; + u8 r = session[s].radius; + u8 *q; +#ifdef STAT_CALLS + STAT(call_sendchap); +#endif + if (!r) + { + log(1, 0, s, t, "No RADIUS to send challenge\n"); + STAT(tunnel_tx_errors); + return ; + } + log(1, 0, s, t, "Send CHAP challenge\n"); + { // new challenge + int n; + for (n = 0; n < 15; n++) + radius[r].auth[n] = rand(); + } + radius[r].chap = 1; // CHAP not PAP + radius[r].id++; + if (radius[r].state != RADIUSCHAP) + radius[r].try = 0; + radius[r].state = RADIUSCHAP; + radius[r].retry = backoff(radius[r].try++); + if (radius[r].try > 5) + { + sessionshutdown(s, "Timeout CHAP"); + STAT(tunnel_tx_errors); + return ; + } + q = makeppp(b, 0, 0, t, s, PPPCHAP); + *q = 1; // challenhe + q[1] = radius[r].id; // ID + q[4] = 16; // length + memcpy(q + 5, radius[r].auth, 16); // challenge + strcpy(q + 21, hostname); // our name + *(u16 *) (q + 2) = htons(strlen(hostname) + 21); // length + tunnelsend(b, strlen(hostname) + 21 + (q - b), t); // send it +} + +// fill in a L2TP message with a PPP frame, +// copies existing PPP message and changes magic number if seen +// returns start of PPP frame +u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype) +{ + *(u16 *) (b + 0) = htons(0x0002); // L2TP with no options + *(u16 *) (b + 2) = htons(tunnel[t].far); // tunnel + *(u16 *) (b + 4) = htons(session[s].far); // session + b += 6; + if (mtype != PPPLCP && !(session[s].flags & SESSIONACFC)) + { + *(u16 *) b = htons(0xFF03); // HDLC header + b += 2; + } + if (mtype < 0x100 && session[s].flags & SESSIONPFC) + *b++ = mtype; + else + { + *(u16 *) b = htons(mtype); + b += 2; + } + if (p && l) + memcpy(b, p, l); + return b; +} + +// find a PPP option, returns point to option, or 0 if not found +u8 *findppp(u8 * b, u8 mtype) +{ + u16 l = ntohs(*(u16 *) (b + 2)); + if (l < 4) + return 0; + b += 4; + l -= 4; + while (l) + { + if (l < b[1] || !b[1]) + return 0; // faulty + if (*b == mtype) + return b; + l -= b[1]; + b += b[1]; + } + return 0; +} + +// Send initial LCP ConfigReq +void initlcp(tunnelidt t, sessionidt s) +{ + char b[500] = {0}, *q; + + q = makeppp(b, NULL, 0, t, s, PPPLCP); + log(4, 0, s, t, "Sending LCP ConfigReq for PAP\n"); + *q = ConfigReq; + *(u8 *)(q + 1) = (time_now % 255) + 1; // ID + *(u16 *)(q + 2) = htons(8); // Length + *(u8 *)(q + 4) = 3; + *(u8 *)(q + 5) = 4; + *(u16 *)(q + 6) = htons(0xC023); // PAP + tunnelsend(b, 12 + 8, t); +} + diff --git a/radius.c b/radius.c new file mode 100644 index 0000000..3f1d541 --- /dev/null +++ b/radius.c @@ -0,0 +1,614 @@ +// L2TPNS Radius Stuff +// $Id: radius.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "md5.h" +#include "constants.h" +#include "l2tpns.h" +#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; + +const char *radius_state(int state) +{ + static char *tmp = NULL; + int i; + for (i = 0; radius_states[i]; i++) + if (i == state) return radius_states[i]; + + if (tmp == NULL) tmp = (char *)calloc(64, 1); + sprintf(tmp, "%d", state); + return tmp; +} + +// Set up socket for radius requests +void initrad(void) +{ + radfd = socket(AF_INET, SOCK_DGRAM, UDP); +} + +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; +} + +u8 radiusnew(sessionidt s) +{ + u8 r; + if (!radiusfree) + { + 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])); + radius[r].session = s; + return r; +} + +// Send a RADIUS request +void radiussend(u8 r, u8 state) +{ + struct sockaddr_in addr; + u8 b[4096]; // RADIUS packet + char pass[129]; + int pl; + u8 *p; + sessionidt s; +#ifdef STAT_CALLS + STAT(call_radiussend); +#endif + if (!numradiusservers) + { + log(0, 0, 0, 0, "No RADIUS servers\n"); + return; + } + if (!radiussecret) + { + log(0, 0, 0, 0, "No RADIUS secret\n"); + return; + } + s = radius[r].session; + + if (state != RADIUSAUTH && !radius_accounting) + { + // Radius accounting is turned off + radiusclear(r, s); + return; + } + + if (radius[r].state != state) + radius[r].try = 0; + 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 (s) + { + sessionshutdown(s, "RADIUS timeout"); + STAT(radius_timeout); + } + else + { + STAT(radius_retries); + radius[r].state = RADIUSWAIT; + radius[r].retry = 100; + } + return ; + } + // contruct RADIUS access request + switch (state) + { + case RADIUSAUTH: + b[0] = 1; // access request + break; + case RADIUSSTART: + case RADIUSSTOP: + b[0] = 4; // accounting request + break; + default: + log(0, 0, 0, 0, "Unknown radius state %d\n", state); + } + b[1] = r; // identifier + memcpy(b + 4, radius[r].auth, 16); + p = b + 20; + if (s) + { + *p = 1; // user name + p[1] = strlen(session[s].user) + 2; + strcpy(p + 2, session[s].user); + p += p[1]; + } + if (state == RADIUSAUTH) + { + if (radius[r].chap) + { + *p = 3; // CHAP password + p[1] = 19; // length + p[2] = radius[r].id; // ID + memcpy(p + 3, radius[r].pass, 16); // response from CHAP request + p += p[1]; + *p = 60; // CHAP Challenge + p[1] = 18; // length + memcpy(p + 2, radius[r].auth, 16); + p += p[1]; + } + else + { + strcpy(pass, radius[r].pass); + pl = strlen(pass); + while (pl & 15) + pass[pl++] = 0; // pad + if (pl) + { // encrypt + hasht hash; + int p = 0; + while (p < pl) + { + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, radiussecret, strlen(radiussecret)); + if (p) + MD5Update(&ctx, pass + p - 16, 16); + else + MD5Update(&ctx, radius[r].auth, 16); + MD5Final(hash, &ctx); + do + { + pass[p] ^= hash[p & 15]; + p++; + } + while (p & 15); + } + } + *p = 2; // password + p[1] = pl + 2; + if (pl) + memcpy(p + 2, pass, pl); + p += p[1]; + } + } + else if (state == RADIUSSTART || state == RADIUSSTOP) + { // accounting + *p = 40; // accounting type + p[1] = 6; + *(u32 *) (p + 2) = htonl((state == RADIUSSTART) ? 1 : 2); + p += p[1]; + if (s) + { + *p = 44; // session ID + p[1] = 18; + sprintf(p + 2, "%08X%08X", session[s].id, session[s].opened); + p += p[1]; + if (state == RADIUSSTOP) + { // stop + *p = 42; // input octets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].cin); + p += p[1]; + *p = 43; // output octets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].cout); + p += p[1]; + *p = 46; // session time + p[1] = 6; + *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened); + p += p[1]; + *p = 47; // input packets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].pin); + p += p[1]; + *p = 48; // output spackets + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].pout); + p += p[1]; + } + else + { // start + *p = 41; // delay + p[1] = 6; + *(u32 *) (p + 2) = htonl(time(NULL) - session[s].opened); + p += p[1]; + } + } + } + if (s) + { + *p = 5; // NAS-Port + p[1] = 6; + *(u32 *) (p + 2) = htonl(s); + p += p[1]; + } + if (s && session[s].ip) + { + *p = 8; // Framed-IP-Address + p[1] = 6; + *(u32 *) (p + 2) = htonl(session[s].ip); + p += p[1]; + } + if (*session[s].called) + { + *p = 30; // called + p[1] = strlen(session[s].called) + 2; + strcpy(p + 2, session[s].called); + p += p[1]; + } + if (*radius[r].calling) + { + *p = 31; // calling + p[1] = strlen(radius[r].calling) + 2; + strcpy(p + 2, radius[r].calling); + p += p[1]; + } + else if (*session[s].calling) + { + *p = 31; // calling + p[1] = strlen(session[s].calling) + 2; + strcpy(p + 2, session[s].calling); + p += p[1]; + } + // NAS-IP-Address + *p = 4; + p[1] = 6; + *(u32 *)(p + 2) = bind_address; + p += p[1]; + + // All AVpairs added + *(u16 *) (b + 2) = htons(p - b); + if (state != RADIUSAUTH) + { + // Build auth for accounting packet + char z[16] = {0}; + char hash[16] = {0}; + MD5_CTX ctx; + MD5Init(&ctx); + MD5Update(&ctx, b, 4); + MD5Update(&ctx, z, 16); + MD5Update(&ctx, b + 20, (p - b) - 20); + MD5Update(&ctx, radiussecret, strlen(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]); + addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT); + + log_hex(5, "RADIUS Send", b, (p - b)); + sendto(radfd, b, p - b, 0, (void *) &addr, sizeof(addr)); +} + +// process RADIUS response +void processrad(u8 * buf, int len) +{ + u8 b[MAXCONTROL]; + MD5_CTX ctx; + u8 r; + sessionidt s; + tunnelidt t = 0; + hasht hash; + u8 routes = 0; + +#ifdef STAT_CALLS + STAT(call_processrad); +#endif + log_hex(5, "RADIUS Response", buf, len); + if (len < 20 || len < ntohs(*(u16 *) (buf + 2))) + { + log(1, 0, 0, 0, "Duff RADIUS response length %d\n", len); + return ; + } + len = ntohs(*(u16 *) (buf + 2)); + r = buf[1]; + s = radius[r].session; + log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n", radius_states[radius[r].state], r, s); + if (!s && radius[r].state != RADIUSSTOP) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); + return; + } + if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); + return; + } + t = session[s].tunnel; + MD5Init(&ctx); + MD5Update(&ctx, buf, 4); + MD5Update(&ctx, radius[r].auth, 16); + MD5Update(&ctx, buf + 20, len - 20); + MD5Update(&ctx, radiussecret, strlen(radiussecret)); + MD5Final(hash, &ctx); + do { + if (memcmp(hash, buf + 4, 16)) + { + log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response\n"); + radius[r].state = RADIUSWAIT; + break; + } + if ((radius[r].state == RADIUSAUTH && *buf != 2 && *buf != 3) || + ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && *buf != 5)) + { + log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response %d\n", *buf); + radius[r].state = RADIUSWAIT; + break; + } + if (radius[r].state == RADIUSAUTH) + { + log(4, 0, s, session[s].tunnel, " Original response is \"%s\"\n", (*buf == 2) ? "accept" : "reject"); + // process auth response + if (radius[r].chap) + { + // CHAP + u8 *p = makeppp(b, 0, 0, t, s, PPPCHAP); + + { + struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPCHAP }; + run_plugins(PLUGIN_POST_AUTH, &packet); + *buf = packet.auth_allowed ? 2 : 3; + } + + log(3, 0, s, session[s].tunnel, " CHAP User %s authentication %s.\n", session[s].user, + (*buf == 2) ? "allowed" : "denied"); + *p = (*buf == 2) ? 3 : 4; // ack/nak + p[1] = radius[r].id; + *(u16 *) (p + 2) = ntohs(4); // no message + tunnelsend(b, (p - b) + 4, t); // send it + } + else + { + // PAP + u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + + { + struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPPAP }; + run_plugins(PLUGIN_POST_AUTH, &packet); + *buf = packet.auth_allowed ? 2 : 3; + } + + log(3, 0, s, session[s].tunnel, " PAP User %s authentication %s.\n", session[s].user, + (*buf == 2) ? "allowed" : "denied"); + // ack/nak + *p = *buf; + p[1] = radius[r].id; + *(u16 *) (p + 2) = ntohs(5); + p[4] = 0; // no message + tunnelsend(b, (p - b) + 5, t); // send it + } + + if (*buf == 2) + { + // Login successful + // Extract IP, routes, etc + u8 *p = buf + 20; + u8 *e = buf + len; + for (p = buf + 20; p < e && p[1]; p += p[1]) + { + if (*p == 8) + { + // Statically assigned address + log(3, 0, s, session[s].tunnel, " Radius reply contains IP address %s\n", inet_toa(*(u32 *) (p + 2))); + session[s].ip = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 135) + { + // DNS address + log(3, 0, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + session[s].dns1 = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 136) + { + // DNS address + log(3, 0, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + session[s].dns2 = ntohl(*(u32 *) (p + 2)); + } + else if (*p == 22) + { + // framed-route + ipt ip = 0, mask = 0; + u8 u = 0; + u8 bits = 0; + u8 *n = p + 2; + u8 *e = p + p[1]; + while (n < e && (isdigit(*n) || *n == '.')) + { + if (*n == '.') + { + ip = (ip << 8) + u; + u = 0; + } + else + u = u * 10 + *n - '0'; + n++; + } + ip = (ip << 8) + u; + if (*n == '/') + { + n++; + while (n < e && isdigit(*n)) + bits = bits * 10 + *n++ - '0'; + mask = (( -1) << (32 - bits)); + } + else if ((ip >> 24) < 128) + mask = 0xFF0000; + else if ((ip >> 24) < 192) + mask = 0xFFFF0000; + else + mask = 0xFFFFFF00; + if (routes == MAXROUTE) + { + log(1, 0, s, session[s].tunnel, " Too many routes\n"); + } + else + { + log(3, 0, s, session[s].tunnel, " Radius reply contains route for %d/%d\n", + inet_toa(ip), + inet_toa(mask)); + session[s].route[routes].ip = ip; + session[s].route[routes].mask = mask; + routes++; + } + } + else if (*p == 26) + { + // Vendor-Specific Attribute + int vendor = ntohl(*(int *)(p + 2)); + char attrib = *(p + 6); + char attrib_length = *(p + 7) - 2; + log(3, 0, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%d Attrib=%d Length=%d\n", vendor, attrib, attrib_length); + if (attrib_length == 0) continue; + if (attrib != 1) + log(3, 0, s, session[s].tunnel, " Unknown vendor-specific\n"); + else + { + char *avpair, *value, *key, *newp; + avpair = key = calloc(attrib_length + 1, 1); + memcpy(avpair, p + 8, attrib_length); + log(3, 0, s, session[s].tunnel, " Cisco-Avpair value: %s\n", avpair); + do { + value = strchr(key, '='); + if (!value) break; + *value++ = 0; + + // Trim quotes off reply string + if (*value == '\'' || *value == '\"') + { + char *x; + value++; + x = value + strlen(value) - 1; + if (*x == '\'' || *x == '\"') + *x = 0; + } + + // Run hooks + newp = strchr(value, ','); + if (newp) *newp++ = 0; + { + struct param_radius_response p = { &tunnel[session[s].tunnel], &session[s], key, value }; + run_plugins(PLUGIN_RADIUS_RESPONSE, &p); + } + key = newp; + } while (newp); + free(avpair); + } + } + } + } + else if (*buf == 3) + { + log(2, 0, s, session[s].tunnel, " Authentication denied for %s\n", session[s].user); + break; + } + + // Check for Assign-IP-Address + if (!session[s].ip || session[s].ip == 0xFFFFFFFE) + { + session[s].ip = assign_ip_address(); + 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) + { + session[s].dns1 = htonl(default_dns1); + log(3, 0, s, t, " Sending dns1 = %s\n", inet_toa(default_dns1)); + } + if (!session[s].dns2 && default_dns2) + { + session[s].dns2 = htonl(default_dns2); + log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(default_dns2)); + } + + if (session[s].ip) + { + // Valid Session, set it up + session[s].sid = 0; + sessionsetup(t, s, routes); + } + else + { + log(0, 0, s, t, " End of processrad(), but no valid session exists.\n"); + sessionkill(s, "Can't create valid session"); + } + } + else + { + log(3, 0, s, t, " RADIUS response in state %d\n", radius[r].state); + } + } while (0); + + // finished with RADIUS + radiusclear(r, s); +} + +// Send a retry for RADIUS/CHAP message +void radiusretry(u8 r) +{ + sessionidt s = radius[r].session; + tunnelidt t = 0; +#ifdef STAT_CALLS + STAT(call_radiusretry); +#endif + if (s) + t = session[s].tunnel; + radius[r].retry = 0; + switch (radius[r].state) + { + case RADIUSCHAP: // sending CHAP down PPP + sendchap(t, s); + break; + case RADIUSIPCP: + sendipcp(t, s); // send IPCP + break; + case RADIUSAUTH: // sending auth to RADIUS server + radiussend(r, RADIUSAUTH); + break; + case RADIUSSTART: // sending start accounting to RADIUS server + radiussend(r, RADIUSSTART); + break; + case RADIUSSTOP: // sending stop accounting to RADIUS server + radiussend(r, RADIUSSTOP); + break; + default: + case RADIUSNULL: // Not in use + case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server + // free up RADIUS task + radiusclear(r, s); + log(3, 0, s, session[s].tunnel, "Freeing up radius session %d\n", r); + break; + } +} + diff --git a/rl.c b/rl.c new file mode 100644 index 0000000..7b2084b --- /dev/null +++ b/rl.c @@ -0,0 +1,158 @@ +// L2TPNS Rate Limiting Stuff +// $Id: rl.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#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; + +#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", + NULL + }; + int i; + + if (!rl_rate) return; + + log(2, 0, 0, 0, "Initializing HTB\n"); + for (i = 0; commands[i] && *commands[i]; i++) + { + log(3, 0, 0, 0, "Running \"%s\"\n", commands[i]); + 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 (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); + 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 %s", + t, filter_buckets[t].handle); + log(3, 0, 0, 0, "%s\n", cmd); + system(cmd); +#endif + + next_tbf++; + return t; +} + +u16 rl_get_tbf() +{ + int i; + if (!rl_rate) return 0; + + for (i = 1; i < MAXSESSION; i++) + { + if (!filter_buckets[i].in_use && *filter_buckets[i].handle) + { + filter_buckets[i].in_use = 1; + log(2, 0, 0, 0, "Returning tbf %s\n", filter_buckets[i].handle); + return i; + } + } + i = rl_create_tbf(); + if (i) filter_buckets[i].in_use = 1; + return i; +} + +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); + filter_buckets[t].in_use = 0; +} + +void rl_destroy_tbf(u16 t) +{ + char cmd[2048]; + if (!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); + 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"); + memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); +} + diff --git a/stamp-h b/stamp-h new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h @@ -0,0 +1 @@ +timestamp diff --git a/throttle.c b/throttle.c new file mode 100644 index 0000000..ba551d2 --- /dev/null +++ b/throttle.c @@ -0,0 +1,73 @@ +// L2TPNS Throttle Stuff +// $Id: throttle.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; + +// Throttle or Unthrottle a session +int throttle_session(sessionidt s, int throttle) +{ + if (!rl_rate) return 0; + + if (!*session[s].user) + return 0; // User not logged in + + if (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)), + session[s].tbf); + log(4, 0, s, session[s].tunnel, "Running %s\n", cmd); + system(cmd); + } + else + { + char cmd[2048] = {0}; + log(2, 0, s, session[s].tunnel, "Unthrottling session %d for user %s\n", s, session[s].user); + if (session[s].tbf) + { + int count = 10; + snprintf(cmd, 2048, "iptables -t mangle -D 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); + while (--count) + { + int status = system(cmd); + if (WEXITSTATUS(status) != 0) break; + } + system(cmd); + + rl_done_tbf(session[s].tbf); + session[s].tbf = 0; + } + } + session[s].throttle = throttle; + return 0; +} + diff --git a/util.c b/util.c new file mode 100644 index 0000000..343e956 --- /dev/null +++ b/util.c @@ -0,0 +1,16 @@ +/* Misc util functions */ + +#include "l2tpns.h" + +#include +#include +#include +#include + +char *inet_toa(unsigned long addr) +{ + struct in_addr in; + memcpy(&in, &addr, sizeof(unsigned long)); + return inet_ntoa(in); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..ec2c017 --- /dev/null +++ b/util.h @@ -0,0 +1 @@ +char *inet_toa(unsigned long addr); From 7c1104efffac3af9f068ace3834f2720518ff54a Mon Sep 17 00:00:00 2001 From: David Parrish Date: Wed, 25 Feb 2004 02:44:41 +0000 Subject: [PATCH 002/482] Add license --- COPYING | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. From fc0a36320874bea43b9fd73df0e0990bfd3b59cd Mon Sep 17 00:00:00 2001 From: David Parrish Date: Fri, 5 Mar 2004 00:09:03 +0000 Subject: [PATCH 003/482] * 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 --- Changes | 22 + INSTALL | 5 +- Makefile.in | 1 + arp.c | 5 +- bounce.c | 90 ---- cli.c | 880 ++++++++++++++++++++++---------- cluster.c | 2 +- cluster.h | 3 +- cluster_master.c | 41 +- cluster_slave.c | 63 ++- garden.c | 67 ++- icmp.c | 86 ++++ l2tpns.c | 1276 +++++++++++++++++++++++----------------------- l2tpns.h | 201 +++++--- ll.c | 2 +- md5.h | 2 - nsctl.c | 2 +- plugin.h | 22 +- ppp.c | 18 +- radius.c | 98 ++-- rl.c | 72 +-- throttle.c | 21 +- 22 files changed, 1731 insertions(+), 1248 deletions(-) create mode 100644 Changes delete mode 100644 bounce.c create mode 100644 icmp.c 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; +FILE *save_config_fh; -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); +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) - { - fprintf(w, "Invalid tunnel id \"%s\"\r\n", argv[i]); - continue; - } - 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); + show_all = 1; + } + else + { + // Show individual tunnel + for (i = 0; i < argc; i++) + { + 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); + } + 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"); + cli_print(cli, ""); - 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, "%-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; - fprintf(w, "%-15s %4s %8s %s\r\n", "IP Address", "Used", "Session", "User"); + if (argc > 0 && strcmp(argv[0], "all") == 0) + show_all = 1; + + 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++; } - - fprintf(w, "%-15s %4s %8d %s\r\n", - inet_toa(ip_address_pool[i].address), - (s) ? "Y" : "N", - s, - session[s].user); } - fprintf(w, "\r\nFree: %d\r\nUsed: %d\r\n", free, used); + + if (!show_all) + cli_print(cli, "(Not displaying unused addresses)"); + + 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) + +void print_save_config(struct cli_def *cli, char *string) { - 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); + 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; +} + +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); + } + + cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used); return CLI_OK; } -int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc) +int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc) { - fprintf(w, "Counters cleared\r\n"); + 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_show_banana(struct cli_def *cli, char *command, char **argv, int argc) +{ + 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 (!log_stream && log_filename) - { - if ((log_stream = fopen(log_filename, "a"))) - fseek(log_stream, 0, SEEK_END); - setbuf(log_stream, NULL); - } - if (!log_stream) - { - log_stream = stderr; - setbuf(log_stream, NULL); - } + if (config->debug < level) return; va_start(ap, format); - fprintf(log_stream, "%s %02d/%02d ", time_now_string, t, s); - vfprintf(log_stream, format, ap); + if (log_stream) + { + vsnprintf(message2, 65535, format, ap); + snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2); + fprintf(log_stream, message); + } + else if (syslog_log) + { + 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_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++) + log(level, address, s, t, "%s (%d bytes):\n", title, maxsize); + setvbuf(log_stream, NULL, _IOFBF, 16384); + + for (i = 0; i < maxsize; ) { - fprintf(log_stream, "%02X ", d[j]); - 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); + } + + for (; j < i + 16; j++) + { + 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); } - for (; j < i + 16; j++) - { - 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); + 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; - - if (sb.st_mtime < (time(NULL) - 60)) - { - log(0, 0, 0, 0, "State file is too old to read\n"); - unlink(STATEFILE); - return; - } - - if (!(f = fopen(STATEFILE, "r"))) - { - log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno)); - unlink(STATEFILE); - return; - } - fseek(f, 0, 0); - - log(1, 0, 0, 0, "Reading state information\n"); - { - 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++; - } - { - 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; - } - fclose(f); - log(0, 0, 0, 0, "Loaded saved state information\n"); + // Ignore saved state if NOSTATEFILE exists + if (config->ignore_cluster_updates) + { unlink(STATEFILE); + return; + } + + if (stat(STATEFILE, &sb) < 0) + return; + + 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, "Error reading ip %d from state file: %s\n", i, strerror(errno)); + exit(1); + } + + if (itmp.address != ip_address_pool[i].address) + { + 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) + { + 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"); } void dump_state() { - FILE *f; + FILE *f; + u32 buf[2]; - if (!config_save_state) return; - - if ((f = fopen(STATEFILE, "w"))) - { - u32 i; - log(1, 0, 0, 0, "Dumping state information\n"); - - i = MAXTUNNEL; - fwrite(&i, sizeof(i), 1, f); // Number of tunnels - - log(2, 0, 0, 0, "Dumping %lu tunnels\n", i); - fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f); - - 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); - - fclose(f); - } - else - { - log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno)); - } + if (!config->save_state) return; + + do { + if (!(f = fopen(STATEFILE, "w"))) + break; + + log(1, 0, 0, 0, "Dumping state information\n"); + + if (fwrite(DUMP_MAGIC, sizeof(DUMP_MAGIC)-1, 1, f) != 1) break; + + 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; + + 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; } +static int facility_value(char *name) +{ + 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; +} + +void update_config() +{ + int i; + + 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); + + // Update logging + closelog(); + syslog_log = 0; + if (log_stream) + { + fclose(log_stream); + log_stream = NULL; + } + if (*config->log_filename) + { + if (strstr(config->log_filename, "file:") == config->log_filename) + { + if ((log_stream = fopen((char *)(config->log_filename + 5), "a"))) + { + fseek(log_stream, 0, SEEK_END); + setbuf(log_stream, NULL); + } + else + { + log_stream = stderr; + setbuf(log_stream, NULL); + } + } + else if (strstr(config->log_filename, "syslog:") == config->log_filename) + { + char *p = config->log_filename + 7; + if (*p) + { + openlog("l2tpns", LOG_PID, facility_value(p)); + syslog_log = 1; + } + } + } + else + { + log_stream = stderr; + setbuf(log_stream, NULL); + } + + + // Update radius + config->numradiusservers = 0; + for (i = 0; i < MAXRADSERVER; i++) + if (config->radiusserver[i]) config->numradiusservers++; + + if (!config->numradiusservers) + { + 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]) + { + // Plugin added + add_plugin(config->plugins[i]); + } + else if (*config->old_plugins[i]) + { + // Plugin removed + remove_plugin(config->old_plugins[i]); + } + } + memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); + config->reload_config = 0; +} + void read_config_file() { 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)); + 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; } - if (radiussecret) - { - free(radiussecret); - radiussecret = NULL; - } - - if (l2tpsecret) - { - free(l2tpsecret); - l2tpsecret = NULL; - } - - if (log_filename) - { - free(log_filename); - log_filename = NULL; - } - - if (snoop_destination_host) - { - 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) - { - log(1, 0, 0, 0, "Disabled throttling.\n"); - } - else - { - log(1, 0, 0, 0, "Enabled throttling (rate is %lu kbits/s)\n", rl_rate); - } - } 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) - { - 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; - } - } 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); - } - } - - if (snoop_destination_host) - { - if (inet_aton(snoop_destination_host, &snoop_addr.sin_addr)) - { - snoop_addr.sin_port = htons(snoop_destination_port); - snoop_addr.sin_family = AF_INET; - } - else - { - log(0, 0, 0, 0, "Can't find address for snoop host %s\n", snoop_destination_host); - } - } - - free(buf); + 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) - { - log(0, 0, 0, 0, "No RADIUS servers\n"); - return; - } - if (!radiussecret) - { - log(0, 0, 0, 0, "No RADIUS secret\n"); - return; - } s = radius[r].session; + if (!config->numradiusservers) + { + log(0, 0, s, session[s].tunnel, "No RADIUS servers\n"); + return; + } + if (!*config->radiussecret) + { + log(0, 0, s, session[s].tunnel, "No RADIUS secret\n"); + return; + } - 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); From 0739aa6e77f9567c3018154dd30310b17af63f03 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Fri, 5 Mar 2004 00:21:50 +0000 Subject: [PATCH 004/482] New config file format --- etc/l2tpns.cfg.default | 62 +++++++++++++----------------------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/etc/l2tpns.cfg.default b/etc/l2tpns.cfg.default index 2620ce0..3934559 100644 --- a/etc/l2tpns.cfg.default +++ b/etc/l2tpns.cfg.default @@ -1,43 +1,19 @@ -# Comment out to log to stderr -log file = /var/log/l2tpns - -# This must be defined, or it won't work -# It's only used if the LAC requests tunnel authentication -l2tp secret = foobar - -# You can have multiple radius server entries, but ony one radius secret -radius server = radius.yourdomain.com.au -radius secret = barfoo - -# Turn on or off Radius Accounting (START and STOP records) -radius accounting = 0 - -# Only 2 dns server entries are allowed -dns server = 192.168.1.1 -dns server = 192.168.1.2 - -# Set this to 0 to disable throttling -throttle rate = 0 - -# This can be from 1 to 5 -# At 5, all packets are logged and your system will run -# very slowly -# 2 will show errors only -debug = 2 - -# Save / load state on restart -save state = no - -# Cluster Management -#cluster master = 192.168.1.15 - -# Where accounting information will be dumped. Comment out to disable. -accounting dir = /var/run/l2tpns/acct/ - -# You need to set this to the IP address of the tun interface -# if you want to use clustering -#bind address = 127.0.0.1 - -# Uncomment this if you wish to use the walled garden plugin -#plugin = garden - +set debug 3 +set log_file "syslog:local5" +set l2tp_secret "secret" +set primary_dns 1.2.3.4 +set secondary_dns 1.2.3.5 +set save_state yes +set snoop_host 1.2.3.4 +set snoop_port 41001 +set primary_radius 1.2.3.6 +set secondary_radius 1.2.3.7 +set radius_accounting yes +set radius_secret "secret" +set bind_address 0.0.0.0 +set cluster_master 0.0.0.0 +set throttle_speed 64 +set accounting_dir "/var/run/l2tpns/acct/" +set setuid 0 +set dump_speed no +load plugin "garden" From 0c736f6fe826c7fd17dd6cee106c205c06a93916 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Fri, 5 Mar 2004 00:22:06 +0000 Subject: [PATCH 005/482] Don't use configure it's a waste of time --- Makefile.in => Makefile | 27 +- cli.c | 10 +- cluster_slave.c | 6 +- config.h | 3 + config.h.in | 213 -- configure | 7102 --------------------------------------- configure.in | 48 - garden.c | 12 +- l2tpns.c | 16 +- l2tpns.h | 4 +- ppp.c | 4 +- 11 files changed, 36 insertions(+), 7409 deletions(-) rename Makefile.in => Makefile (76%) create mode 100644 config.h delete mode 100644 config.h.in delete mode 100755 configure delete mode 100644 configure.in diff --git a/Makefile.in b/Makefile similarity index 76% rename from Makefile.in rename to Makefile index 7055f58..76fcd80 100644 --- a/Makefile.in +++ b/Makefile @@ -1,19 +1,13 @@ -subdirs = @subdirs@ -top_srcdir = @top_srcdir@ -srcdir = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ -bindir = @bindir@ -infodir = @infodir@ -etcdir = @sysconfdir@ -libdir = @prefix@/lib/l2tpns +PREFIX= +bindir = $(PREFIX)/usr/sbin +etcdir = $(PREFIX)/etc/l2tpns +libdir = $(PREFIX)/usr/lib/l2tpns -CC = @CC@ -CFLAGS=-Wall @CFLAGS@ -LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ -INSTALL = @INSTALL@ -DEFS = @DEFS@ +CC = gcc +CFLAGS=-Wall -g -O2 +LDFLAGS = +LIBS = -lm -ldl -lcli +INSTALL = /usr/bin/install -c OBJS= md5.o \ icmp.o \ @@ -48,9 +42,6 @@ nsctl: nsctl.o control.o clean: /bin/rm -f *.o *.so l2tpns cluster_master nsctl -distclean: - /bin/rm -f Makefile config.h config.status config.cache config.log - install: all $(INSTALL) -D -o root -g root -m 0755 l2tpns $(bindir)/l2tpns $(INSTALL) -D -o root -g root -m 0755 cluster_master $(bindir)/cluster_master diff --git a/cli.c b/cli.c index af73a6c..e796787 100644 --- a/cli.c +++ b/cli.c @@ -1,5 +1,5 @@ // L2TPNS Command Line Interface -// $Id: cli.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: cli.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ // vim: sw=4 ts=8 #include @@ -39,7 +39,7 @@ extern char hostname[]; extern struct Tringbuffer *ringbuffer; #endif -char *rcs_id = "$Id: cli.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $"; +char *rcs_id = "$Id: cli.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $"; char *debug_levels[] = { "CRIT", @@ -290,7 +290,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) 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, " Walled Garden: %s", session[s].walled_garden ? "YES" : "no"); cli_print(cli, " Filter Bucket: %s", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none"); } return CLI_OK; @@ -304,7 +304,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) "IP", "I", "T", - "S", + "G", "opened", "downloaded", "uploaded", @@ -324,7 +324,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) userip, (session[i].snoop) ? "Y" : "N", (session[i].throttle) ? "Y" : "N", - (session[i].servicenet) ? "Y" : "N", + (session[i].walled_garden) ? "Y" : "N", abs(time_now - (unsigned long)session[i].opened), (unsigned long)session[i].total_cout, (unsigned long)session[i].total_cin, diff --git a/cluster_slave.c b/cluster_slave.c index 289d96d..534e21f 100644 --- a/cluster_slave.c +++ b/cluster_slave.c @@ -1,5 +1,5 @@ // L2TPNS Cluster Master -// $Id: cluster_slave.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: cluster_slave.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ #include #include @@ -164,10 +164,6 @@ int handle_session(char *buf, int l) } } } - /* - if (session[s].servicenet) - servicenet_session(s, 1); - */ return 0; } diff --git a/config.h b/config.h new file mode 100644 index 0000000..b5b3405 --- /dev/null +++ b/config.h @@ -0,0 +1,3 @@ +#define LIBDIR "/usr/lib/l2tpns" +#define ETCDIR "/etc/l2tpns" +#define BINDIR "/usr/sbin" diff --git a/config.h.in b/config.h.in deleted file mode 100644 index 16b6b08..0000000 --- a/config.h.in +++ /dev/null @@ -1,213 +0,0 @@ -/* config.h.in. Generated from configure.in by autoheader. */ - -/* Define to 1 if you have the `alarm' function. */ -#undef HAVE_ALARM - -/* Define to 1 if you have the header file. */ -#undef HAVE_ARPA_INET_H - -/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -#undef HAVE_DOPRNT - -/* Define to 1 if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define to 1 if you have the `fork' function. */ -#undef HAVE_FORK - -/* Define to 1 if you have the `gethostbyname' function. */ -#undef HAVE_GETHOSTBYNAME - -/* Define to 1 if you have the `gethostname' function. */ -#undef HAVE_GETHOSTNAME - -/* Define to 1 if you have the `getpagesize' function. */ -#undef HAVE_GETPAGESIZE - -/* Define to 1 if you have the `gettimeofday' function. */ -#undef HAVE_GETTIMEOFDAY - -/* Define to 1 if you have the `inet_ntoa' function. */ -#undef HAVE_INET_NTOA - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#undef HAVE_MALLOC - -/* Define to 1 if you have the header file. */ -#undef HAVE_MALLOC_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the `memset' function. */ -#undef HAVE_MEMSET - -/* Define to 1 if you have a working `mmap' system call. */ -#undef HAVE_MMAP - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETDB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_NETINET_IN_H - -/* Define to 1 if you have the `pow' function. */ -#undef HAVE_POW - -/* Define to 1 if you have the `select' function. */ -#undef HAVE_SELECT - -/* Define to 1 if you have the `socket' function. */ -#undef HAVE_SOCKET - -/* Define to 1 if `stat' has the bug that it succeeds when given the - zero-length file name argument. */ -#undef HAVE_STAT_EMPTY_STRING_BUG - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP - -/* Define to 1 if you have the `strchr' function. */ -#undef HAVE_STRCHR - -/* Define to 1 if you have the `strdup' function. */ -#undef HAVE_STRDUP - -/* Define to 1 if you have the `strerror' function. */ -#undef HAVE_STRERROR - -/* Define to 1 if you have the `strftime' function. */ -#undef HAVE_STRFTIME - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strrchr' function. */ -#undef HAVE_STRRCHR - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_FILE_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_IOCTL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SELECT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SOCKET_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have that is POSIX.1 compatible. */ -#undef HAVE_SYS_WAIT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `vfork' function. */ -#undef HAVE_VFORK - -/* Define to 1 if you have the header file. */ -#undef HAVE_VFORK_H - -/* Define to 1 if you have the `vprintf' function. */ -#undef HAVE_VPRINTF - -/* Define to 1 if `fork' works. */ -#undef HAVE_WORKING_FORK - -/* Define to 1 if `vfork' works. */ -#undef HAVE_WORKING_VFORK - -/* Define to 1 if `lstat' dereferences a symlink specified with a trailing - slash. */ -#undef LSTAT_FOLLOWS_SLASHED_SYMLINK - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if the C compiler supports function prototypes. */ -#undef PROTOTYPES - -/* Define as the return type of signal handlers (`int' or `void'). */ -#undef RETSIGTYPE - -/* Define to the type of arg 1 for `select'. */ -#undef SELECT_TYPE_ARG1 - -/* Define to the type of args 2, 3 and 4 for `select'. */ -#undef SELECT_TYPE_ARG234 - -/* Define to the type of arg 5 for `select'. */ -#undef SELECT_TYPE_ARG5 - -/* Define to 1 if the `setvbuf' function takes the buffering type as its - second argument and the buffer pointer as the third, as on System V before - release 3. */ -#undef SETVBUF_REVERSED - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Define to 1 if you can safely include both and . */ -#undef TIME_WITH_SYS_TIME - -/* Define like PROTOTYPES; this can be used by system headers. */ -#undef __PROTOTYPES - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to rpl_malloc if the replacement function should be used. */ -#undef malloc - -/* Define to `int' if does not define. */ -#undef pid_t - -/* Define to `unsigned' if does not define. */ -#undef size_t - -/* Define as `fork' if `vfork' does not work. */ -#undef vfork - -#undef HAVE_LIBCLI -#undef HAVE_LIBM -#undef HAVE_LIBDL - -#undef LIBDIR -#undef ETCDIR -#undef BINDIR - diff --git a/configure b/configure deleted file mode 100755 index 52c95f2..0000000 --- a/configure +++ /dev/null @@ -1,7102 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.58 for l2tpns 1.0.0. -# -# Report bugs to . -# -# Copyright (C) 2003 Free Software Foundation, Inc. -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' -elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then - set -o posix -fi -DUALCASE=1; export DUALCASE # for MKS sh - -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi - - -# Work around bugs in pre-3.0 UWIN ksh. -$as_unset ENV MAIL MAILPATH -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - $as_unset $as_var - fi -done - -# Required to use basename. -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - - -# Name of the executable. -as_me=`$as_basename "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)$' \| \ - . : '\(.\)' 2>/dev/null || -echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } - /^X\/\(\/\/\)$/{ s//\1/; q; } - /^X\/\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - - -# PATH needs CR, and LINENO needs CR and PATH. -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" || { - # Find who we are. Look in the path if we contain no path at all - # relative or not. - case $0 in - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done - - ;; - esac - # We did not find ourselves, most probably we were run as `sh COMMAND' - # in which case we are not to be found in the path. - if test "x$as_myself" = x; then - as_myself=$0 - fi - if test ! -f "$as_myself"; then - { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 - { (exit 1); exit 1; }; } - fi - case $CONFIG_SHELL in - '') - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for as_base in sh bash ksh sh5; do - case $as_dir in - /*) - if ("$as_dir/$as_base" -c ' - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then - $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } - $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } - CONFIG_SHELL=$as_dir/$as_base - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$0" ${1+"$@"} - fi;; - esac - done -done -;; - esac - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line before each line; the second 'sed' does the real - # work. The second script uses 'N' to pair each line-number line - # with the numbered line, and appends trailing '-' during - # substitution so that $LINENO is not a special case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) - sed '=' <$as_myself | - sed ' - N - s,$,-, - : loop - s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, - t loop - s,-$,, - s,^['$as_cr_digits']*\n,, - ' >$as_me.lineno && - chmod +x $as_me.lineno || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensible to this). - . ./$as_me.lineno - # Exit status is that of the last command. - exit -} - - -case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in - *c*,-n*) ECHO_N= ECHO_C=' -' ECHO_T=' ' ;; - *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; - *) ECHO_N= ECHO_C='\c' ECHO_T= ;; -esac - -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - # We could just check for DJGPP; but this test a) works b) is more generic - # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). - if test -f conf$$.exe; then - # Don't use ln at all; we don't have any links - as_ln_s='cp -p' - else - as_ln_s='ln -s' - fi -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.file - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_executable_p="test -f" - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -# IFS -# We need space, tab and new line, in precisely that order. -as_nl=' -' -IFS=" $as_nl" - -# CDPATH. -$as_unset CDPATH - - -# Name of the host. -# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -exec 6>&1 - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_config_libobj_dir=. -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= -SHELL=${CONFIG_SHELL-/bin/sh} - -# Maximum number of lines to put in a shell here document. -# This variable seems obsolete. It should probably be removed, and -# only ac_max_sed_lines should be used. -: ${ac_max_here_lines=38} - -# Identity of this package. -PACKAGE_NAME='l2tpns' -PACKAGE_TARNAME='l2tpns' -PACKAGE_VERSION='1.0.0' -PACKAGE_STRING='l2tpns 1.0.0' -PACKAGE_BUGREPORT='fred_nerk@sourceforge.net' - -ac_unique_file="ll.c" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#if HAVE_SYS_TYPES_H -# include -#endif -#if HAVE_SYS_STAT_H -# include -#endif -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -#endif -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif -#if HAVE_STRINGS_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CPP EGREP LIBOBJS LTLIBOBJS' -ac_subst_files='' - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datadir='${prefix}/share' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -libdir='${exec_prefix}/lib' -includedir='${prefix}/include' -oldincludedir='/usr/include' -infodir='${prefix}/info' -mandir='${prefix}/man' - -ac_prev= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval "$ac_prev=\$ac_option" - ac_prev= - continue - fi - - ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_option in - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad | --data | --dat | --da) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ - | --da=*) - datadir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/-/_/g'` - eval "enable_$ac_feature=no" ;; - - -enable-* | --enable-*) - ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/-/_/g'` - case $ac_option in - *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; - *) ac_optarg=yes ;; - esac - eval "enable_$ac_feature='$ac_optarg'" ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst \ - | --locals | --local | --loca | --loc | --lo) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* \ - | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package| sed 's/-/_/g'` - case $ac_option in - *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; - *) ac_optarg=yes ;; - esac - eval "with_$ac_package='$ac_optarg'" ;; - - -without-* | --without-*) - ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/-/_/g'` - eval "with_$ac_package=no" ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) { echo "$as_me: error: unrecognized option: $ac_option -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 - { (exit 1); exit 1; }; } - ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` - eval "$ac_envvar='$ac_optarg'" - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - { echo "$as_me: error: missing argument to $ac_option" >&2 - { (exit 1); exit 1; }; } -fi - -# Be sure to have absolute paths. -for ac_var in exec_prefix prefix -do - eval ac_val=$`echo $ac_var` - case $ac_val in - [\\/$]* | ?:[\\/]* | NONE | '' ) ;; - *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; };; - esac -done - -# Be sure to have absolute paths. -for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ - localstatedir libdir includedir oldincludedir infodir mandir -do - eval ac_val=$`echo $ac_var` - case $ac_val in - [\\/$]* | ?:[\\/]* ) ;; - *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; };; - esac -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used." >&2 - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then its parent. - ac_confdir=`(dirname "$0") 2>/dev/null || -$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$0" : 'X\(//\)[^/]' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$0" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r $srcdir/$ac_unique_file; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r $srcdir/$ac_unique_file; then - if test "$ac_srcdir_defaulted" = yes; then - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 - { (exit 1); exit 1; }; } - else - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 - { (exit 1); exit 1; }; } - fi -fi -(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || - { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 - { (exit 1); exit 1; }; } -srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` -ac_env_build_alias_set=${build_alias+set} -ac_env_build_alias_value=$build_alias -ac_cv_env_build_alias_set=${build_alias+set} -ac_cv_env_build_alias_value=$build_alias -ac_env_host_alias_set=${host_alias+set} -ac_env_host_alias_value=$host_alias -ac_cv_env_host_alias_set=${host_alias+set} -ac_cv_env_host_alias_value=$host_alias -ac_env_target_alias_set=${target_alias+set} -ac_env_target_alias_value=$target_alias -ac_cv_env_target_alias_set=${target_alias+set} -ac_cv_env_target_alias_value=$target_alias -ac_env_CC_set=${CC+set} -ac_env_CC_value=$CC -ac_cv_env_CC_set=${CC+set} -ac_cv_env_CC_value=$CC -ac_env_CFLAGS_set=${CFLAGS+set} -ac_env_CFLAGS_value=$CFLAGS -ac_cv_env_CFLAGS_set=${CFLAGS+set} -ac_cv_env_CFLAGS_value=$CFLAGS -ac_env_LDFLAGS_set=${LDFLAGS+set} -ac_env_LDFLAGS_value=$LDFLAGS -ac_cv_env_LDFLAGS_set=${LDFLAGS+set} -ac_cv_env_LDFLAGS_value=$LDFLAGS -ac_env_CPPFLAGS_set=${CPPFLAGS+set} -ac_env_CPPFLAGS_value=$CPPFLAGS -ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} -ac_cv_env_CPPFLAGS_value=$CPPFLAGS -ac_env_CPP_set=${CPP+set} -ac_env_CPP_value=$CPP -ac_cv_env_CPP_set=${CPP+set} -ac_cv_env_CPP_value=$CPP - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures l2tpns 1.0.0 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -_ACEOF - - cat <<_ACEOF -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --datadir=DIR read-only architecture-independent data [PREFIX/share] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --infodir=DIR info documentation [PREFIX/info] - --mandir=DIR man documentation [PREFIX/man] -_ACEOF - - cat <<\_ACEOF -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of l2tpns 1.0.0:";; - esac - cat <<\_ACEOF - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have - headers in a nonstandard directory - CPP C preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - ac_popdir=`pwd` - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d $ac_dir || continue - ac_builddir=. - -if test "$ac_dir" != .; then - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A "../" for each directory in $ac_dir_suffix. - ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` -else - ac_dir_suffix= ac_top_builddir= -fi - -case $srcdir in - .) # No --srcdir option. We are building in place. - ac_srcdir=. - if test -z "$ac_top_builddir"; then - ac_top_srcdir=. - else - ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` - fi ;; - [\\/]* | ?:[\\/]* ) # Absolute path. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir ;; - *) # Relative path. - ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_builddir$srcdir ;; -esac - -# Do not use `cd foo && pwd` to compute absolute paths, because -# the directories may not exist. -case `pwd` in -.) ac_abs_builddir="$ac_dir";; -*) - case "$ac_dir" in - .) ac_abs_builddir=`pwd`;; - [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; - *) ac_abs_builddir=`pwd`/"$ac_dir";; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_builddir=${ac_top_builddir}.;; -*) - case ${ac_top_builddir}. in - .) ac_abs_top_builddir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; - *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_srcdir=$ac_srcdir;; -*) - case $ac_srcdir in - .) ac_abs_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; - *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_srcdir=$ac_top_srcdir;; -*) - case $ac_top_srcdir in - .) ac_abs_top_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; - *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; - esac;; -esac - - cd $ac_dir - # Check for guested configure; otherwise get Cygnus style configure. - if test -f $ac_srcdir/configure.gnu; then - echo - $SHELL $ac_srcdir/configure.gnu --help=recursive - elif test -f $ac_srcdir/configure; then - echo - $SHELL $ac_srcdir/configure --help=recursive - elif test -f $ac_srcdir/configure.ac || - test -f $ac_srcdir/configure.in; then - echo - $ac_configure --help - else - echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi - cd "$ac_popdir" - done -fi - -test -n "$ac_init_help" && exit 0 -if $ac_init_version; then - cat <<\_ACEOF -l2tpns configure 1.0.0 -generated by GNU Autoconf 2.58 - -Copyright (C) 2003 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit 0 -fi -exec 5>config.log -cat >&5 <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by l2tpns $as_me 1.0.0, which was -generated by GNU Autoconf 2.58. Invocation command line was - - $ $0 $@ - -_ACEOF -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -hostinfo = `(hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - echo "PATH: $as_dir" -done - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_sep= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) - ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; - 2) - ac_configure_args1="$ac_configure_args1 '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" - # Get rid of the leading space. - ac_sep=" " - ;; - esac - done -done -$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } -$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Be sure not to use single quotes in there, as some shells, -# such as our DU 5.0 friend, will then `close' the trap. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - cat <<\_ASBOX -## ---------------- ## -## Cache variables. ## -## ---------------- ## -_ASBOX - echo - # The following way of writing the cache mishandles newlines in values, -{ - (set) 2>&1 | - case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in - *ac_space=\ *) - sed -n \ - "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" - ;; - *) - sed -n \ - "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" - ;; - esac; -} - echo - - cat <<\_ASBOX -## ----------------- ## -## Output variables. ## -## ----------------- ## -_ASBOX - echo - for ac_var in $ac_subst_vars - do - eval ac_val=$`echo $ac_var` - echo "$ac_var='"'"'$ac_val'"'"'" - done | sort - echo - - if test -n "$ac_subst_files"; then - cat <<\_ASBOX -## ------------- ## -## Output files. ## -## ------------- ## -_ASBOX - echo - for ac_var in $ac_subst_files - do - eval ac_val=$`echo $ac_var` - echo "$ac_var='"'"'$ac_val'"'"'" - done | sort - echo - fi - - if test -s confdefs.h; then - cat <<\_ASBOX -## ----------- ## -## confdefs.h. ## -## ----------- ## -_ASBOX - echo - sed "/^$/d" confdefs.h | sort - echo - fi - test "$ac_signal" != 0 && - echo "$as_me: caught signal $ac_signal" - echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core && - rm -rf conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status - ' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -rf conftest* confdefs.h -# AIX cpp loses on an empty file, so make sure it contains at least a newline. -echo >confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer explicitly selected file to automatically selected ones. -if test -z "$CONFIG_SITE"; then - if test "x$prefix" != xNONE; then - CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" - else - CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" - fi -fi -for ac_site_file in $CONFIG_SITE; do - if test -r "$ac_site_file"; then - { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 -echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then - { echo "$as_me:$LINENO: loading cache $cache_file" >&5 -echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . $cache_file;; - *) . ./$cache_file;; - esac - fi -else - { echo "$as_me:$LINENO: creating cache $cache_file" >&5 -echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in `(set) 2>&1 | - sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val="\$ac_cv_env_${ac_var}_value" - eval ac_new_val="\$ac_env_${ac_var}_value" - case $ac_old_set,$ac_new_set in - set,) - { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 -echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 -echo "$as_me: former value: $ac_old_val" >&2;} - { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 -echo "$as_me: current value: $ac_new_val" >&2;} - ac_cache_corrupted=: - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) - ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 -echo "$as_me: error: changes in the environment can compromise the build" >&2;} - { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 -echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ac_config_headers="$ac_config_headers config.h" - - -# Checks for programs. -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - CC=$ac_ct_CC -else - CC="$ac_cv_prog_CC" -fi - -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6 -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - - test -n "$ac_ct_CC" && break -done - - CC=$ac_ct_CC -fi - -fi - - -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - -# Provide some information about the compiler. -echo "$as_me:$LINENO:" \ - "checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 - (eval $ac_compiler --version &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 - (eval $ac_compiler -v &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 - (eval $ac_compiler -V &5) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 -echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 -ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` -if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 - (eval $ac_link_default) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Find the output, starting from the most likely. This scheme is -# not robust to junk in `.', hence go to wildcards (a.*) only as a last -# resort. - -# Be careful to initialize this variable, since it used to be cached. -# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. -ac_cv_exeext= -# b.out is created by i960 compilers. -for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) - ;; - conftest.$ac_ext ) - # This is the source file. - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - # FIXME: I believe we export ac_cv_exeext for Libtool, - # but it would be cool to find out if it's true. Does anybody - # maintain Libtool? --akim. - export ac_cv_exeext - break;; - * ) - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: C compiler cannot create executables -See \`config.log' for more details." >&5 -echo "$as_me: error: C compiler cannot create executables -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } -fi - -ac_exeext=$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_file" >&5 -echo "${ECHO_T}$ac_file" >&6 - -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether the C compiler works" >&5 -echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 -# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { echo "$as_me:$LINENO: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - fi - fi -fi -echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 - -rm -f a.out a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -# Check the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 -echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 -echo "$as_me:$LINENO: result: $cross_compiling" >&5 -echo "${ECHO_T}$cross_compiling" >&6 - -echo "$as_me:$LINENO: checking for suffix of executables" >&5 -echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - export ac_cv_exeext - break;; - * ) break;; - esac -done -else - { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest$ac_cv_exeext -echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 -echo "${ECHO_T}$ac_cv_exeext" >&6 - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -echo "$as_me:$LINENO: checking for suffix of object files" >&5 -echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 -if test "${ac_cv_objext+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 -echo "${ECHO_T}$ac_cv_objext" >&6 -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_compiler_gnu=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 -GCC=`test $ac_compiler_gnu = yes && echo yes` -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -CFLAGS="-g" -echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_prog_cc_g=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 -echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 -if test "${ac_cv_prog_cc_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_prog_cc_stdc=no -ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std1 is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std1. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -# Don't try gcc -ansi; that turns off useful extensions and -# breaks some systems' header files. -# AIX -qlanglvl=ansi -# Ultrix and OSF/1 -std1 -# HP-UX 10.20 and later -Ae -# HP-UX older versions -Aa -D_HPUX_SOURCE -# SVR4 -Xc -D__EXTENSIONS__ -for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_prog_cc_stdc=$ac_arg -break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext -done -rm -f conftest.$ac_ext conftest.$ac_objext -CC=$ac_save_CC - -fi - -case "x$ac_cv_prog_cc_stdc" in - x|xno) - echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6 ;; - *) - echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 - CC="$CC $ac_cv_prog_cc_stdc" ;; -esac - -# Some people use a C++ compiler to compile C. Since we use `exit', -# in C++ we need to declare it. In case someone uses the same compiler -# for both compiling C and C++ we need to have the C++ compiler decide -# the declaration of exit, since it's the most demanding environment. -cat >conftest.$ac_ext <<_ACEOF -#ifndef __cplusplus - choke me -#endif -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - for ac_declaration in \ - '' \ - 'extern "C" void std::exit (int) throw (); using std::exit;' \ - 'extern "C" void std::exit (int); using std::exit;' \ - 'extern "C" void exit (int) throw ();' \ - 'extern "C" void exit (int);' \ - 'void exit (int);' -do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -#include -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -continue -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_declaration -int -main () -{ -exit (42); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -done -rm -f conftest* -if test -n "$ac_declaration"; then - echo '#ifdef __cplusplus' >>confdefs.h - echo $ac_declaration >>confdefs.h - echo '#endif' >>confdefs.h -fi - -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_aux_dir= -for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do - if test -f $ac_dir/install-sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f $ac_dir/install.sh; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f $ac_dir/shtool; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 -echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} - { (exit 1); exit 1; }; } -fi -ac_config_guess="$SHELL $ac_aux_dir/config.guess" -ac_config_sub="$SHELL $ac_aux_dir/config.sub" -ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 -echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 -if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in - ./ | .// | /cC/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - done - done - ;; -esac -done - - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. We don't cache a - # path for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the path is relative. - INSTALL=$ac_install_sh - fi -fi -echo "$as_me:$LINENO: result: $INSTALL" >&5 -echo "${ECHO_T}$INSTALL" >&6 - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - - -# Checks for header files. - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 -echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -echo "$as_me:$LINENO: result: $CPP" >&5 -echo "${ECHO_T}$CPP" >&6 -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether non-existent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : -else - { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -echo "$as_me:$LINENO: checking for egrep" >&5 -echo $ECHO_N "checking for egrep... $ECHO_C" >&6 -if test "${ac_cv_prog_egrep+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if echo a | (grep -E '(a|b)') >/dev/null 2>&1 - then ac_cv_prog_egrep='grep -E' - else ac_cv_prog_egrep='egrep' - fi -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 -echo "${ECHO_T}$ac_cv_prog_egrep" >&6 - EGREP=$ac_cv_prog_egrep - - -echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_stdc=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - exit(2); - exit (0); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6 -if test $ac_cv_header_stdc = yes; then - -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF - -fi - -echo "$as_me:$LINENO: checking for sys/wait.h that is POSIX.1 compatible" >&5 -echo $ECHO_N "checking for sys/wait.h that is POSIX.1 compatible... $ECHO_C" >&6 -if test "${ac_cv_header_sys_wait_h+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -#endif -#ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) -#endif - -int -main () -{ - int s; - wait (&s); - s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_sys_wait_h=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_sys_wait_h=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_header_sys_wait_h" >&5 -echo "${ECHO_T}$ac_cv_header_sys_wait_h" >&6 -if test $ac_cv_header_sys_wait_h = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_SYS_WAIT_H 1 -_ACEOF - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. - - - - - - - - - -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default - -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_Header=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_Header=no" -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - - - - - - - - - - - - - -for ac_header in arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## ---------------------------------------- ## -## Report this to fred_nerk@sourceforge.net ## -## ---------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -# Checks for typedefs, structures, and compiler characteristics. -echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 -echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 -if test "${ac_cv_c_const+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ -/* FIXME: Include the comments suggested by Paul. */ -#ifndef __cplusplus - /* Ultrix mips cc rejects this. */ - typedef int charset[2]; - const charset x; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *ccp; - char **p; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - ccp = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++ccp; - p = (char**) ccp; - ccp = (char const *const *) p; - { /* SCO 3.2v4 cc rejects this. */ - char *t; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - } -#endif - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_c_const=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_c_const=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 -echo "${ECHO_T}$ac_cv_c_const" >&6 -if test $ac_cv_c_const = no; then - -cat >>confdefs.h <<\_ACEOF -#define const -_ACEOF - -fi - -echo "$as_me:$LINENO: checking for pid_t" >&5 -echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 -if test "${ac_cv_type_pid_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -if ((pid_t *) 0) - return 0; -if (sizeof (pid_t)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_pid_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_pid_t=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 -echo "${ECHO_T}$ac_cv_type_pid_t" >&6 -if test $ac_cv_type_pid_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define pid_t int -_ACEOF - -fi - -echo "$as_me:$LINENO: checking for size_t" >&5 -echo $ECHO_N "checking for size_t... $ECHO_C" >&6 -if test "${ac_cv_type_size_t+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -if ((size_t *) 0) - return 0; -if (sizeof (size_t)) - return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_size_t=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_size_t=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 -echo "${ECHO_T}$ac_cv_type_size_t" >&6 -if test $ac_cv_type_size_t = yes; then - : -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned -_ACEOF - -fi - -echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 -echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 -if test "${ac_cv_header_time+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include - -int -main () -{ -if ((struct tm *) 0) -return 0; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_header_time=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_header_time=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 -echo "${ECHO_T}$ac_cv_header_time" >&6 -if test $ac_cv_header_time = yes; then - -cat >>confdefs.h <<\_ACEOF -#define TIME_WITH_SYS_TIME 1 -_ACEOF - -fi - - -# Checks for library functions. - - -for ac_header in unistd.h vfork.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## ---------------------------------------- ## -## Report this to fred_nerk@sourceforge.net ## -## ---------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - -for ac_func in fork vfork -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -if test "x$ac_cv_func_fork" = xyes; then - echo "$as_me:$LINENO: checking for working fork" >&5 -echo $ECHO_N "checking for working fork... $ECHO_C" >&6 -if test "${ac_cv_func_fork_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_fork_works=cross -else - cat >conftest.$ac_ext <<_ACEOF -/* By Ruediger Kuhlmann. */ - #include - #if HAVE_UNISTD_H - # include - #endif - /* Some systems only have a dummy stub for fork() */ - int main () - { - if (fork() < 0) - exit (1); - exit (0); - } -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_fork_works=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_fork_works=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 -echo "${ECHO_T}$ac_cv_func_fork_works" >&6 - -else - ac_cv_func_fork_works=$ac_cv_func_fork -fi -if test "x$ac_cv_func_fork_works" = xcross; then - case $host in - *-*-amigaos* | *-*-msdosdjgpp*) - # Override, as these systems have only a dummy fork() stub - ac_cv_func_fork_works=no - ;; - *) - ac_cv_func_fork_works=yes - ;; - esac - { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 -echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} -fi -ac_cv_func_vfork_works=$ac_cv_func_vfork -if test "x$ac_cv_func_vfork" = xyes; then - echo "$as_me:$LINENO: checking for working vfork" >&5 -echo $ECHO_N "checking for working vfork... $ECHO_C" >&6 -if test "${ac_cv_func_vfork_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_vfork_works=cross -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Thanks to Paul Eggert for this test. */ -#include -#include -#include -#include -#include -#if HAVE_UNISTD_H -# include -#endif -#if HAVE_VFORK_H -# include -#endif -/* On some sparc systems, changes by the child to local and incoming - argument registers are propagated back to the parent. The compiler - is told about this with #include , but some compilers - (e.g. gcc -O) don't grok . Test for this by using a - static variable whose address is put into a register that is - clobbered by the vfork. */ -static void -#ifdef __cplusplus -sparc_address_test (int arg) -# else -sparc_address_test (arg) int arg; -#endif -{ - static pid_t child; - if (!child) { - child = vfork (); - if (child < 0) { - perror ("vfork"); - _exit(2); - } - if (!child) { - arg = getpid(); - write(-1, "", 0); - _exit (arg); - } - } -} - -int -main () -{ - pid_t parent = getpid (); - pid_t child; - - sparc_address_test (0); - - child = vfork (); - - if (child == 0) { - /* Here is another test for sparc vfork register problems. This - test uses lots of local variables, at least as many local - variables as main has allocated so far including compiler - temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris - 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should - reuse the register of parent for one of the local variables, - since it will think that parent can't possibly be used any more - in this routine. Assigning to the local variable will thus - munge parent in the parent process. */ - pid_t - p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), - p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); - /* Convince the compiler that p..p7 are live; otherwise, it might - use the same hardware register for all 8 local variables. */ - if (p != p1 || p != p2 || p != p3 || p != p4 - || p != p5 || p != p6 || p != p7) - _exit(1); - - /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent - from child file descriptors. If the child closes a descriptor - before it execs or exits, this munges the parent's descriptor - as well. Test for this by closing stdout in the child. */ - _exit(close(fileno(stdout)) != 0); - } else { - int status; - struct stat st; - - while (wait(&status) != child) - ; - exit( - /* Was there some problem with vforking? */ - child < 0 - - /* Did the child fail? (This shouldn't happen.) */ - || status - - /* Did the vfork/compiler bug occur? */ - || parent != getpid() - - /* Did the file descriptor bug occur? */ - || fstat(fileno(stdout), &st) != 0 - ); - } -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_vfork_works=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_vfork_works=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 -echo "${ECHO_T}$ac_cv_func_vfork_works" >&6 - -fi; -if test "x$ac_cv_func_fork_works" = xcross; then - ac_cv_func_vfork_works=$ac_cv_func_vfork - { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 -echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} -fi - -if test "x$ac_cv_func_vfork_works" = xyes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_WORKING_VFORK 1 -_ACEOF - -else - -cat >>confdefs.h <<\_ACEOF -#define vfork fork -_ACEOF - -fi -if test "x$ac_cv_func_fork_works" = xyes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_WORKING_FORK 1 -_ACEOF - -fi - -if test $ac_cv_c_compiler_gnu = yes; then - echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 -echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 -if test "${ac_cv_prog_gcc_traditional+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_pattern="Autoconf.*'x'" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -Autoconf TIOCGETP -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "$ac_pattern" >/dev/null 2>&1; then - ac_cv_prog_gcc_traditional=yes -else - ac_cv_prog_gcc_traditional=no -fi -rm -f conftest* - - - if test $ac_cv_prog_gcc_traditional = no; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -Autoconf TCGETA -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "$ac_pattern" >/dev/null 2>&1; then - ac_cv_prog_gcc_traditional=yes -fi -rm -f conftest* - - fi -fi -echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 -echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 - if test $ac_cv_prog_gcc_traditional = yes; then - CC="$CC -traditional" - fi -fi - - -for ac_header in stdlib.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## ---------------------------------------- ## -## Report this to fred_nerk@sourceforge.net ## -## ---------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - -echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 -echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6 -if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_malloc_0_nonnull=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#if STDC_HEADERS || HAVE_STDLIB_H -# include -#else -char *malloc (); -#endif - -int -main () -{ -exit (malloc (0) ? 0 : 1); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_malloc_0_nonnull=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_malloc_0_nonnull=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 -echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6 -if test $ac_cv_func_malloc_0_nonnull = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 1 -_ACEOF - -else - cat >>confdefs.h <<\_ACEOF -#define HAVE_MALLOC 0 -_ACEOF - - case $LIBOBJS in - "malloc.$ac_objext" | \ - *" malloc.$ac_objext" | \ - "malloc.$ac_objext "* | \ - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; -esac - - -cat >>confdefs.h <<\_ACEOF -#define malloc rpl_malloc -_ACEOF - -fi - - - -echo "$as_me:$LINENO: checking for working memcmp" >&5 -echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6 -if test "${ac_cv_func_memcmp_working+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_memcmp_working=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ - - /* Some versions of memcmp are not 8-bit clean. */ - char c0 = 0x40, c1 = 0x80, c2 = 0x81; - if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) - exit (1); - - /* The Next x86 OpenStep bug shows up only when comparing 16 bytes - or more and with at least one buffer not starting on a 4-byte boundary. - William Lewis provided this test program. */ - { - char foo[21]; - char bar[21]; - int i; - for (i = 0; i < 4; i++) - { - char *a = foo + i; - char *b = bar + i; - strcpy (a, "--------01111111"); - strcpy (b, "--------10000000"); - if (memcmp (a, b, 16) >= 0) - exit (1); - } - exit (0); - } - - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_memcmp_working=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_memcmp_working=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 -echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6 -test $ac_cv_func_memcmp_working = no && case $LIBOBJS in - "memcmp.$ac_objext" | \ - *" memcmp.$ac_objext" | \ - "memcmp.$ac_objext "* | \ - *" memcmp.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" ;; -esac - - - - -for ac_header in stdlib.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## ---------------------------------------- ## -## Report this to fred_nerk@sourceforge.net ## -## ---------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -for ac_func in getpagesize -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - -echo "$as_me:$LINENO: checking for working mmap" >&5 -echo $ECHO_N "checking for working mmap... $ECHO_C" >&6 -if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_mmap_fixed_mapped=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -/* malloc might have been renamed as rpl_malloc. */ -#undef malloc - -/* Thanks to Mike Haertel and Jim Avera for this test. - Here is a matrix of mmap possibilities: - mmap private not fixed - mmap private fixed at somewhere currently unmapped - mmap private fixed at somewhere already mapped - mmap shared not fixed - mmap shared fixed at somewhere currently unmapped - mmap shared fixed at somewhere already mapped - For private mappings, we should verify that changes cannot be read() - back from the file, nor mmap's back from the file at a different - address. (There have been systems where private was not correctly - implemented like the infamous i386 svr4.0, and systems where the - VM page cache was not coherent with the file system buffer cache - like early versions of FreeBSD and possibly contemporary NetBSD.) - For shared mappings, we should conversely verify that changes get - propagated back to all the places they're supposed to be. - - Grep wants private fixed already mapped. - The main things grep needs to know about mmap are: - * does it exist and is it safe to write into the mmap'd area - * how to use it (BSD variants) */ - -#include -#include - -#if !STDC_HEADERS && !HAVE_STDLIB_H -char *malloc (); -#endif - -/* This mess was copied from the GNU getpagesize.h. */ -#if !HAVE_GETPAGESIZE -/* Assume that all systems that can run configure have sys/param.h. */ -# if !HAVE_SYS_PARAM_H -# define HAVE_SYS_PARAM_H 1 -# endif - -# ifdef _SC_PAGESIZE -# define getpagesize() sysconf(_SC_PAGESIZE) -# else /* no _SC_PAGESIZE */ -# if HAVE_SYS_PARAM_H -# include -# ifdef EXEC_PAGESIZE -# define getpagesize() EXEC_PAGESIZE -# else /* no EXEC_PAGESIZE */ -# ifdef NBPG -# define getpagesize() NBPG * CLSIZE -# ifndef CLSIZE -# define CLSIZE 1 -# endif /* no CLSIZE */ -# else /* no NBPG */ -# ifdef NBPC -# define getpagesize() NBPC -# else /* no NBPC */ -# ifdef PAGESIZE -# define getpagesize() PAGESIZE -# endif /* PAGESIZE */ -# endif /* no NBPC */ -# endif /* no NBPG */ -# endif /* no EXEC_PAGESIZE */ -# else /* no HAVE_SYS_PARAM_H */ -# define getpagesize() 8192 /* punt totally */ -# endif /* no HAVE_SYS_PARAM_H */ -# endif /* no _SC_PAGESIZE */ - -#endif /* no HAVE_GETPAGESIZE */ - -int -main () -{ - char *data, *data2, *data3; - int i, pagesize; - int fd; - - pagesize = getpagesize (); - - /* First, make a file with some known garbage in it. */ - data = (char *) malloc (pagesize); - if (!data) - exit (1); - for (i = 0; i < pagesize; ++i) - *(data + i) = rand (); - umask (0); - fd = creat ("conftest.mmap", 0600); - if (fd < 0) - exit (1); - if (write (fd, data, pagesize) != pagesize) - exit (1); - close (fd); - - /* Next, try to mmap the file at a fixed address which already has - something else allocated at it. If we can, also make sure that - we see the same garbage. */ - fd = open ("conftest.mmap", O_RDWR); - if (fd < 0) - exit (1); - data2 = (char *) malloc (2 * pagesize); - if (!data2) - exit (1); - data2 += (pagesize - ((long) data2 & (pagesize - 1))) & (pagesize - 1); - if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED, fd, 0L)) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data2 + i)) - exit (1); - - /* Finally, make sure that changes to the mapped area do not - percolate back to the file as seen by read(). (This is a bug on - some variants of i386 svr4.0.) */ - for (i = 0; i < pagesize; ++i) - *(data2 + i) = *(data2 + i) + 1; - data3 = (char *) malloc (pagesize); - if (!data3) - exit (1); - if (read (fd, data3, pagesize) != pagesize) - exit (1); - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data3 + i)) - exit (1); - close (fd); - exit (0); -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_mmap_fixed_mapped=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_mmap_fixed_mapped=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_mmap_fixed_mapped" >&5 -echo "${ECHO_T}$ac_cv_func_mmap_fixed_mapped" >&6 -if test $ac_cv_func_mmap_fixed_mapped = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_MMAP 1 -_ACEOF - -fi -rm -f conftest.mmap - - - -for ac_header in sys/select.h sys/socket.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 -else - # Is the header compilable? -echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_header_compiler=no -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6 - -# Is the header present? -echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 - (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null; then - if test -s conftest.err; then - ac_cpp_err=$ac_c_preproc_warn_flag - ac_cpp_err=$ac_cpp_err$ac_c_werror_flag - else - ac_cpp_err= - fi -else - ac_cpp_err=yes -fi -if test -z "$ac_cpp_err"; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6 - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( - cat <<\_ASBOX -## ---------------------------------------- ## -## Report this to fred_nerk@sourceforge.net ## -## ---------------------------------------- ## -_ASBOX - ) | - sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 -if eval "test \"\${$as_ac_Header+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 - -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - -echo "$as_me:$LINENO: checking types of arguments for select" >&5 -echo $ECHO_N "checking types of arguments for select... $ECHO_C" >&6 -if test "${ac_cv_func_select_args+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - for ac_arg234 in 'fd_set *' 'int *' 'void *'; do - for ac_arg1 in 'int' 'size_t' 'unsigned long' 'unsigned'; do - for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#if HAVE_SYS_SELECT_H -# include -#endif -#if HAVE_SYS_SOCKET_H -# include -#endif - -int -main () -{ -extern int select ($ac_arg1, - $ac_arg234, $ac_arg234, $ac_arg234, - $ac_arg5); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3 -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - done - done -done -# Provide a safe default value. -: ${ac_cv_func_select_args='int,int *,struct timeval *'} - -fi -echo "$as_me:$LINENO: result: $ac_cv_func_select_args" >&5 -echo "${ECHO_T}$ac_cv_func_select_args" >&6 -ac_save_IFS=$IFS; IFS=',' -set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'` -IFS=$ac_save_IFS -shift - -cat >>confdefs.h <<_ACEOF -#define SELECT_TYPE_ARG1 $1 -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define SELECT_TYPE_ARG234 ($2) -_ACEOF - - -cat >>confdefs.h <<_ACEOF -#define SELECT_TYPE_ARG5 ($3) -_ACEOF - -rm -f conftest* - -echo "$as_me:$LINENO: checking for function prototypes" >&5 -echo $ECHO_N "checking for function prototypes... $ECHO_C" >&6 -if test "$ac_cv_prog_cc_stdc" != no; then - echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6 - -cat >>confdefs.h <<\_ACEOF -#define PROTOTYPES 1 -_ACEOF - - -cat >>confdefs.h <<\_ACEOF -#define __PROTOTYPES 1 -_ACEOF - -else - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 -fi - -echo "$as_me:$LINENO: checking whether setvbuf arguments are reversed" >&5 -echo $ECHO_N "checking whether setvbuf arguments are reversed... $ECHO_C" >&6 -if test "${ac_cv_func_setvbuf_reversed+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_cv_func_setvbuf_reversed=no - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -# if PROTOTYPES - int (setvbuf) (FILE *, int, char *, size_t); -# endif -int -main () -{ -char buf; return setvbuf (stdout, _IOLBF, &buf, 1); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -# if PROTOTYPES - int (setvbuf) (FILE *, int, char *, size_t); -# endif -int -main () -{ -char buf; return setvbuf (stdout, &buf, _IOLBF, 1); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - # It compiles and links either way, so it must not be declared - # with a prototype and most likely this is a K&R C compiler. - # Try running it. - if test "$cross_compiling" = yes; then - : # Assume setvbuf is not reversed when cross-compiling. -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -int -main () -{ -/* This call has the arguments reversed. - A reversed system may check and see that the address of buf - is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */ - char buf; - if (setvbuf (stdout, _IOLBF, &buf, 1) != 0) - exit (1); - putchar ('\r'); - exit (0); /* Non-reversed systems SEGV here. */ - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_setvbuf_reversed=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -rm -f core *.core -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi - ac_cv_func_setvbuf_reversed=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_func_setvbuf_reversed" >&5 -echo "${ECHO_T}$ac_cv_func_setvbuf_reversed" >&6 -if test $ac_cv_func_setvbuf_reversed = yes; then - -cat >>confdefs.h <<\_ACEOF -#define SETVBUF_REVERSED 1 -_ACEOF - -fi - -echo "$as_me:$LINENO: checking return type of signal handlers" >&5 -echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 -if test "${ac_cv_type_signal+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#ifdef signal -# undef signal -#endif -#ifdef __cplusplus -extern "C" void (*signal (int, void (*)(int)))(int); -#else -void (*signal ()) (); -#endif - -int -main () -{ -int i; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_type_signal=void -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_type_signal=int -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 -echo "${ECHO_T}$ac_cv_type_signal" >&6 - -cat >>confdefs.h <<_ACEOF -#define RETSIGTYPE $ac_cv_type_signal -_ACEOF - - -echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 -echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6 -if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - rm -f conftest.sym conftest.file -echo >conftest.file -if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then - if test "$cross_compiling" = yes; then - ac_cv_func_lstat_dereferences_slashed_symlink=no -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -struct stat sbuf; - /* Linux will dereference the symlink and fail. - That is better in the sense that it means we will not - have to compile and use the lstat wrapper. */ - exit (lstat ("conftest.sym/", &sbuf) ? 0 : 1); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_lstat_dereferences_slashed_symlink=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_lstat_dereferences_slashed_symlink=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -else - # If the `ln -s' command failed, then we probably don't even - # have an lstat function. - ac_cv_func_lstat_dereferences_slashed_symlink=no -fi -rm -f conftest.sym conftest.file - -fi -echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 -echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6 - -test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && - -cat >>confdefs.h <<_ACEOF -#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 -_ACEOF - - -if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then - case $LIBOBJS in - "lstat.$ac_objext" | \ - *" lstat.$ac_objext" | \ - "lstat.$ac_objext "* | \ - *" lstat.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; -esac - -fi - -echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 -echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6 -if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then - ac_cv_func_stat_empty_string_bug=yes -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -struct stat sbuf; - exit (stat ("", &sbuf) ? 1 : 0); - ; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func_stat_empty_string_bug=yes -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -( exit $ac_status ) -ac_cv_func_stat_empty_string_bug=no -fi -rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi -fi -echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 -echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6 -if test $ac_cv_func_stat_empty_string_bug = yes; then - case $LIBOBJS in - "stat.$ac_objext" | \ - *" stat.$ac_objext" | \ - "stat.$ac_objext "* | \ - *" stat.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS stat.$ac_objext" ;; -esac - - -cat >>confdefs.h <<_ACEOF -#define HAVE_STAT_EMPTY_STRING_BUG 1 -_ACEOF - -fi - - -for ac_func in strftime -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -else - # strftime is in -lintl on SCO UNIX. -echo "$as_me:$LINENO: checking for strftime in -lintl" >&5 -echo $ECHO_N "checking for strftime in -lintl... $ECHO_C" >&6 -if test "${ac_cv_lib_intl_strftime+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lintl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char strftime (); -int -main () -{ -strftime (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_intl_strftime=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_intl_strftime=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_intl_strftime" >&5 -echo "${ECHO_T}$ac_cv_lib_intl_strftime" >&6 -if test $ac_cv_lib_intl_strftime = yes; then - cat >>confdefs.h <<\_ACEOF -#define HAVE_STRFTIME 1 -_ACEOF - -LIBS="-lintl $LIBS" -fi - -fi -done - - -for ac_func in vprintf -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -echo "$as_me:$LINENO: checking for _doprnt" >&5 -echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 -if test "${ac_cv_func__doprnt+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define _doprnt to an innocuous variant, in case declares _doprnt. - For example, HP-UX 11i declares gettimeofday. */ -#define _doprnt innocuous__doprnt - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char _doprnt (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef _doprnt - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char _doprnt (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub__doprnt) || defined (__stub____doprnt) -choke me -#else -char (*f) () = _doprnt; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != _doprnt; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_func__doprnt=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_func__doprnt=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 -echo "${ECHO_T}$ac_cv_func__doprnt" >&6 -if test $ac_cv_func__doprnt = yes; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_DOPRNT 1 -_ACEOF - -fi - -fi -done - - - - - - - - - - - - - - - - -for ac_func in alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr -do -as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` -echo "$as_me:$LINENO: checking for $ac_func" >&5 -echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 -if eval "test \"\${$as_ac_var+set}\" = set"; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define $ac_func to an innocuous variant, in case declares $ac_func. - For example, HP-UX 11i declares gettimeofday. */ -#define $ac_func innocuous_$ac_func - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $ac_func (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $ac_func - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -{ -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char $ac_func (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined (__stub_$ac_func) || defined (__stub___$ac_func) -choke me -#else -char (*f) () = $ac_func; -#endif -#ifdef __cplusplus -} -#endif - -int -main () -{ -return f != $ac_func; - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - eval "$as_ac_var=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -eval "$as_ac_var=no" -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 -echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 -if test `eval echo '${'$as_ac_var'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - -# Checks for libraries. - -echo "$as_me:$LINENO: checking for cli_init in -lcli" >&5 -echo $ECHO_N "checking for cli_init in -lcli... $ECHO_C" >&6 -if test "${ac_cv_lib_cli_cli_init+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcli $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char cli_init (); -int -main () -{ -cli_init (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_cli_cli_init=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_cli_cli_init=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_cli_cli_init" >&5 -echo "${ECHO_T}$ac_cv_lib_cli_cli_init" >&6 -if test $ac_cv_lib_cli_cli_init = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBCLI 1 -_ACEOF - - LIBS="-lcli $LIBS" - -fi - - -echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 -echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 -if test "${ac_cv_lib_dl_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char dlopen (); -int -main () -{ -dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_dl_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_dl_dlopen=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 -if test $ac_cv_lib_dl_dlopen = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBDL 1 -_ACEOF - - LIBS="-ldl $LIBS" - -fi - - -echo "$as_me:$LINENO: checking for pow in -lm" >&5 -echo $ECHO_N "checking for pow in -lm... $ECHO_C" >&6 -if test "${ac_cv_lib_m_pow+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lm $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char pow (); -int -main () -{ -pow (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_lib_m_pow=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -ac_cv_lib_m_pow=no -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5 -echo "${ECHO_T}$ac_cv_lib_m_pow" >&6 -if test $ac_cv_lib_m_pow = yes; then - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBM 1 -_ACEOF - - LIBS="-lm $LIBS" - -fi - - -cat >>confdefs.h <<_ACEOF -#define LIBDIR "$prefix/lib/l2tpns" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define ETCDIR "$sysconfdir" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define BINDIR "$prefix/bin" -_ACEOF - - - ac_config_files="$ac_config_files Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, don't put newlines in cache variables' values. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -{ - (set) 2>&1 | - case `(ac_space=' '; set | grep ac_space) 2>&1` in - *ac_space=\ *) - # `set' does not quote correctly, so add quotes (double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \). - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n \ - "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" - ;; - esac; -} | - sed ' - t clear - : clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - : end' >>confcache -if diff $cache_file confcache >/dev/null 2>&1; then :; else - if test -w $cache_file; then - test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" - cat confcache >$cache_file - else - echo "not updating unwritable cache $cache_file" - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/; -s/:*\${srcdir}:*/:/; -s/:*@srcdir@:*/:/; -s/^\([^=]*=[ ]*\):*/\1/; -s/:*$//; -s/^[^=]*=[ ]*$//; -}' -fi - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_i=`echo "$ac_i" | - sed 's/\$U\././;s/\.o$//;s/\.obj$//'` - # 2. Add them. - ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" - ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: ${CONFIG_STATUS=./config.status} -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 -echo "$as_me: creating $CONFIG_STATUS" >&6;} -cat >$CONFIG_STATUS <<_ACEOF -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false -SHELL=\${CONFIG_SHELL-$SHELL} -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## - -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' -elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then - set -o posix -fi -DUALCASE=1; export DUALCASE # for MKS sh - -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi - - -# Work around bugs in pre-3.0 UWIN ksh. -$as_unset ENV MAIL MAILPATH -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - $as_unset $as_var - fi -done - -# Required to use basename. -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - - -# Name of the executable. -as_me=`$as_basename "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)$' \| \ - . : '\(.\)' 2>/dev/null || -echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } - /^X\/\(\/\/\)$/{ s//\1/; q; } - /^X\/\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - - -# PATH needs CR, and LINENO needs CR and PATH. -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" || { - # Find who we are. Look in the path if we contain no path at all - # relative or not. - case $0 in - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done - - ;; - esac - # We did not find ourselves, most probably we were run as `sh COMMAND' - # in which case we are not to be found in the path. - if test "x$as_myself" = x; then - as_myself=$0 - fi - if test ! -f "$as_myself"; then - { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 -echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} - { (exit 1); exit 1; }; } - fi - case $CONFIG_SHELL in - '') - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for as_base in sh bash ksh sh5; do - case $as_dir in - /*) - if ("$as_dir/$as_base" -c ' - as_lineno_1=$LINENO - as_lineno_2=$LINENO - as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then - $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } - $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } - CONFIG_SHELL=$as_dir/$as_base - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$0" ${1+"$@"} - fi;; - esac - done -done -;; - esac - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line before each line; the second 'sed' does the real - # work. The second script uses 'N' to pair each line-number line - # with the numbered line, and appends trailing '-' during - # substitution so that $LINENO is not a special case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) - sed '=' <$as_myself | - sed ' - N - s,$,-, - : loop - s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, - t loop - s,-$,, - s,^['$as_cr_digits']*\n,, - ' >$as_me.lineno && - chmod +x $as_me.lineno || - { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 -echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensible to this). - . ./$as_me.lineno - # Exit status is that of the last command. - exit -} - - -case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in - *c*,-n*) ECHO_N= ECHO_C=' -' ECHO_T=' ' ;; - *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; - *) ECHO_N= ECHO_C='\c' ECHO_T= ;; -esac - -if expr a : '\(a\)' >/dev/null 2>&1; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - # We could just check for DJGPP; but this test a) works b) is more generic - # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). - if test -f conf$$.exe; then - # Don't use ln at all; we don't have any links - as_ln_s='cp -p' - else - as_ln_s='ln -s' - fi -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.file - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_executable_p="test -f" - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -# IFS -# We need space, tab and new line, in precisely that order. -as_nl=' -' -IFS=" $as_nl" - -# CDPATH. -$as_unset CDPATH - -exec 6>&1 - -# Open the log real soon, to keep \$[0] and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. Logging --version etc. is OK. -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX -} >&5 -cat >&5 <<_CSEOF - -This file was extended by l2tpns $as_me 1.0.0, which was -generated by GNU Autoconf 2.58. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -_CSEOF -echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 -echo >&5 -_ACEOF - -# Files that config.status was made for. -if test -n "$ac_config_files"; then - echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_headers"; then - echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_links"; then - echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS -fi - -if test -n "$ac_config_commands"; then - echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS -fi - -cat >>$CONFIG_STATUS <<\_ACEOF - -ac_cs_usage="\ -\`$as_me' instantiates files from templates according to the -current configuration. - -Usage: $0 [OPTIONS] [FILE]... - - -h, --help print this help, then exit - -V, --version print version number, then exit - -q, --quiet do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to ." -_ACEOF - -cat >>$CONFIG_STATUS <<_ACEOF -ac_cs_version="\\ -l2tpns config.status 1.0.0 -configured by $0, generated by GNU Autoconf 2.58, - with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" - -Copyright (C) 2003 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." -srcdir=$srcdir -INSTALL="$INSTALL" -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF -# If no file are specified by the user, then we need to provide default -# value. By we need to know if files were specified by the user. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=*) - ac_option=`expr "x$1" : 'x\([^=]*\)='` - ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` - ac_shift=: - ;; - -*) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - *) # This is not an option, so the user has probably given explicit - # arguments. - ac_option=$1 - ac_need_defaults=false;; - esac - - case $ac_option in - # Handling of the options. -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --vers* | -V ) - echo "$ac_cs_version"; exit 0 ;; - --he | --h) - # Conflict between --help and --header - { { echo "$as_me:$LINENO: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&5 -echo "$as_me: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&2;} - { (exit 1); exit 1; }; };; - --help | --hel | -h ) - echo "$ac_cs_usage"; exit 0 ;; - --debug | --d* | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - CONFIG_FILES="$CONFIG_FILES $ac_optarg" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" - ac_need_defaults=false;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&5 -echo "$as_me: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&2;} - { (exit 1); exit 1; }; } ;; - - *) ac_config_targets="$ac_config_targets $1" ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF -if \$ac_cs_recheck; then - echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 - exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion -fi - -_ACEOF - - - - - -cat >>$CONFIG_STATUS <<\_ACEOF -for ac_config_target in $ac_config_targets -do - case "$ac_config_target" in - # Handling of arguments. - "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 -echo "$as_me: error: invalid argument: $ac_config_target" >&2;} - { (exit 1); exit 1; }; };; - esac -done - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason to put it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Create a temporary directory, and hook for its removal unless debugging. -$debug || -{ - trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 - trap '{ (exit 1); exit 1; }' 1 2 13 15 -} - -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" -} || -{ - tmp=./confstat$$-$RANDOM - (umask 077 && mkdir $tmp) -} || -{ - echo "$me: cannot create a temporary directory in ." >&2 - { (exit 1); exit 1; } -} - -_ACEOF - -cat >>$CONFIG_STATUS <<_ACEOF - -# -# CONFIG_FILES section. -# - -# No need to generate the scripts if there are no CONFIG_FILES. -# This happens for instance when ./config.status config.h -if test -n "\$CONFIG_FILES"; then - # Protect against being on the right side of a sed subst in config.status. - sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; - s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF -s,@SHELL@,$SHELL,;t t -s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t -s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t -s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t -s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t -s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t -s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t -s,@exec_prefix@,$exec_prefix,;t t -s,@prefix@,$prefix,;t t -s,@program_transform_name@,$program_transform_name,;t t -s,@bindir@,$bindir,;t t -s,@sbindir@,$sbindir,;t t -s,@libexecdir@,$libexecdir,;t t -s,@datadir@,$datadir,;t t -s,@sysconfdir@,$sysconfdir,;t t -s,@sharedstatedir@,$sharedstatedir,;t t -s,@localstatedir@,$localstatedir,;t t -s,@libdir@,$libdir,;t t -s,@includedir@,$includedir,;t t -s,@oldincludedir@,$oldincludedir,;t t -s,@infodir@,$infodir,;t t -s,@mandir@,$mandir,;t t -s,@build_alias@,$build_alias,;t t -s,@host_alias@,$host_alias,;t t -s,@target_alias@,$target_alias,;t t -s,@DEFS@,$DEFS,;t t -s,@ECHO_C@,$ECHO_C,;t t -s,@ECHO_N@,$ECHO_N,;t t -s,@ECHO_T@,$ECHO_T,;t t -s,@LIBS@,$LIBS,;t t -s,@CC@,$CC,;t t -s,@CFLAGS@,$CFLAGS,;t t -s,@LDFLAGS@,$LDFLAGS,;t t -s,@CPPFLAGS@,$CPPFLAGS,;t t -s,@ac_ct_CC@,$ac_ct_CC,;t t -s,@EXEEXT@,$EXEEXT,;t t -s,@OBJEXT@,$OBJEXT,;t t -s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t -s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t -s,@INSTALL_DATA@,$INSTALL_DATA,;t t -s,@CPP@,$CPP,;t t -s,@EGREP@,$EGREP,;t t -s,@LIBOBJS@,$LIBOBJS,;t t -s,@LTLIBOBJS@,$LTLIBOBJS,;t t -CEOF - -_ACEOF - - cat >>$CONFIG_STATUS <<\_ACEOF - # Split the substitutions into bite-sized pieces for seds with - # small command number limits, like on Digital OSF/1 and HP-UX. - ac_max_sed_lines=48 - ac_sed_frag=1 # Number of current file. - ac_beg=1 # First line for current file. - ac_end=$ac_max_sed_lines # Line after last line for current file. - ac_more_lines=: - ac_sed_cmds= - while $ac_more_lines; do - if test $ac_beg -gt 1; then - sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag - else - sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag - fi - if test ! -s $tmp/subs.frag; then - ac_more_lines=false - else - # The purpose of the label and of the branching condition is to - # speed up the sed processing (if there are no `@' at all, there - # is no need to browse any of the substitutions). - # These are the two extra sed commands mentioned above. - (echo ':t - /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed - if test -z "$ac_sed_cmds"; then - ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" - else - ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" - fi - ac_sed_frag=`expr $ac_sed_frag + 1` - ac_beg=$ac_end - ac_end=`expr $ac_end + $ac_max_sed_lines` - fi - done - if test -z "$ac_sed_cmds"; then - ac_sed_cmds=cat - fi -fi # test -n "$CONFIG_FILES" - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue - # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". - case $ac_file in - - | *:- | *:-:* ) # input from stdin - cat >$tmp/stdin - ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - * ) ac_file_in=$ac_file.in ;; - esac - - # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. - ac_dir=`(dirname "$ac_file") 2>/dev/null || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - { if $as_mkdir_p; then - mkdir -p "$ac_dir" - else - as_dir="$ac_dir" - as_dirs= - while test ! -d "$as_dir"; do - as_dirs="$as_dir $as_dirs" - as_dir=`(dirname "$as_dir") 2>/dev/null || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - done - test ! -n "$as_dirs" || mkdir $as_dirs - fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 -echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} - { (exit 1); exit 1; }; }; } - - ac_builddir=. - -if test "$ac_dir" != .; then - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A "../" for each directory in $ac_dir_suffix. - ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` -else - ac_dir_suffix= ac_top_builddir= -fi - -case $srcdir in - .) # No --srcdir option. We are building in place. - ac_srcdir=. - if test -z "$ac_top_builddir"; then - ac_top_srcdir=. - else - ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` - fi ;; - [\\/]* | ?:[\\/]* ) # Absolute path. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir ;; - *) # Relative path. - ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_builddir$srcdir ;; -esac - -# Do not use `cd foo && pwd` to compute absolute paths, because -# the directories may not exist. -case `pwd` in -.) ac_abs_builddir="$ac_dir";; -*) - case "$ac_dir" in - .) ac_abs_builddir=`pwd`;; - [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; - *) ac_abs_builddir=`pwd`/"$ac_dir";; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_builddir=${ac_top_builddir}.;; -*) - case ${ac_top_builddir}. in - .) ac_abs_top_builddir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; - *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_srcdir=$ac_srcdir;; -*) - case $ac_srcdir in - .) ac_abs_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; - *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_srcdir=$ac_top_srcdir;; -*) - case $ac_top_srcdir in - .) ac_abs_top_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; - *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; - esac;; -esac - - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_builddir$INSTALL ;; - esac - - if test x"$ac_file" != x-; then - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - rm -f "$ac_file" - fi - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - if test x"$ac_file" = x-; then - configure_input= - else - configure_input="$ac_file. " - fi - configure_input=$configure_input"Generated from `echo $ac_file_in | - sed 's,.*/,,'` by configure." - - # First look for the input files in the build tree, otherwise in the - # src tree. - ac_file_inputs=`IFS=: - for f in $ac_file_in; do - case $f in - -) echo $tmp/stdin ;; - [\\/$]*) - # Absolute (can't be DOS-style, as IFS=:) - test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - echo "$f";; - *) # Relative - if test -f "$f"; then - # Build tree - echo "$f" - elif test -f "$srcdir/$f"; then - # Source tree - echo "$srcdir/$f" - else - # /dev/null tree - { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - fi;; - esac - done` || { (exit 1); exit 1; } -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF - sed "$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s,@configure_input@,$configure_input,;t t -s,@srcdir@,$ac_srcdir,;t t -s,@abs_srcdir@,$ac_abs_srcdir,;t t -s,@top_srcdir@,$ac_top_srcdir,;t t -s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t -s,@builddir@,$ac_builddir,;t t -s,@abs_builddir@,$ac_abs_builddir,;t t -s,@top_builddir@,$ac_top_builddir,;t t -s,@abs_top_builddir@,$ac_abs_top_builddir,;t t -s,@INSTALL@,$ac_INSTALL,;t t -" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out - rm -f $tmp/stdin - if test x"$ac_file" != x-; then - mv $tmp/out $ac_file - else - cat $tmp/out - rm -f $tmp/out - fi - -done -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF - -# -# CONFIG_HEADER section. -# - -# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where -# NAME is the cpp macro being defined and VALUE is the value it is being given. -# -# ac_d sets the value in "#define NAME VALUE" lines. -ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' -ac_dB='[ ].*$,\1#\2' -ac_dC=' ' -ac_dD=',;t' -# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". -ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' -ac_uB='$,\1#\2define\3' -ac_uC=' ' -ac_uD=',;t' - -for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue - # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". - case $ac_file in - - | *:- | *:-:* ) # input from stdin - cat >$tmp/stdin - ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` - ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; - * ) ac_file_in=$ac_file.in ;; - esac - - test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - - # First look for the input files in the build tree, otherwise in the - # src tree. - ac_file_inputs=`IFS=: - for f in $ac_file_in; do - case $f in - -) echo $tmp/stdin ;; - [\\/$]*) - # Absolute (can't be DOS-style, as IFS=:) - test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - # Do quote $f, to prevent DOS paths from being IFS'd. - echo "$f";; - *) # Relative - if test -f "$f"; then - # Build tree - echo "$f" - elif test -f "$srcdir/$f"; then - # Source tree - echo "$srcdir/$f" - else - # /dev/null tree - { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 -echo "$as_me: error: cannot find input file: $f" >&2;} - { (exit 1); exit 1; }; } - fi;; - esac - done` || { (exit 1); exit 1; } - # Remove the trailing spaces. - sed 's/[ ]*$//' $ac_file_inputs >$tmp/in - -_ACEOF - -# Transform confdefs.h into two sed scripts, `conftest.defines' and -# `conftest.undefs', that substitutes the proper values into -# config.h.in to produce config.h. The first handles `#define' -# templates, and the second `#undef' templates. -# And first: Protect against being on the right side of a sed subst in -# config.status. Protect against being in an unquoted here document -# in config.status. -rm -f conftest.defines conftest.undefs -# Using a here document instead of a string reduces the quoting nightmare. -# Putting comments in sed scripts is not portable. -# -# `end' is used to avoid that the second main sed command (meant for -# 0-ary CPP macros) applies to n-ary macro definitions. -# See the Autoconf documentation for `clear'. -cat >confdef2sed.sed <<\_ACEOF -s/[\\&,]/\\&/g -s,[\\$`],\\&,g -t clear -: clear -s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp -t end -s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp -: end -_ACEOF -# If some macros were called several times there might be several times -# the same #defines, which is useless. Nevertheless, we may not want to -# sort them, since we want the *last* AC-DEFINE to be honored. -uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines -sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs -rm -f confdef2sed.sed - -# This sed command replaces #undef with comments. This is necessary, for -# example, in the case of _POSIX_SOURCE, which is predefined and required -# on some systems where configure will not decide to define it. -cat >>conftest.undefs <<\_ACEOF -s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, -_ACEOF - -# Break up conftest.defines because some shells have a limit on the size -# of here documents, and old seds have small limits too (100 cmds). -echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS -echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS -echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS -echo ' :' >>$CONFIG_STATUS -rm -f conftest.tail -while grep . conftest.defines >/dev/null -do - # Write a limited-size here document to $tmp/defines.sed. - echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS - # Speed up: don't consider the non `#define' lines. - echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS - # Work around the forget-to-reset-the-flag bug. - echo 't clr' >>$CONFIG_STATUS - echo ': clr' >>$CONFIG_STATUS - sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS - echo 'CEOF - sed -f $tmp/defines.sed $tmp/in >$tmp/out - rm -f $tmp/in - mv $tmp/out $tmp/in -' >>$CONFIG_STATUS - sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail - rm -f conftest.defines - mv conftest.tail conftest.defines -done -rm -f conftest.defines -echo ' fi # grep' >>$CONFIG_STATUS -echo >>$CONFIG_STATUS - -# Break up conftest.undefs because some shells have a limit on the size -# of here documents, and old seds have small limits too (100 cmds). -echo ' # Handle all the #undef templates' >>$CONFIG_STATUS -rm -f conftest.tail -while grep . conftest.undefs >/dev/null -do - # Write a limited-size here document to $tmp/undefs.sed. - echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS - # Speed up: don't consider the non `#undef' - echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS - # Work around the forget-to-reset-the-flag bug. - echo 't clr' >>$CONFIG_STATUS - echo ': clr' >>$CONFIG_STATUS - sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS - echo 'CEOF - sed -f $tmp/undefs.sed $tmp/in >$tmp/out - rm -f $tmp/in - mv $tmp/out $tmp/in -' >>$CONFIG_STATUS - sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail - rm -f conftest.undefs - mv conftest.tail conftest.undefs -done -rm -f conftest.undefs - -cat >>$CONFIG_STATUS <<\_ACEOF - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - if test x"$ac_file" = x-; then - echo "/* Generated by configure. */" >$tmp/config.h - else - echo "/* $ac_file. Generated by configure. */" >$tmp/config.h - fi - cat $tmp/in >>$tmp/config.h - rm -f $tmp/in - if test x"$ac_file" != x-; then - if diff $ac_file $tmp/config.h >/dev/null 2>&1; then - { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 -echo "$as_me: $ac_file is unchanged" >&6;} - else - ac_dir=`(dirname "$ac_file") 2>/dev/null || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - { if $as_mkdir_p; then - mkdir -p "$ac_dir" - else - as_dir="$ac_dir" - as_dirs= - while test ! -d "$as_dir"; do - as_dirs="$as_dir $as_dirs" - as_dir=`(dirname "$as_dir") 2>/dev/null || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - done - test ! -n "$as_dirs" || mkdir $as_dirs - fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 -echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} - { (exit 1); exit 1; }; }; } - - rm -f $ac_file - mv $tmp/config.h $ac_file - fi - else - cat $tmp/config.h - rm -f $tmp/config.h - fi -done -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF - -{ (exit 0); exit 0; } -_ACEOF -chmod +x $CONFIG_STATUS -ac_clean_files=$ac_clean_files_save - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || { (exit 1); exit 1; } -fi - diff --git a/configure.in b/configure.in deleted file mode 100644 index f7b8b81..0000000 --- a/configure.in +++ /dev/null @@ -1,48 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.58) -AC_INIT([l2tpns], [1.0.0], [fred_nerk@sourceforge.net]) -AC_CONFIG_SRCDIR([ll.c]) -AC_CONFIG_HEADER([config.h]) - -# Checks for programs. -AC_PROG_CC -AC_PROG_INSTALL - -# Checks for header files. -AC_HEADER_STDC -AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h memory.h netdb.h netinet/in.h stdlib.h string.h sys/file.h sys/ioctl.h sys/socket.h sys/time.h unistd.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_TYPE_PID_T -AC_TYPE_SIZE_T -AC_HEADER_TIME - -# Checks for library functions. -AC_FUNC_FORK -AC_PROG_GCC_TRADITIONAL -AC_FUNC_MALLOC -AC_FUNC_MEMCMP -AC_FUNC_MMAP -AC_FUNC_SELECT_ARGTYPES -AC_FUNC_SETVBUF_REVERSED -AC_TYPE_SIGNAL -AC_FUNC_STAT -AC_FUNC_STRFTIME -AC_FUNC_VPRINTF -AC_CHECK_FUNCS([alarm gethostbyname gethostname gettimeofday inet_ntoa memset pow select socket strcasecmp strchr strdup strerror strrchr]) - -# Checks for libraries. -AC_CHECK_LIB([cli], [cli_init]) -AC_CHECK_LIB([dl], [dlopen]) -AC_CHECK_LIB([m], [pow]) - -AC_DEFINE_UNQUOTED(LIBDIR, ["$prefix/lib/l2tpns"]) -AC_DEFINE_UNQUOTED(ETCDIR, ["$sysconfdir"]) -AC_DEFINE_UNQUOTED(BINDIR, ["$prefix/bin"]) - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/garden.c b/garden.c index 1083b65..e9eecb2 100644 --- a/garden.c +++ b/garden.c @@ -34,19 +34,19 @@ int plugin_post_auth(struct param_post_auth *data) p.log(3, 0, 0, 0, "Walled Garden allowing login\n"); data->auth_allowed = 1; - data->s->garden = 1; + data->s->walled_garden = 1; return PLUGIN_RET_OK; } int plugin_new_session(struct param_new_session *data) { - if (data->s->garden) garden_session(data->s, 1); + if (data->s->walled_garden) garden_session(data->s, 1); return PLUGIN_RET_OK; } int plugin_kill_session(struct param_new_session *data) { - if (data->s->garden) garden_session(data->s, 0); + if (data->s->walled_garden) garden_session(data->s, 0); return PLUGIN_RET_OK; } @@ -119,7 +119,7 @@ int garden_session(sessiont *s, int flag) 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->garden = 1; + s->walled_garden = 1; } else { @@ -145,7 +145,7 @@ int garden_session(sessiont *s, int flag) if (WEXITSTATUS(status) != 0) break; } - s->garden = 0; + s->walled_garden = 0; if (!s->die) { /* OK, we're up! */ @@ -153,7 +153,7 @@ int garden_session(sessiont *s, int flag) p.radiussend(r, RADIUSSTART); } } - s->garden = flag; + s->walled_garden = flag; return 1; } diff --git a/l2tpns.c b/l2tpns.c index 7e591ce..24f62d5 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -417,7 +417,7 @@ sessionidt sessionbyuser(char *username) #ifdef STAT_CALLS STAT(call_sessionbyuser); #endif - for (s = 1; s < MAXSESSION && (session[s].servicenet || strncmp(session[s].user, username, 128)); s++); + for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++); if (s < MAXSESSION) return s; return 0; @@ -770,7 +770,7 @@ void controladd(controlt * c, tunnelidt t, sessionidt s) void sessionshutdown(sessionidt s, char *reason) { int dead = session[s].die; - int servicenet = session[s].servicenet; + int walled_garden = session[s].walled_garden; #ifdef STAT_CALLS STAT(call_sessionshutdown); @@ -789,7 +789,7 @@ void sessionshutdown(sessionidt s, char *reason) } // RADIUS Stop message - if (session[s].opened && !servicenet && !dead) { + if (session[s].opened && !walled_garden && !dead) { u8 r = session[s].radius; if (!r) { @@ -1970,7 +1970,7 @@ int assign_ip_address(sessionidt s) 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 (!session[s].walled_garden && ip_address_pool[i].user[0] && !strcmp(u, ip_address_pool[i].user)) { best = i; reuse = 1; @@ -1995,7 +1995,7 @@ int assign_ip_address(sessionidt s) session[s].ip_pool_index = best; ip_address_pool[best].assigned = 1; ip_address_pool[best].last = time_now; - if (session[s].servicenet) + if (session[s].walled_garden) /* Don't track addresses of users in walled garden (note: this means that their address isn't "sticky" even if they get un-gardened). */ @@ -2145,7 +2145,7 @@ void dump_acct_info() for (i = 0; i < MAXSESSION; i++) { - if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].servicenet) + if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) continue; if (!f) { @@ -2240,7 +2240,7 @@ int main(int argc, char *argv[]) initdata(); 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"); + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.3 2004-03-05 00:22:06 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. @@ -2700,7 +2700,7 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes) { if (i == s) continue; if (ip == session[i].ip) sessionkill(i, "Duplicate IP address"); - if (!session[s].servicenet && !session[i].servicenet && strcasecmp(user, session[i].user) == 0) + if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0) sessionkill(i, "Duplicate session for user"); } diff --git a/l2tpns.h b/l2tpns.h index ff2e9e9..a705b8e 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ #include #include @@ -132,7 +132,7 @@ typedef struct sessions u8 flags; // various bit flags u8 snoop; // are we snooping this session? u8 throttle; // is this session throttled? - u8 servicenet; // is this session servicenetted? + u8 walled_garden; // is this session gardened? u16 mru; // maximum receive unit u16 tbf; // filter bucket for throttling char random_vector[MAXTEL]; diff --git a/ppp.c b/ppp.c index bacd7b8..e4f4e81 100644 --- a/ppp.c +++ b/ppp.c @@ -1,5 +1,5 @@ // L2TPNS PPP Stuff -// $Id: ppp.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: ppp.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ #include #include @@ -438,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].servicenet) + if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) if (!r) r = radiusnew(s); if (r) From 8c270aac273a1577af0b1f7b82ec1d78ca9e81cf Mon Sep 17 00:00:00 2001 From: David Parrish Date: Fri, 5 Mar 2004 00:22:45 +0000 Subject: [PATCH 006/482] . --- .cvsignore | 1 + conform.cfg | 137 ------------------------------ install-sh | 238 ---------------------------------------------------- l2tpns.c | 3 +- 4 files changed, 3 insertions(+), 376 deletions(-) delete mode 100644 conform.cfg delete mode 100755 install-sh diff --git a/.cvsignore b/.cvsignore index 9a84440..c65a666 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,5 +1,6 @@ *.o l2tpns +nsctl state.dump *.swp cluster_master diff --git a/conform.cfg b/conform.cfg deleted file mode 100644 index fabea05..0000000 --- a/conform.cfg +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/perl -w -# vim:ft=perl - -die "l2tpns requires RedHat 7.3 or above" if i_isa("SOE_linux_rh6"); - -my $restart = 0; - -my %conf = (); -for my $c (i_isa_fetchall('L2tpns_config')) { - foreach my $opt (keys %$c) { - if (ref $conf{$opt} and ref $conf{$opt} eq 'ARRAY') { - $c->{$opt} = [ $c->{$opt} ] unless ref $c->{$opt}; - push @{$conf{$opt}}, @{$c->{$opt}}; - } elsif (ref $c->{$opt} and ref $c->{$opt} eq 'ARRAY') { - # Make sure to copy to avoid changing /etc/machine - $conf{$opt} = [ $conf{$opt} ] if $conf{$opt}; - $conf{$opt} ||= []; - push @{$conf{$opt}}, @{$c->{$opt}}; - } else { - $conf{$opt} = $c->{$opt}; - } - } -} - -$conf{Address_pool} ||= i_isa("Address_pool"); # backwards compat - -unless (i_isa("No_throttle")) { - chomp(my $kernel = `uname -r`); - print "WARN: l2tpns requires kernel 2.4.18-187OIE1. This is included in $_path/rpm/kernel-2.4.18-187OIE1.i386.rpm\n" - unless ($kernel eq '2.4.18-187OIE1' || $kernel =~ /^2\.4\.2\d/); -} - -# Recompile the server if needed -if ((stat("src/l2tpns.c"))[9] > (stat("src/l2tpns"))[9]) { - chdir("src"); - command("make clean"); - command("make"); - chdir(".."); - $restart++; -} - -command("mkdir /dev/net") and ++$restart unless -d "/dev/net"; -command("mknod /dev/net/tun c 10 200") and ++$restart unless -c "/dev/net/tun"; - -my $cluster = i_isa('Gateway_cluster'); -my $cluster_name = $cluster->{master} || $cluster->{slave} || die 'Not a master or a slave' if $cluster; -my $master = $cluster && $cluster->{master}; -my $command = $master ? "cluster_master" : "l2tpns"; -push @{$m{$_class}->{Monitor}->{process}->{tests}}, $command; - - -if ($cluster) { - $conf{'save state'} ||= 'no'; - if (!$master && !$cluster->{bind_address}) { - die 'No bind address for cluster slave'; - } - $conf{'bind address'} ||= $cluster->{bind_address} unless $master; - my $cluster_master; - my @cluster_slaves = (); - my @cluster_slave_addresses = (); - foreach my $host (type_list('Gateway_cluster')) { - my $host_conf = OIE::Conform::i_isa(\%m, $host, 'Gateway_cluster'); - if ($host_conf->{master} eq $cluster_name) { - $cluster_master = $host; - } elsif ($host_conf->{slave} eq $cluster_name) { - push @cluster_slaves, $host; - push @{$conf{Address_pool}}, map { "$host_conf->{bind_address}:$_" } @{$m{$host}->{L2tpns_config}->{Address_pool}} if $master; - push @cluster_slave_addresses, $m{$host}->{int_eth0}->{ip}; - } - } - - if ($master) { - push @{$m{$_class}->{inittab_include}}, - "$_path/src/cluster_master $m{$iam}->{int_eth0}->{ip}"; - push @{$m{$_class}->{inittab_disable}}, - "$_path/src/l2tpns"; - $m{$_class}->{Firewall}->{$_} = '32792:udp' - foreach @cluster_slave_addresses; - } - $conf{'cluster master'} ||= $m{$cluster_master}->{int_eth0}->{ip}; -} - -# Build up address pool -my $pool = $conf{Address_pool}; -if ($pool) { - my $address_pool = ""; - - foreach (@$pool) { - $address_pool .= "$_\n"; - } - - text_install("$_path/etc/ip_pool.txt", $address_pool) and $restart++; -} else { - print "WARN: No Address_pool defined in machines.\n"; -} -delete $conf{"Address_pool"}; # Don't add it to the conf file - -my $servicenet = $conf{"servicenet"}; -if ($servicenet) { - $conf{'servicenet'} = 'yes'; - push @{$conf{plugin}}, 'servicenet' unless grep /^servicenet$/, @{$conf{plugin}}; - file_install("/etc/rc.d/rc.firewall.INPUT.servicenet", "$_path/etc/rc.firewall.INPUT.servicenet", undef, undef, - "s/#SERVICENET#/$servicenet/g") - and queue_command("/etc/rc.d/rc.firewall"); -} else { - $conf{'servicenet'} = 'no'; - # Uninstall - if (-f "/etc/rc.d/rc.firewall.INPUT.servicenet") { - unlink "/etc/rc.d/rc.firewall.INPUT.servicenet"; - command("iptables -F snet"); - } -} - -# Note that we don't file_install the config file, but instead modify it -# in place - -my $config = slurp_file("$_path/etc/l2tpns.cfg"); - -# plugins need to go first, else they won't pick up params -foreach my $p (@{$conf{plugin}}) { - $config =~ s/^#?\s*plugin\s+=\s+\Q$p\E$/plugin = $p/mg or - $config = "plugin = $p\n\n$config"; -} -delete $conf{plugin}; - -foreach my $c (keys %conf) { - $config =~ s/^#?\s*\Q$c\E\s+=\s+.*$/$c = $conf{$c}/mg or - $config .= "$c = $conf{$c}\n\n"; -} - -file_install("/etc/rc.d/rc.firewall.INPUT.l2tpns", "$_path/etc/rc.firewall.INPUT.l2tpns") - and queue_command("/etc/rc.d/rc.firewall"); - -text_install("$_path/etc/l2tpns.cfg", $config) and $restart++; - -queue_command("killall $command") if $restart; - diff --git a/install-sh b/install-sh deleted file mode 100755 index ab74c88..0000000 --- a/install-sh +++ /dev/null @@ -1,238 +0,0 @@ -#!/bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. -# - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -tranformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/l2tpns.c b/l2tpns.c index 24f62d5..6927eb5 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -2240,7 +2240,8 @@ int main(int argc, char *argv[]) initdata(); init_cli(); read_config_file(); - log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + log(1, 0, 0, 0, "L2TPNS Version 1.1.0 - http://l2tpns.sourceforge.net/\n"); + log(1, 0, 0, 0, "Licensed under the GPL\n"); /* Start up the cluster first, so that we don't have two machines with * the same IP at once. From 303ef1e6b17ff6b4bed91317c0b93350d837b734 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Tue, 9 Mar 2004 00:31:49 +0000 Subject: [PATCH 007/482] Don't mention configure, it's not used anymore --- INSTALL | 1 - 1 file changed, 1 deletion(-) diff --git a/INSTALL b/INSTALL index 22f03a9..cade5e5 100644 --- a/INSTALL +++ b/INSTALL @@ -12,7 +12,6 @@ Brief Installation guide for L2TPNS 2. Compile -./configure --prefix=/usr --sysconfdir=/etc/l2tpns make From 7d87e78a120c59391dc7f522875363b3c5c05336 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 22 Mar 2004 05:26:17 +0000 Subject: [PATCH 008/482] Fix makefile error --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 76fcd80..7c48446 100644 --- a/Makefile +++ b/Makefile @@ -49,12 +49,12 @@ install: all $(INSTALL) -D -o root -g root -m 0600 etc/l2tpns.cfg.default $(etcdir)/l2tpns.cfg $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool $(INSTALL) -D -o root -g root -m 0600 etc/users.default $(etcdir)/l2tpns.users - for PLUGIN in $(PLUGINS); do - $(INSTALL) -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN) + for PLUGIN in $(PLUGINS); do \ + $(INSTALL) -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN); \ done - if [ ! -e /dev/net/tun ]; then - mkdir /dev/net - mknod /dev/net/tun c 10 200 + if [ ! -e /dev/net/tun ]; then \ + mkdir /dev/net; \ + mknod /dev/net/tun c 10 200; \ fi %.so: %.c From 8ed6b75f5ff7cdf8f9cea56ed63282d4547f2f0b Mon Sep 17 00:00:00 2001 From: David Parrish Date: Wed, 24 Mar 2004 04:56:51 +0000 Subject: [PATCH 009/482] Added autosnoop and autothrottle modules --- Makefile | 2 +- autosnoop.c | 42 ++++++++++++++++++++++++++++++++++++++++++ autothrottle.c | 42 ++++++++++++++++++++++++++++++++++++++++++ etc/l2tpns.cfg.default | 2 ++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 autosnoop.c create mode 100644 autothrottle.c diff --git a/Makefile b/Makefile index 7c48446..a7f3631 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ OBJS= md5.o \ control.o \ util.o \ -PLUGINS=garden.so +PLUGINS=garden.so autothrottle.so autosnoop.so all: l2tpns cluster_master nsctl $(PLUGINS) diff --git a/autosnoop.c b/autosnoop.c new file mode 100644 index 0000000..1c27190 --- /dev/null +++ b/autosnoop.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "plugin.h" +#include "control.h" + +int __plugin_api_version = 1; +struct pluginfuncs p; + +int plugin_radius_response(struct param_radius_response *data) +{ + if (strcmp(data->key, "intercept") == 0) + { + if (strcmp(data->value, "yes") == 0) + { + p.log(3, 0, 0, 0, " Intercepting user\n"); + data->s->snoop = 1; + } + else if (strcmp(data->value, "no") == 0) + { + p.log(3, 0, 0, 0, " Not intercepting user\n"); + data->s->snoop = 0; + } + } + return PLUGIN_RET_OK; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + if (!funcs) return 0; + memcpy(&p, funcs, sizeof(p)); + + return 1; +} + +void plugin_done() +{ +} + diff --git a/autothrottle.c b/autothrottle.c new file mode 100644 index 0000000..4d985a2 --- /dev/null +++ b/autothrottle.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include "l2tpns.h" +#include "plugin.h" +#include "control.h" + +int __plugin_api_version = 1; +struct pluginfuncs p; + +int plugin_radius_response(struct param_radius_response *data) +{ + if (strcmp(data->key, "throttle") == 0) + { + if (strcmp(data->value, "yes") == 0) + { + p.log(3, 0, 0, 0, " Throttling user\n"); + data->s->throttle = 1; + } + else if (strcmp(data->value, "no") == 0) + { + p.log(3, 0, 0, 0, " Not throttling user\n"); + data->s->throttle = 0; + } + } + return PLUGIN_RET_OK; +} + +int plugin_init(struct pluginfuncs *funcs) +{ + if (!funcs) return 0; + memcpy(&p, funcs, sizeof(p)); + + return 1; +} + +void plugin_done() +{ +} + diff --git a/etc/l2tpns.cfg.default b/etc/l2tpns.cfg.default index 3934559..f6b26e1 100644 --- a/etc/l2tpns.cfg.default +++ b/etc/l2tpns.cfg.default @@ -17,3 +17,5 @@ set accounting_dir "/var/run/l2tpns/acct/" set setuid 0 set dump_speed no load plugin "garden" +load plugin "autothrottle" +load plugin "autosnoop" From aa41dea6e7978d0e4569905792cffe857bfb4d86 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Wed, 24 Mar 2004 07:15:56 +0000 Subject: [PATCH 010/482] Add manual. It's very long --- Docs/manual.html | 885 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 885 insertions(+) create mode 100644 Docs/manual.html diff --git a/Docs/manual.html b/Docs/manual.html new file mode 100644 index 0000000..a4ae0b3 --- /dev/null +++ b/Docs/manual.html @@ -0,0 +1,885 @@ + + +L2TPNS Manual + + + +

L2TPNS Manual

+
    +
  1. Overview
  2. +
  3. Installation
  4. +
  5. Configuration
  6. +
  7. Controlling the process
  8. +
  9. Command-Line Interface
  10. +
  11. Throttling
  12. +
  13. Interception
  14. +
  15. Authentication
  16. +
  17. Plugins
  18. +
  19. Walled Garden
  20. +
  21. Clustering
  22. +
  23. Performance
  24. +
+

Overview

+L2TPNS is half of a complete L2TP implementation. It supports only the +LNS side of the connection.

+ +L2TP (Layer 2 Tunneling Protocol) is designed to allow any layer 2 +protocol (e.g. Ethernet, PPP) to be tunneled over an IP connection. L2TPNS +implements PPP over L2TP only.

+ +There are a couple of other L2TP imlementations, of which l2tpd is probably the +most popular. l2tpd also will handle being either end of a tunnel, and +is a lot more configurable than L2TPNS. However, due to the way it works, +it is nowhere near as scalable.

+ +L2TPNS uses the TUN/TAP interface provided by the Linux kernel to receive +and send packets. Using some packet manipulation it doesn't require a +single interface per connection, as l2tpd does.

+ +This allows it to scale extremely well to very high loads and very high +numbers of connections.

+ +It also has a plugin architecture which allows custom code to be run +during processing. An example of this is in the walled garden module +included.

+ +
+Documentation is not my best skill. If you find any problems +with this document, or if you wish to contribute, please email david@dparrish.com.

+ +

Installation

+

Requirements

+ +
    +
  1. Linux kernel version 2.4 or above, with the Tun/Tap interface either +compiled in, or as a module.
  2. + +
  3. libcli 1.5 or greater.
    You can get this from http://sourceforge.net/projects/libcli
  4. + +
  5. The iproute2 user-space tools. These are used for throttling, +so if you don't want to throttle then this is not required.
    You +may also need to patch tc and the kernel to include HTB +support. You can find the relevant patches and instructions at http://luxik.cdi.cz/~devik/qos/htb/.
  6. + +
+ +

Compile

+ +You can generally get away with just running make from the source +directory. This will compile the daemon, associated tools and any modules +shipped with the distribution.

+ +

Install

+ +After you have successfully compiled everything, run make +install to install it. By default, the binaries are installed into +/usr/sbin, the configuration into /etc/l2tpns, and the +modules into /usr/lib/l2tpns.

+ +You will definately need to edit the configuration file before you start. +See the Configuration section for more information.

+ +You should also create the appropriate iptables chains if you want to use +throttling or walled garden. +

+# Create the walled garden stuff
+iptables -t nat -N l2tpns
+iptables -t nat -F l2tpns
+iptables -t nat -A PREROUTING -j l2tpns
+iptables -t nat -A l2tpns -j garden_users
+# Create the throttling stuff
+iptables -t mangle -N l2tpns
+iptables -t mangle -F l2tpns
+iptables -t mangle -N throttle
+iptables -t mangle -F throttle
+iptables -t mangle -A PREROUTING -j l2tpns
+iptables -t mangle -A l2tpns -j throttle
+
+ +

Running it

+ +You only need to run /usr/sbin/l2tpns as root to start it. It does +not detach to a daemon process, so you should perhaps run it from init.

+ +By default there is no log destination set, so all log messages will go to +stdout.

+ +

Configuration

+ +All configuration of the software is done from the files installed into +/etc/l2tpns. + +

l2tpns.cfg

+ +This is the main configuration file for L2TPNS. The format of the file is a +list of commands that can be run through the command-line interface. This +file can also be written directly by the L2TPNS process if a user runs the +write memory command, so any comments will be lost. However if your +policy is not to write the config by the program, then feel free to comment +the file with a # at the beginning of the line.

+ +A list of the possible configuration directives follows. Each of these +should be set by a line like:

+

+set configstring "value"
+set ipaddress 192.168.1.1
+set boolean true
+
+ +
    +
  • debug (int)
    +Sets the level of messages that will be written to the log file. The value +should be between 0 and 5, with 0 being no debugging, and 5 being the +highest. A rough description of the levels is: +
      +
    1. Critical Errors - Things are probably broken
    2. +
    3. Errors - Things might have gone wrong, but probably will recover
    4. +
    5. Warnings - Just in case you care what is not quite perfect
    6. +
    7. Information - Parameters of control packets
    8. +
    9. Calls - For tracing the execution of the code
    10. +
    11. Packets - Everything, including a hex dump of all packets processed... probably twice
    12. +
    +Note that the higher you set the debugging level, the slower the program +will run. Also, at level 5 a LOT of information will be logged. This should +only ever be used for working out why it doesn't work at all. +

    +

  • + +
  • log_file (string)
    +This will be where all logging and debugging information is written +to. This can be either a filename, such as /var/log/l2tpns, or +the special magic string syslog:facility, where facility +is any one of the syslog logging facilities, such as local5. +

    +

  • + +
  • l2tp_secret (string)
    +This sets the string that L2TPNS will use for authenticating tunnel request. +This must be the same as the LAC, or authentication will fail. This will +only actually be used if the LAC requests authentication. +

    +

  • + +
  • primary_dns (ip address)
    +Whenever a PPP connection is established, DNS servers will be sent to the +user, both a primary and a secondary. If either is set to 0.0.0.0, then that +one will not be sent.
    +This sets the first DNS entry that will be sent. +

    +

  • + +
  • secondary_dns (ip address)
    +See primary_dns. +

    +

  • + +
  • snoop_host (ip address)
    +Whenever a user is intercepted, a copy of their traffic will be sent to this +IP address, using the port specified by snoop_port. Each packet +will be sent as UDP. +

    +

  • + +
  • snoop_port (int)
    +See snoop_host. +

    +

  • + +
  • primary_radius (ip address)
    +This sets the primary radius server used for both authentication and +accounting. If this server does not respond, then the secondary radius +server will be used. +

    +

  • + +
  • secondary_radius (ip address)
    +See primary_radius. +

    +

  • + +
  • radius_accounting (boolean)
    +If set to true, then radius accounting packets will be sent. This means that +a Start record will be sent when the session is successfully authenticated, +and a Stop record will be sent when the session is closed. +

    +

  • + +
  • radius_secret (string)
    +This secret will be used in all radius queries. If this is not set then +radius queries will fail. +

    +

  • + +
  • bind_address (ip address)
    +When the tun interface is created, it is assigned the address specified +here. If no address is given, 1.1.1.1 is used.
    +If an address is given here, then packets containing user traffic should be +routed via this address, otherwise the primary address of the machine.
    +This is set automatically by the cluster master when taking over a failed +machine. +

    +

  • + +
  • cluster_master (ip address)
    +This sets the address of the cluster master. See the Clustering +section for more information on configuring a cluster. +

    +

  • + +
  • throttle_speed (int)
    +Sets the speed (in kbits/s) which sessions will be limited to. If this is +set to 0, then throttling will not be used at all. Note: You can set this by +the CLI, but changes will not affect currently connected users. +

    +

  • + +
  • dump_speed (boolean)
    +If set to true, then the current bandwidth utilization will be logged every +second. Even if this is disabled, you can see this information by running +the uptime command on the CLI. +

    +

  • + +
  • setuid (int)
    +After starting up and binding the interface, change UID to this. This +doesn't work properly. +

    +

  • + +
  • accounting_dir (string)
    +If set to a directory, then every 5 minutes the current usage for every +connected use will be dumped to a file. Each file dumped begins with a +header, where each line is prefixed by #. Following the header is a single +line for every connected user, fields separated by a space.
    +The fields are username, ip, qos, uptxoctets, downrxoctets. The qos +field is 1 if a standard user, and 2 if the user is throttled. + +

    +

  • + +
  • save_state (boolean)
    +If set to true, a state file will be dumped to disk when the process dies. +This will be restored on startup, loading all active tunnels and sessions. +

    +

  • + +
+ +

l2tpns.users

+ +This file's sole purpose is to manage access to the command-line +interface. If this file doesn't exist, then anyone who can get to port +23 will be allowed access without a username / password.

+ +If this is not what you want, then create this file and put in it a list of +username / password pairs, separated by a :. e.g.:

+ +

+user.1:randompassword
+fred:bhPe4rD1ME8.s
+bob:SP2RHKl3Q3qo6
+
+ +Keep in mind that the password should be in clear-text. There is no user +privilege distinction, so anyone on this list will have full control of the +system.

+ +

l2tpns.ip_pool

+ +This file is used to configure the IP address pool which user addresses are +assigned from. This file should contain either an IP address or a IP mask +per line. e.g.:

+ +

+192.168.1.1
+192.168.1.2
+192.168.1.3
+192.168.4.0/24
+172.16.0.0/16
+10.0.0.0/8
+
+ +Keep in mind that L2TPNS can only handle 65535 connections per process, so +don't put more than 65535 IP addresses in the configuration file. They will +be wasted. + +

Controlling the process

+ +A running L2TPNS process can be controlled in a number of ways. The primary +method of control is by the Command-Line Interface (CLI).

+ +You can also remotely send commands to modules via the nsctl client +provided. This currently only works with the walled garden module, but +modification is trivial to support other modules.

+ +Also, there are a number of signals that L2TPNS understands and takes action +when it receives them. + +

Command-Line Interface

+ +You can access the command line interface by telnet'ing to port 23. There is +no IP address restriction, so it's a good idea to firewall this port off +from anyone who doesn't need access to it. See l2tpns.users for information +on restricting access based on a username and password.

+ +The CLI gives you real-time control over almost everything in +the process. The interface is designed to look like a CISCO +device, and supports things like command history, line editing and +context sensitive help. This is provided by linking with the libcli library.

+ +After you have connected to the telnet port (and perhaps logged in), you +will be presented with a prompt

l2tpns>

+ +You can type help to get a list of all possible commands, but this +list could be quite long. A brief overview of the more important commands +follows: + +

    +
  • show session
    +Without specifying a session ID, this will list all tunnels currently +connected. If you specify a session ID, you will be given all information on +a single tunnel. Note that the full session list can be around 185 columns +wide, so you should probably use a wide terminal to see the list +properly.

    +The columns listed in the overview are: + + + + + + + + + + + + + + +
    SIDSession ID
    TIDTunnel ID - Use with show tunnel tid
    UsernameThe username given in the PPP + authentication. If this is *, then LCP authentication has not + completed.
    IPThe IP address given to the session. If + this is 0.0.0.0, LCP negotiation has not completed.
    IIntercept - Y or N depending on whether the + session is being snooped. See snoop.
    TThrottled - Y or N if the session is + currently throttled. See throttle.
    GWalled Garden - Y or N if the user is + trapped in the walled garden. This field is present even if the + garden module is not loaded.
    openedThe number of seconds since the + session started
    downloadedNumber of bytes downloaded by the user
    uploadedNumber of bytes uploaded by the user
    idleThe number of seconds since traffic was + detected on the session
    LACThe IP address of the LAC the session is + connected to.
    CLIThe Calling-Line-Identification field + provided during the session setup. This field is generated by the + LAC.
    +

    +

  • + +
  • show tunnel
    +This will show all the open tunnels in a summary, or detail on a single +tunnel if you give a tunnel id.

    +The columns listed in the overview are: + + + + + + +
    TIDTunnel ID
    HostnameThe hostname for the tunnel as + provided by the LAC. This has no relation to DNS, it is just + a text field.
    IPThe IP address of the LAC
    StateTunnel state - Free, Open, Dieing, + Opening
    SessionsThe number of open sessions on the + tunnel
    +

    +

  • + +
  • show pool
    +Displays the current IP address pool allocation. This will only display +addresses that are in use, or are reserved for re-allocation to a +disconnected user.

    +If an address is not currently in use, but has been used, then in the User +column the username will be shown in square brackets, followed by the time +since the address was used: +

    +IP Address      Used  Session User
    +192.168.100.6     N           [joe.user] 1548s
    +
    +

    +

  • + +
  • show radius
    +Show a summary of the in-use radius sessions. This list should not be very +long, as radius sessions should be cleaned up as soon as they are used. The +columns listed are: + + + + + + +
    RadiusThe ID of the radius request. This is + sent in the packet to the radius server for identification.
    StateThe state of the request - WAIT, CHAP, + AUTH, IPCP, START, STOP, NULL.
    SessionThe session ID that this radius + request is associated with
    RetryIf a response does not appear to the + request, it will retry at this time. This is a unix timestamp.
    TryRetry count. The radius request is + discarded after 3 retries.
    +

    +

  • + +
  • show running-config
    +This will list the current running configuration. This is in a format that +can either be pasted into the configuration file, or run directly at the +command line. +

    +

  • + +
  • show counters
    +Internally, counters are kept of key values, such as bytes and packets +transferred, as well as function call counters. This function displays all +these counters, and is probably only useful for debugging.

    +You can reset these counters by running clear counters. +

    +

  • + +
  • write memory
    +This will write the current running configuration to the config file +l2tpns.cfg, which will be run on a restart. +

    +

  • + +
  • snoop
    +You must specify a username, which will be intercepted for the current +session. Specify no snoop username to disable interception for the +current session.

    +If you want interception to be permanent, you will have to modify the radius +response for the user. See Interception. +

    +

  • + +
  • throttle
    +You must specify a username, which will be throttled for the current +session. Specify no throttle username to disable throttling for the +current session.

    +If you want throttling to be permanent, you will have to modify the radius +response for the user. See Throttling. +

    +

  • + +
  • drop session
    +This will cleanly disconnect a session. You must specify a session id, which +you can get from show session. This will send a disconnect message +to the remote end. +

    +

  • + +
  • drop tunnel
    +This will cleanly disconnect a tunnel, as well as all sessions on that +tunnel. It will send a disconnect message for each session individually, and +after 10 seconds it will send a tunnel disconnect message. +

    +

  • + +
  • load plugin
    +Load a plugin. You must specify the plugin name, and it will search in +/usr/lib/l2tpns for plugin.so. You can unload a loaded plugin with +remove plugin. +

    +

  • + +
  • set
    +Set a configuration variable. You must specify the variable name, and the +value. If the value contains any spaces, you should quote the value with +double quotes ("). +

    +

  • + +
  • uptime
    +This will show how long the L2TPNS process has been running, and the current +bandwidth utilization: +
    +17:10:35 up 8 days, 2212 users, load average: 0.21, 0.17, 0.16
    +Bandwidth: UDP-ETH:6/6  ETH-UDP:13/13  TOTAL:37.6   IN:3033 OUT:2569
    +
    +The bandwidth line contains 4 sets of values.
    +UDP-ETH is the current bandwidth going from the LAC to the ethernet +(user uploads), in mbits/sec.
    +ETH-UDP is the current bandwidth going from ethernet to the LAC (user +downloads).
    +TOTAL is the total aggregate bandwidth in mbits/s.
    +IN and OUT are packets/per-second going between UDP-ETH and ETH-UDP. +

    +These counters are updated every second. +

    +

  • +
+ +

nsctl

+ +nsctl was implemented (badly) to allow messages to be passed to modules.

+ +You must pass at least 2 parameters: host and command. The +host is the address of the L2TPNS server which you want to send the message +to.
+Command can currently be either garden or ungarden. With +both of these commands, you must give a session ID as the 3rd parameter. +This will activate or deactivate the walled garden for a session +temporarily. + +

Signals

+ +While the process is running, you can send it a few different signals, using +the kill command. +
+killall -HUP l2tpns
+
+ +The signals understood are: +
    +
  • SIGHUP - Reload the config from disk

  • +
  • SIGTERM / SIGINT - Shut down for a restart. This will dump the current +state to disk (if save_state is set to true). Upon restart, the +process will read this saved state to resume active sessions.

    +This is really useful when doing an upgrade, as the code can change without +dropping any users. However, if the internal structures such as +sessiont or tunnelt change, then this saved state file +will not reload, and none of the sessions will be recreated. This is bad.

    +If these structures do change, you should kill the server with SIGQUIT, +which won't dump the state.

  • +
  • SIGQUIT - Shut down cleanly. This will send a disconnect message for +every active session and tunnel before shutting down. This is a good idea +when upgrading the code, as no sessions will be left with the remote end +thinking they are open.
  • +
+ +

Throttling

+ +L2TPNS contains support for slowing down user sessions to whatever speed you +desire. You must first enable the global setting throttle_speed +before this will be activated.

+ +If you wish a session to be throttled permanently, you should set the +Vendor-Specific radius value Cisco-Avpair="throttle=yes", which +will be handled by the autothrottle module.

+ +Otherwise, you can enable and disable throttling an active session using +the throttle CLI command.

+ +Throttling is actually performed using a combination of iptables and tc.
+First, a HTB bucket is created using tc (unless one is already created and +unused).
+Secondly, an iptables rule is inserted into the throttle chanin in the +mangle table so all packets destined for the user's IP address go into the +HTB.

+ +You can check the packets being throttled using the tc command. Find the HTB +handle by doing show session id in the CLI, next to the Filter +Bucket tag. Then at the shell prompt, you can run: +

+tc -s class ls dev tun0 | grep -A3 1:870
+class htb 1:870 root prio 0 rate 28Kbit ceil 28Kbit burst 15Kb cburst 1634b 
+ Sent 27042557 bytes 41464 pkts (dropped 1876, overlimits 0) 
+ lended: 41471 borrowed: 0 giants: 0
+ tokens: 3490743 ctokens: 353601
+
+ +

Interception

+ +You may have to deal with legal requirements to be able to intercept a +user's traffic at any time. L2TPNS allows you to begin and end interception +on the fly, as well as at authentication time.

+ +When a user is being intercepted, a copy of every packet they send and +receive will be sent wrapped in a UDP packet to the IP address and port set +in the snoop_host and snoop_port configuration +variables.

+ +The UDP packet contains just the raw IP frame, with no extra headers.

+ +To enable interception on a connected user, use the snoop username +and no snoop username CLI commands. These will enable interception +immediately.

+ +If you wish the user to be intercepted whenever they reconnect, you will +need to modify the radius response to include the Vendor-Specific value +Cisco-Avpair="intercept=yes". For this feature to be enabled, +you need to have the autosnoop module loaded.

+ +

Authentication

+ +Whenever a session connects, it is not fully set up until authentication is +completed. The remote end must send a PPP CHAP or PPP PAP authentication +request to L2TPNS.

+ +This request is sent to the radius server, which will hopefully respond with +Auth-Accept or Auth-Reject.

+ +If Auth-Accept is received, the session is set up and an IP address is +assigned. The radius server can include a Framed-IP-Address field in the +reply, and that address will be assigned to the client. It can also include +specific DNS servers, and a Framed-Route if that is required.

+ +If Auth-Reject is received, then the client is sent a PPP AUTHNAK packet, +at which point they should disconnect. The exception to this is when the +walled garden module is loaded, in which case the user still receives the +PPP AUTHACK, but their session is flagged as being a garden'd user, and they +should not receive any service.

+ +The radius reply can also contain a Vendor-Specific attribute called +Cisco-Avpair. This field is a freeform text field that most CISCO +devices understand to contain configuration instructions for the session. In +the case of L2TPNS it is expected to be of the form +

+key=value,key2=value2,key3=value3,keyn=value
+
+ +Each key-value pair is separated and passed to any modules loaded. The +autosnoop and autothrottle understand the keys +intercept and throttle respectively. For example, to have +a user who is to be throttled and intercepted, the Cisco-Avpair value should +contain: +
+intercept=yes,throttle=yes
+
+ +

Plugins

+ +So as to make L2TPNS as flexible as possible (I know the core code is pretty +difficult to understand), it includes a plugin API, which you can use to +hook into certain events.

+ +There are a few example modules included - autosnoop, autothrottle and +garden.

+ +When an event happens that has a hook, L2TPNS looks for a predefined +function name in every loaded module, and runs them in the order the modules +were loaded.

+ +The function should return PLUGIN_RET_OK if it is all OK. If it returns +PLUGIN_RET_STOP, then it is assumed to have worked, but that no further +modules should be run for this event.

+A return of PLUGIN_RET_ERROR means that this module failed, and +no further processing should be done for this event. Use this with care. + +Every event function called takes a specific structure named +param_event, which varies in content with each event. The +function name for each event will be plugin_event, +so for the event timer, the function declaration should look like: +

+int plugin_timer(struct param_timer *data);
+
+ +A list of the available events follows, with a list of all the fields in the +supplied structure: +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EventDescriptionParameters
pre_authThis is called after a radius response has been + received, but before it has been processed by the + code. This will allow you to modify the response in + some way. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
  • username
  • +
  • password
  • +
  • protocol (0xC023 for PAP, 0xC223 for CHAP)
  • +
  • continue_auth - Set to 0 to stop processing authentication modules
  • +
+
post_authThis is called after a radius response has been + received, and the basic checks have been performed. This + is what the garden module uses to force authentication + to be accepted. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
  • username
  • +
  • auth_allowed - This is already set to true or + false depending on whether authentication has been + allowed so far. You can set this to 1 or 0 to force + allow or disallow authentication
  • +
  • protocol (0xC023 for PAP, 0xC223 for CHAP)
  • +
+
packet_rxThis is called whenever a session receives a + packet. Use this sparingly, as this will + seriously slow down the system. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
  • buf - The raw packet data
  • +
  • len - The length of buf
  • +
+
packet_txThis is called whenever a session sends a + packet. Use this sparingly, as this will + seriously slow down the system. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
  • buf - The raw packet data
  • +
  • len - The length of buf
  • +
+
timerThis is run every second, no matter what is happening. + This is called from a signal handler, so make sure anything + you do is reentrant. + +
    +
  • time_now - The current unix timestamp
  • +
+
new_sessionThis is called after a session is fully set up. The + session is now ready to handle traffic. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
+
kill_sessionThis is called when a session is about to be shut down. + This may be called multiple times for the same session. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
+
radius_responseThis is called whenever a radius response includes a + Cisco-Avpair value. The value is split up into + key=value pairs, and each is processed through all + modules. + +
    +
  • t - Tunnel ID
  • +
  • s - Session ID
  • +
  • key
  • +
  • value
  • +
+
controlThis is called in whenever a nsctl packet is received. + This should handle the packet and form a response if + required. + +
    +
  • buf - The raw packet data
  • +
  • l - The raw packet data length
  • +
  • source_ip - Where the request came from
  • +
  • source_port - Where the request came from
  • +
  • response - Allocate a buffer and put your response in here
  • +
  • response_length - Length of response
  • +
  • send_response - true or false whether a response + should be sent. If you set this to true, you must + allocate a response buffer.
  • +
  • type - Type of request (see nsctl.c)
  • +
  • id - ID of request
  • +
  • data - I'm really not sure
  • +
  • data_length - Length of data
  • +
+
+
+ +

Walled Garden

+ +Walled Garden is implemented so that you can provide perhaps limited service +to sessions that incorrectly authenticate.

+ +Whenever a session provides incorrect authentication, and the +radius server responds with Auth-Reject, the walled garden module +(if loaded) will force authentication to succeed, but set the flag +garden in the session structure, and adds an iptables rule to +the garden_users chain to force all packets for the session's IP +address to traverse the garden chain.

+ +This doesn't just work. To set this all up, you will need to create +2 iptables chains on the nat table - garden and garden_users. +

+iptables -t nat -N garden
+iptables -t nat -F garden
+iptables -t nat -N garden_users
+iptables -t nat -F garden_users
+
+ +You should add rules to the garden chain to limit user's traffic. For +example, to force all traffic except DNS to be forwarded to 192.168.1.1, add +these entries to your firewall startup script: +
+iptables -t nat -A garden -p tcp --dport ! 53 -j DNAT --to 192.168.1.1
+iptables -t nat -A garden -p udp --dport ! 53 -j DNAT --to 192.168.1.1
+
+ +L2TPNS will add entries to the garden_users chain as appropriate.

+ +You can check the amount of traffic being captured using the following +command: +

+iptables -t nat -L garden -nvx
+
+ +

Clustering

+ +Clustering is currently broken. But here's how it's supposed to work.

+ +

Performance

+ +Performance is great.

+ +I'd like to include some pretty graphs here that show a linear performance +increase, with no impact by number of connected sessions.

+ +That's really what it looks like.

+ +
+David Parrish
+david@dparrsih.com + + From 61dfc18a840ef16d01c43ce80ada387d27f74fb1 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 5 Apr 2004 05:29:13 +0000 Subject: [PATCH 011/482] Don't use default 1:1 --- rl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rl.c b/rl.c index 49bd72c..7e697d0 100644 --- a/rl.c +++ b/rl.c @@ -1,5 +1,5 @@ // L2TPNS Rate Limiting Stuff -// $Id: rl.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: rl.c,v 1.3 2004-04-05 05:29:13 fred_nerk Exp $ #include #include @@ -25,7 +25,7 @@ int next_tbf = 1; void init_rl() { char *commands[] = { - "tc qdisc add dev " DEVICE " root handle 1: htb default 1", + "tc qdisc add dev " DEVICE " root handle 1: htb", "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", From 9a4f911b3d3bb3cee9141f39a9b53689926780d4 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 5 Apr 2004 05:35:31 +0000 Subject: [PATCH 012/482] Release 1.1.1 --- Changes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changes b/Changes index e2731d8..500cffc 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,8 @@ +* Mon Apr 5 2004 David Parrish 1.1.1 +- Don't mention configure anymore, it's not used +- Added the autosnoop and autothrottle modules +- Don't default to using a htb for the class root + * 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 From 32e5db59062efd8e40d0d5360bab479ef838ebef Mon Sep 17 00:00:00 2001 From: David Parrish Date: Fri, 16 Apr 2004 02:33:32 +0000 Subject: [PATCH 013/482] Small typo fixes from Paul Lampron --- Docs/manual.html | 2 +- Makefile | 2 +- l2tpns.h | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Docs/manual.html b/Docs/manual.html index a4ae0b3..a1c398f 100644 --- a/Docs/manual.html +++ b/Docs/manual.html @@ -880,6 +880,6 @@ That's really what it looks like.


David Parrish
-david@dparrsih.com +david@dparrish.com diff --git a/Makefile b/Makefile index a7f3631..3fbe7db 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ install: all $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool $(INSTALL) -D -o root -g root -m 0600 etc/users.default $(etcdir)/l2tpns.users for PLUGIN in $(PLUGINS); do \ - $(INSTALL) -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN); \ + $(INSTALL) -D -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN); \ done if [ ! -e /dev/net/tun ]; then \ mkdir /dev/net; \ diff --git a/l2tpns.h b/l2tpns.h index a705b8e..3d85ce6 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,11 +1,11 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.4 2004-04-16 02:33:32 fred_nerk Exp $ #include #include #include "config.h" -#define VERSION "1.1.0" +#define VERSION "1.1.2" // Limits #define MAXTUNNEL 500 // could be up to 65535 @@ -32,9 +32,9 @@ #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 CLIUSERS ETCDIR "l2tpns.users" // CLI Users file -#define IPPOOLFILE ETCDIR "l2tpns.ip_pool" // Address pool configuration +#define CONFIGFILE ETCDIR "/l2tpns.cfg" // Configuration 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 From 8a8b45c1741ad0338391dd98726f86bc76f6c706 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 10 May 2004 00:39:34 +0000 Subject: [PATCH 014/482] Init data before trying to use it --- l2tpns.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 6927eb5..b988c7f 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -2199,8 +2199,9 @@ int main(int argc, char *argv[]) } time(&basetime); // start clock - // scan args + initdata(); + // scan args while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) { switch (o) @@ -2237,7 +2238,6 @@ int main(int argc, char *argv[]) initiptables(); initplugins(); - initdata(); init_cli(); read_config_file(); log(1, 0, 0, 0, "L2TPNS Version 1.1.0 - http://l2tpns.sourceforge.net/\n"); From 7a9ca7372714ecd5ca4ce5e527b3f23c078732bd Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:12:02 +0000 Subject: [PATCH 015/482] Add username parameter to "show users" command Fix counting tunnel rx errors as tunnel tx errors Add "show throttle" command --- cli.c | 130 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/cli.c b/cli.c index e796787..9bdcd39 100644 --- a/cli.c +++ b/cli.c @@ -1,5 +1,5 @@ // L2TPNS Command Line Interface -// $Id: cli.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ +// $Id: cli.c,v 1.4 2004-05-24 04:12:02 fred_nerk Exp $ // vim: sw=4 ts=8 #include @@ -28,7 +28,8 @@ extern struct Tstats *_statistics; extern int cli_pid; struct cli_def *cli = NULL; int cli_quit = 0; -extern int clifd, udpfd, tapfd, snoopfd, radfd, ifrfd, cluster_sockfd; +extern int clifd, udpfd, tapfd, snoopfd, ifrfd, cluster_sockfd; +extern int *radfds; extern sessionidt *cli_session_kill; extern tunnelidt *cli_tunnel_kill; extern tbft *filter_buckets; @@ -39,7 +40,7 @@ extern char hostname[]; extern struct Tringbuffer *ringbuffer; #endif -char *rcs_id = "$Id: cli.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $"; +char *rcs_id = "$Id: cli.c,v 1.4 2004-05-24 04:12:02 fred_nerk Exp $"; char *debug_levels[] = { "CRIT", @@ -75,6 +76,7 @@ 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_show_throttle(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); @@ -86,8 +88,6 @@ 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); @@ -107,13 +107,14 @@ void init_cli() c = cli_register_command(cli, NULL, "show", NULL, 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, "users", cmd_show_users, "Show a list of all connected users or details of selected user"); 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, "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"); + cli_register_command(cli, c, "throttle", cmd_show_throttle, "List all token bucket filters in use"); #ifdef STATISTICS cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals"); @@ -143,12 +144,6 @@ void init_cli() 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 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"); @@ -202,13 +197,16 @@ void init_cli() void cli_do(int sockfd) { + int i; + if (fork()) return; // Close sockets if (udpfd) close(udpfd); udpfd = 0; if (tapfd) close(tapfd); tapfd = 0; if (snoopfd) close(snoopfd); snoopfd = 0; - if (radfd) close(radfd); radfd = 0; + for (i = 0; i < config->num_radfds; i++) + if (radfds[i]) close(radfds[i]); if (ifrfd) close(ifrfd); ifrfd = 0; if (cluster_sockfd) close(cluster_sockfd); cluster_sockfd = 0; if (clifd) close(clifd); clifd = 0; @@ -285,6 +283,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) 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, " MRU: %d", session[s].mru); 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); @@ -413,14 +412,36 @@ 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) { + char sid[32][8]; + char *sargv[32]; + int sargc = 0; int i; for (i = 0; i < MAXSESSION; i++) { if (!session[i].opened) continue; if (!session[i].user[0]) continue; - cli_print(cli, "%s", - session[i].user); + if (argc > 0) + { + int j; + for (j = 0; j < argc && sargc < 32; j++) + { + if (strcmp(argv[j], session[i].user) == 0) + { + snprintf(sid[sargc], sizeof(sid[0]), "%d", i); + sargv[sargc] = sid[sargc]; + sargc++; + } + } + + continue; + } + + cli_print(cli, "%s", session[i].user); } + + if (sargc > 0) + return cmd_show_session(cli, "users", sargv, sargc); + return CLI_OK; } @@ -446,7 +467,7 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) 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_tx_errors), GET_STAT(tunnel_retries)); cli_print(cli, ""); @@ -527,9 +548,8 @@ int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) 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); + inet_toa(ip_address_pool[i].address), ip_address_pool[i].session, session[ip_address_pool[i].session].user); used++; } @@ -627,7 +647,7 @@ int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc) 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"); + cli_print(cli, "%6s%5s%6s%9s%9s%4s", "Radius", "Sock", "State", "Session", "Retry", "Try"); time(&time_now); @@ -643,8 +663,9 @@ int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc) if (!show_all && radius[i].state == RADIUSNULL) continue; - cli_print(cli, "%6d%6s%9d%9u%4d", - i, + cli_print(cli, "%6d%5d%6s%9d%9u%4d", + i >> RADIUS_SHIFT, + i & RADIUS_MASK, states[radius[i].state], radius[i].session, radius[i].retry, @@ -670,6 +691,24 @@ int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } +int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + cli_print(cli, "Token bucket filters:"); + cli_print(cli, "%-6s %8s %-4s", "ID", "Handle", "Used"); + for (i = 0; i < MAXSESSION; i++) + { + if (!*filter_buckets[i].handle) + continue; + + cli_print(cli, "%-6d %8s %c", + i, + filter_buckets[i].handle, + (filter_buckets[i].in_use) ? 'Y' : 'N'); + } + return CLI_OK; +} + int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc) { cli_print(cli, " _\n" @@ -939,9 +978,10 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - throttle_session(s, 1); - - cli_print(cli, "throttling user %s", argv[i]); + if (!throttle_session(s, 1)) + cli_print(cli, "error throttling %s", argv[i]); + else + cli_print(cli, "throttling user %s", argv[i]); } return CLI_OK; } @@ -966,7 +1006,7 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) } for (i = 0; i < argc; i++) - { + { if (!(s = sessionbyuser(argv[i]))) { cli_print(cli, "User %s is not connected", argv[i]); @@ -1047,46 +1087,6 @@ int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } -int cmd_watch_session(struct cli_def *cli, char *command, char **argv, int argc) -{ - sessionidt s; - - if (argc != 1) - { - cli_print(cli, "Specify a single session to debug (0 to disable)"); - return CLI_OK; - } - s = atoi(argv[0]); - - if (debug_session) - cli_print(cli, "No longer debugging session %d", debug_session); - - if (s) cli_print(cli, "Debugging session %d.", s); - debug_session = s; - - return CLI_OK; -} - -int cmd_watch_tunnel(struct cli_def *cli, char *command, char **argv, int argc) -{ - tunnelidt s; - - if (argc != 1) - { - cli_print(cli, "Specify a single tunnel to debug (0 to disable)"); - return CLI_OK; - } - s = atoi(argv[0]); - - if (debug_tunnel) - cli_print(cli, "No longer debugging tunnel %d", debug_tunnel); - - if (s) cli_print(cli, "Debugging tunnel %d.", s); - debug_tunnel = s; - - return CLI_OK; -} - int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc) { int i, firstfree = 0; From ebd407a833b9c868fcc534d1451c3ebe25cf1d1d Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:12:34 +0000 Subject: [PATCH 016/482] Add gcc __attribute__ to logging functions --- cluster_master.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster_master.c b/cluster_master.c index 4516f4e..fbd4f4c 100644 --- a/cluster_master.c +++ b/cluster_master.c @@ -1,5 +1,5 @@ // L2TPNS Cluster Master -// $Id: cluster_master.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: cluster_master.c,v 1.3 2004-05-24 04:12:34 fred_nerk Exp $ #include #include @@ -57,7 +57,7 @@ int backup_down(slave *s); int return_state(slave *s); slave *find_slave(uint32_t address); #define log _log -void _log(int level, const char *format, ...); +void _log(int level, const char *format, ...) __attribute__((format (printf, 2, 3))); void log_hex(int level, const char *title, const char *data, int maxsize); /* Catch our forked processes exiting */ @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) signal(SIGCHLD, sigchild_handler); - log(0, "Cluster Manager $Id: cluster_master.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ starting\n"); + log(0, "Cluster Manager $Id: cluster_master.c,v 1.3 2004-05-24 04:12:34 fred_nerk Exp $ starting\n"); to.tv_sec = 1; to.tv_usec = 0; From b1087392f5ee0a220d9ac7417d65d5289abf46ab Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:12:48 +0000 Subject: [PATCH 017/482] Fix logging parameter errors --- cluster_slave.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cluster_slave.c b/cluster_slave.c index 534e21f..1ed03b0 100644 --- a/cluster_slave.c +++ b/cluster_slave.c @@ -1,5 +1,5 @@ // L2TPNS Cluster Master -// $Id: cluster_slave.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ +// $Id: cluster_slave.c,v 1.4 2004-05-24 04:12:48 fred_nerk Exp $ #include #include @@ -78,7 +78,7 @@ int handle_tunnel(char *buf, int l) // 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)); + log(1, 0, 0, 0, "Discarding tunnel message from cluster master.\n"); return 0; } @@ -114,7 +114,7 @@ int handle_session(char *buf, int l) // 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)); + log(1, 0, 0, 0, "Discarding session message from cluster master.\n"); return 0; } From 2957bcf7b09637719356ab701d3705acb0b093e7 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:13:06 +0000 Subject: [PATCH 018/482] Use multiple radius sockets to allow more concurrent authentication requests --- garden.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/garden.c b/garden.c index e9eecb2..b8f6277 100644 --- a/garden.c +++ b/garden.c @@ -149,7 +149,7 @@ int garden_session(sessiont *s, int flag) if (!s->die) { /* OK, we're up! */ - u8 r = p.radiusnew(p.get_id_by_session(s)); + u16 r = p.radiusnew(p.get_id_by_session(s)); p.radiussend(r, RADIUSSTART); } } From 69a383f460ae05ca3694f67744198c269141d14a Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:18:23 +0000 Subject: [PATCH 019/482] Fix SEGFAULT --- garden.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/garden.c b/garden.c index b8f6277..c3ce34a 100644 --- a/garden.c +++ b/garden.c @@ -68,7 +68,7 @@ int plugin_control(struct param_control *data) sprintf((data->response + data->response_length), "%s", errormsg); data->response_length += strlen(errormsg) + 1; - p.log(3, 0, 0, 0, "Unknown session %s\n", session); + p.log(3, 0, 0, 0, "Unknown session %d\n", session); return PLUGIN_RET_STOP; } *(short *)(data->response + 2) = ntohs(PKT_RESP_OK); From c861a50b72291756d2f9bf195f7dd7c9b94e8be9 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:20:28 +0000 Subject: [PATCH 020/482] - Use multiple radius sockets to allow more concurrent authentication requests - Add gcc __attribute__ to logging functions - Fix warnings shown by __attribute__ - Make sure regular cleanup happens regularly under high load - Add variable cleanup_interval for changing cleanup interval - Add support for reading more than one packet per fd in each processing loop - This is configurable with the multi_read_count variable - Remove segv handler so core dumps can happen - Use nonblocking sockets - Increase tun queue length - Fix minimum length of IP packets - Remove per-packet plugin hooks (they are slow) - Don't drop session if no free RADIUS - Don't expire more than 1000 sessions per cleanup interval - Remove -a and -c command-line options. They don't work anyway - Don't require file: in log_filename --- l2tpns.c | 338 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 185 insertions(+), 153 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index b988c7f..83bca7d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -49,7 +49,7 @@ 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 *radfds = NULL; // RADIUS requests file handles int ifrfd = -1; // File descriptor for routing, etc time_t basetime = 0; // base clock char hostname[1000] = ""; // us. @@ -62,7 +62,6 @@ FILE *log_stream = NULL; struct sockaddr_in snoop_addr = {0}; extern int cluster_sockfd; unsigned long last_sid = 0; -int handle_interface = 0; int clifd = 0; sessionidt *cli_session_kill = NULL; tunnelidt *cli_tunnel_kill = NULL; @@ -99,6 +98,8 @@ struct config_descriptt config_values[] = { CONFIG("accounting_dir", accounting_dir, STRING), CONFIG("setuid", target_uid, INT), CONFIG("dump_speed", dump_speed, BOOL), + CONFIG("cleanup_interval", cleanup_interval, INT), + CONFIG("multi_read_count", multi_read_count, INT), { NULL, 0, 0, 0 }, }; @@ -132,7 +133,6 @@ void sighup_handler(int); void sigterm_handler(int); void sigquit_handler(int); void sigchild_handler(int); -void sigsegv_handler(int); void read_config_file(); void read_state(); void dump_state(); @@ -285,6 +285,10 @@ void inittap(void) log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); exit(-1); } + { + int flags = fcntl(tapfd, F_GETFL, 0); + fcntl(tapfd, F_SETFL, flags | O_NONBLOCK); + } if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) { log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); @@ -295,7 +299,7 @@ void inittap(void) ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); sin.sin_family = AF_INET; - sin.sin_addr.s_addr = handle_interface ? config->bind_address : 0x01010101; // 1.1.1.1 + sin.sin_addr.s_addr = config->bind_address ? config->bind_address : 0x01010101; // 1.1.1.1 memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) @@ -303,6 +307,13 @@ void inittap(void) perror("set tap addr"); exit( -1); } + /* Bump up the qlen to deal with bursts from the network */ + ifr.ifr_qlen = 1000; + if (ioctl(ifrfd, SIOCSIFTXQLEN, (void *) &ifr) < 0) + { + perror("set tap qlen"); + exit( -1); + } ifr.ifr_flags = IFF_UP; if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) { @@ -336,6 +347,10 @@ void initudp(void) addr.sin_addr.s_addr = config->bind_address; udpfd = socket(AF_INET, SOCK_DGRAM, UDP); setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + { + int flags = fcntl(udpfd, F_GETFL, 0); + fcntl(udpfd, F_SETFL, flags | O_NONBLOCK); + } if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) { perror("udp bind"); @@ -361,6 +376,7 @@ sessionidt sessionbyip(ipt ip) { unsigned char *a = (unsigned char *)&ip; char **d = (char **) ip_hash; + sessionidt s; #ifdef STAT_CALLS STAT(call_sessionbyip); @@ -370,7 +386,10 @@ sessionidt sessionbyip(ipt ip) if (!(d = (char **) d[(size_t) *a++])) return 0; if (!(d = (char **) d[(size_t) *a++])) return 0; - return (ipt) d[(size_t) *a]; + s = (ipt) d[(size_t) *a]; + if (s && session[s].tunnel) + return s; + return 0; } void cache_sessionid(ipt ip, sessionidt s) @@ -425,31 +444,33 @@ sessionidt sessionbyuser(char *username) void send_garp(ipt ip) { - int s; - struct ifreq ifr; - unsigned char mac[6]; + int s; + struct ifreq ifr; + unsigned char mac[6]; - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) - { - perror("socket"); - exit(-1); - } - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); - if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) - { - perror("get eth0 hwaddr"); - exit(-1); - } - memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) - { - perror("get eth0 ifindex"); - exit(-1); - } - close(s); - sendarp(ifr.ifr_ifindex, mac, ip); + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + log(0, 0, 0, 0, "Error creating socket for GARP: %s\n", strerror(errno)); + return; + } + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); + if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) + { + log(0, 0, 0, 0, "Error getting eth0 hardware address for GARP: %s\n", strerror(errno)); + close(s); + return; + } + memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) + { + log(0, 0, 0, 0, "Error getting eth0 interface index for GARP: %s\n", strerror(errno)); + close(s); + return; + } + close(s); + sendarp(ifr.ifr_ifindex, mac, ip); } // Find session by username, 0 for not found @@ -537,7 +558,7 @@ void processarp(u8 * buf, int len) s = sessionbyip(htonl(ip)); if (s) { - log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, session[s].tunnel, s); + log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255); memcpy(buf + 4, buf + 10, 6); // set destination as source *(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address *(u16 *) (buf + 12) = htons(tapmac[1]); @@ -565,9 +586,20 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t) #ifdef STAT_CALLS STAT(call_tunnelsend); #endif + if (!t) + { + static int backtrace_count = 0; + log(0, 0, 0, t, "tunnelsend called with 0 as tunnel id\n"); + STAT(tunnel_tx_errors); + log_backtrace(backtrace_count, 5) + return; + } + if (!tunnel[t].ip) { + static int backtrace_count = 0; log(1, 0, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); + log_backtrace(backtrace_count, 5) STAT(tunnel_tx_errors); return; } @@ -615,7 +647,7 @@ void processipout(u8 * buf, int len) #ifdef STAT_CALLS STAT(call_processipout); #endif - if (len < 38) + if (len < MIN_IP_SIZE) { log(1, 0, 0, 0, "Short IP, %d bytes\n", len); STAT(tunnel_tx_errors); @@ -648,12 +680,6 @@ void processipout(u8 * buf, int len) log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); - // Plugin hook - { - struct param_packet_rx packet = { &tunnel[t], &session[s], buf, len }; - run_plugins(PLUGIN_PACKET_RX, &packet); - } - // Add on L2TP header { u8 *p = makeppp(b, buf, len, t, s, PPPIP); @@ -776,7 +802,10 @@ void sessionshutdown(sessionidt s, char *reason) STAT(call_sessionshutdown); #endif if (!session[s].tunnel) + { + log(3, session[s].ip, s, session[s].tunnel, "Called sessionshutdown on a session with no tunnel.\n"); return; // not a live session + } if (!session[s].die) log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); @@ -790,7 +819,7 @@ void sessionshutdown(sessionidt s, char *reason) // RADIUS Stop message if (session[s].opened && !walled_garden && !dead) { - u8 r = session[s].radius; + u16 r = session[s].radius; if (!r) { if (!(r = radiusnew(s))) @@ -811,7 +840,7 @@ void sessionshutdown(sessionidt s, char *reason) if (session[s].ip) { // IP allocated, clear and unroute - u8 r; + u16 r; if (session[s].route[0].ip) { routeset(session[s].ip, 0, 0, 0); @@ -839,7 +868,7 @@ void sessionshutdown(sessionidt s, char *reason) void sendipcp(tunnelidt t, sessionidt s) { u8 buf[MAXCONTROL]; - u8 r = session[s].radius; + u16 r = session[s].radius; u8 *q; #ifdef STAT_CALLS STAT(call_sendipcp); @@ -859,7 +888,7 @@ void sendipcp(tunnelidt t, sessionidt s) } q = makeppp(buf, 0, 0, t, s, PPPIPCP); *q = ConfigReq; - q[1] = r; // ID, dont care, we only send one type of request + q[1] = r << RADIUS_SHIFT; // ID, dont care, we only send one type of request *(u16 *) (q + 2) = htons(10); q[4] = 3; q[5] = 6; @@ -876,10 +905,10 @@ void sessionkill(sessionidt s, char *reason) sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed + log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason); memset(&session[s], 0, sizeof(session[s])); session[s].next = sessionfree; sessionfree = s; - log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason); cluster_send_session(s); } @@ -1143,7 +1172,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) fatal = flags; continue; } - log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n"); + log(4, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n"); } if (*b & 0x3C) { @@ -1294,7 +1323,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) memcpy(tmp, b, (n >= 30) ? 30 : n); session[s].tx_connect_speed = atol(tmp); } - log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%d>\n", + log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%lu>\n", session[s].tx_connect_speed); break; case 38: // rx connect speed @@ -1309,7 +1338,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) memcpy(tmp, b, (n >= 30) ? 30 : n); session[s].rx_connect_speed = atol(tmp); } - log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%d>\n", + log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%lu>\n", session[s].rx_connect_speed); break; case 25: // Physical Channel ID @@ -1337,7 +1366,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) case 31: // Proxy Authentication Challenge { memcpy(radius[session[s].radius].auth, b, 16); - log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge (%X)\n", radius[session[s].radius].auth); + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge\n"); break; } case 32: // Proxy Authentication ID @@ -1457,7 +1486,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) } else { - u8 r; + u16 r; controlt *c; s = sessionfree; @@ -1468,7 +1497,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) if (!(r = radiusnew(s))) { log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); - sessionkill(s, "no free RADIUS sesions"); return; } @@ -1617,42 +1645,45 @@ void processtap(u8 * buf, int len) } if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP processarp(buf, len); - else if (*(u16 *) (buf + 2) == htons(PKTIP)) // ARP + else if (*(u16 *) (buf + 2) == htons(PKTIP)) // IP processipout(buf, len); - else - { - log(1, 0, 0, 0, "Unexpected tap packet %04X, %d bytes\n", ntohs(*(u16 *) (buf + 2)), len); - } } // main loop - gets packets on tap or udp and processes them void mainloop(void) { fd_set cr; - int cn; + int cn, i; u8 buf[65536]; struct timeval to; + clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc clockt next_acct = slow + ACCT_TIME; clockt next_cluster_ping = slow + 50; + clockt next_clean = time_now + config->cleanup_interval; to.tv_sec = 1; to.tv_usec = 0; - log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, radfd=%d, cluster_sockfd=%d, controlfd=%d\n", - udpfd, tapfd, radfd, cluster_sockfd, controlfd); + log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, cluster_sockfd=%d, controlfd=%d\n", + udpfd, tapfd, cluster_sockfd, controlfd); FD_ZERO(&cr); FD_SET(udpfd, &cr); FD_SET(tapfd, &cr); - FD_SET(radfd, &cr); FD_SET(controlfd, &cr); FD_SET(clifd, &cr); 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; if (cn < clifd) cn = clifd; if (cn < cluster_sockfd) cn = cluster_sockfd; + for (i = 0; i < config->num_radfds; i++) + { + if (!radfds[i]) continue; + FD_SET(radfds[i], &cr); + if (radfds[i] > cn) + cn = radfds[i]; + } while (!main_quit) { @@ -1680,16 +1711,35 @@ void mainloop(void) struct sockaddr_in addr; int alen = sizeof(addr); if (FD_ISSET(udpfd, &r)) - processudp(buf, recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen), &addr); - else if (FD_ISSET(tapfd, &r)) - processtap(buf, read(tapfd, buf, sizeof(buf))); - else if (FD_ISSET(radfd, &r)) - processrad(buf, recv(radfd, buf, sizeof(buf), 0)); - else if (FD_ISSET(cluster_sockfd, &r)) + { + int c, n; + for (c = 0; c < config->multi_read_count; c++) + { + if ((n = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0) + processudp(buf, n, &addr); + else + break; + } + } + if (FD_ISSET(tapfd, &r)) + { + int c, n; + for (c = 0; c < config->multi_read_count; c++) + { + if ((n = read(tapfd, buf, sizeof(buf))) > 0) + processtap(buf, n); + else + break; + } + } + for (i = 0; i < config->num_radfds; i++) + if (FD_ISSET(radfds[i], &r)) + processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i); + if (FD_ISSET(cluster_sockfd, &r)) processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)); - else if (FD_ISSET(controlfd, &r)) + if (FD_ISSET(controlfd, &r)) processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); - else if (FD_ISSET(clifd, &r)) + if (FD_ISSET(clifd, &r)) { struct sockaddr_in addr; int sockfd; @@ -1706,20 +1756,22 @@ void mainloop(void) close(sockfd); } } - else - { - log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n); - continue; - } } - else if (n == 0) { // handle timeouts + + /* Handle timeouts. Make sure that this gets run anyway, even if there was + * something to read, else under load this will never actually run.... + */ + if (n == 0 || next_clean <= time_now) { clockt when = now(); clockt best = when + 100; // default timeout sessionidt s; tunnelidt t; - u8 r; + int count; + u16 r; + log(3, 0, 0, 0, "Begin regular cleanup\n"); for (r = 1; r < MAXRADIUS; r++) + { if (radius[r].state && radius[r].retry) { if (radius[r].retry <= when) @@ -1727,6 +1779,9 @@ void mainloop(void) if (radius[r].retry && radius[r].retry < best) best = radius[r].retry; } + else if (radius[r].state && !radius[r].retry) + radius[r].retry = backoff(radius[r].try+1); + } for (t = 1; t < MAXTUNNEL; t++) { // check for expired tunnels @@ -1770,7 +1825,7 @@ void mainloop(void) if (cli_session_kill[0]) { int i; - for (i = 1; i < MAXSESSION && cli_session_kill[i]; i++) + for (i = 0; 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 administrator"); @@ -1789,12 +1844,14 @@ void mainloop(void) } } + count = 0; for (s = 1; s < MAXSESSION; s++) { // check for expired sessions if (session[s].die && session[s].die <= when) { sessionkill(s, "Expired"); + if (++count >= 1000) break; continue; } @@ -1803,6 +1860,7 @@ void mainloop(void) { sessionkill(s, "No response to LCP ECHO requests"); STAT(session_timeout); + if (++count >= 1000) break; continue; } @@ -1818,8 +1876,9 @@ void mainloop(void) *(u32 *)(q + 4) = 0; // Magic Number (not supported) log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", - time_now - session[s].last_packet); + (int)(time_now - session[s].last_packet)); tunnelsend(b, 24, session[s].tunnel); // send it + if (++count >= 1000) break; continue; } } @@ -1837,11 +1896,12 @@ void mainloop(void) cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname)); } - if (best <= when) - best = when + 1; // should not really happen - to.tv_sec = (best - when) / 10; - to.tv_usec = 100000 * ((best - when) % 10); - log(5, 0, 0, 0, "Next time check in %d.%d seconds\n", (best - when) / 10, ((best - when) % 10)); + if (best < when + config->cleanup_interval) + best = when + config->cleanup_interval; // Throttle to at most once per 10 seconds + next_clean = time_now + config->cleanup_interval; + to.tv_sec = config->cleanup_interval; + to.tv_usec = 0; + log(3, 0, 0, 0, "End regular cleanup, next in %d seconds\n", config->cleanup_interval); } } } @@ -1995,6 +2055,7 @@ int assign_ip_address(sessionidt s) session[s].ip_pool_index = best; ip_address_pool[best].assigned = 1; ip_address_pool[best].last = time_now; + ip_address_pool[best].session = s; if (session[s].walled_garden) /* Don't track addresses of users in walled garden (note: this means that their address isn't "sticky" even if they get @@ -2005,7 +2066,7 @@ int assign_ip_address(sessionidt s) 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); + "assign_ip_address(): %s ip address %d from pool\n", reuse ? "Reusing" : "Allocating", best); return 1; } @@ -2025,6 +2086,7 @@ void free_ip_address(sessionidt s) uncache_sessionid(session[s].ip); session[s].ip = 0; ip_address_pool[i].assigned = 0; + ip_address_pool[i].session = 0; ip_address_pool[i].last = time_now; } @@ -2077,7 +2139,7 @@ void initippool() *p++ = 0; if (!*p || !(numbits = atoi(p))) { - log(0, 0, 0, 0, "Invalid pool range %s/\n", buf, p); + log(0, 0, 0, 0, "Invalid pool range %s\n", buf); continue; } start = end = ntohl(inet_addr(pool)); @@ -2092,7 +2154,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); + log(5, 0, 0, 0, "Adding route for address pool %s/%lu\n", inet_toa(htonl(start)), 32 + mask); memset(&r, 0, sizeof(r)); r.rt_dev = config->tapdevice; r.rt_dst.sa_family = AF_INET; @@ -2102,7 +2164,8 @@ void initippool() r.rt_flags = (RTF_UP | RTF_STATIC); if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0) { - log(0, 0, 0, 0, "Error adding ip address pool route %s/%d: %s\n", inet_toa(start), mask, strerror(errno)); + log(0, 0, 0, 0, "Error adding ip address pool route %s/%lu: %s\n", + inet_toa(start), mask, strerror(errno)); } } else @@ -2145,7 +2208,7 @@ void dump_acct_info() 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].ip || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) continue; if (!f) { @@ -2188,20 +2251,9 @@ int main(int argc, char *argv[]) _program_name = strdup(argv[0]); - { - struct rlimit rlim; - rlim.rlim_cur = RLIM_INFINITY; - rlim.rlim_max = RLIM_INFINITY; - // Remove the maximum core size - setrlimit(RLIMIT_CORE, &rlim); - // Make core dumps go to /tmp - chdir("/tmp"); - } - time(&basetime); // start clock - initdata(); - // scan args + while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) { switch (o) @@ -2209,21 +2261,9 @@ int main(int argc, char *argv[]) case 'v': config->debug++; break; - case 'c': - strncpy(config->config_file, optarg, sizeof(config->config_file) - 1); - break; case 'h': strncpy(hostname, optarg, 999); break; - case 'a': - myip = inet_addr(optarg); - if (myip == INADDR_NONE) { - log(0, 0, 0, 0, "Invalid ip %s\n", optarg); - exit(-1); - } - 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-v\t\tDebug\n"); @@ -2235,13 +2275,25 @@ int main(int argc, char *argv[]) // Start the timer routine off time(&time_now); strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + signal(SIGALRM, sigalrm_handler); + siginterrupt(SIGALRM, 0); initiptables(); initplugins(); + initdata(); init_cli(); read_config_file(); - log(1, 0, 0, 0, "L2TPNS Version 1.1.0 - http://l2tpns.sourceforge.net/\n"); - log(1, 0, 0, 0, "Licensed under the GPL\n"); + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.6 2004-05-24 04:20:28 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + { + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + // Remove the maximum core size + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + log(0, 0, 0, 0, "Can't set ulimit: %s\n", strerror(errno)); + // Make core dumps go to /tmp + chdir("/tmp"); + } /* Start up the cluster first, so that we don't have two machines with * the same IP at once. @@ -2257,9 +2309,8 @@ int main(int argc, char *argv[]) initrad(); initippool(); init_rl(); - if (handle_interface) { + if (config->bind_address) send_garp(config->bind_address); - } // If NOSTATEFILE exists, we will ignore any updates from the cluster master for this execution if (!unlink(NOSTATEFILE)) @@ -2267,13 +2318,11 @@ int main(int argc, char *argv[]) read_state(); - signal(SIGALRM, sigalrm_handler); signal(SIGHUP, sighup_handler); signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigquit_handler); signal(SIGCHLD, sigchild_handler); - signal(SIGSEGV, sigsegv_handler); alarm(1); @@ -2369,14 +2418,6 @@ void sigchild_handler(int signal) ; } -void sigsegv_handler(int signal) -{ - log(0, 0, 0, 0, "----------------------------------------------\n"); - log(0, 0, 0, 0, "- SEGFAULT! -\n"); - log(0, 0, 0, 0, "----------------------------------------------\n"); - _exit(0); -} - void read_state() { struct stat sb; @@ -2490,7 +2531,6 @@ void read_state() for (i = 0; i < MAXSESSION; 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); @@ -2597,9 +2637,18 @@ void update_config() } if (*config->log_filename) { - if (strstr(config->log_filename, "file:") == config->log_filename) + if (strstr(config->log_filename, "syslog:") == config->log_filename) { - if ((log_stream = fopen((char *)(config->log_filename + 5), "a"))) + char *p = config->log_filename + 7; + if (*p) + { + openlog("l2tpns", LOG_PID, facility_value(p)); + syslog_log = 1; + } + } + else if (strchr(config->log_filename, '/') == config->log_filename) + { + if ((log_stream = fopen((char *)(config->log_filename), "a"))) { fseek(log_stream, 0, SEEK_END); setbuf(log_stream, NULL); @@ -2610,15 +2659,6 @@ void update_config() setbuf(log_stream, NULL); } } - else if (strstr(config->log_filename, "syslog:") == config->log_filename) - { - char *p = config->log_filename + 7; - if (*p) - { - openlog("l2tpns", LOG_PID, facility_value(p)); - syslog_log = 1; - } - } } else { @@ -2637,6 +2677,8 @@ void update_config() log(0, 0, 0, 0, "No RADIUS servers defined!\n"); } + config->num_radfds = 2 << RADIUS_SHIFT; + // Update plugins for (i = 0; i < MAXPLUGINS; i++) { @@ -2654,6 +2696,8 @@ void update_config() } } memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); + if (!config->cleanup_interval) config->cleanup_interval = 10; + if (!config->multi_read_count) config->multi_read_count = 1; config->reload_config = 0; } @@ -2672,7 +2716,6 @@ void read_config_file() log(3, 0, 0, 0, "Done reading config file\n"); fclose(f); update_config(); - log_stream = NULL; } int sessionsetup(tunnelidt t, sessionidt s, u8 routes) @@ -2693,7 +2736,7 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes) // 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 gardened 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; @@ -2723,7 +2766,7 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes) // Force throttling on or off // This has the advantage of cleaning up after another throttled user who may have left // firewall rules lying around - throttle_session(s, session[s].throttle); + session[s].throttle = throttle_session(s, session[s].throttle); { struct param_new_session data = { &tunnel[t], &session[s] }; @@ -2813,7 +2856,7 @@ void add_plugin(char *plugin_name) int *v = dlsym(p, "__plugin_api_version"); if (!v || *v != PLUGIN_API_VERSION) { - log(1, 0, 0, 0, " Plugin load failed: API version mismatch\n", dlerror()); + log(1, 0, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror()); dlclose(p); return; } @@ -2822,14 +2865,14 @@ void add_plugin(char *plugin_name) initfunc = dlsym(p, "plugin_init"); if (!initfunc) { - log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist.\n", dlerror()); + log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist: %s\n", dlerror()); dlclose(p); return; } if (!initfunc(&funcs)) { - log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE.\n", dlerror()); + log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror()); dlclose(p); return; } @@ -2918,18 +2961,7 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) param.response = resp; param.response_length = l; - if (param.type == PKT_LOAD_PLUGIN && param.data_length) - { - add_plugin(param.data); - } - else if (param.type == PKT_UNLOAD_PLUGIN && param.data_length) - { - remove_plugin(param.data); - } - else - { - run_plugins(PLUGIN_CONTROL, ¶m); - } + run_plugins(PLUGIN_CONTROL, ¶m); if (param.send_response) { From 11627344e7bbceb18bb9c06b3707a6e04610265e Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:24:06 +0000 Subject: [PATCH 021/482] Add log_backtrace Use multiple radius sockets to allow more concurrent authentication requests Fix minimum length of IP packets Add support for reading more than one packet per fd in each processing loop Add variable cleanup_interval for changing cleanup interval Bump version to 1.2.0 --- l2tpns.h | 88 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/l2tpns.h b/l2tpns.h index 3d85ce6..8cdc2fc 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,16 +1,19 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.4 2004-04-16 02:33:32 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.5 2004-05-24 04:24:06 fred_nerk Exp $ #include #include -#include "config.h" +#include -#define VERSION "1.1.2" +#define VERSION "1.2.0" // Limits #define MAXTUNNEL 500 // could be up to 65535 #define MAXSESSION 50000 // could be up to 65535 -#define MAXRADIUS 255 +#define RADIUS_SHIFT 5 +#define RADIUS_MASK ((unsigned short)(((unsigned short)~0) >> (16 - RADIUS_SHIFT))) +#define MAXRADIUS ((2 << (RADIUS_SHIFT - 1)) * 255) + #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 @@ -30,11 +33,11 @@ #define TAPDEVICE "/dev/net/tun" #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 CLIUSERS ETCDIR "/l2tpns.users" // CLI Users file -#define IPPOOLFILE ETCDIR "/l2tpns.ip_pool" // Address pool configuration +#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 CLIUSERS ETCDIR "/l2tpns.users" // CLI Users file +#define IPPOOLFILE ETCDIR "/l2tpns.ip_pool" // Address pool configuration #ifndef LIBDIR #define LIBDIR "/usr/lib/l2tpns" #endif @@ -52,6 +55,7 @@ #define PPPCCP 0x80FD #define PPPIP 0x0021 #define PPPMP 0x003D +#define MIN_IP_SIZE 0x20 enum { ConfigReq = 1, @@ -99,10 +103,8 @@ controlt; typedef struct stbft { - struct stbft *next; char handle[10]; char in_use; - int mark; } tbft; @@ -128,7 +130,7 @@ typedef struct sessions 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) + u16 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? @@ -172,26 +174,27 @@ typedef struct tunnels tunnelt; // 180 bytes per radius session -typedef struct radiuss // outstanding RADIUS requests +typedef struct radiuss // outstanding RADIUS requests { - sessionidt session; // which session this applies to - hasht auth; // request authenticator - clockt retry; // when to try next - char calling[MAXTEL]; // calling number - char pass[129]; // password - u8 id; // ID for PPP response - u8 try; // which try we are on - u8 state; // state of radius requests - u8 chap; // set if CHAP used (is CHAP identifier) + sessionidt session; // which session this applies to + hasht auth; // request authenticator + clockt retry; // when to try next + char calling[MAXTEL]; // calling number + char pass[129]; // password + u8 id; // ID for PPP response + u8 try; // which try we are on + u8 state; // state of radius requests + u8 chap; // set if CHAP used (is CHAP identifier) } radiust; 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) + ipt address; + char assigned; // 1 if assigned, 0 if free + sessionidt session; + clockt last; // last used + char user[129]; // user (try to have ip addresses persistent) } ippoolt; @@ -325,6 +328,8 @@ struct configt char config_file[128]; int reload_config; // flag to re-read config (set by cli) + int cleanup_interval; // interval between regular cleanups (in seconds) + int multi_read_count; // amount of packets to read per fd in processing loop char tapdevice[10]; // tap device name char log_filename[128]; @@ -334,6 +339,7 @@ struct configt int radius_accounting; ipt radiusserver[MAXRADSERVER]; // radius servers u8 numradiusservers; // radius server count + short num_radfds; // Number of radius filehandles allocated ipt default_dns1, default_dns2; @@ -350,6 +356,8 @@ struct configt int dump_speed; char plugins[64][MAXPLUGINS]; char old_plugins[64][MAXPLUGINS]; + + int next_tbf; // Next HTB id available to use }; struct config_descriptt @@ -380,11 +388,11 @@ void dumplcp(char *p, int l); // radius.c void initrad(void); -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); +void radiussend(u16 r, u8 state); +void processrad(u8 *buf, int len, char socket_index); +void radiusretry(u16 r); +u16 radiusnew(sessionidt s); +void radiusclear(u16 r, sessionidt s); // throttle.c int throttle_session(sessionidt s, int throttle); @@ -437,7 +445,7 @@ void mainloop(void); #ifndef log_hex #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(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) __attribute__((format (printf, 5, 6))); void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize); void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response); int sessionsetup(tunnelidt t, sessionidt s, u8 routes); @@ -460,3 +468,19 @@ void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int pac extern tunnelt *tunnel; extern sessiont *session; #define sessionfree (session[0].next) + +#define log_backtrace(count, max) \ +if (count++ < max) { \ + void *array[20]; \ + char **strings; \ + int size, i; \ + log(0, 0, 0, t, "Backtrace follows"); \ + size = backtrace(array, 10); \ + strings = backtrace_symbols(array, size); \ + if (strings) for (i = 0; i < size; i++) \ + { \ + log(0, 0, 0, t, "%s\n", strings[i]); \ + } \ + free(strings); \ +} + From f764cfffe6c453f11d6d850d002df071097cc23f Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:24:41 +0000 Subject: [PATCH 022/482] Use multiple radius sockets to allow more concurrent authentication requests --- plugin.h | 4 +- radius.c | 151 ++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/plugin.h b/plugin.h index b41b16f..e980579 100644 --- a/plugin.h +++ b/plugin.h @@ -30,8 +30,8 @@ struct pluginfuncs sessiont *(*get_session_by_id)(sessionidt s); sessionidt (*get_id_by_session)(sessiont *s); void (*sessionkill)(sessionidt s, char *reason); - u8 (*radiusnew)(sessionidt s); - void (*radiussend)(u8 r, u8 state); + u16 (*radiusnew)(sessionidt s); + void (*radiussend)(u16 r, u8 state); }; struct param_pre_auth diff --git a/radius.c b/radius.c index 458b5f4..a90fe13 100644 --- a/radius.c +++ b/radius.c @@ -1,5 +1,5 @@ // L2TPNS Radius Stuff -// $Id: radius.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: radius.c,v 1.3 2004-05-24 04:27:11 fred_nerk Exp $ #include #include @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -20,9 +21,9 @@ extern radiust *radius; extern sessiont *session; extern tunnelt *tunnel; extern u32 sessionid; -extern int radfd; extern struct Tstats *_statistics; extern struct configt *config; +extern int *radfds; const char *radius_state(int state) { @@ -39,30 +40,51 @@ const char *radius_state(int state) // Set up socket for radius requests void initrad(void) { - radfd = socket(AF_INET, SOCK_DGRAM, UDP); + int i; + log(3, 0, 0, 0, "Creating %d sockets for RADIUS queries\n", config->num_radfds); + radfds = calloc(sizeof(int), config->num_radfds); + for (i = 0; i < config->num_radfds; i++) + { + int flags; + if (!radfds[i]) radfds[i] = socket(AF_INET, SOCK_DGRAM, UDP); + flags = fcntl(radfds[i], F_GETFL, 0); + fcntl(radfds[i], F_SETFL, flags | O_NONBLOCK); + } } -void radiusclear(u8 r, sessionidt s) +void radiusclear(u16 r, sessionidt s) { if (s) session[s].radius = 0; memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL; } -static u8 new_radius() +int next_radius_id = 1; + +static u16 new_radius() { - u8 i; - for (i = 1; i < MAXRADIUS; i++) + u16 i; + int loops = 0; + for (i = next_radius_id; ; i = (i + 1) % MAXRADIUS) { if (radius[i].state == RADIUSNULL) + { + next_radius_id = (next_radius_id + 1) % MAXRADIUS; return i; + } + if (next_radius_id == i) + { + if (++loops == 2) + { + log(0, 0, 0, 0, "Can't find a free radius session! This is very bad!\n"); + return 0; + } + } } - log(0, 0, 0, 0, "Can't find a free radius session! This could be bad!\n"); - return 0; } -u8 radiusnew(sessionidt s) +u16 radiusnew(sessionidt s) { - u8 r; + u16 r; if (!(r = new_radius())) { log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n"); @@ -77,7 +99,7 @@ u8 radiusnew(sessionidt s) } // Send a RADIUS request -void radiussend(u8 r, u8 state) +void radiussend(u16 r, u8 state) { struct sockaddr_in addr; u8 b[4096]; // RADIUS packet @@ -111,12 +133,21 @@ void radiussend(u8 r, u8 state) radius[r].try = 0; 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); + log(4, 0, s, session[s].tunnel, "Send RADIUS id %d sock %d state %s try %d\n", + r >> RADIUS_SHIFT, r & RADIUS_MASK, + radius_state(radius[r].state), radius[r].try); if (radius[r].try > config->numradiusservers * 2) { if (s) { - sessionshutdown(s, "RADIUS timeout"); + if (state == RADIUSAUTH) + sessionshutdown(s, "RADIUS timeout"); + else + { + log(1, 0, s, session[s].tunnel, "RADIUS timeout, but in state %s so don't timeout session\n", + radius_states[state]); + radiusclear(r, s); + } STAT(radius_timeout); } else @@ -130,17 +161,17 @@ void radiussend(u8 r, u8 state) // contruct RADIUS access request switch (state) { - case RADIUSAUTH: - b[0] = 1; // access request - break; - case RADIUSSTART: - case RADIUSSTOP: - b[0] = 4; // accounting request - break; - default: - log(0, 0, 0, 0, "Unknown radius state %d\n", state); + case RADIUSAUTH: + b[0] = 1; // access request + break; + case RADIUSSTART: + case RADIUSSTOP: + b[0] = 4; // accounting request + break; + default: + log(0, 0, 0, 0, "Unknown radius state %d\n", state); } - b[1] = r; // identifier + b[1] = r >> RADIUS_SHIFT; // identifier memcpy(b + 4, radius[r].auth, 16); p = b + 20; if (s) @@ -307,15 +338,15 @@ void radiussend(u8 r, u8 state) addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT); log_hex(5, "RADIUS Send", b, (p - b)); - sendto(radfd, b, p - b, 0, (void *) &addr, sizeof(addr)); + sendto(radfds[r & RADIUS_MASK], b, p - b, 0, (void *) &addr, sizeof(addr)); } // process RADIUS response -void processrad(u8 * buf, int len) +void processrad(u8 *buf, int len, char socket_index) { u8 b[MAXCONTROL]; MD5_CTX ctx; - u8 r; + u16 r; sessionidt s; tunnelidt t = 0; hasht hash; @@ -331,9 +362,10 @@ void processrad(u8 * buf, int len) return ; } len = ntohs(*(u16 *) (buf + 2)); - r = buf[1]; + r = socket_index | (buf[1] << RADIUS_SHIFT); s = radius[r].session; - log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n", radius_states[radius[r].state], r, s); + log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n", + radius_states[radius[r].state], r, s); if (!s && radius[r].state != RADIUSSTOP) { log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); @@ -473,9 +505,12 @@ void processrad(u8 * buf, int len) } else { - log(3, 0, s, session[s].tunnel, " Radius reply contains route for %d/%d\n", - inet_toa(ip), - inet_toa(mask)); + char *ips, *masks; + ips = strdup(inet_toa(ip)); + masks = strdup(inet_toa(mask)); + log(3, 0, s, session[s].tunnel, " Radius reply contains route for %s/%s\n", ips, masks); + free(ips); + free(masks); session[s].route[routes].ip = ip; session[s].route[routes].mask = mask; routes++; @@ -540,7 +575,7 @@ void processrad(u8 * buf, int len) 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"); + log(0, 0, s, t, " No IP allocated by radius. The IP address pool is FULL!\n"); } if (!session[s].dns1 && config->default_dns1) { @@ -567,7 +602,7 @@ void processrad(u8 * buf, int len) } else { - log(3, 0, s, t, " RADIUS response in state %d\n", radius[r].state); + log(3, 0, s, t, " RADIUS response in state %s\n", radius_states[radius[r].state]); } } while (0); @@ -576,7 +611,7 @@ void processrad(u8 * buf, int len) } // Send a retry for RADIUS/CHAP message -void radiusretry(u8 r) +void radiusretry(u16 r) { sessionidt s = radius[r].session; tunnelidt t = 0; @@ -585,31 +620,31 @@ void radiusretry(u8 r) #endif if (s) t = session[s].tunnel; - radius[r].retry = 0; + radius[r].retry = backoff(radius[r].try + 1); switch (radius[r].state) { - case RADIUSCHAP: // sending CHAP down PPP - sendchap(t, s); - break; - case RADIUSIPCP: - sendipcp(t, s); // send IPCP - break; - case RADIUSAUTH: // sending auth to RADIUS server - radiussend(r, RADIUSAUTH); - break; - case RADIUSSTART: // sending start accounting to RADIUS server - radiussend(r, RADIUSSTART); - break; - case RADIUSSTOP: // sending stop accounting to RADIUS server - radiussend(r, RADIUSSTOP); - break; - default: - case RADIUSNULL: // Not in use - case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server - // free up RADIUS task - radiusclear(r, s); - log(3, 0, s, session[s].tunnel, "Freeing up radius session %d\n", r); - break; + case RADIUSCHAP: // sending CHAP down PPP + sendchap(t, s); + break; + case RADIUSIPCP: + sendipcp(t, s); // send IPCP + break; + case RADIUSAUTH: // sending auth to RADIUS server + radiussend(r, RADIUSAUTH); + break; + case RADIUSSTART: // sending start accounting to RADIUS server + radiussend(r, RADIUSSTART); + break; + case RADIUSSTOP: // sending stop accounting to RADIUS server + radiussend(r, RADIUSSTOP); + break; + default: + case RADIUSNULL: // Not in use + case RADIUSWAIT: // waiting timeout before available, in case delayed reply from RADIUS server + // free up RADIUS task + radiusclear(r, s); + log(3, 0, s, session[s].tunnel, "Freeing up radius session %d\n", r); + break; } } From 546f9704529c049736840ec4ca5befdca794be04 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:26:01 +0000 Subject: [PATCH 023/482] Use multiple radius sockets to allow more concurrent authentication requests Remove per-packet plugin hooks (they are slow) --- ppp.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/ppp.c b/ppp.c index e4f4e81..3136d30 100644 --- a/ppp.c +++ b/ppp.c @@ -1,5 +1,5 @@ // L2TPNS PPP Stuff -// $Id: ppp.c,v 1.3 2004-03-05 00:22:06 fred_nerk Exp $ +// $Id: ppp.c,v 1.4 2004-05-24 04:26:01 fred_nerk Exp $ #include #include @@ -87,7 +87,7 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) } else { // set up RADIUS request - u8 r = session[s].radius; + u16 r = session[s].radius; // Run PRE_AUTH plugins struct param_pre_auth packet = { &tunnel[t], &session[s], strdup(user), strdup(pass), PPPPAP, 1 }; @@ -115,7 +115,7 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) // Process CHAP messages void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) { - u8 r; + u16 r; u16 len; #ifdef STAT_CALLS @@ -187,8 +187,8 @@ void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) } radius[r].chap = 1; - radiussend(r, RADIUSAUTH); log(3, 0, s, t, "CHAP login %s\n", session[s].user); + radiussend(r, RADIUSAUTH); } char *ppp_lcp_types[] = { @@ -407,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(4, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); + log(5, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); tunnelsend(b, l + (q - b), t); // send it } else if (*p == EchoReply) @@ -437,7 +437,7 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) } if (*p == ConfigAck) { // happy with our IPCP - u8 r = session[s].radius; + u16 r = session[s].radius; if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) if (!r) r = radiusnew(s); @@ -560,12 +560,6 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) *(u32 *)p = htonl(0x00000800); l += 4; - // Plugin hook - { - struct param_packet_rx packet = { &tunnel[t], &session[s], p, l }; - run_plugins(PLUGIN_PACKET_TX, &packet); - } - // send to ethernet if (write(tapfd, p, l) < 0) { @@ -622,7 +616,7 @@ void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) void sendchap(tunnelidt t, sessionidt s) { u8 b[MAXCONTROL]; - u8 r = session[s].radius; + u16 r = session[s].radius; u8 *q; #ifdef STAT_CALLS STAT(call_sendchap); From 503df19134a588fb5b33e85de3113b65af05978c Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:28:41 +0000 Subject: [PATCH 024/482] Check return code when throttling users --- rl.c | 55 ++++++++++++++++++++++++++++++------------------------ throttle.c | 13 ++++++++----- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/rl.c b/rl.c index 7e697d0..9f2f14a 100644 --- a/rl.c +++ b/rl.c @@ -1,32 +1,32 @@ // L2TPNS Rate Limiting Stuff -// $Id: rl.c,v 1.3 2004-04-05 05:29:13 fred_nerk Exp $ +// $Id: rl.c,v 1.4 2004-05-24 04:28:41 fred_nerk Exp $ +#include +#include +#include #include +#include +#include #include #include -#include -#include +#include +#include +#include #include -#include -#include #include "l2tpns.h" extern radiust *radius; extern sessiont *session; extern u32 sessionid; -extern int radfd; extern tbft *filter_buckets; extern struct configt *config; #define DEVICE "tun0" -int next_tbf = 1; - void init_rl() { char *commands[] = { "tc qdisc add dev " DEVICE " root handle 1: htb", - "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 2>&1 >/dev/null", @@ -50,22 +50,31 @@ u16 rl_create_tbf() char cmd[2048]; if (!config->rl_rate) return 0; - if (next_tbf >= MAXSESSION) return 0; - t = next_tbf++; + t = ++config->next_tbf; + if (config->next_tbf >= MAXSESSION) return 0; snprintf(filter_buckets[t].handle, 9, "1:%d0", t); 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, config->rl_rate); log(3, 0, 0, 0, "%s\n", cmd); - system(cmd); + if (WEXITSTATUS(system(cmd)) != 0) + { + memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); + log(0, 0, 0, 0, "tc returned an error creating a token bucket\n"); + return 0; + } snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid %s", t, filter_buckets[t].handle); log(3, 0, 0, 0, "%s\n", cmd); - system(cmd); + if (WEXITSTATUS(system(cmd)) != 0) + { + memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); + log(0, 0, 0, 0, "tc returned an error creating a filter\n"); + return 0; + } - next_tbf++; return t; } @@ -76,12 +85,12 @@ u16 rl_get_tbf() for (i = 1; i < MAXSESSION; i++) { - if (!filter_buckets[i].in_use && *filter_buckets[i].handle) - { - filter_buckets[i].in_use = 1; - log(2, 0, 0, 0, "Returning tbf %s\n", filter_buckets[i].handle); - return i; - } + if (!*filter_buckets[i].handle) continue; + if (filter_buckets[i].in_use) continue; + + filter_buckets[i].in_use = 1; + log(2, 0, 0, 0, "Returning tbf %s\n", filter_buckets[i].handle); + return i; } i = rl_create_tbf(); if (i) filter_buckets[i].in_use = 1; @@ -91,7 +100,6 @@ u16 rl_get_tbf() void rl_done_tbf(u16 t) { if (!t) return; - 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; } @@ -106,9 +114,8 @@ void rl_destroy_tbf(u16 t) return; } snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); - system(cmd); - system("iptables -t mangle -D l2tpns -j throttle 2>&1 >/dev/null"); - system("iptables -t mangle -X throttle 2>&1 >/dev/null"); + if (WEXITSTATUS(system(cmd)) != 0) + log(0, 0, 0, 0, "tc returned an error deleting a token bucket\n"); memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); } diff --git a/throttle.c b/throttle.c index e0731f0..fd24712 100644 --- a/throttle.c +++ b/throttle.c @@ -1,5 +1,5 @@ // L2TPNS Throttle Stuff -// $Id: throttle.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: throttle.c,v 1.3 2004-05-24 04:29:21 fred_nerk Exp $ #include #include @@ -19,7 +19,6 @@ extern radiust *radius; extern sessiont *session; extern u32 sessionid; -extern int radfd; extern tbft *filter_buckets; extern struct configt *config; @@ -41,12 +40,16 @@ int throttle_session(sessionidt s, int throttle) 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); + log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s (bucket %s)\n", s, session[s].user, filter_buckets[session[s].tbf].handle); 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); + if (WEXITSTATUS(system(cmd)) != 0) + { + log(2, 0, s, session[s].tunnel, "iptables returned an error. Session is not throttled\n"); + return 0; + } } else { @@ -69,6 +72,6 @@ int throttle_session(sessionidt s, int throttle) } } session[s].throttle = throttle; - return 0; + return session[s].throttle; } From 3bfd1c4e87cbe74f5c2ad81ef7a4944ce41cb311 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:29:38 +0000 Subject: [PATCH 025/482] Optimisations --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3fbe7db..b193c37 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ etcdir = $(PREFIX)/etc/l2tpns libdir = $(PREFIX)/usr/lib/l2tpns CC = gcc -CFLAGS=-Wall -g -O2 +CFLAGS=-Wall -g -O3 -funroll-loops -fomit-frame-pointer -finline-functions LDFLAGS = LIBS = -lm -ldl -lcli INSTALL = /usr/bin/install -c From 144bd8fb6b3d220ca5f604883503fb48988b741d Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:33:31 +0000 Subject: [PATCH 026/482] Version 1.2.0 --- Changes | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Changes b/Changes index 500cffc..5900809 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,27 @@ +* Mon May 24 2004 David Parrish 1.2.0 +- Fix SEGFAULT in garden module +- Use multiple radius sockets to allow more concurrent authentication requests +- Add username parameter to "show users" command +- Fix counting tunnel rx errors as tunnel tx errors +- Add "show throttle" command +- Add gcc __attribute__ to logging functions +- Fix warnings shown by __attribute__ +- Make sure regular cleanup happens regularly under high load +- Add variable cleanup_interval for changing cleanup interval +- Add support for reading more than one packet per fd in each processing loop +- This is configurable with the multi_read_count variable +- Remove segv handler so core dumps can happen +- Use nonblocking sockets +- Increase tun queue length +- Fix minimum length of IP packets +- Remove per-packet plugin hooks (they are slow) +- Don't drop session if no free RADIUS +- Don't expire more than 1000 sessions per cleanup interval +- Remove -a and -c command-line options. They don't work anyway +- Don't require file: in log_filename +- Bump version to 1.2.0 +- Check return code when throttling users + * Mon Apr 5 2004 David Parrish 1.1.1 - Don't mention configure anymore, it's not used - Added the autosnoop and autothrottle modules From e98737a1683046b634692bd9e5406e53b89a65c9 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:33:35 +0000 Subject: [PATCH 027/482] Foo --- Makefile | 2 ++ l2tpns.h | 5 +++-- stamp-h | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 stamp-h diff --git a/Makefile b/Makefile index b193c37..18be310 100644 --- a/Makefile +++ b/Makefile @@ -60,3 +60,5 @@ install: all %.so: %.c $(CC) -fPIC -shared -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBPATH) +%.o: %.c l2tpns.h + $(CC) -c -o $@ $< $(CFLAGS) diff --git a/l2tpns.h b/l2tpns.h index 8cdc2fc..f007154 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,9 +1,10 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.5 2004-05-24 04:24:06 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.6 2004-05-24 04:33:35 fred_nerk Exp $ #include #include -#include +#include +#include "config.h" #define VERSION "1.2.0" diff --git a/stamp-h b/stamp-h deleted file mode 100644 index 9788f70..0000000 --- a/stamp-h +++ /dev/null @@ -1 +0,0 @@ -timestamp From b4451ee1a4e9b38331aee04e53092c3a2ee84b50 Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 24 May 2004 04:42:50 +0000 Subject: [PATCH 028/482] Add -d detach option --- l2tpns.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 83bca7d..433cf69 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -2254,10 +2254,15 @@ int main(int argc, char *argv[]) time(&basetime); // start clock // scan args - while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) + while ((o = getopt(argc, argv, "vc:h:a:d")) >= 0) { switch (o) { + case 'd': + // Double fork to detach from terminal + if (fork()) exit(0); + if (fork()) exit(0); + break; case 'v': config->debug++; break; @@ -2266,7 +2271,7 @@ int main(int argc, char *argv[]) break; case '?': default: - 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"); + printf("Args are:\n\t-d\tDetach from terminal\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; } @@ -2283,7 +2288,7 @@ int main(int argc, char *argv[]) initdata(); init_cli(); read_config_file(); - log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.6 2004-05-24 04:20:28 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.7 2004-05-24 04:42:50 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; From c239d4b22829335fda98b66ce045a3f624436eeb Mon Sep 17 00:00:00 2001 From: David Parrish Date: Wed, 23 Jun 2004 03:52:24 +0000 Subject: [PATCH 029/482] * Wed Jun 23 2004 David Parrish 2.0.0 - Major release - Completely replace active/standby clustering with a new peer-to-peer clustering method which allows much greater throughput and is a lot more fault tolerant - Add internal tbf implementation for throttling without relying on tc and kernel HTB - Add support for iBGP and eBGP to advertise routes - Add cli commands "show cluster", "show bgp", "show ipcache", "show throttle", "show tbf", "suspend bgp", "restart bgp", "show user" - Interception destination must be set per-user - If SMP machine, allow use of SCHED_FIFO, which should improve performance - Added config option to send GARP at startup - Added plugin_become_master and plugin_new_session_master plugin hooks - Remove useless sessionsendarp(). This isn't needed now that we are using TUN instead of TAP. - ICMP rate limiting so not every unreachable packet is replied with an ICMP unreachable message - mangle table is not required on anything but the cluster master, so slaves will drop the mangle table and attempt to unload the ip_conntrack module - Statically assigned IP addresses (by Radius) work now - Add -d command-line flag to detach and become a daemon - Configuration file is now "/etc/l2tpns/startup-config" - Reduced MIN_IP_SIZE to 0x19 to stop a pile of Short IP warnings - Resend initial IPCP request until it's acknowleged by the client - Better radius session cleanup logic - Many miscellaenous bugfixes and performance enhancements - Thanks to Michael O'Reilly and Brendan O'Dea for most of these new features --- Changes | 29 + Makefile | 56 +- autosnoop.c | 25 +- autothrottle.c | 11 +- bgp.c | 1308 +++++++++++++++++++++++++++++++++ bgp.h | 202 ++++++ cli.c | 207 ++++-- cluster.c | 1420 +++++++++++++++++++++++++++++++++++- cluster.h | 84 ++- cluster_master.c | 517 ------------- cluster_slave.c | 270 ------- config.h | 1 + constants.c | 2 +- constants.h | 6 +- control.h | 2 +- garden.c | 170 +++-- icmp.c | 4 +- l2tpns.c | 1817 +++++++++++++++++++++++++++++++++------------- l2tpns.h | 171 +++-- ll.c | 2 +- ll.h | 2 +- machines.cfg | 39 - md5.h | 6 +- plugin.h | 6 +- ppp.c | 206 ++++-- radius.c | 82 ++- rl.c | 121 --- tbf.c | 400 ++++++++++ tbf.h | 13 + throttle.c | 77 -- util.h | 5 + 31 files changed, 5417 insertions(+), 1844 deletions(-) create mode 100644 bgp.c create mode 100644 bgp.h delete mode 100644 cluster_master.c delete mode 100644 cluster_slave.c delete mode 100644 machines.cfg delete mode 100644 rl.c create mode 100644 tbf.c create mode 100644 tbf.h delete mode 100644 throttle.c diff --git a/Changes b/Changes index 5900809..d026dfe 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,32 @@ +* Wed Jun 23 2004 David Parrish 2.0.0 +- Major release +- Completely replace active/standby clustering with a new peer-to-peer + clustering method which allows much greater throughput and is a lot more fault + tolerant +- Add internal tbf implementation for throttling without relying on tc and + kernel HTB +- Add support for iBGP and eBGP to advertise routes +- Add cli commands "show cluster", "show bgp", "show ipcache", "show throttle", + "show tbf", "suspend bgp", "restart bgp", "show user" +- Interception destination must be set per-user +- If SMP machine, allow use of SCHED_FIFO, which should improve performance +- Added config option to send GARP at startup +- Added plugin_become_master and plugin_new_session_master plugin hooks +- Remove useless sessionsendarp(). This isn't needed now that we are using TUN + instead of TAP. +- ICMP rate limiting so not every unreachable packet is replied with an ICMP + unreachable message +- mangle table is not required on anything but the cluster master, so slaves + will drop the mangle table and attempt to unload the ip_conntrack module +- Statically assigned IP addresses (by Radius) work now +- Add -d command-line flag to detach and become a daemon +- Configuration file is now "/etc/l2tpns/startup-config" +- Reduced MIN_IP_SIZE to 0x19 to stop a pile of Short IP warnings +- Resend initial IPCP request until it's acknowleged by the client +- Better radius session cleanup logic +- Many miscellaenous bugfixes and performance enhancements +- Thanks to Michael O'Reilly and Brendan O'Dea for most of these new features + * Mon May 24 2004 David Parrish 1.2.0 - Fix SEGFAULT in garden module - Use multiple radius sockets to allow more concurrent authentication requests diff --git a/Makefile b/Makefile index 18be310..04bf448 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,9 @@ etcdir = $(PREFIX)/etc/l2tpns libdir = $(PREFIX)/usr/lib/l2tpns CC = gcc -CFLAGS=-Wall -g -O3 -funroll-loops -fomit-frame-pointer -finline-functions +DEFINES= -DBGP -DRINGBUFFER -DSTAT_CALLS -DSTATISTICS +OPTIM=-g -O3 -funroll-loops -fomit-frame-pointer -finline-functions +CFLAGS=-Wall $(OPTIM) $(DEFINES) LDFLAGS = LIBS = -lm -ldl -lcli INSTALL = /usr/bin/install -c @@ -15,36 +17,38 @@ OBJS= md5.o \ l2tpns.o \ ppp.o \ radius.o \ - throttle.o \ - rl.o \ ll.o \ cluster.o \ - cluster_slave.o \ arp.o \ constants.o \ ll.o \ control.o \ util.o \ + tbf.o \ + bgp.o \ PLUGINS=garden.so autothrottle.so autosnoop.so -all: l2tpns cluster_master nsctl $(PLUGINS) +all: l2tpns nsctl $(PLUGINS) l2tpns: $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(DEFS) -cluster_master: cluster_master.o ll.o cluster.o util.o - $(CC) $(CFLAGS) -o $@ $^ $(DEFS) - nsctl: nsctl.o control.o $(CC) $(CFLAGS) -o $@ $^ $(DEFS) clean: - /bin/rm -f *.o *.so l2tpns cluster_master nsctl + /bin/rm -f *.o *.so l2tpns nsctl + +depend: + (sed -n 'p; /^## Dependencies: (autogenerated) ##/q' Makefile && \ + gcc -MM $(DEFINES) $(OBJS:.o=.c) && \ + gcc -MM $(DEFINES) $(PLUGINS:.so=.c) | sed 's/\.o/.so/') >Makefile.tmp + mv Makefile Makefile.bak + mv Makefile.tmp Makefile install: all $(INSTALL) -D -o root -g root -m 0755 l2tpns $(bindir)/l2tpns - $(INSTALL) -D -o root -g root -m 0755 cluster_master $(bindir)/cluster_master $(INSTALL) -D -o root -g root -m 0755 nsctl $(bindir)/nsctl $(INSTALL) -D -o root -g root -m 0600 etc/l2tpns.cfg.default $(etcdir)/l2tpns.cfg $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool @@ -57,8 +61,32 @@ install: all mknod /dev/net/tun c 10 200; \ fi -%.so: %.c - $(CC) -fPIC -shared -o $@ $^ $(LDFLAGS) $(LIBS) $(LIBPATH) +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< -%.o: %.c l2tpns.h - $(CC) -c -o $@ $< $(CFLAGS) +%.so: %.c + $(CC) -fPIC -shared $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) $(LIBPATH) + +.PHONY: all clean depend + +## Dependencies: (autogenerated) ## +md5.o: md5.c md5.h +icmp.o: icmp.c l2tpns.h config.h +cli.o: cli.c l2tpns.h config.h util.h cluster.h tbf.h bgp.h +l2tpns.o: l2tpns.c md5.h l2tpns.h config.h cluster.h plugin.h ll.h \ + constants.h control.h util.h tbf.h bgp.h +ppp.o: ppp.c l2tpns.h config.h constants.h plugin.h util.h tbf.h \ + cluster.h +radius.o: radius.c md5.h constants.h l2tpns.h config.h plugin.h util.h +ll.o: ll.c ll.h +cluster.o: cluster.c l2tpns.h config.h cluster.h util.h tbf.h bgp.h +arp.o: arp.c l2tpns.h config.h +constants.o: constants.c constants.h +ll.o: ll.c ll.h +control.o: control.c control.h +util.o: util.c l2tpns.h config.h +tbf.o: tbf.c l2tpns.h config.h tbf.h +bgp.o: bgp.c l2tpns.h config.h bgp.h util.h +garden.so: garden.c l2tpns.h config.h plugin.h control.h +autothrottle.so: autothrottle.c l2tpns.h config.h plugin.h control.h +autosnoop.so: autosnoop.c l2tpns.h config.h plugin.h control.h diff --git a/autosnoop.c b/autosnoop.c index 1c27190..7ab4321 100644 --- a/autosnoop.c +++ b/autosnoop.c @@ -8,21 +8,27 @@ #include "control.h" int __plugin_api_version = 1; -struct pluginfuncs p; +struct pluginfuncs *p; int plugin_radius_response(struct param_radius_response *data) { if (strcmp(data->key, "intercept") == 0) { - if (strcmp(data->value, "yes") == 0) + char *x; + data->s->snoop_ip = 0; + data->s->snoop_port = 0; + if ((x = strchr(data->value, ':'))) { - p.log(3, 0, 0, 0, " Intercepting user\n"); - data->s->snoop = 1; + *x++ = 0; + if (*data->value) data->s->snoop_ip = inet_addr(data->value); + if (data->s->snoop_ip == INADDR_NONE) data->s->snoop_ip = 0; + if (*x) data->s->snoop_port = atoi(x); + p->log(3, 0, 0, 0, " Intercepting user to %s:%d\n", + p->inet_toa(data->s->snoop_ip), data->s->snoop_port); } - else if (strcmp(data->value, "no") == 0) + else { - p.log(3, 0, 0, 0, " Not intercepting user\n"); - data->s->snoop = 0; + p->log(3, 0, 0, 0, " Not Intercepting user (reply string should be snoop=ip:port)\n"); } } return PLUGIN_RET_OK; @@ -30,10 +36,7 @@ int plugin_radius_response(struct param_radius_response *data) int plugin_init(struct pluginfuncs *funcs) { - if (!funcs) return 0; - memcpy(&p, funcs, sizeof(p)); - - return 1; + return ((p = funcs)) ? 1 : 0; } void plugin_done() diff --git a/autothrottle.c b/autothrottle.c index 4d985a2..4def160 100644 --- a/autothrottle.c +++ b/autothrottle.c @@ -8,7 +8,7 @@ #include "control.h" int __plugin_api_version = 1; -struct pluginfuncs p; +struct pluginfuncs *p; int plugin_radius_response(struct param_radius_response *data) { @@ -16,12 +16,12 @@ int plugin_radius_response(struct param_radius_response *data) { if (strcmp(data->value, "yes") == 0) { - p.log(3, 0, 0, 0, " Throttling user\n"); + p->log(3, 0, 0, 0, " Throttling user\n"); data->s->throttle = 1; } else if (strcmp(data->value, "no") == 0) { - p.log(3, 0, 0, 0, " Not throttling user\n"); + p->log(3, 0, 0, 0, " Not throttling user\n"); data->s->throttle = 0; } } @@ -30,10 +30,7 @@ int plugin_radius_response(struct param_radius_response *data) int plugin_init(struct pluginfuncs *funcs) { - if (!funcs) return 0; - memcpy(&p, funcs, sizeof(p)); - - return 1; + return ((p = funcs)) ? 1 : 0; } void plugin_done() diff --git a/bgp.c b/bgp.c new file mode 100644 index 0000000..ec6d9c2 --- /dev/null +++ b/bgp.c @@ -0,0 +1,1308 @@ +/* + * BGPv4 + * Used to advertise routes for upstream (l2tp port, rather than gratiutious + * arp) and downstream--allowing routers to load-balance both. + * + * Implementation limitations: + * - We never listen for incoming connections (session always initiated by us). + * - Any routes advertised by the peer are accepted, but ignored. + * - No password support; neither RFC1771 (which no-one seems to do anyway) + * nor RFC2385 (which requires a kernel patch on 2.4 kernels). + */ + +/* $Id: bgp.c,v 1.1 2004-06-23 03:52:24 fred_nerk Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "l2tpns.h" +#include "bgp.h" +#include "util.h" + +static void bgp_clear(struct bgp_peer *peer); +static void bgp_set_retry(struct bgp_peer *peer); +static void bgp_cidr(in_addr_t ip, in_addr_t mask, struct bgp_ip_prefix *pfx); +static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, + struct bgp_route_list *new); + +static void bgp_free_routes(struct bgp_route_list *routes); +static char const *bgp_state_str(enum bgp_state state); +static char const *bgp_msg_type_str(u8 type); +static int bgp_connect(struct bgp_peer *peer); +static int bgp_handle_connect(struct bgp_peer *peer); +static int bgp_write(struct bgp_peer *peer); +static int bgp_read(struct bgp_peer *peer); +static int bgp_handle_input(struct bgp_peer *peer); +static int bgp_send_open(struct bgp_peer *peer); +static int bgp_send_keepalive(struct bgp_peer *peer); +static int bgp_send_update(struct bgp_peer *peer); +static int bgp_send_notification(struct bgp_peer *peer, u8 code, u8 subcode); + +static u16 our_as; + +/* prepare peer structure, globals */ +int bgp_setup(int as) +{ + int i; + struct bgp_peer *peer; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + peer = &bgp_peers[i]; + memset(peer, 0, sizeof(*peer)); + + peer->addr = INADDR_NONE; + peer->sock = -1; + peer->state = peer->next_state = Disabled; + + if (!((peer->outbuf = malloc(sizeof(*peer->outbuf))) + && (peer->inbuf = malloc(sizeof(*peer->inbuf))))) + { + log(0, 0, 0, 0, "Can't allocate buffers for bgp peer (%s)\n", + strerror(errno)); + + return 0; + } + } + + if (as < 1) + as = 0; + + if ((our_as = as)) + return 0; + + bgp_routes = 0; + bgp_configured = 0; /* set by bgp_start */ + + return 1; +} + +/* start connection with a peer */ +int bgp_start(struct bgp_peer *peer, char *name, int as, int enable) +{ + struct hostent *h; + int ibgp; + int i; + struct bgp_path_attr a; + char path_attrs[64]; + char *p = path_attrs; + in_addr_t ip; + u32 metric = htonl(BGP_METRIC); + u32 no_export = htonl(BGP_COMMUNITY_NO_EXPORT); + + if (!our_as) + return 0; + + if (peer->state != Disabled) + bgp_halt(peer); + + snprintf(peer->name, sizeof(peer->name), "%s", name); + + if (!(h = gethostbyname(name)) || h->h_addrtype != AF_INET) + { + log(0, 0, 0, 0, "Can't get address for BGP peer %s (%s)\n", + name, h ? "no address" : hstrerror(h_errno)); + + return 0; + } + + memcpy(&peer->addr, h->h_addr, sizeof(peer->addr)); + peer->as = as > 0 ? as : our_as; + ibgp = peer->as == our_as; + + /* clear buffers, go to Idle state */ + peer->next_state = Idle; + bgp_clear(peer); + + /* set initial routing state */ + peer->routing = enable; + + /* all our routes use the same attributes, so prepare it in advance */ + if (peer->path_attrs) + free(peer->path_attrs); + + peer->path_attr_len = 0; + + /* ORIGIN */ + a.flags = BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_ORIGIN; + a.data.s.len = 1; + a.data.s.value[0] = BGP_PATH_ATTR_CODE_ORIGIN_IGP; + +#define ADD_ATTRIBUTE() do { \ + i = BGP_PATH_ATTR_SIZE(a); \ + memcpy(p, &a, i); \ + p += i; \ + peer->path_attr_len += i; } while (0) + + ADD_ATTRIBUTE(); + + /* AS_PATH */ + a.flags = BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_AS_PATH; + if (ibgp) + { + /* empty path */ + a.data.s.len = 0; + } + else + { + /* just our AS */ + struct { + u8 type; + u8 len; + u16 value; + } as_path = { + BGP_PATH_ATTR_CODE_AS_PATH_AS_SEQUENCE, + 1, + htons(our_as), + }; + + a.data.s.len = sizeof(as_path); + memcpy(&a.data.s.value, &as_path, sizeof(as_path)); + } + + ADD_ATTRIBUTE(); + + /* NEXT_HOP */ + a.flags = BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_NEXT_HOP; + ip = my_address; /* we're it */ + a.data.s.len = sizeof(ip); + memcpy(a.data.s.value, &ip, sizeof(ip)); + + ADD_ATTRIBUTE(); + + /* MULTI_EXIT_DISC */ + a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL; + a.code = BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC; + a.data.s.len = sizeof(metric); + memcpy(a.data.s.value, &metric, sizeof(metric)); + + ADD_ATTRIBUTE(); + + if (ibgp) + { + u32 local_pref = htonl(BGP_LOCAL_PREF); + + /* LOCAL_PREF */ + a.flags = BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_LOCAL_PREF; + a.data.s.len = sizeof(local_pref); + memcpy(a.data.s.value, &local_pref, sizeof(local_pref)); + + ADD_ATTRIBUTE(); + } + + /* COMMUNITIES */ + a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_TRANS; + a.code = BGP_PATH_ATTR_CODE_COMMUNITIES; + a.data.s.len = sizeof(no_export); + memcpy(a.data.s.value, &no_export, sizeof(no_export)); + + ADD_ATTRIBUTE(); + + if (!(peer->path_attrs = malloc(peer->path_attr_len))) + { + log(0, 0, 0, 0, "Can't allocate path_attrs for %s (%s)\n", + name, strerror(errno)); + + return 0; + } + + memcpy(peer->path_attrs, path_attrs, peer->path_attr_len); + + log(4, 0, 0, 0, "Initiating BGP connection to %s (routing %s)\n", + name, enable ? "enabled" : "suspended"); + + /* we have at least one peer configured */ + bgp_configured = 1; + + /* connect */ + return bgp_connect(peer); +} + +/* clear counters, timers, routes and buffers; close socket; move to + next_state, which may be Disabled or Idle */ +static void bgp_clear(struct bgp_peer *peer) +{ + if (peer->sock != -1) + { + close(peer->sock); + peer->sock = -1; + } + + peer->keepalive_time = 0; + peer->hold = 0; + peer->expire_time = 0; + + bgp_free_routes(peer->routes); + peer->routes = 0; + + peer->outbuf->packet.header.len = 0; + peer->outbuf->done = 0; + peer->inbuf->packet.header.len = 0; + peer->inbuf->done = 0; + + peer->cli_flag = 0; + + if (peer->state != peer->next_state) + { + peer->state = peer->next_state; + peer->state_time = time_now; + + log(4, 0, 0, 0, "BGP peer %s: state %s\n", peer->name, + bgp_state_str(peer->next_state)); + } +} + +/* initiate a clean shutdown */ +void bgp_stop(struct bgp_peer *peer) +{ + log(4, 0, 0, 0, "Terminating BGP connection to %s\n", peer->name); + bgp_send_notification(peer, BGP_ERR_CEASE, 0); +} + +/* drop connection (if any) and set state to Disabled */ +void bgp_halt(struct bgp_peer *peer) +{ + log(4, 0, 0, 0, "Aborting BGP connection to %s\n", peer->name); + peer->next_state = Disabled; + bgp_clear(peer); +} + +/* drop connection (if any) and set to Idle for connection retry */ +int bgp_restart(struct bgp_peer *peer) +{ + peer->next_state = Idle; + bgp_clear(peer); + + /* restart now */ + peer->retry_time = time_now; + peer->retry_count = 0; + + /* connect */ + return bgp_connect(peer); +} + +static void bgp_set_retry(struct bgp_peer *peer) +{ + if (peer->retry_count++ < BGP_MAX_RETRY) + { + peer->retry_time = time_now + (BGP_RETRY_BACKOFF * peer->retry_count); + peer->next_state = Idle; + bgp_clear(peer); + } + else + bgp_halt(peer); /* give up */ +} + +/* convert ip/mask to CIDR notation */ +static void bgp_cidr(in_addr_t ip, in_addr_t mask, struct bgp_ip_prefix *pfx) +{ + int i; + u32 b; + + /* convert to prefix notation */ + pfx->len = 32; + pfx->prefix = ip; + + if (!mask) /* bogus */ + mask = 0xffffffff; + + for (i = 0; i < 32 && ((b = ntohl(1 << i)), !(mask & b)); i++) + { + pfx->len--; + pfx->prefix &= ~b; + } +} + +/* insert route into list; sorted */ +static struct bgp_route_list *bgp_insert_route(struct bgp_route_list *head, + struct bgp_route_list *new) +{ + struct bgp_route_list *p = head; + struct bgp_route_list *e = 0; + + while (p && memcmp(&p->dest, &new->dest, sizeof(p->dest)) < 0) + { + e = p; + p = p->next; + } + + if (e) + { + new->next = e->next; + e->next = new; + } + else + { + new->next = head; + head = new; + } + + return head; +} + +/* add route to list for peers */ +/* + * Note: this doesn't do route aggregation, nor drop routes if a less + * specific match already exists (partly because I'm lazy, but also so + * that if that route is later deleted we don't have to be concerned + * about adding back the more specific one). + */ +int bgp_add_route(in_addr_t ip, in_addr_t mask) +{ + struct bgp_route_list *r = bgp_routes; + struct bgp_route_list add; + int i; + + bgp_cidr(ip, mask, &add.dest); + add.next = 0; + + /* check for duplicate */ + while (r) + { + i = memcmp(&r->dest, &add.dest, sizeof(r->dest)); + if (!i) + return 1; /* already covered */ + + if (i > 0) + break; + + r = r->next; + } + + /* insert into route list; sorted */ + if (!(r = malloc(sizeof(*r)))) + { + log(0, 0, 0, 0, "Can't allocate route for %s/%d (%s)\n", + inet_toa(add.dest.prefix), add.dest.len, strerror(errno)); + + return 0; + } + + memcpy(r, &add, sizeof(*r)); + bgp_routes = bgp_insert_route(bgp_routes, r); + + /* flag established peers for update */ + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established) + bgp_peers[i].update_routes = 1; + + log(4, 0, 0, 0, "Registered BGP route %s/%d\n", inet_toa(add.dest.prefix), + add.dest.len); + + return 1; +} + +/* remove route from list for peers */ +int bgp_del_route(in_addr_t ip, in_addr_t mask) +{ + struct bgp_route_list *r = bgp_routes; + struct bgp_route_list *e = 0; + struct bgp_route_list del; + int i; + + bgp_cidr(ip, mask, &del.dest); + del.next = 0; + + /* find entry in routes list and remove */ + while (r) + { + i = memcmp(&r->dest, &del.dest, sizeof(r->dest)); + if (!i) + { + if (e) + e->next = r->next; + else + bgp_routes = r->next; + + free(r); + break; + } + + e = r; + + if (i > 0) + r = 0; /* stop */ + else + r = r->next; + } + + /* not found */ + if (!r) + return 1; + + /* flag established peers for update */ + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established) + bgp_peers[i].update_routes = 1; + + log(4, 0, 0, 0, "Removed BGP route %s/%d\n", inet_toa(del.dest.prefix), + del.dest.len); + + return 1; +} + +/* enable or disable routing */ +void bgp_enable_routing(int enable) +{ + int i; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + bgp_peers[i].routing = enable; + + /* flag established peers for update */ + if (bgp_peers[i].state == Established) + bgp_peers[i].update_routes = 1; + } + + log(4, 0, 0, 0, "%s BGP routing\n", enable ? "Enabled" : "Suspended"); +} + +/* return a bitmask indicating if the socket should be added to the + read set (1) and or write set (2) for select */ +int bgp_select_state(struct bgp_peer *peer) +{ + int flags = 0; + + if (!bgp_configured) + return 0; + + if (peer->state == Disabled || peer->state == Idle) + return 0; + + if (peer->inbuf->done < BGP_MAX_PACKET_SIZE) + flags |= 1; + + if (peer->state == Connect || /* connection in progress */ + peer->update_routes || /* routing updates */ + peer->outbuf->packet.header.len) /* pending output */ + flags |= 2; + + return flags; +} + +/* process bgp peer */ +int bgp_process(struct bgp_peer *peer, int readable, int writable) +{ + if (!bgp_configured) + return 0; + + if (*peer->name && peer->cli_flag == BGP_CLI_RESTART) + return bgp_restart(peer); + + if (peer->state == Disabled) + return 1; + + if (peer->cli_flag) + { + switch (peer->cli_flag) + { + case BGP_CLI_SUSPEND: + if (peer->routing) + { + peer->routing = 0; + if (peer->state == Established) + peer->update_routes = 1; + } + + break; + + case BGP_CLI_ENABLE: + if (!peer->routing) + { + peer->routing = 1; + if (peer->state == Established) + peer->update_routes = 1; + } + + break; + } + + peer->cli_flag = 0; + } + + /* handle empty/fill of buffers */ + if (writable) + { + int r = 1; + if (peer->state == Connect) + r = bgp_handle_connect(peer); + else if (peer->outbuf->packet.header.len) + r = bgp_write(peer); + + if (!r) + return 0; + } + + if (readable) + { + if (!bgp_read(peer)) + return 0; + } + + /* process input buffer contents */ + while (peer->inbuf->done >= sizeof(peer->inbuf->packet.header) + && !peer->outbuf->packet.header.len) /* may need to queue a response */ + { + if (bgp_handle_input(peer) < 0) + return 0; + } + + /* process pending updates */ + if (peer->update_routes + && !peer->outbuf->packet.header.len) /* ditto */ + { + if (!bgp_send_update(peer)) + return 0; + } + + /* process timers */ + if (peer->state == Established) + { + if (time_now > peer->expire_time) + { + log(1, 0, 0, 0, "No message from BGP peer %s in %ds\n", + peer->name, peer->hold); + + bgp_send_notification(peer, BGP_ERR_HOLD_TIMER_EXP, 0); + return 0; + } + + if (time_now > peer->keepalive_time && !peer->outbuf->packet.header.len) + bgp_send_keepalive(peer); + } + else if (peer->state == Idle) + { + if (time_now > peer->retry_time) + return bgp_connect(peer); + } + else if (time_now > peer->state_time + BGP_KEEPALIVE_TIME) + { + log(1, 0, 0, 0, "%s timer expired for BGP peer %s\n", + bgp_state_str(peer->state), peer->name); + + return bgp_restart(peer); + } + + return 1; +} + +static void bgp_free_routes(struct bgp_route_list *routes) +{ + struct bgp_route_list *tmp; + + while ((tmp = routes)) + { + routes = tmp->next; + free(tmp); + } +} + +static char const *bgp_state_str(enum bgp_state state) +{ + switch (state) + { + case Disabled: return "Disabled"; + case Idle: return "Idle"; + case Connect: return "Connect"; + case Active: return "Active"; + case OpenSent: return "OpenSent"; + case OpenConfirm: return "OpenConfirm"; + case Established: return "Established"; + } + + return "?"; +} + +static char const *bgp_msg_type_str(u8 type) +{ + switch (type) + { + case BGP_MSG_OPEN: return "OPEN"; + case BGP_MSG_UPDATE: return "UPDATE"; + case BGP_MSG_NOTIFICATION: return "NOTIFICATION"; + case BGP_MSG_KEEPALIVE: return "KEEPALIVE"; + } + + return "?"; +} + +/* attempt to connect to peer */ +static int bgp_connect(struct bgp_peer *peer) +{ + static int bgp_port = 0; + struct sockaddr_in addr; + + if (!bgp_port) + { + struct servent *serv; + if (!(serv = getservbyname("bgp", "tcp"))) + { + log(0, 0, 0, 0, "Can't get bgp service (%s)\n", strerror(errno)); + return 0; + } + + bgp_port = serv->s_port; + } + + if ((peer->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + log(0, 0, 0, 0, "Can't create a socket for BGP peer %s (%s)\n", + peer->name, strerror(errno)); + + peer->state = peer->next_state = Disabled; + return 0; + } + + /* set to non-blocking */ + fcntl(peer->sock, F_SETFL, fcntl(peer->sock, F_GETFL, 0) | O_NONBLOCK); + + /* try connect */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = bgp_port; + addr.sin_addr.s_addr = peer->addr; + + while (connect(peer->sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) + { + if (errno == EINTR) /* SIGALARM handler */ + continue; + + if (errno != EINPROGRESS) + { + log(1, 0, 0, 0, "Can't connect to BGP peer %s (%s)\n", + inet_ntoa(addr.sin_addr), strerror(errno)); + + bgp_set_retry(peer); + return 0; + } + + peer->state = Connect; + peer->state_time = time_now; + + log(4, 0, 0, 0, "BGP peer %s: state Connect\n", peer->name); + return 1; + } + + peer->state = Active; + peer->state_time = time_now; + peer->retry_time = peer->retry_count = 0; + + log(4, 0, 0, 0, "BGP peer %s: state Active\n", inet_ntoa(addr.sin_addr)); + + return bgp_send_open(peer); +} + +/* complete partial connection (state = Connect) */ +static int bgp_handle_connect(struct bgp_peer *peer) +{ + int err = 0; + int len = sizeof(int); + getsockopt(peer->sock, SOL_SOCKET, SO_ERROR, &err, &len); + if (err) + { + log(1, 0, 0, 0, "Can't connect to BGP peer %s (%s)\n", peer->name, + strerror(err)); + + bgp_set_retry(peer); + return 0; + } + + peer->state = Active; + peer->state_time = time_now; + + log(4, 0, 0, 0, "BGP peer %s: state Active\n", peer->name); + + return bgp_send_open(peer); +} + +/* initiate a write */ +static int bgp_write(struct bgp_peer *peer) +{ + int len = htons(peer->outbuf->packet.header.len); + int r; + + while ((r = write(peer->sock, &peer->outbuf->packet + peer->outbuf->done, + len - peer->outbuf->done)) == -1) + { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 1; + + if (errno == EPIPE) + log(1, 0, 0, 0, "Connection to BGP peer %s closed\n", peer->name); + else + log(1, 0, 0, 0, "Can't write to BGP peer %s (%s)\n", peer->name, + strerror(errno)); + + bgp_set_retry(peer); + return 0; + } + + if (r < len) + { + peer->outbuf->done += r; + return 1; + } + + log(4, 0, 0, 0, "Sent %s to BGP peer %s\n", + bgp_msg_type_str(peer->outbuf->packet.header.type), peer->name); + + peer->outbuf->packet.header.len = 0; + peer->outbuf->done = 0; + + if (peer->state == Established) + peer->keepalive_time = time_now + BGP_KEEPALIVE_TIME; + + if (peer->state != peer->next_state) + { + if (peer->next_state == Disabled || peer->next_state == Idle) + { + bgp_clear(peer); + return 0; + } + + peer->state = peer->next_state; + peer->state_time = time_now; + + log(4, 0, 0, 0, "BGP peer %s: state %s\n", peer->name, + bgp_state_str(peer->state)); + } + + return 1; +} + +/* initiate a read */ +static int bgp_read(struct bgp_peer *peer) +{ + int r; + + while ((r = read(peer->sock, &peer->inbuf->packet + peer->inbuf->done, + BGP_MAX_PACKET_SIZE - peer->inbuf->done)) < 1) + { + if (!r) + { + log(1, 0, 0, 0, "Connection to BGP peer %s closed\n", peer->name); + } + else + { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 1; + + log(1, 0, 0, 0, "Can't read from BGP peer %s (%s)\n", peer->name, + strerror(errno)); + } + + bgp_set_retry(peer); + return 0; + } + + peer->inbuf->done += r; + return 1; +} + +/* process buffered packets */ +static int bgp_handle_input(struct bgp_peer *peer) +{ + struct bgp_packet *p = &peer->inbuf->packet; + int len = ntohs(p->header.len); + + if (len > BGP_MAX_PACKET_SIZE) + { + log(1, 0, 0, 0, "Bad header length from BGP %s\n", peer->name); + bgp_send_notification(peer, BGP_ERR_HEADER, BGP_ERR_HDR_BAD_LEN); + return 0; + } + + if (peer->inbuf->done < len) + return 0; + + log(4, 0, 0, 0, "Received %s from BGP peer %s\n", + bgp_msg_type_str(p->header.type), peer->name); + + switch (p->header.type) + { + case BGP_MSG_OPEN: + { + struct bgp_data_open data; + int i; + + for (i = 0; i < sizeof(p->header.marker); i++) + { + if ((unsigned char) p->header.marker[i] != 0xff) + { + log(1, 0, 0, 0, "Invalid marker from BGP peer %s\n", + peer->name); + + bgp_send_notification(peer, BGP_ERR_HEADER, + BGP_ERR_HDR_NOT_SYNC); + + return 0; + } + } + + if (peer->state != OpenSent) + { + log(1, 0, 0, 0, "OPEN from BGP peer %s in %s state\n", + peer->name, bgp_state_str(peer->state)); + + bgp_send_notification(peer, BGP_ERR_FSM, 0); + return 0; + } + + memcpy(&data, p->data, len - sizeof(p->header)); + + if (data.version != BGP_VERSION) + { + log(1, 0, 0, 0, "Bad version (%d) sent by BGP peer %s\n", + (int) data.version, peer->name); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_VERSION); + return 0; + } + + if (ntohs(data.as) != peer->as) + { + log(1, 0, 0, 0, "Bad AS sent by BGP peer %s (got %d, " + "expected %d)\n", peer->name, (int) htons(data.as), + (int) peer->as); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_BAD_AS); + return 0; + } + + if ((peer->hold = ntohs(data.hold_time)) < 10) + { + log(1, 0, 0, 0, "Bad hold time (%d) from BGP peer %s\n", + peer->hold, peer->name); + + bgp_send_notification(peer, BGP_ERR_OPEN, BGP_ERR_OPN_HOLD_TIME); + return 0; + } + + /* next transition requires an exchange of keepalives */ + bgp_send_keepalive(peer); + + /* FIXME: may need to check for optional params */ + } + + break; + + case BGP_MSG_KEEPALIVE: + if (peer->state == OpenConfirm) + { + peer->state = peer->next_state = Established; + peer->state_time = time_now; + peer->keepalive_time = time_now + BGP_KEEPALIVE_TIME; + peer->update_routes = 1; + peer->retry_count = 0; + peer->retry_time = 0; + + log(4, 0, 0, 0, "BGP peer %s: state Established\n", peer->name); + } + + break; + + case BGP_MSG_NOTIFICATION: + if (len > sizeof(p->header)) + { + struct bgp_data_notification *notification = + (struct bgp_data_notification *) p->data; + + if (notification->error_code == BGP_ERR_CEASE) + { + log(4, 0, 0, 0, "BGP peer %s sent CEASE\n", peer->name); + bgp_halt(peer); + return 0; + } + + /* FIXME: should handle more notifications */ + log(4, 0, 0, 0, "BGP peer %s sent unhandled NOTIFICATION %d\n", + peer->name, (int) notification->error_code); + } + + break; + } + + /* reset timer */ + peer->expire_time = time_now + peer->hold; + + /* see if there's another message in the same packet/buffer */ + if (peer->inbuf->done > len) + { + peer->inbuf->done -= len; + memmove(p, (char *) p + len, peer->inbuf->done); + } + else + { + peer->inbuf->packet.header.len = 0; + peer->inbuf->done = 0; + } + + return peer->inbuf->done; +} + +/* send/buffer OPEN message */ +static int bgp_send_open(struct bgp_peer *peer) +{ + struct bgp_data_open data; + u16 len = sizeof(peer->outbuf->packet.header); + + memset(peer->outbuf->packet.header.marker, 0xff, + sizeof(peer->outbuf->packet.header.marker)); + + peer->outbuf->packet.header.type = BGP_MSG_OPEN; + + data.version = BGP_VERSION; + data.as = htons(our_as); + data.hold_time = htons(BGP_HOLD_TIME); + data.identifier = my_address; + data.opt_len = 0; + + memcpy(peer->outbuf->packet.data, &data, BGP_DATA_OPEN_SIZE); + len += BGP_DATA_OPEN_SIZE; + + peer->outbuf->packet.header.len = htons(len); + peer->outbuf->done = 0; + peer->next_state = OpenSent; + + return bgp_write(peer); +} + +/* send/buffer KEEPALIVE message */ +static int bgp_send_keepalive(struct bgp_peer *peer) +{ + memset(peer->outbuf->packet.header.marker, 0xff, + sizeof(peer->outbuf->packet.header.marker)); + + peer->outbuf->packet.header.type = BGP_MSG_KEEPALIVE; + peer->outbuf->packet.header.len = + htons(sizeof(peer->outbuf->packet.header)); + + peer->outbuf->done = 0; + peer->next_state = (peer->state == OpenSent) ? OpenConfirm : peer->state; + + return bgp_write(peer); +} + +/* send/buffer UPDATE message */ +static int bgp_send_update(struct bgp_peer *peer) +{ + u16 unf_len = 0; + u16 attr_len; + u16 len = sizeof(peer->outbuf->packet.header); + struct bgp_route_list *have = peer->routes; + struct bgp_route_list *want = peer->routing ? bgp_routes : 0; + struct bgp_route_list *e = 0; + struct bgp_route_list *add = 0; + int s; + + char *data = (char *) &peer->outbuf->packet.data; + + /* need leave room for attr_len, bgp_path_attrs and one prefix */ + char *max = (char *) &peer->outbuf->packet.data + + sizeof(peer->outbuf->packet.data) + - sizeof(attr_len) - peer->path_attr_len - sizeof(struct bgp_ip_prefix); + + /* skip over unf_len */ + data += sizeof(unf_len); + len += sizeof(unf_len); + + memset(peer->outbuf->packet.header.marker, 0xff, + sizeof(peer->outbuf->packet.header.marker)); + + peer->outbuf->packet.header.type = BGP_MSG_UPDATE; + + peer->update_routes = 0; /* tentatively clear */ + + /* find differences */ + while ((have || want) && data < (max - sizeof(struct bgp_ip_prefix))) + { + if (have) + s = want + ? memcmp(&have->dest, &want->dest, sizeof(have->dest)) + : -1; + else + s = 1; + + if (s < 0) /* found one to delete */ + { + struct bgp_route_list *tmp = have; + have = have->next; + + s = BGP_IP_PREFIX_SIZE(tmp->dest); + memcpy(data, &tmp->dest, s); + data += s; + unf_len += s; + len += s; + + log(5, 0, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n", + inet_toa(tmp->dest.prefix), tmp->dest.len, peer->name); + + free(tmp); + + if (e) + e->next = have; + else + peer->routes = have; + } + else + { + if (!s) /* same */ + { + e = have; /* stash the last found to relink above */ + have = have->next; + want = want->next; + } + else if (s > 0) /* addition reqd. */ + { + if (add) + { + peer->update_routes = 1; /* only one add per packet */ + if (!have) + break; + } + else + add = want; + + if (want) + want = want->next; + } + } + } + + if (have || want) + peer->update_routes = 1; /* more to do */ + + /* anything changed? */ + if (!(unf_len || add)) + return 1; + + /* go back and insert unf_len */ + unf_len = htons(unf_len); + memcpy(&peer->outbuf->packet.data, &unf_len, sizeof(unf_len)); + + if (add) + { + if (!(e = malloc(sizeof(*e)))) + { + log(0, 0, 0, 0, "Can't allocate route for %s/%d (%s)\n", + inet_toa(add->dest.prefix), add->dest.len, strerror(errno)); + + return 0; + } + + memcpy(e, add, sizeof(*e)); + e->next = 0; + peer->routes = bgp_insert_route(peer->routes, e); + + attr_len = htons(peer->path_attr_len); + memcpy(data, &attr_len, sizeof(attr_len)); + data += sizeof(attr_len); + len += sizeof(attr_len); + + memcpy(data, peer->path_attrs, peer->path_attr_len); + data += peer->path_attr_len; + len += peer->path_attr_len; + + s = BGP_IP_PREFIX_SIZE(add->dest); + memcpy(data, &add->dest, s); + data += s; + len += s; + + log(5, 0, 0, 0, "Advertising route %s/%d to BGP peer %s\n", + inet_toa(add->dest.prefix), add->dest.len, peer->name); + } + else + { + attr_len = 0; + memcpy(data, &attr_len, sizeof(attr_len)); + data += sizeof(attr_len); + len += sizeof(attr_len); + } + + peer->outbuf->packet.header.len = htons(len); + peer->outbuf->done = 0; + + return bgp_write(peer); +} + +/* send/buffer NOTIFICATION message */ +static int bgp_send_notification(struct bgp_peer *peer, u8 code, u8 subcode) +{ + struct bgp_data_notification data; + u16 len = 0; + + data.error_code = code; + len += sizeof(data.error_code); + + data.error_subcode = subcode; + len += sizeof(data.error_code); + + memset(peer->outbuf->packet.header.marker, 0xff, + sizeof(peer->outbuf->packet.header.marker)); + + peer->outbuf->packet.header.type = BGP_MSG_NOTIFICATION; + peer->outbuf->packet.header.len = + htons(sizeof(peer->outbuf->packet.header) + len); + + memcpy(peer->outbuf->packet.data, &data, len); + + peer->outbuf->done = 0; + peer->next_state = code == BGP_ERR_CEASE ? Disabled : Idle; + + /* we're dying; ignore any pending input */ + peer->inbuf->packet.header.len = 0; + peer->inbuf->done = 0; + + return bgp_write(peer); +} + +/* CLI stuff */ + +#include + +int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + int hdr = 0; + char *addr; + + if (!bgp_configured) + return CLI_OK; + + cli_print(cli, "BGPv%d router identifier %s, local AS number %d, " + "hold time %ds", BGP_VERSION, inet_toa(my_address), (int) our_as, + BGP_HOLD_TIME); + + time(&time_now); + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + if (!*bgp_peers[i].name) + continue; + + addr = inet_toa(bgp_peers[i].addr); + if (argc && strcmp(addr, argv[0]) && + strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) + continue; + + if (!hdr++) + { + cli_print(cli, ""); + cli_print(cli, "Peer AS Address " + "State Retries Retry in Route Pend"); + cli_print(cli, "------------------ ----- --------------- " + "----------- ------- -------- ----- ----"); + } + + cli_print(cli, "%-18.18s %5d %15s %-11s %7d %7ds %5s %4s", + bgp_peers[i].name, + bgp_peers[i].as, + addr, + bgp_state_str(bgp_peers[i].state), + bgp_peers[i].retry_count, + bgp_peers[i].retry_time ? bgp_peers[i].retry_time - time_now : 0, + bgp_peers[i].routing ? "yes" : "no", + bgp_peers[i].update_routes ? "yes" : "no"); + } + + return CLI_OK; +} + +int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + char *addr; + + if (!bgp_configured) + return CLI_OK; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + if (bgp_peers[i].state != Established) + continue; + + if (!bgp_peers[i].routing) + continue; + + addr = inet_toa(bgp_peers[i].addr); + if (argc && strcmp(addr, argv[0]) && strcmp(bgp_peers[i].name, argv[0])) + continue; + + bgp_peers[i].cli_flag = BGP_CLI_SUSPEND; + cli_print(cli, "Suspending peer %s", bgp_peers[i].name); + } + + return CLI_OK; +} + +int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + char *addr; + + if (!bgp_configured) + return CLI_OK; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + if (bgp_peers[i].state != Established) + continue; + + if (bgp_peers[i].routing) + continue; + + addr = inet_toa(bgp_peers[i].addr); + if (argc && strcmp(addr, argv[0]) && + strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) + continue; + + bgp_peers[i].cli_flag = BGP_CLI_ENABLE; + cli_print(cli, "Un-suspending peer %s", bgp_peers[i].name); + } + + return CLI_OK; +} + +int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + char *addr; + + if (!bgp_configured) + return CLI_OK; + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + if (!*bgp_peers[i].name) + continue; + + addr = inet_toa(bgp_peers[i].addr); + if (argc && strcmp(addr, argv[0]) && + strncmp(bgp_peers[i].name, argv[0], strlen(argv[0]))) + continue; + + bgp_peers[i].cli_flag = BGP_CLI_RESTART; + cli_print(cli, "Restarting peer %s", bgp_peers[i].name); + } + + return CLI_OK; +} diff --git a/bgp.h b/bgp.h new file mode 100644 index 0000000..635a65d --- /dev/null +++ b/bgp.h @@ -0,0 +1,202 @@ +/* BGPv4 (RFC1771) */ +/* $Id: bgp.h,v 1.1 2004-06-23 03:52:24 fred_nerk Exp $ */ + +#ifndef __BGP_H__ +#define __BGP_H__ + +#define BGP_MAX_PACKET_SIZE 4096 +#define BGP_HOLD_TIME 180 /* seconds before peer times us out */ +#define BGP_KEEPALIVE_TIME 60 /* seconds between messages */ +#define BGP_MAX_RETRY 42 /* maximum number of times to retry */ +#define BGP_RETRY_BACKOFF 60 /* number of seconds between retries, + cumulative */ + +#define BGP_METRIC 1 /* multi_exit_disc */ +#define BGP_LOCAL_PREF 100 /* local preference value */ + +struct bgp_header { + char marker[16]; + u16 len; + u8 type; +} __attribute__ ((packed)); + +/* bgp_header.type */ +#define BGP_MSG_OPEN 1 +#define BGP_MSG_UPDATE 2 +#define BGP_MSG_NOTIFICATION 3 +#define BGP_MSG_KEEPALIVE 4 + +struct bgp_packet { + struct bgp_header header; + char data[BGP_MAX_PACKET_SIZE - sizeof(struct bgp_header)]; /* variable */ +} __attribute__ ((packed)); + +struct bgp_data_open { + u8 version; +#define BGP_VERSION 4 + u16 as; + u16 hold_time; + u32 identifier; + u8 opt_len; +#define BGP_DATA_OPEN_SIZE 10 /* size of struct excluding opt_params */ + char opt_params[sizeof(((struct bgp_packet *)0)->data) - BGP_DATA_OPEN_SIZE]; /* variable */ +} __attribute__ ((packed)); + +struct bgp_ip_prefix { + u8 len; + u32 prefix; /* variable */ +} __attribute__ ((packed)); + +#define BGP_IP_PREFIX_SIZE(p) (1 + ((p).len / 8) + ((p).len % 8 != 0)) + +struct bgp_path_attr { + u8 flags; + u8 code; + union { + struct { + u8 len; + char value[29]; /* semi-random size, adequate for l2tpns */ + } __attribute__ ((packed)) s; /* short */ + struct { + u16 len; + char value[28]; + } __attribute__ ((packed)) e; /* extended */ + } data; /* variable */ +} __attribute__ ((packed)); + +/* bgp_path_attr.flags (bitfields) */ +#define BGP_PATH_ATTR_FLAG_OPTIONAL (1 << 7) +#define BGP_PATH_ATTR_FLAG_TRANS (1 << 6) +#define BGP_PATH_ATTR_FLAG_PARTIAL (1 << 5) +#define BGP_PATH_ATTR_FLAG_EXTLEN (1 << 4) + +/* bgp_path_attr.code, ...value */ +#define BGP_PATH_ATTR_CODE_ORIGIN 1 /* well-known, mandatory */ +# define BGP_PATH_ATTR_CODE_ORIGIN_IGP 0 +# define BGP_PATH_ATTR_CODE_ORIGIN_EGP 1 +# define BGP_PATH_ATTR_CODE_ORIGIN_INCOMPLETE 2 +#define BGP_PATH_ATTR_CODE_AS_PATH 2 /* well-known, mandatory */ +# define BGP_PATH_ATTR_CODE_AS_PATH_AS_SET 1 +# define BGP_PATH_ATTR_CODE_AS_PATH_AS_SEQUENCE 2 +#define BGP_PATH_ATTR_CODE_NEXT_HOP 3 /* well-known, mandatory */ +#define BGP_PATH_ATTR_CODE_MULTI_EXIT_DISC 4 /* optional, non-transitive */ +#define BGP_PATH_ATTR_CODE_LOCAL_PREF 5 /* well-known, discretionary */ +#define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE 6 /* well-known, discretionary */ +#define BGP_PATH_ATTR_CODE_AGGREGATOR 7 /* optional, transitive */ +#define BGP_PATH_ATTR_CODE_COMMUNITIES 8 /* optional, transitive (RFC1997) */ + +#define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \ + ? ((p).data.e.len + 1) : (p).data.s.len) + 3) + +/* well known COMMUNITIES */ +#define BGP_COMMUNITY_NO_EXPORT 0xffffff01 /* don't advertise outside confederation */ +#define BGP_COMMUNITY_NO_ADVERTISE 0xffffff02 /* don't advertise to any peer */ +#define BGP_COMMUNITY_NO_EXPORT_SUBCONFED 0xffffff03 /* don't advertise to any other AS */ + +struct bgp_data_notification { + u8 error_code; + u8 error_subcode; + char data[sizeof(((struct bgp_packet *)0)->data) - 2]; /* variable */ +} __attribute__ ((packed)); + +/* bgp_data_notification.error_code, .error_subcode */ +#define BGP_ERR_HEADER 1 +# define BGP_ERR_HDR_NOT_SYNC 1 +# define BGP_ERR_HDR_BAD_LEN 2 +# define BGP_ERR_HDR_BAD_TYPE 3 +#define BGP_ERR_OPEN 2 +# define BGP_ERR_OPN_VERSION 1 +# define BGP_ERR_OPN_BAD_AS 2 +# define BGP_ERR_OPN_BAD_IDENT 3 +# define BGP_ERR_OPN_UNSUP_PARAM 4 +# define BGP_ERR_OPN_AUTH_FAILURE 5 +# define BGP_ERR_OPN_HOLD_TIME 6 +#define BGP_ERR_UPDATE 3 +# define BGP_ERR_UPD_BAD_ATTR_LIST 1 +# define BGP_ERR_UPD_UNKN_WK_ATTR 2 +# define BGP_ERR_UPD_MISS_WK_ATTR 3 +# define BGP_ERR_UPD_BAD_ATTR_FLAG 4 +# define BGP_ERR_UPD_BAD_ATTR_LEN 5 +# define BGP_ERR_UPD_BAD_ORIGIN 6 +# define BGP_ERR_UPD_ROUTING_LOOP 7 +# define BGP_ERR_UPD_BAD_NEXT_HOP 8 +# define BGP_ERR_UPD_BAD_OPT_ATTR 9 +# define BGP_ERR_UPD_BAD_NETWORK 10 +# define BGP_ERR_UPD_BAD_AS_PATH 11 +#define BGP_ERR_HOLD_TIMER_EXP 4 +#define BGP_ERR_FSM 5 +#define BGP_ERR_CEASE 6 + +enum bgp_state { + Disabled, /* initial, or failed */ + Idle, /* trying to connect */ + Connect, /* connect issued */ + Active, /* connected, waiting to send OPEN */ + OpenSent, /* OPEN sent, waiting for peer OPEN */ + OpenConfirm, /* KEEPALIVE sent, waiting for peer KEEPALIVE */ + Established, /* established */ +}; + +struct bgp_route_list { + struct bgp_ip_prefix dest; + struct bgp_route_list *next; +}; + +struct bgp_buf { + struct bgp_packet packet; /* BGP packet */ + size_t done; /* bytes sent/recvd */ +}; + +/* state */ +struct bgp_peer { + char name[32]; /* peer name */ + in_addr_t addr; /* peer address */ + int as; /* AS number */ + int sock; + enum bgp_state state; /* FSM state */ + enum bgp_state next_state; /* next state after outbuf cleared */ + time_t state_time; /* time of last state change */ + time_t keepalive_time; /* time to send next keepalive */ + time_t retry_time; /* time for connection retry */ + int retry_count; /* connection retry count */ + int hold; /* hold time from peer */ + time_t expire_time; /* time next peer packet expected */ + int routing; /* propagate routes */ + int update_routes; /* UPDATE required */ + struct bgp_route_list *routes; /* routes known by this peer */ + struct bgp_buf *outbuf; /* pending output */ + struct bgp_buf *inbuf; /* pending input */ + int cli_flag; /* updates requested from CLI */ + char *path_attrs; /* path attrs to send in UPDATE message */ + int path_attr_len; /* length of path attrs */ +}; + +/* bgp_peer.cli_flag */ +#define BGP_CLI_SUSPEND 1 +#define BGP_CLI_ENABLE 2 +#define BGP_CLI_RESTART 3 + +#define BGP_NUM_PEERS 2 +extern struct bgp_peer *bgp_peers; +extern struct bgp_route_list *bgp_routes; +extern int bgp_configured; + +/* actions */ +int bgp_setup(int as); +int bgp_start(struct bgp_peer *peer, char *name, int as, int enable); +void bgp_stop(struct bgp_peer *peer); +void bgp_halt(struct bgp_peer *peer); +int bgp_restart(struct bgp_peer *peer); +int bgp_add_route(in_addr_t ip, in_addr_t mask); +int bgp_del_route(in_addr_t ip, in_addr_t mask); +void bgp_enable_routing(int enable); +int bgp_select_state(struct bgp_peer *peer); +int bgp_process(struct bgp_peer *peer, int readable, int writable); + +/* CLI */ +int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc); + +#endif /* __BGP_H__ */ diff --git a/cli.c b/cli.c index 9bdcd39..892b8d1 100644 --- a/cli.c +++ b/cli.c @@ -1,5 +1,5 @@ // L2TPNS Command Line Interface -// $Id: cli.c,v 1.4 2004-05-24 04:12:02 fred_nerk Exp $ +// $Id: cli.c,v 1.5 2004-06-23 03:52:24 fred_nerk Exp $ // vim: sw=4 ts=8 #include @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -16,23 +17,26 @@ #include #include #include +#include #include "l2tpns.h" -#include "libcli.h" #include "util.h" +#include "cluster.h" +#include "tbf.h" +#ifdef BGP +#include "bgp.h" +#endif extern tunnelt *tunnel; extern sessiont *session; extern radiust *radius; extern ippoolt *ip_address_pool; extern struct Tstats *_statistics; -extern int cli_pid; struct cli_def *cli = NULL; int cli_quit = 0; extern int clifd, udpfd, tapfd, snoopfd, ifrfd, cluster_sockfd; extern int *radfds; 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[]; @@ -40,7 +44,7 @@ extern char hostname[]; extern struct Tringbuffer *ringbuffer; #endif -char *rcs_id = "$Id: cli.c,v 1.4 2004-05-24 04:12:02 fred_nerk Exp $"; +char *rcs_id = "$Id: cli.c,v 1.5 2004-06-23 03:52:24 fred_nerk Exp $"; char *debug_levels[] = { "CRIT", @@ -77,6 +81,7 @@ 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_show_throttle(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_cluster(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); @@ -99,22 +104,33 @@ void init_cli() FILE *f; char buf[4096]; struct cli_command *c; + struct cli_command *c2; int on = 1; struct sockaddr_in addr; cli = cli_init(); c = cli_register_command(cli, NULL, "show", NULL, NULL); + cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana"); +#ifdef BGP + cli_register_command(cli, c, "bgp", cmd_show_bgp, "Show BGP status"); +#endif /* BGP */ + cli_register_command(cli, c, "cluster", cmd_show_cluster, "Show cluster information"); + cli_register_command(cli, c, "ipcache", cmd_show_ipcache, "Show contents of the IP cache"); + cli_register_command(cli, c, "plugins", cmd_show_plugins, "List all installed plugins"); + cli_register_command(cli, c, "pool", cmd_show_pool, "Show the IP address allocation pool"); + cli_register_command(cli, c, "radius", cmd_show_radius, "Show active radius queries"); + cli_register_command(cli, c, "running-config", cmd_show_run, "Show the currently running configuration"); 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, "tbf", cmd_show_tbf, "List all token bucket filters in use"); + cli_register_command(cli, c, "throttle", cmd_show_throttle, "List all throttled sessions and associated TBFs"); 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 or details of selected user"); 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, "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"); - cli_register_command(cli, c, "throttle", cmd_show_throttle, "List all token bucket filters in use"); + + c2 = cli_register_command(cli, c, "histogram", NULL, NULL); + cli_register_command(cli, c2, "idle", cmd_show_hist_idle, "Show histogram of session idle times"); + cli_register_command(cli, c2, "open", cmd_show_hist_open, "Show histogram of session durations"); #ifdef STATISTICS cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals"); @@ -131,18 +147,25 @@ void init_cli() 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"); + 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, "suspend", NULL, NULL); + cli_register_command(cli, c, "bgp", cmd_suspend_bgp, "Withdraw routes from BGP peer"); c = cli_register_command(cli, NULL, "no", NULL, 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"); + c2 = cli_register_command(cli, c, "suspend", NULL, NULL); + cli_register_command(cli, c2, "bgp", cmd_no_suspend_bgp, "Advertise routes to BGP peer"); c = cli_register_command(cli, NULL, "drop", NULL, 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, "Set the level of logging that is shown on the console"); + c = cli_register_command(cli, NULL, "restart", NULL, NULL); + cli_register_command(cli, c, "bgp", cmd_restart_bgp, "Restart BGP"); c = cli_register_command(cli, NULL, "load", NULL, NULL); cli_register_command(cli, c, "plugin", cmd_load_plugin, "Load a plugin"); @@ -200,6 +223,30 @@ void cli_do(int sockfd) int i; if (fork()) return; + if (config->scheduler_fifo) + { + int ret; + struct sched_param params = {0}; + params.sched_priority = 0; + if ((ret = sched_setscheduler(0, SCHED_OTHER, ¶ms)) == 0) + { + log(3, 0, 0, 0, "Dropped FIFO scheduler\n"); + } + else + { + log(0, 0, 0, 0, "Error setting scheduler to OTHER: %s\n", strerror(errno)); + log(0, 0, 0, 0, "This is probably really really bad.\n"); + } + } + + signal(SIGPIPE, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGUSR1, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGKILL, SIG_DFL); + signal(SIGALRM, SIG_DFL); + signal(SIGTERM, SIG_DFL); // Close sockets if (udpfd) close(udpfd); udpfd = 0; @@ -210,14 +257,11 @@ void cli_do(int sockfd) if (ifrfd) close(ifrfd); ifrfd = 0; if (cluster_sockfd) close(cluster_sockfd); cluster_sockfd = 0; if (clifd) close(clifd); clifd = 0; - - signal(SIGPIPE, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGHUP, SIG_DFL); - signal(SIGUSR1, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGKILL, SIG_DFL); - signal(SIGALRM, SIG_DFL); +#ifdef BGP + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].sock != -1) + close(bgp_peers[i].sock); +#endif /* BGP */ log(3, 0, 0, 0, "Accepted connection to CLI\n"); @@ -231,7 +275,7 @@ void cli_do(int sockfd) { char prompt[1005]; - snprintf(prompt, 1005, "%s> ", hostname); + snprintf(prompt, 1005, "l2tpns> "); cli_loop(cli, sockfd, prompt); } @@ -281,16 +325,20 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) 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, " Bytes In/Out: %lu/%lu", (unsigned long)session[s].total_cout, (unsigned long)session[s].total_cin); + cli_print(cli, " Pkts In/Out: %lu/%lu", (unsigned long)session[s].pout, (unsigned long)session[s].pin); cli_print(cli, " MRU: %d", session[s].mru); 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"); + if (session[s].snoop_ip && session[s].snoop_port) + cli_print(cli, " Intercepted: %s:%d", inet_toa(session[s].snoop_ip), session[s] .snoop_port); + else + cli_print(cli, " Intercepted: no"); cli_print(cli, " Throttled: %s", session[s].throttle ? "YES" : "no"); cli_print(cli, " Walled Garden: %s", session[s].walled_garden ? "YES" : "no"); - cli_print(cli, " Filter Bucket: %s", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none"); + cli_print(cli, " Filter BucketI: %d", session[s].tbf_in); + cli_print(cli, " Filter BucketO: %d", session[s].tbf_out); } return CLI_OK; } @@ -321,7 +369,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) session[i].tunnel, session[i].user[0] ? session[i].user : "*", userip, - (session[i].snoop) ? "Y" : "N", + (session[i].snoop_ip && session[i].snoop_port) ? "Y" : "N", (session[i].throttle) ? "Y" : "N", (session[i].walled_garden) ? "Y" : "N", abs(time_now - (unsigned long)session[i].opened), @@ -490,6 +538,9 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) 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)); + cli_print(cli, "%-30s%lu", "cluster_forwarded", GET_STAT(c_forwarded)); + cli_print(cli, "%-30s%lu", "recv_forward", GET_STAT(recv_forward)); + #ifdef STAT_CALLS cli_print(cli, "\n%-30s%-10s", "Counter", "Value"); @@ -549,7 +600,7 @@ int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) if (ip_address_pool[i].assigned) { cli_print(cli, "%-15s Y %8d %s", - inet_toa(ip_address_pool[i].address), ip_address_pool[i].session, session[ip_address_pool[i].session].user); + inet_toa(htonl(ip_address_pool[i].address)), ip_address_pool[i].session, session[ip_address_pool[i].session].user); used++; } @@ -557,10 +608,10 @@ int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) { if (ip_address_pool[i].last) cli_print(cli, "%-15s N %8s [%s] %ds", - inet_toa(ip_address_pool[i].address), "", + inet_toa(htonl(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)); + cli_print(cli, "%-15s N", inet_toa(htonl(ip_address_pool[i].address))); free++; } @@ -588,7 +639,6 @@ int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc) cmd_show_run(cli, command, argv, argc); cli_print_callback(cli, NULL); fclose(save_config_fh); - sleep(1); } else { @@ -698,13 +748,13 @@ int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "%-6s %8s %-4s", "ID", "Handle", "Used"); for (i = 0; i < MAXSESSION; i++) { - if (!*filter_buckets[i].handle) + if (!session[i].throttle) continue; - cli_print(cli, "%-6d %8s %c", + cli_print(cli, "%-6d %8d %8d", i, - filter_buckets[i].handle, - (filter_buckets[i].in_use) ? 'Y' : 'N'); + session[i].tbf_in, + session[i].tbf_out); } return CLI_OK; } @@ -741,6 +791,11 @@ int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } if (!argc) { cli_print(cli, "Specify a user to drop"); @@ -787,6 +842,11 @@ int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) int i; tunnelidt tid; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } if (!argc) { cli_print(cli, "Specify a tunnel to drop"); @@ -842,6 +902,11 @@ int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } if (!argc) { cli_print(cli, "Specify a session id to drop"); @@ -889,33 +954,56 @@ 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 i; + ipt ip; + u16 port; sessionidt s; - if (!argc) + if (!config->cluster_iam_master) { - cli_print(cli, "Specify a user"); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + + if (argc < 3) + { + cli_print(cli, "Specify username ip port"); + return CLI_OK; + } + for (i = 0; i < argc; i++) { if (strchr(argv[i], '?')) { - cli_print(cli, "username ..."); + cli_print(cli, "username ip port"); return CLI_OK; } } - for (i = 0; i < argc; i++) - { - if (!(s = sessionbyuser(argv[i]))) - { - cli_print(cli, "User %s is not connected", argv[i]); - continue; - } - session[s].snoop = 1; - cli_print(cli, "Snooping user %s", argv[i]); + if (!(s = sessionbyuser(argv[0]))) + { + cli_print(cli, "User %s is not connected", argv[0]); + return CLI_OK; } + + ip = inet_addr(argv[1]); + if (!ip || ip == INADDR_NONE) + { + cli_print(cli, "Cannot parse IP \"%s\"", argv[1]); + return CLI_OK; + } + + port = atoi(argv[2]); + if (!port) + { + cli_print(cli, "Invalid port %s", argv[2]); + return CLI_OK; + } + + session[s].snoop_ip = ip; + session[s].snoop_port = port; + + cli_print(cli, "Snooping user %s to %s:%d", argv[0], inet_toa(session[s].snoop_ip), session[s].snoop_port); return CLI_OK; } @@ -924,6 +1012,12 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } + if (!argc) { cli_print(cli, "Specify a user"); @@ -945,7 +1039,8 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - session[s].snoop = 0; + session[s].snoop_ip = 0; + session[s].snoop_port = 0; cli_print(cli, "Not snooping user %s", argv[i]); } @@ -957,6 +1052,11 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } if (!argc) { cli_print(cli, "Specify a user"); @@ -978,10 +1078,10 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - if (!throttle_session(s, 1)) - cli_print(cli, "error throttling %s", argv[i]); + if (!throttle_session(s, config->rl_rate)) + cli_print(cli, "Error throttling %s", argv[i]); else - cli_print(cli, "throttling user %s", argv[i]); + cli_print(cli, "Throttling user %s", argv[i]); } return CLI_OK; } @@ -991,6 +1091,11 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } if (!argc) { cli_print(cli, "Specify a user"); @@ -1265,7 +1370,7 @@ int regular_stuff(struct cli_def *cli) if (show_message) { - ipt address = ntohl(ringbuffer->buffer[i].address); + ipt address = htonl(ringbuffer->buffer[i].address); char *ipaddr; struct in_addr addr; diff --git a/cluster.c b/cluster.c index 4803d13..6f4695d 100644 --- a/cluster.c +++ b/cluster.c @@ -1,8 +1,9 @@ // L2TPNS Clustering Stuff -// $Id: cluster.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ #include #include +// $Id: cluster.c,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ +#include +#include #include -#include #include #include #include @@ -15,71 +16,1422 @@ #include #include #include +#include + +#include "l2tpns.h" #include "cluster.h" +#include "util.h" +#include "tbf.h" -int cluster_sockfd = 0; -int cluster_server = 0; -uint32_t vip_address; -extern int debug; -void _log_hex(int level, const char *title, const char *data, int maxsize); -#define log_hex(a,b,c,d) -#ifndef log_hex -#define log_hex(a,b,c,d) do{if (a > debug) _log_hex(a,b,c,d);}while (0) +#ifdef BGP +#include "bgp.h" #endif +/* + * All cluster packets have the same format. + * + * One or more instances of + * a 32 bit 'type' id. + * a 32 bit 'extra' data dependant on the 'type'. + * zero or more bytes of structure data, dependant on the type. + * + */ +// Module variables. +int cluster_sockfd = 0; // The filedescriptor for the cluster communications port. + +ipt my_address = 0; // The network address of my ethernet port. +static int walk_session_number = 0; // The next session to send when doing the slow table walk. +static int walk_tunnel_number = 0; // The next tunnel to send when doing the slow table walk. + +static int hsess, fsess; // Saved copies of the highest used session id, and the first free one. + +#define MAX_HEART_SIZE (8192) // Maximum size of heartbeat packet. Must be less than max IP packet size :) +#define MAX_CHANGES (MAX_HEART_SIZE/(sizeof(sessiont) + sizeof(int) ) - 2) // Assumes a session is the biggest type! + +static struct { + int type; + int id; +} cluster_changes[MAX_CHANGES]; // Queue of changed structures that need to go out when next heartbeat. + +static struct { + int seq; + int size; + char data[MAX_HEART_SIZE]; +} past_hearts[HB_HISTORY_SIZE]; // Ring buffer of heartbeats that we've recently sent out. Needed so + // we can re-transmit if needed. + +static struct { + u32 peer; + time_t basetime; + clockt timestamp; + int uptodate; +} peers[CLUSTER_MAX_SIZE]; // List of all the peers we've heard from. +static int num_peers; // Number of peers in list. +static int have_peers; // At least one peer + +int rle_decompress(u8 ** src_p, int ssize, u8 *dst, int dsize); +int rle_compress(u8 ** src_p, int ssize, u8 *dst, int dsize); + +// // Create a listening socket -int cluster_init(uint32_t bind_address, int server) +// +// This joins the cluster multi-cast group. +// +int cluster_init() { struct sockaddr_in addr; + struct sockaddr_in interface_addr; + struct ip_mreq mreq; + struct ifreq ifr; + int opt = 0; - vip_address = bind_address; - cluster_server = !!server; + config->cluster_undefined_sessions = MAXSESSION-1; + config->cluster_undefined_tunnels = MAXTUNNEL-1; + + if (!config->cluster_address) + return 0; + if (!*config->cluster_interface) + return 0; cluster_sockfd = socket(AF_INET, SOCK_DGRAM, UDP); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; - addr.sin_port = htons(cluster_server ? CLUSTERPORT : CLUSTERCLIENTPORT); + addr.sin_port = htons(CLUSTERPORT); addr.sin_addr.s_addr = INADDR_ANY; setsockopt(cluster_sockfd, SOL_SOCKET, SO_REUSEADDR, &addr, sizeof(addr)); + if (bind(cluster_sockfd, (void *) &addr, sizeof(addr)) < 0) { - perror("bind"); - exit(-1); + log(0, 0, 0, 0, "Failed to bind cluster socket: %s\n", strerror(errno)); + return -1; } + strcpy(ifr.ifr_name, config->cluster_interface); + if (ioctl(cluster_sockfd, SIOCGIFADDR, &ifr) < 0) { + log(0, 0, 0, 0, "Failed to get interface address for (%s): %s\n", config->cluster_interface, strerror(errno)); + return -1; + } + + memcpy(&interface_addr, &ifr.ifr_addr, sizeof(interface_addr) ); + my_address = interface_addr.sin_addr.s_addr; + + // Join multicast group. + mreq.imr_multiaddr.s_addr = config->cluster_address; + mreq.imr_interface = interface_addr.sin_addr; + + + opt = 0; // Turn off multicast loopback. + setsockopt(cluster_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &opt, sizeof(opt)); + + if (setsockopt(cluster_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + log(0, 0, 0, 0, "Failed to setsockopt (join mcast group): %s\n", strerror(errno)); + return -1; + } + + if (setsockopt (cluster_sockfd, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr)) < 0) { + log(0, 0, 0, 0, "Failed to setsockopt (set mcast interface): %s\n", strerror(errno)); + return -1; + } + + config->cluster_last_hb = config->current_time; + config->cluster_seq_number = -1; + return cluster_sockfd; } -int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen) + +// +// Send a chunk of data to the entire cluster (usually via the multicast +// address ). +// + +int cluster_send_data(void *data, int datalen) { - size_t l = 1 + sizeof(uint32_t) + datalen; - char *buf = NULL; struct sockaddr_in addr = {0}; if (!cluster_sockfd) return -1; - if (!ip_address) return 0; + if (!config->cluster_address) return 0; - buf = calloc(l, 1); - *(uint32_t *)(buf) = htonl(vip); - *(char *)(buf+sizeof(uint32_t)) = type; - - if (data && datalen > 0) - memcpy((char *)(buf + sizeof(uint32_t) + 1), data, datalen); - - addr.sin_addr.s_addr = ip_address; - addr.sin_port = htons(cluster_server ? CLUSTERCLIENTPORT : CLUSTERPORT); + addr.sin_addr.s_addr = config->cluster_address; + addr.sin_port = htons(CLUSTERPORT); addr.sin_family = AF_INET; - log_hex(4, "Cluster send", buf, l); +// log_hex(4, "Cluster send", data, datalen); // VERY big data packets. How about we don't.. - if (sendto(cluster_sockfd, buf, l, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) + log(5,0,0,0, "Cluster send data: %d bytes\n", datalen); + + if (sendto(cluster_sockfd, data, datalen, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) { - perror("sendto"); - free(buf); + log(0, 0, 0, 0, "sendto: %s\n", strerror(errno)); return -1; } - free(buf); return 0; } + +// +// Add a chunk of data to a heartbeat packet. +// Maintains the format. Assumes that the caller +// has passed in a big enough buffer! +// +static void add_type(char ** p, int type, int more, char * data, int size) +{ + * ( (u32*)(*p) ) = type; + *p += sizeof(u32); + + * ( (u32*)(*p) ) = more; + *p += sizeof(u32); + + if (data && size > 0) { + memcpy(*p, data, size); + (*p) += size; + } +} + +void cluster_uptodate(void) +{ + if (config->cluster_iam_uptodate) + return; + + if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels) + return; + + config->cluster_iam_uptodate = 1; + + log(0,0,0,0, "Now uptodate with master.\n"); + + // If we're not a master, or if we have no slaves + // then start taking traffic.. + if (!config->cluster_iam_master || !have_peers) + { +#ifdef BGP + if (bgp_configured) + bgp_enable_routing(1); + else +#endif /* BGP */ + if (config->send_garp) + send_garp(config->bind_address); // Start taking traffic. + } +} + +// +// Send a unicast UDP packet to a peer with 'data' as the +// contents. +// +int peer_send_data(u32 peer, char * data, int size) +{ + struct sockaddr_in addr = {0}; + + if (!cluster_sockfd) return -1; + if (!config->cluster_address) return 0; + + if (!peer) // Odd?? + return -1; + + addr.sin_addr.s_addr = peer; + addr.sin_port = htons(CLUSTERPORT); + addr.sin_family = AF_INET; + + log_hex(5, "Peer send", data, size); + + if (sendto(cluster_sockfd, data, size, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) + { + log(0, 0, 0, 0, "sendto: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +// +// Send a structured message to a peer with a single element of type 'type'. +// +int peer_send_message(u32 peer, int type, int more, char * data, int size) +{ + char buf[65536]; // Vast overkill. + char * p = buf; + + log(4,0,0,0, "Sending message to peer (type %d, more %d, size %d)\n", type, more, size); + add_type(&p, type, more, data, size); + + return peer_send_data(peer, buf, (p-buf) ); +} + +// +// Forward a state changing packet to the master. +// +// The master just processes the payload as if it had +// received it off the tap device. +// +int master_forward_packet(char * data, int size, u32 addr, int port) +{ + char buf[65536]; // Vast overkill. + char * p = buf; + + if (!config->cluster_master_address) // No election has been held yet. Just skip it. + return -1; + + log(4,0,0,0, "Forwarding packet from %s to master (size %d)\n", inet_toa(addr), size); + + STAT(c_forwarded); + add_type(&p, C_FORWARD, addr, (char*) &port, sizeof(port) ); + memcpy(p, data, size); + p += size; + + return peer_send_data(config->cluster_master_address, buf, (p-buf) ); + +} + +// +// Forward a throttled packet to the master for handling. +// +// The master just drops the packet into the appropriate +// token bucket queue, and lets normal processing take care +// of it. +// +int master_throttle_packet(int tbfid, char * data, int size) +{ + char buf[65536]; // Vast overkill. + char * p = buf; + + if (!config->cluster_master_address) // No election has been held yet. Just skip it. + return -1; + + log(4,0,0,0, "Throttling packet master (size %d, tbfid %d)\n", size, tbfid); + + add_type(&p, C_THROTTLE, tbfid, data, size); + + return peer_send_data(config->cluster_master_address, buf, (p-buf) ); + +} + +// +// Forward a walled garden packet to the master for handling. +// +// The master just writes the packet straight to the tun +// device (where is will normally loop through the +// firewall rules, and come back in on the tun device) +// +// (Note that this must be called with the tun header +// as the start of the data). +int master_garden_packet(sessionidt s, char *data, int size) +{ + char buf[65536]; // Vast overkill. + char *p = buf; + + if (!config->cluster_master_address) // No election has been held yet. Just skip it. + return -1; + + log(4,0,0,0, "Walled garden packet to master (size %d)\n", size); + + add_type(&p, C_GARDEN, s, data, size); + + return peer_send_data(config->cluster_master_address, buf, (p-buf)); + +} + +// +// Send a chunk of data as a heartbeat.. +// We save it in the history buffer as we do so. +// +static void send_heartbeat(int seq, char * data, int size) +{ + int i; + + if (size > sizeof(past_hearts[0].data)) { + log(0,0,0,0, "Tried to heartbeat something larger than the maximum packet!\n"); + kill(0, SIGTERM); + } + i = seq % HB_HISTORY_SIZE; + past_hearts[i].seq = seq; + past_hearts[i].size = size; + memcpy(&past_hearts[i].data, data, size); // Save it. + cluster_send_data(data, size); +} + +// +// Send an 'i am alive' message to every machine in the cluster. +// +void cluster_send_ping(time_t basetime) +{ + char buff[100 + sizeof(pingt)]; + char *p = buff; + pingt x; + + if (config->cluster_iam_master && basetime) // We're heartbeating so no need to ping. + return; + + log(5,0,0,0, "Sending cluster ping...\n"); + + x.ver = 1; + x.addr = config->bind_address; + x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels; + x.basetime = basetime; + + add_type(&p, C_PING, basetime, (char *) &x, sizeof(x)); + cluster_send_data(buff, (p-buff) ); +} + +// +// Walk the session counters looking for non-zero ones to send +// to the master. We send up to 100 of them at one time. +// We examine a maximum of 2000 sessions. +// (50k max session should mean that we normally +// examine the entire session table every 25 seconds). + +#define MAX_B_RECS (400) +void master_update_counts(void) +{ + int i, c; + bytest b[MAX_B_RECS+1]; + + if (config->cluster_iam_master) // Only happens on the slaves. + return; + + if (!config->cluster_master_address) // If we don't have a master, skip it for a while. + return; + + i = MAX_B_RECS * 5; // Examine max 2000 sessions; + if (config->cluster_highest_sessionid > i) + i = config->cluster_highest_sessionid; + + for ( c = 0; i > 0 ; --i) { + // Next session to look at. + walk_session_number++; + if ( walk_session_number > config->cluster_highest_sessionid) + walk_session_number = 1; + + if (!sess_count[walk_session_number].cin && !sess_count[walk_session_number].cout) + continue; // Unused. Skip it. + + b[c].sid = walk_session_number; + b[c].in = sess_count[walk_session_number].cin; + b[c].out = sess_count[walk_session_number].cout; + + if (++c > MAX_B_RECS) // Send a max of 400 elements in a packet. + break; + + // Reset counters. + sess_count[walk_session_number].cin = sess_count[walk_session_number].cout = 0; + } + + if (!c) // Didn't find any that changes. Get out of here! + return; + + + // Forward the data to the master. + log(4,0,0,0, "Sending byte counters to master (%d elements)\n", c); + peer_send_message(config->cluster_master_address, C_BYTES, c, (char*) &b, sizeof(b[0]) * c); + return; +} + +// +// Check that we have a master. If it's been too +// long since we heard from a master then hold an election. +// +void cluster_check_master(void) +{ + int i, count, tcount, high_sid = 0; + int last_free = 0; + int had_peers = have_peers; + clockt t = config->current_time; + + if (config->current_time < (config->cluster_last_hb + HB_TIMEOUT) ) + return; // Everything's ok. return. + + if (!config->cluster_iam_master) + log(0,0,0,0, "Master timed out! Holding election...\n"); + + config->cluster_last_hb = config->current_time + 1; + + for (i = have_peers = 0; i < num_peers ; ++i) { + if ((peers[i].timestamp + HB_TIMEOUT) < t) + continue; // Stale peer! Skip them. + + if (!peers[i].basetime) + continue; // Shutdown peer! Skip them. + + have_peers = 1; + if (peers[i].basetime < basetime) { + log(1,0,0,0, "Expecting %s to become master\n", inet_toa(peers[i].peer) ); + return; // They'll win the election. Get out of here. + } + + if (peers[i].basetime == basetime && + peers[i].peer > my_address) { + log(1,0,0,0, "Expecting %s to become master\n", inet_toa(peers[i].peer) ); + return; // They'll win the election. Wait for them to come up. + } + } + + if (config->cluster_iam_master) // If we're the master, we've already won + { +#ifdef BGP + // master lost all slaves, need to handle traffic ourself + if (bgp_configured && had_peers && !have_peers) + bgp_enable_routing(1); +#endif /* BGP */ + return; + } + + // Wow. it's been ages since I last heard a heartbeat + // and I'm better than an of my peers so it's time + // to become a master!!! + + config->cluster_iam_master = 1; + config->cluster_master_address = 0; + + log(0,0,0,0, "I am declaring myself the master!\n"); + +#ifdef BGP + if (bgp_configured && have_peers) + bgp_enable_routing(0); /* stop handling traffic */ +#endif /* BGP */ + + if (config->cluster_seq_number == -1) + config->cluster_seq_number = 0; + + // + // Go through and mark all the tunnels as defined. + // Count the highest used tunnel number as well. + // + config->cluster_highest_tunnelid = 0; + for (i = 0, tcount = 0; i < MAXTUNNEL; ++i) { + if (tunnel[i].state == TUNNELUNDEF) + tunnel[i].state = TUNNELFREE; + + if (tunnel[i].state != TUNNELFREE && i > config->cluster_highest_tunnelid) + config->cluster_highest_tunnelid = i; + } + + // + // Go through and mark all the sessions as being defined. + // reset the idle timeouts. + // add temporary byte counters to permanent ones. + // Re-string the free list. + // Find the ID of the highest session. + last_free = 0; + high_sid = 0; + config->cluster_highest_sessionid = 0; + for (i = 0, count = 0; i < MAXSESSION; ++i) { + if (session[i].tunnel == T_UNDEF) { + session[i].tunnel = T_FREE; + ++count; + } + + if (session[i].tunnel == T_FREE) { // Unused session. Add to free list. + session[last_free].next = i; + session[i].next = 0; + last_free = i; + } + + // Reset all the idle timeouts.. + session[i].last_packet = time_now; + + // Accumulate un-sent byte counters. + session[i].cin += sess_count[i].cin; + session[i].cout += sess_count[i].cout; + session[i].total_cin += sess_count[i].cin; + session[i].total_cout += sess_count[i].cout; + + sess_count[i].cin = sess_count[i].cout = 0; + + session[i].radius = 0; // Reset authentication as the radius blocks aren't up to date. + + if (session[i].sid >= high_sid) // This is different to the index into the session table!!! + high_sid = session[i].sid+1; + + + session[i].tbf_in = session[i].tbf_out = 0; // Remove stale pointers from old master. + throttle_session(i, session[i].throttle); + +// I'm unsure about this. --mo +// It's potentially a good thing, but it could send a +// LOT of packets. +// if (session[i].throttle) +// cluster_send_session(s); // Tell the slaves about the new tbf indexes. + + if (session[i].tunnel != T_FREE && i > config->cluster_highest_sessionid) + config->cluster_highest_sessionid = i; + + } + + session[last_free].next = 0; // End of chain. + last_sid = high_sid; // Keep track of the highest used session ID. + + become_master(); + + rebuild_address_pool(); + + // If we're not the very first master, this is a big issue! + if(count>0) + log(0,0,0,0, "Warning: Fixed %d uninitialized sessions in becoming master!\n", count); + + config->cluster_undefined_sessions = 0; + config->cluster_undefined_tunnels = 0; + + // + // FIXME. We need to fix up the tunnel control message + // queue here! There's a number of other variables we + // should also update. + cluster_uptodate(); +} + + +// +// Check that our session table is validly matching what the +// master has in mind. +// +// In particular, if we have too many sessions marked 'undefined' +// we fix it up here, and we ensure that the 'first free session' +// pointer is valid. +// +static void cluster_check_sessions(int highsession, int freesession_ptr, int hightunnel) +{ + int i; + + sessionfree = freesession_ptr; // Keep the freesession ptr valid. + + if (config->cluster_iam_uptodate) + return; + + if (highsession > config->cluster_undefined_sessions && hightunnel > config->cluster_undefined_tunnels) + return; + + // Clear out defined sessions, counting the number of + // undefs remaining. + config->cluster_undefined_sessions = 0; + for (i = 1 ; i < MAXSESSION; ++i) { + if (i > highsession) { + session[i].tunnel = 0; // Defined. + continue; + } + if (session[i].tunnel != T_UNDEF) + continue; + ++config->cluster_undefined_sessions; + } + + // Clear out defined tunnels, counting the number of + // undefs remaining. + config->cluster_undefined_tunnels = 0; + for (i = 1 ; i < MAXTUNNEL; ++i) { + if (i > hightunnel) { + tunnel[i].state = TUNNELFREE; // Defined. + continue; + } + if (tunnel[i].state != TUNNELUNDEF) + continue; + ++config->cluster_undefined_tunnels; + } + + + if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels) { + log(2,0,0,0, "Cleared undefined sessions/tunnels. %d sess (high %d), %d tunn (high %d)\n", + config->cluster_undefined_sessions, highsession, config->cluster_undefined_tunnels, hightunnel); + return; + } + + // Are we up to date? + + if (!config->cluster_iam_uptodate) + cluster_uptodate(); +} + +int hb_add_type(char **p, int type, int id) +{ + switch (type) { + case C_CSESSION: { // Compressed C_SESSION. + u8 c[sizeof(sessiont) * 2]; // Bigger than worst case. + u8 *d = (u8 *) &session[id]; + u8 *orig = d; + int size; + + size = rle_compress( &d, sizeof(sessiont), c, sizeof(c) ); + + // Did we compress the full structure, and is the size actually + // reduced?? + if ( (d - orig) == sizeof(sessiont) && size < sizeof(sessiont) ) { + add_type(p, C_CSESSION, id, (char*) c, size); + break; + } + // Failed to compress : Fall through. + } + case C_SESSION: add_type(p, C_SESSION, id, + (char*) &session[id], sizeof(sessiont)); + break; + + case C_CTUNNEL: { // Compressed C_TUNNEL + u8 c[sizeof(tunnelt) * 2]; // Bigger than worst case. + u8 *d = (u8 *) &tunnel[id]; + u8 *orig = d; + int size; + + size = rle_compress( &d, sizeof(tunnelt), c, sizeof(c) ); + + // Did we compress the full structure, and is the size actually + // reduced?? + if ( (d - orig) == sizeof(tunnelt) && size < sizeof(tunnelt) ) { + add_type(p, C_CTUNNEL, id, c, size); + break; + } + // Failed to compress : Fall through. + } + case C_TUNNEL: add_type(p, C_TUNNEL, id, + (char*) &tunnel[id], sizeof(tunnelt)); + break; + default: + log(0,0,0,0, "Found an invalid type in heart queue! (%d)\n", type); + kill(0, SIGTERM); + } + return 0; +} + +// +// Send a heartbeat, incidently sending out any queued changes.. +// +void cluster_heartbeat(int highsession, int freesession, int hightunnel) +{ + int i, count = 0, tcount = 0; + char buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ]; + heartt h; + char * p = buff; + + if (!config->cluster_iam_master) // Only the master does this. + return; + + hsess = highsession; + fsess = freesession; + // Fill out the heartbeat header. + h.version = HB_VERSION; + h.seq = config->cluster_seq_number; + h.basetime = basetime; + h.clusterid = config->bind_address; // Will this do?? + h.basetime = basetime; + h.highsession = highsession; + h.freesession = freesession; + h.hightunnel = hightunnel; + h.size_sess = sizeof(sessiont); // Just in case. + h.size_tunn = sizeof(tunnelt); + + add_type(&p, C_HEARTBEAT, HB_VERSION, (char*) &h, sizeof(h) ); + + for (i = 0; i < config->cluster_num_changes; ++i) { + hb_add_type(&p, cluster_changes[i].type, cluster_changes[i].id); + } + + if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? + log(0,0,0,0, "Overrun the heartbeat buffer! This is fatal. Exiting. (size %d)\n", p - buff); + kill(0, SIGTERM); + } + + // + // Fill out the packet with sessions from the session table... + // (not forgetting to leave space so we can get some tunnels in too ) + while ( (p + sizeof(u32) * 2 + sizeof(sessiont) * 2 ) < (buff + MAX_HEART_SIZE) ) { + + if (!walk_session_number) // session #0 isn't valid. + ++walk_session_number; + + if (count >= highsession) // If we're a small cluster, don't go wild. + break; + + hb_add_type(&p, C_CSESSION, walk_session_number); + walk_session_number = (1+walk_session_number)%(highsession+1); // +1 avoids divide by zero. + + ++count; // Count the number of extra sessions we're sending. + } + + // + // Fill out the packet with tunnels from the tunnel table... + // + while ( (p + sizeof(u32) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE) ) { + + if (!walk_tunnel_number) // tunnel #0 isn't valid. + ++walk_tunnel_number; + + if (tcount >= config->cluster_highest_tunnelid) + break; + + hb_add_type(&p, C_CTUNNEL, walk_tunnel_number); + walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1); // +1 avoids divide by zero. + + ++tcount; + } + + // + // Did we do something wrong? + if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? + log(0,0,0,0, "Overran the heartbeat buffer now! This is fatal. Exiting. (size %d)\n", p - buff); + kill(0, SIGTERM); + } + + log(3,0,0,0, "Sending heartbeat with %d changes (%d x-sess, %d x-tunnels, %d highsess, %d hightun size %d)\n", + config->cluster_num_changes, count, tcount, config->cluster_highest_sessionid, + config->cluster_highest_tunnelid, (p-buff)); + + config->cluster_num_changes = 0; + + send_heartbeat(h.seq, buff, (p-buff) ); // Send out the heartbeat to the cluster, keeping a copy of it. + + config->cluster_seq_number = (config->cluster_seq_number+1)%HB_MAX_SEQ; // Next seq number to use. +} + +// +// A structure of type 'type' has changed; Add it to the queue to send. +// +int type_changed(int type, int id) +{ + int i; + + for (i = 0 ; i < config->cluster_num_changes ; ++i) + if ( cluster_changes[i].id == id && + cluster_changes[i].type == type) + return 0; // Already marked for change. + + cluster_changes[i].type = type; + cluster_changes[i].id = id; + ++config->cluster_num_changes; + + if (config->cluster_num_changes > MAX_CHANGES) + cluster_heartbeat(config->cluster_highest_sessionid, fsess, config->cluster_highest_tunnelid); + + return 1; +} + + +// A particular session has been changed! +int cluster_send_session(int sid) +{ + if (!config->cluster_iam_master) { + log(0,0,sid,0, "I'm not a master, but I just tried to change a session!\n"); + return -1; + } + + return type_changed(C_CSESSION, sid); +} + +// A particular tunnel has been changed! +int cluster_send_tunnel(int tid) +{ + if (!config->cluster_iam_master) { + log(0,0,0,tid, "I'm not a master, but I just tried to change a tunnel!\n"); + return -1; + } + + return type_changed(C_CTUNNEL, tid); +} + + +// +// We're a master, and a slave has just told us that it's +// missed a packet. We'll resend it every packet since +// the last one it's seen. +// +int cluster_catchup_slave(int seq, u32 slave) +{ + int s; + int diff; + + log(1,0,0,0, "Slave %s sent LASTSEEN with seq %d\n", inet_toa(slave), seq); + + diff = config->cluster_seq_number - seq; // How many packet do we need to send? + if (diff < 0) + diff += HB_MAX_SEQ; + + if (diff >= HB_HISTORY_SIZE) { // Ouch. We don't have the packet to send it! + log(0,0,0,0, "A slaved asked for message %d when our seq number is %d. Killing it.\n", + seq, config->cluster_seq_number); + return peer_send_message(slave, C_KILL, seq, NULL, 0);// Kill the slave. Nothing else to do. + } + + // Now resend every packet that it missed, in order. + while (seq != config->cluster_seq_number) { + s = seq%HB_HISTORY_SIZE; + if (seq != past_hearts[s].seq) { + log(0,0,0,0, "Tried to re-send heartbeat for %s but %d doesn't match %d! (%d,%d)\n", + inet_toa(slave), seq, past_hearts[s].seq, s, config->cluster_seq_number); + return -1; // What to do here!? + } + peer_send_data(slave, past_hearts[s].data, past_hearts[s].size); + seq = (seq+1)%HB_MAX_SEQ; // Increment to next seq number. + } + return 0; // All good! +} + +// +// We've heard from another peer! Add it to the list +// that we select from at election time. +// +int cluster_add_peer(u32 peer, time_t basetime, pingt *p) +{ + int i; + u32 clusterid; + + clusterid = p->addr; + if (clusterid != config->bind_address) + { + // Is this for us? + log(4,0,0,0, "Skipping ping from %s (different cluster)\n", inet_toa(peer)); + return 0; + } + + // Is this the master shutting down?? + if (peer == config->cluster_master_address && !basetime) { + config->cluster_master_address = 0; + config->cluster_last_hb = 0; // Force an election. + cluster_check_master(); + return 0; + } + + for (i = 0; i < num_peers ; ++i) + { + if (peers[i].peer != peer) + continue; + + // This peer already exists. Just update the timestamp. + peers[i].basetime = basetime; + peers[i].timestamp = config->current_time; + break; + } + + if (i >= num_peers) + { + log(4,0,0,0, "Adding %s as a peer\n", inet_toa(peer)); + + // Not found. Is there a stale slot to re-use? + for (i = 0; i < num_peers ; ++i) + { + if (peers[i].peer != peer) + continue; + if ((peers[i].timestamp + HB_TIMEOUT * 10) < config->current_time) // Stale. + break; + } + + if (i >= CLUSTER_MAX_SIZE) + { + // Too many peers!! + log(0,0,0,0, "Tried to add %s as a peer, but I already have %d of them!\n", inet_toa(peer), i); + return -1; + } + + peers[i].peer = peer; + peers[i].basetime = basetime; + peers[i].timestamp = config->current_time; + if (i == num_peers) + ++num_peers; + + log(1,0,0,0, "Added %s as a new peer. Now %d peers\n", inet_toa(peer), num_peers); + } + +#ifdef BGP + /* drop routes if we've now got a peer */ + if (bgp_configured && config->cluster_iam_master && !have_peers) + bgp_enable_routing(0); +#endif /* BGP */ + + have_peers = 1; + + return 1; +} + +/* Handle the slave updating the byte counters for the master. */ +// +// Note that we don't mark the session as dirty; We rely on +// the slow table walk to propogate this back out to the slaves. +// +int cluster_handle_bytes(char * data, int size) +{ + bytest * b; + + b = (bytest*) data; + + log(3,0,0,0, "Got byte counter update (size %d)\n", size); + + /* Loop around, adding the byte + counts to each of the sessions. */ + + while (size >= sizeof(*b) ) { + if (b->sid > MAXSESSION) { + log(0,0,0,0, "Got C_BYTES with session #%d!\n", b->sid); + return -1; /* Abort processing */ + } + + session[b->sid].total_cin += b->in; + session[b->sid].total_cout += b->out; + + session[b->sid].cin += b->in; + session[b->sid].cout += b->out; + session[b->sid].last_packet = time_now; // Reset idle timer! + + size -= sizeof(*b); + ++b; + } + + if (size != 0) + log(0,0,0,0, "Got C_BYTES with %d bytes of trailing junk!\n", size); + + return size; +} + +// +// Handle receiving a session structure in a heartbeat packet. +// +static int cluster_recv_session(int more , u8 * p) +{ + if (more >= MAXSESSION) { + log(0,0,0,0, "DANGER: Received a heartbeat session id > MAXSESSION!\n"); + return -1; + } + + if (session[more].tunnel == T_UNDEF) { + if (config->cluster_iam_uptodate) { // Sanity. + log(0,0,0,0, "I thought I was uptodate but I just found an undefined session!\n"); + } else { + --config->cluster_undefined_sessions; + } + } + + load_session(more, (sessiont*) p); // Copy session into session table.. + + log(5,0,more,0, "Received session update (%d undef)\n", config->cluster_undefined_sessions); + + if (!config->cluster_iam_uptodate) + cluster_uptodate(); // Check to see if we're up to date. + return 0; +} + +static int cluster_recv_tunnel(int more, u8 *p) +{ + if (more >= MAXTUNNEL) { + log(0,0,0,0, "DANGER: Received a tunnel session id > MAXTUNNEL!\n"); + return -1; + } + + if (tunnel[more].state == TUNNELUNDEF) { + if (config->cluster_iam_uptodate) { // Sanity. + log(0,0,0,0, "I thought I was uptodate but I just found an undefined tunnel!\n"); + } else { + --config->cluster_undefined_tunnels; + } + } + + memcpy(&tunnel[more], p, sizeof(tunnel[more]) ); + + // + // Clear tunnel control messages. These are dynamically allocated. + // If we get unlucky, this may cause the tunnel to drop! + // + tunnel[more].controls = tunnel[more].controle = NULL; + tunnel[more].controlc = 0; + + log(5,0,0,more, "Received tunnel update\n"); + + if (!config->cluster_iam_uptodate) + cluster_uptodate(); // Check to see if we're up to date. + + return 0; +} + + +// +// Process a version one heartbeat.. +// +static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u32 addr) +{ + heartt * h; + int s = size - (p-data); + int i, type; + + if (more != HB_VERSION) { + log(0,0,0,0, "Received a heartbeat version that I don't understand!\n"); + return -1; // Ignore it?? + } + // Ok. It's a heartbeat packet from a cluster master! + if (s < sizeof(*h)) + goto shortpacket; + + + h = (heartt*) p; + p += sizeof(*h); + s -= sizeof(*h); + + if (h->clusterid != config->bind_address) + return -1; // It's not part of our cluster. + + if (config->cluster_iam_master) { // Sanity... + // Note that this MUST match the election process above! + + log(0,0,0,0, "I just got a packet claiming to be from a master but _I_ am the master!\n"); + if (!h->basetime) { + log(0,0,0,0, "Heartbeat from addr %s with zero basetime!\n", inet_toa(htonl(addr)) ); + return -1; // Skip it. + } + if (basetime > h->basetime) { + log(0,0,0,0, "They're (%s) an older master than me so I'm gone!\n", inet_toa(htonl(addr))); + kill(0, SIGTERM); + } + if (basetime == h->basetime && my_address < addr) { // Tie breaker. + log(0,0,0,0, "They're a higher IP address than me, so I'm gone!\n"); + kill(0, SIGTERM); + } + return -1; // Skip it. + } + + if (config->cluster_seq_number == -1) // Don't have one. Just align to the master... + config->cluster_seq_number = h->seq; + + config->cluster_last_hb = config->current_time; // Reset to ensure that we don't become master!! + + if (config->cluster_seq_number != h->seq) { // Out of sequence heartbeat! + log(1,0,0,0, "HB: Got seq# %d but was expecting %d. asking for resend.\n", h->seq, config->cluster_seq_number); + + peer_send_message(addr, C_LASTSEEN, config->cluster_seq_number, NULL, 0); + + config->cluster_last_hb = config->current_time; // Reset to ensure that we don't become master!! + + // Just drop the packet. The master will resend it as part of the catchup. + + return 0; + } + // Save the packet in our buffer. + // This is needed in case we become the master. + config->cluster_seq_number = (h->seq+1)%HB_MAX_SEQ; + i = h->seq % HB_HISTORY_SIZE; + past_hearts[i].seq = h->seq; + past_hearts[i].size = size; + memcpy(&past_hearts[i].data, data, size); // Save it. + + + // Check that we don't have too many undefined sessions, and + // that the free session pointer is correct. + cluster_check_sessions(h->highsession, h->freesession, h->hightunnel); + + // Ok. process the packet... + while ( s > 0) { + + type = * ((u32*) p); + p += sizeof(u32); + s -= sizeof(u32); + + more = * ((u32*) p); + p += sizeof(u32); + s -= sizeof(u32); + + switch (type) { + case C_CSESSION: { // Compressed session structure. + u8 c [ sizeof(sessiont) + 2]; + int size; + u8 * orig_p = p; + + size = rle_decompress((u8 **) &p, s, c, sizeof(c) ); + s -= (p - orig_p); + + if (size != sizeof(sessiont) ) { // Ouch! Very very bad! + log(0,0,0,0, "DANGER: Received a CSESSION that didn't decompress correctly!\n"); + // Now what? Should exit! No-longer up to date! + break; + } + + cluster_recv_session(more, c); + break; + } + case C_SESSION: + if ( s < sizeof(session[more])) + goto shortpacket; + + cluster_recv_session(more, p); + + p += sizeof(session[more]); + s -= sizeof(session[more]); + break; + + case C_CTUNNEL: { // Compressed tunnel structure. + u8 c [ sizeof(tunnelt) + 2]; + int size; + u8 * orig_p = p; + + size = rle_decompress( (u8 **) &p, s, c, sizeof(c) ); + s -= (p - orig_p); + + if (size != sizeof(tunnelt) ) { // Ouch! Very very bad! + log(0,0,0,0, "DANGER: Received a CSESSION that didn't decompress correctly!\n"); + // Now what? Should exit! No-longer up to date! + break; + } + + cluster_recv_tunnel(more, c); + break; + + } + case C_TUNNEL: + if ( s < sizeof(tunnel[more])) + goto shortpacket; + + cluster_recv_tunnel(more, p); + + p += sizeof(tunnel[more]); + s -= sizeof(tunnel[more]); + break; + default: + log(0,0,0,0, "DANGER: I received a heartbeat element where I didn't understand the type! (%d)\n", type); + return -1; // can't process any more of the packet!! + } + } + if (config->cluster_master_address != addr) + { + char *str; + str = strdup(inet_toa(config->cluster_master_address)); + log(0,0,0,0, "My master just changed from %s to %s!\n", str, inet_toa(addr)); + if (str) free(str); + } + + config->cluster_master_address = addr; + config->cluster_last_hb = config->current_time; // Successfully received a heartbeat! + return 0; + +shortpacket: + log(0,0,0,0, "I got an incomplete heartbeat packet! This means I'm probably out of sync!!\n"); + return -1; +} + +// +// We got a packet on the cluster port! +// Handle pings, lastseens, and heartbeats! +// +int processcluster(char * data, int size, u32 addr) +{ + int type, more; + char * p = data; + int s = size; + + if (addr == my_address) + return -1; // Ignore it. Something looped back the multicast! + + log(5,0,0,0, "Process cluster: %d bytes from %s\n", size, inet_toa(addr)); + + if (s <= 0) // Any data there?? + return -1; + + if (s < 8) + goto shortpacket; + + type = * ((u32*) p); + p += sizeof(u32); + s -= sizeof(u32); + + more = * ((u32*) p); + p += sizeof(u32); + s -= sizeof(u32); + + switch (type) { + case C_PING: // Update the peers table. + return cluster_add_peer(addr, more, (pingt*)p); + + case C_LASTSEEN: // Catch up a slave (slave missed a packet). + return cluster_catchup_slave(more, addr); + + case C_FORWARD: { // Forwarded control packet. pass off to processudp. + struct sockaddr_in a; + a.sin_addr.s_addr = more; + + a.sin_port = * (int*) p; + s -= sizeof(int); + p += sizeof(int); + + if (!config->cluster_iam_master) { // huh? + log(0,0,0,0, "I'm not the master, but I got a C_FORWARD from %s?\n", inet_toa(addr)); + return -1; + } + + log(4,0,0,0, "Got a forwarded packet... (%s:%d)\n", inet_toa(more), a.sin_port); + STAT(recv_forward); + processudp(p, s, &a); + return 0; + } + case C_THROTTLE: { // Receive a forwarded packet from a slave. + if (!config->cluster_iam_master) { + log(0,0,0,0, "I'm not the master, but I got a C_THROTTLE from %s?\n", inet_toa(addr)); + return -1; + } + + tbf_queue_packet(more, p, s); // The TBF id tells wether it goes in or out. + return 0; + } + case C_GARDEN: + // Receive a walled garden packet from a slave. + if (!config->cluster_iam_master) { + log(0,0,0,0, "I'm not the master, but I got a C_GARDEN from %s?\n", inet_toa(addr)); + return -1; + } + + tun_write(p, s); + return 0; + + case C_BYTES: + return cluster_handle_bytes(p, s); + + case C_KILL: // The master asked us to die!? (usually because we're too out of date). + if (config->cluster_iam_master) { + log(0,0,0,0, "_I_ am master, but I received a C_KILL from %s! (Seq# %d)\n", inet_toa(addr), more); + return -1; + } + if (more != config->cluster_seq_number) { + log(0,0,0,0, "The master asked us to die but the seq number didn't match!?\n"); + return -1; + } + + if (addr != config->cluster_master_address) { + log(0,0,0,0, "Received a C_KILL from %s which doesn't match config->cluster_master_address (%x)", + inet_toa(addr), config->cluster_master_address); + // We can only warn about it. The master might really have switched! + } + + log(0,0,0,0, "Received a valid C_KILL: I'm going to die now."); + kill(0, SIGTERM); + exit(0); // Lets be paranoid; + return -1; // Just signalling the compiler. + + case C_HEARTBEAT: + log(4,0,0,0, "Got a heartbeat from %s\n", inet_toa(addr)); + + return cluster_process_heartbeat_v2(data, size, more, p, addr); + + default: + log(0,0,0,0, "Strange type packet received on cluster socket (%d)\n", type); + return -1; + } + return 0; +shortpacket: + log(0,0,0,0, "I got an cluster heartbeat packet! This means I'm probably out of sync!!\n"); + return -1; +} + +//==================================================================================================== + +int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + + cli_print(cli, "Cluster status : %s", config->cluster_iam_master ? "Master" : "Slave" ); + cli_print(cli, "My address : %s", inet_toa(my_address)); + cli_print(cli, "VIP address : %s", inet_toa(config->bind_address)); + cli_print(cli, "Multicast address: %s", inet_toa(config->cluster_address)); + cli_print(cli, "Multicast i'face : %s", config->cluster_interface); + + if (!config->cluster_iam_master) { + cli_print(cli, "My master : %s (last heartbeat %.1f seconds old)", + config->cluster_master_address ? inet_toa(config->cluster_master_address) : "Not defined", + 0.1 * (config->current_time - config->cluster_last_hb)); + cli_print(cli, "Uptodate : %s", config->cluster_iam_uptodate ? "Yes" : "No"); + cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number); + cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid); + cli_print(cli, "%d tunnels undefined of %d", config->cluster_undefined_tunnels, config->cluster_highest_tunnelid); + } else { + cli_print(cli, "Next heartbeat # : %d", config->cluster_seq_number); + cli_print(cli, "Highest session : %d", config->cluster_highest_sessionid); + cli_print(cli, "Highest tunnel : %d", config->cluster_highest_tunnelid); + cli_print(cli, "%d changes queued for sending", config->cluster_num_changes); + } + cli_print(cli, "%d peers.", num_peers); + + if (num_peers) + cli_print(cli, "%20s %10s %8s", "Address", "Basetime", "Age"); + for (i = 0; i < num_peers; ++i) { + cli_print(cli, "%20s %10d %8d", inet_toa(peers[i].peer), + peers[i].basetime, config->current_time - peers[i].timestamp); + } + return CLI_OK; +} + +// +// Simple run-length-encoding compression. +// Format is +// 1 byte < 128 = count of non-zero bytes following. // Not legal to be zero. +// n non-zero bytes; +// or +// 1 byte > 128 = (count - 128) run of zero bytes. // +// repeat. +// count == 0 indicates end of compressed stream. +// +// Compress from 'src' into 'dst'. return number of bytes +// used from 'dst'. +// Updates *src_p to indicate 1 past last bytes used. +// +// We could get an extra byte in the zero runs by storing (count-1) +// but I'm playing it safe. +// +// Worst case is a 50% expansion in space required (trying to +// compress { 0x00, 0x01 } * N ) +int rle_compress(u8 ** src_p, int ssize, u8 *dst, int dsize) +{ + int count; + int orig_dsize = dsize; + u8 * x,*src; + src = *src_p; + + while (ssize > 0 && dsize > 2) { + count = 0; + x = dst++; --dsize; // Reserve space for count byte.. + + if (*src) { // Copy a run of non-zero bytes. + while (*src && count < 127 && ssize > 0 && dsize > 1) { // Count number of non-zero bytes. + *dst++ = *src++; + --dsize; --ssize; + ++count; + } + *x = count; // Store number of non-zero bytes. Guarenteed to be non-zero! + + } else { // Compress a run of zero bytes. + while (*src == 0 && count < 127 && ssize > 0) { + ++src; + --ssize; + ++count; + } + *x = count | 0x80 ; + } + } + + *dst++ = 0x0; // Add Stop byte. + --dsize; + + *src_p = src; + return (orig_dsize - dsize); +} + +// +// Decompress the buffer into **p. +// 'psize' is the size of the decompression buffer available. +// +// Returns the number of bytes decompressed. +// +// Decompresses from '*src_p' into 'dst'. +// Return the number of dst bytes used. +// Updates the 'src_p' pointer to point to the +// first un-used byte. +int rle_decompress(u8 ** src_p, int ssize, u8 *dst, int dsize) +{ + int count; + int orig_dsize = dsize; + char * src = *src_p; + + while (ssize >0 && dsize > 0) { // While there's more to decompress, and there's room in the decompress buffer... + count = *src++; --ssize; // get the count byte from the source. + if (count == 0x0) // End marker reached? If so, finish. + break; + + if (count & 0x80) { // Decompress a run of zeros + for (count &= 0x7f ; count > 0 && dsize > 0; --count) { + *dst++ = 0x0; + --dsize; + } + } else { // Copy run of non-zero bytes. + for ( ; count > 0 && ssize && dsize; --count) { // Copy non-zero bytes across. + *dst++ = *src++; + --ssize; --dsize; + } + } + } + *src_p = src; + return (orig_dsize - dsize); +} diff --git a/cluster.h b/cluster.h index 69969eb..9d2e273 100644 --- a/cluster.h +++ b/cluster.h @@ -1,19 +1,85 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: cluster.h,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ -#define C_HELLO 1 -#define C_HELLO_RESPONSE 2 +#ifndef __CLUSTER_H__ +#define __CLUSTER_H__ + + +#define C_HEARTBEAT 1 +#define C_ACK 2 #define C_PING 3 -#define C_TUNNEL 4 -#define C_SESSION 5 +#define C_TUNNEL 4 // Tunnel structure. +#define C_SESSION 5 // Session structure. #define C_GOODBYE 6 +#define C_LASTSEEN 7 // Tell master the last heartbeat that I handled. +#define C_KILL 8 // Tell a slave to die. +#define C_FORWARD 9 // Forwarded packet.. +#define C_BYTES 10 // Update byte counters. +#define C_THROTTLE 11 // A packet for the master to throttle. (The TBF tells direction). +#define C_CSESSION 12 // Compressed session structure. +#define C_CTUNNEL 13 // Compressed tunnel structure. +#define C_GARDEN 14 // Gardened packet + +#define HB_VERSION 2 // Protocol version number.. +#define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!) +#define HB_HISTORY_SIZE 64 // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ) + +#define PING_INTERVAL 5 // 0.5 seconds. Needs to be short to keep session tables fresh. +#define HB_TIMEOUT (15*2*PING_INTERVAL) // 15 seconds without heartbeat triggers an election.. #define CLUSTERPORT 32792 -#define CLUSTERCLIENTPORT 32793 #define UDP 17 #define TIMEOUT 20 #define IL sizeof(int) -int cluster_init(uint32_t bind_address, int server); -int cluster_send_message(unsigned long ip_address, uint32_t vip, char type, void *data, int datalen); -int processcluster(char *buf, int l); +#define CLUSTER_MAX_SIZE 32 // No more than 32 machines in a cluster! + +#define DEFAULT_MCAST_ADDR "239.192.13.13" // Need an assigned number! +#define DEFAULT_MCAST_INTERFACE "eth0" + +typedef struct { + u32 version; // protocol version. + u32 seq; // Sequence number for this heatbeat. + u32 basetime; // What time I started + u32 clusterid; // Id for this cluster? + + u32 highsession; // Id of the highest in-use session. + u32 freesession; // Id of the first free session. + u32 hightunnel; // Id of the highest used tunnel. + u32 size_sess; // Size of the session structure. + + u32 size_tunn; // size of the tunnel structure. + + char reserved[128 - 9*sizeof(u32)]; // Pad out to 128 bytes. +} heartt; + +typedef struct { /* Used to update byte counters on the */ + /* master. */ + u32 sid; + u32 in; + u32 out; +} bytest; + +typedef struct { + u32 addr; // + u32 ver; // version of structure. + u32 undef; // Number of undefined structures. 0 if up-to-date. + u32 basetime; // start time of this peer. +} pingt; + +int cluster_init(); +int processcluster(char *buf, int size, u32 addr); +int cluster_forward_packet(char *buf, int size, u32 addr); +int cluster_send_session(int sid); +int cluster_send_tunnel(int tid); +int master_forward_packet(char * data, int size, u32 addr, int port); +int master_throttle_packet(int tid, char * data, int size); +int master_garden_packet(sessionidt s, char * data, int size); +void master_update_counts(void); + +void cluster_send_ping(time_t basetime); +void cluster_heartbeat(int highsession, int freesession, int hightunnel); +void cluster_check_master(void); +int show_cluster(struct cli_def *cli, char *command, char **argv, int argc); + +#endif /* __CLUSTER_H__ */ diff --git a/cluster_master.c b/cluster_master.c deleted file mode 100644 index fbd4f4c..0000000 --- a/cluster_master.c +++ /dev/null @@ -1,517 +0,0 @@ -// L2TPNS Cluster Master -// $Id: cluster_master.c,v 1.3 2004-05-24 04:12:34 fred_nerk Exp $ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "cluster.h" -#include "ll.h" -#include "util.h" -#include "config.h" - -#define L2TPNS BINDIR "/l2tpns" - -typedef struct -{ - char *hostname; - unsigned long last_message; - uint32_t ip_address; - uint32_t slave_address; - int remove_from_cluster; - int down; - int tunnel_len; - int session_len; - pid_t pid; - - int num_tunnels; - char *tunnels[1000]; - int num_sessions; - char *sessions[13000]; -} slave; - -uint32_t master_address; -linked_list *slaves; -extern int cluster_sockfd; -int debug = 4; - -int processmsg(char *buf, int l, struct sockaddr_in *src_addr); -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); -slave *find_slave(uint32_t address); -#define log _log -void _log(int level, const char *format, ...) __attribute__((format (printf, 2, 3))); -void log_hex(int level, const char *title, const char *data, int maxsize); - -/* Catch our forked processes exiting */ -void sigchild_handler(int signal) -{ - int status; - int pid; - - pid = wait(&status); - /* TODO: catch errors and respawn? */ -} - -int main(int argc, char *argv[]) -{ - slave *s; - char buf[4096]; - struct timeval to; - - if (argc != 2) { - log(0, "Usage: %s
\n", argv[0]); - exit(-1); - } - - master_address = inet_addr(argv[1]); - if (master_address == INADDR_NONE) { - log(0, "Invalid ip %s\n", argv[1]); - exit(-1); - } - - cluster_init(master_address, 1); - slaves = ll_init(); - - signal(SIGCHLD, sigchild_handler); - - log(0, "Cluster Manager $Id: cluster_master.c,v 1.3 2004-05-24 04:12:34 fred_nerk Exp $ starting\n"); - - to.tv_sec = 1; - to.tv_usec = 0; - while (1) - { - fd_set r; - int n; - - FD_ZERO(&r); - FD_SET(cluster_sockfd, &r); - n = select(cluster_sockfd + 1, &r, 0, 0, &to); - if (n < 0) - { - if (errno != EINTR) - { - perror("select"); - exit(-1); - } - continue; - } - else if (n) - { - struct sockaddr_in addr; - int alen = sizeof(addr); - - memset(buf, 0, sizeof(buf)); - if (FD_ISSET(cluster_sockfd, &r)) - processmsg(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); - continue; - } - - // Handle slaves timing out - { - time_t now = time(NULL); - ll_reset(slaves); - while ((s = ll_next(slaves))) - { - if (s->down) continue; - if (s->last_message < (now - TIMEOUT)) - { - log(4, "Slave \"%s\" s->last_message is %lu (timeout is %lu)\n", s->hostname, s->last_message, (now - TIMEOUT)); - if (s->remove_from_cluster) - { - // Remove them from the cluster - ll_delete(slaves, s); - if (s->hostname) free(s->hostname); - free(s); - ll_reset(slaves); - continue; - } - backup_up(s); - } - } - } - - to.tv_sec = 1; - to.tv_usec = 0; - } - - return 0; -} - -int processmsg(char *buf, int l, struct sockaddr_in *src_addr) -{ - slave *s; - char mtype; - uint32_t addr; - - log_hex(4, "Received", buf, l); - if (!buf || l <= sizeof(uint32_t)) return 0; - - addr = ntohl(*(uint32_t*)buf); - buf += sizeof(uint32_t); - l -= sizeof(uint32_t); - - 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: - handle_hello(buf, l, src_addr, addr); - break; - case C_PING: - if (!find_slave(addr)) - handle_hello(buf, l, src_addr, addr); - else - handle_ping(buf, l, addr); - break; - case C_TUNNEL: - if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr); - handle_tunnel(buf, l, addr); - break; - case C_SESSION: - 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; -} - -int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr) -{ - slave *s; - char *hostname; - - hostname = calloc(l + 1, 1); - memcpy(hostname, buf, l); - - // Is this a slave we have state information for? - if ((s = find_slave(addr))) - { - if (src_addr->sin_addr.s_addr == master_address) - { - log(1, "Got hello from \"%s\", local backup for %s.\n", hostname, inet_toa(s->ip_address)); - } - else if (s->down) - { - log(1, "Slave \"%s\" (for %s) has come back.\n", hostname, inet_toa(s->ip_address)); - backup_down(s); - } - else - { - log(1, "Slave \"%s\" said hello and we didn't know it was down.\n", s->hostname); - } - - /* Reset the hostname if needed */ - free(s->hostname); - s->hostname = hostname; - } else { - // No state information, it's a new slave - s = calloc(sizeof(slave), 1); - s->ip_address = addr; - ll_push(slaves, s); - s->hostname = hostname; - log(1, "New slave added to cluster \"%s\"\n", s->hostname); - } - - s->slave_address = src_addr->sin_addr.s_addr; - - // Send state information back - return_state(s); - - s->last_message = time(NULL); - - return 0; -} - -int handle_tunnel(char *buf, int l, uint32_t addr) -{ - int tid; - slave *s; - if (!(s = find_slave(addr))) - { - log(0, "handle_tunnel() called with no valid slave\n"); - return 0; - } - s->last_message = time(NULL); - - // Skip hostname - tid = *(char *)buf; - buf += (tid + 1); - l -= (tid + 1); - - // Grab tunnel ID - tid = *(int *)buf; - buf += sizeof(int); - l -= sizeof(int); - - log(3, "Received tunnel %d from \"%s\" (%d bytes long)\n", tid, s->hostname, l); - - // Allocate memory for it if it's not already - if (!s->tunnels[tid]) - { - s->tunnels[tid] = malloc(l); - s->num_tunnels++; - s->tunnel_len = l; - } - - memcpy(s->tunnels[tid], buf, l); - return l; -} - -int handle_session(char *buf, int l, uint32_t addr) -{ - slave *s; - int sid; - char hostname[4096] = {0}; - if (!(s = find_slave(addr))) - { - log(0, "handle_session() called with no valid slave\n"); - return 0; - } - s->last_message = time(NULL); - - // Skip hostname - sid = *(char *)buf; - memcpy(hostname, (char *)(buf + 1), sid); - buf += (sid + 1); - l -= (sid + 1); - log(0, "Hostname is %s\n", hostname); - - // Grab session ID - sid = *(int *)buf; - buf += sizeof(int); - l -= sizeof(int); - - log(3, "Received session %d from \"%s\" (%d bytes long)\n", sid, s->hostname, l); - - // Allocate memory for it if it's not already - if (!s->sessions[sid]) - { - s->sessions[sid] = malloc(l); - s->num_sessions++; - s->session_len = l; - } - - memcpy(s->sessions[sid], buf, l); - return l; -} - -int handle_ping(char *buf, int l, uint32_t addr) -{ - slave *s; - if (!(s = find_slave(addr))) - { - log(0, "handle_ping() called with no valid slave\n"); - return 0; - } - s->last_message = time(NULL); - - return 0; -} - -int return_state(slave *s) -{ - char *packet; - int i; - int num_tunnels = 0, num_sessions = 0; - int pktlen; - - log(3, "Sending state information to \"%s\"\n", s->hostname); - - for (i = 0; i < 1000; i++) - if (s->tunnels[i]) num_tunnels++; - - for (i = 0; i < 13000; i++) - if (s->sessions[i]) num_sessions++; - - if (!num_sessions && !num_tunnels) return 0; - - packet = calloc(IL * 4, 1); - *(int *)(packet + IL * 0) = num_tunnels; - *(int *)(packet + IL * 1) = num_sessions; - *(int *)(packet + IL * 2) = s->tunnel_len; - *(int *)(packet + IL * 3) = s->session_len; - cluster_send_message(s->slave_address, s->ip_address, C_HELLO_RESPONSE, packet, IL * 4); - free(packet); - - // Send tunnels one-by-one, in order - log(0, "Sending %d tunnels of %d bytes each\n", num_tunnels, s->tunnel_len); - pktlen = s->tunnel_len + sizeof(int); - packet = malloc(pktlen); - for (i = 0; i < 1000; i++) - { - if (s->tunnels[i]) - { - *(int *)packet = i; - memcpy((char *)(packet + sizeof(int)), s->tunnels[i], s->tunnel_len); - log(0, "Sending tunnel %d\n", i); - cluster_send_message(s->slave_address, s->ip_address, C_TUNNEL, packet, pktlen); - } - } - free(packet); - - // Send sessions one-by-one, in order - log(0, "Sending %d sessions of %d bytes each\n", num_sessions, s->session_len); - pktlen = s->session_len + sizeof(int); - packet = malloc(pktlen); - for (i = 0; i < 13000; i++) - { - if (s->sessions[i]) - { - *(int *)packet = i; - memcpy((char *)(packet + sizeof(int)), s->sessions[i], s->session_len); - log(0, "Sending session %d\n", i); - cluster_send_message(s->slave_address, s->ip_address, C_SESSION, packet, pktlen); - } - } - free(packet); - - return 0; -} - -slave *find_slave(uint32_t address) -{ - slave *s; - - ll_reset(slaves); - while ((s = ll_next(slaves))) - { - if (s->ip_address == address) - { - return s; - } - } - return NULL; -} - -void _log(int level, const char *format, ...) -{ - va_list ap; - if (debug < level) return; - - va_start(ap, format); - vfprintf(stderr, format, ap); -} - -void log_hex(int level, const char *title, const char *data, int maxsize) -{ - unsigned int i, j; - unsigned const char *d = (unsigned const char *)data; - - if (debug < level) return; - log(level, "%s (%d bytes):\n", title, maxsize); - setvbuf(stderr, NULL, _IOFBF, 16384); - for (i = 0; i < maxsize; ) - { - fprintf(stderr, "%4X: ", i); - for (j = i; j < maxsize && j < (i + 16); j++) - { - fprintf(stderr, "%02X ", d[j]); - if (j == i + 7) - fputs(": ", stderr); - } - - for (; j < i + 16; j++) - { - fputs(" ", stderr); - if (j == i + 7) - fputs(": ", stderr); - } - - fputs(" ", stderr); - for (j = i; j < maxsize && j < (i + 16); j++) - { - if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20) - fputc(d[j], stderr); - else - fputc('.', stderr); - - if (j == i + 7) - fputs(" ", stderr); - } - - i = j; - fputs("\n", stderr); - } - fflush(stderr); - setbuf(stderr, NULL); -} - -int backup_up(slave *s) -{ - log(2, "Becoming backup for \"%s\" (%s).\n", s->hostname, inet_toa(s->ip_address)); - s->pid = fork(); - if (!s->pid) - { - if (execl(L2TPNS, L2TPNS, "-a", inet_toa(s->ip_address), NULL) < 0) - log(0, "Error execing backup " L2TPNS ": %s\n", strerror(errno)); - exit(0); - } - s->down = 1; - return 0; -} - -int backup_down(slave *s) -{ - log(2, "Not being backup for \"%s\" (%s) anymore.\n", s->hostname, inet_toa(s->ip_address)); - s->down = 0; - if (s->pid) { - kill(s->pid, SIGTERM); - sleep(2); - kill(s->pid, SIGKILL); - } - 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 deleted file mode 100644 index 1ed03b0..0000000 --- a/cluster_slave.c +++ /dev/null @@ -1,270 +0,0 @@ -// L2TPNS Cluster Master -// $Id: cluster_slave.c,v 1.4 2004-05-24 04:12:48 fred_nerk Exp $ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "l2tpns.h" -#include "cluster.h" -#include "ll.h" -#include "util.h" - -// vim: sw=4 ts=8 - -extern int cluster_sockfd; -extern char hostname[1000]; -extern ippoolt *ip_address_pool; -extern uint32_t vip_address; -extern struct configt *config; - -int handle_tunnel(char *buf, int l); -int handle_session(char *buf, int l); -int handle_hello_response(char *buf, int l); - -int processcluster(char *buf, int l) -{ - char mtype; - uint32_t addr; - - log_hex(4, "Cluster receive", buf, l); - if (!buf || l <= sizeof(uint32_t)) return 0; - - addr = ntohl(*(uint32_t*)buf); - buf += sizeof(uint32_t); - l -= sizeof(uint32_t); - - if (addr != vip_address) - { - log(0, 0, 0, 0, "Received cluster message for VIP %s, which isn't ours\n", inet_toa(addr)); - } - - mtype = *buf; buf++; l--; - - switch (mtype) - { - case C_HELLO: - break; - case C_HELLO_RESPONSE: - handle_hello_response(buf, l); - break; - case C_PING: - break; - case C_TUNNEL: - handle_tunnel(buf, l); - break; - case C_SESSION: - handle_session(buf, l); - break; - } - return mtype; - - return 0; -} - -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"); - 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); - - if (t > MAXTUNNEL) - { - log(0, 0, 0, t, "Cluster master tried to send tunnel %d, which is bigger than MAXTUNNEL (%d)\n", t, MAXTUNNEL); - return 0; - } - - if (l != sizeof(tunnelt)) - { - log(1, 0, 0, t, "Discarding bogus tunnel message (%d bytes instead of %d).\n", l, sizeof(tunnelt)); - return 0; - } - - memcpy(&tunnel[t], buf, l); - log(3, 0, 0, t, "Cluster master sent tunnel for %s\n", tunnel[t].hostname); - - tunnel[t].controlc = 0; - tunnel[t].controls = NULL; - tunnel[t].controle = NULL; - return 0; -} - -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"); - 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); - - if (s > MAXSESSION) - { - log(0, 0, s, 0, "Cluster master tried to send session %d, which is bigger than MAXSESSION (%d)\n", s, MAXSESSION); - return 0; - } - - if (l != sizeof(sessiont)) - { - log(1, 0, s, 0, "Discarding short session message (%d bytes instead of %d).\n", l, sizeof(sessiont)); - return 0; - } - - if (s > 1) - { - session[s-1].next = session[s].next; - } - - if (sessionfree == s) - { - sessionfree = session[s].next; - } - - memcpy(&session[s], buf, l); - session[s].tbf = 0; - session[s].throttle = 0; - if (session[s].opened) - { - log(2, 0, s, session[s].tunnel, "Cluster master sent active session for user %s\n", session[s].user); - sessionsetup(session[s].tunnel, s, 0); - if (session[s].ip && session[s].ip != 0xFFFFFFFE) - { - int x; - for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++) - { - if (ip_address_pool[x].address == session[s].ip) - { - ip_address_pool[x].assigned = 1; - break; - } - } - } - } - return 0; -} - -int handle_hello_response(char *buf, int l) -{ - int numtunnels, numsessions; - - /* The cluster master has downed the address, so send another garp */ - send_garp(vip_address); - - if (!l) return 0; - - if (l < (4 * IL)) - { - log(1, 0, 0, 0, "Cluster master sent invalid hello response: %d bytes instead of %d\n", l, (4 * IL)); - return 0; - } - numtunnels = *(int *)(buf + IL * 0); - numsessions = *(int *)(buf + IL * 1); - if (numtunnels == 0 && numsessions == 0) - { - log(2, 0, 0, 0, "Cluster master has no state information for us.\n"); - return 0; - } - log(2, 0, 0, 0, "The cluster master will send %d tunnels and %d sessions.\n", numtunnels, numsessions); - return 0; -} - -int cluster_send_session(int s) -{ - char *packet; - int len = 0; - - if (!cluster_sockfd) return 1; - - packet = malloc(4096); - - // Hostname - len = strlen(hostname); - *(char *)packet = len; - memcpy((char *)(packet + 1), hostname, len); - len++; - - // Session ID - *(int *)(packet + len) = s; - len += sizeof(int); - - // Session data - memcpy((char *)(packet + len), &session[s], sizeof(sessiont)); - len += sizeof(sessiont); - - cluster_send_message(config->cluster_address, vip_address, C_SESSION, packet, len); - free(packet); - - return 1; -} - -int cluster_send_tunnel(int t) -{ - char *packet; - int len = 0; - - packet = malloc(4096); - - // Hostname - len = strlen(hostname); - *(char *)packet = len; - memcpy((char *)(packet + 1), hostname, len); - len++; - - // Tunnel ID - *(int *)(packet + len) = t; - len += sizeof(int); - - // Tunnel data - memcpy((char *)(packet + len), &tunnel[t], sizeof(tunnelt)); - len += sizeof(tunnelt); - - 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/config.h b/config.h index b5b3405..bd17966 100644 --- a/config.h +++ b/config.h @@ -1,3 +1,4 @@ #define LIBDIR "/usr/lib/l2tpns" #define ETCDIR "/etc/l2tpns" #define BINDIR "/usr/sbin" +#define DATADIR "/tmp" diff --git a/constants.c b/constants.c index 46258b1..c53a26f 100644 --- a/constants.c +++ b/constants.c @@ -4,7 +4,7 @@ const char *lcp_types[MAX_LCP_TYPE+1] = { "Reserved", "Maximum-Receive-Unit", - "Reserved 2", + "Async-Control-Map", "Authentication-Protocol", "Quality-Protocol", "Magic-Number", diff --git a/constants.h b/constants.h index 3ac8983..e561260 100644 --- a/constants.h +++ b/constants.h @@ -1,5 +1,5 @@ - -// enum these ? +#ifndef __CONSTANTS_H__ +#define __CONSTANTS_H__ #define MAX_LCP_TYPE 8 extern const char *lcp_types[MAX_LCP_TYPE+1]; @@ -25,3 +25,5 @@ extern const char *radius_states[MAX_RADIUS_STATE+1]; #define MAX_L2TP_MESSAGE_TYPE 16 extern const char *l2tp_message_types[MAX_L2TP_MESSAGE_TYPE+1]; + +#endif /* __CONSTANTS_H__ */ diff --git a/control.h b/control.h index 041d3ce..60e0d03 100644 --- a/control.h +++ b/control.h @@ -15,4 +15,4 @@ int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len); void dump_packet(char *packet, FILE *stream); int read_packet(int sockfd, char *packet); -#endif +#endif /* __CONTROL_H__ */ diff --git a/garden.c b/garden.c index c3ce34a..7477089 100644 --- a/garden.c +++ b/garden.c @@ -8,21 +8,28 @@ #include "control.h" int __plugin_api_version = 1; -struct pluginfuncs p; +static struct pluginfuncs *p = 0; -char *init_commands[] = { - // This is for incoming connections to a gardened user - "iptables -t nat -N garden_users 2>&1 >/dev/null", +static int iam_master = 0; // We're all slaves! Slaves I tell you! + +char *up_commands[] = { + "iptables -t nat -N garden >/dev/null 2>&1", // Create a chain that all gardened users will go through + "iptables -t nat -F garden", + ". " PLUGINCONF "/build-garden", // Populate with site-specific DNAT rules + "iptables -t nat -N garden_users >/dev/null 2>&1",// Empty chain, users added/removed by garden_session "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 + "iptables -t nat -A PREROUTING -j garden_users", // DNAT any users on the garden_users chain + NULL, }; -char *done_commands[] = { - "iptables -t nat -F garden_users 2>&1 >/dev/null", - "iptables -t nat -D l2tpns -j garden_users", - NULL +char *down_commands[] = { + "iptables -t nat -F PREROUTING", + "iptables -t nat -F garden_users", + "iptables -t nat -X garden_users", + "iptables -t nat -F garden", + "iptables -t nat -X garden", + "rmmod iptable_nat ip_conntrack", + NULL, }; int garden_session(sessiont *s, int flag); @@ -32,7 +39,7 @@ 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, "Walled Garden allowing login\n"); + p->log(3, 0, 0, 0, "Walled Garden allowing login\n"); data->auth_allowed = 1; data->s->walled_garden = 1; return PLUGIN_RET_OK; @@ -40,13 +47,23 @@ int plugin_post_auth(struct param_post_auth *data) int plugin_new_session(struct param_new_session *data) { - if (data->s->walled_garden) garden_session(data->s, 1); + if (!iam_master) + return PLUGIN_RET_OK; // Slaves don't do walled garden processing. + + if (data->s->walled_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 (!iam_master) + return PLUGIN_RET_OK; // Slaves don't do walled garden processing. + + if (data->s->walled_garden) + garden_session(data->s, 0); + return PLUGIN_RET_OK; } @@ -55,12 +72,21 @@ int plugin_control(struct param_control *data) sessiont *s; sessionidt session; - if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN) return PLUGIN_RET_OK; - if (!data->data && data->data_length) return PLUGIN_RET_OK; + if (!iam_master) // All garden processing happens on the master. + return PLUGIN_RET_OK; + + 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; // Really? + if (!session) + return PLUGIN_RET_OK; + data->send_response = 1; - s = p.get_session_by_id(session); + s = p->get_session_by_id(session); if (!s || !s->ip) { char *errormsg = "Session not connected"; @@ -68,7 +94,7 @@ int plugin_control(struct param_control *data) sprintf((data->response + data->response_length), "%s", errormsg); data->response_length += strlen(errormsg) + 1; - p.log(3, 0, 0, 0, "Unknown session %d\n", session); + p->log(3, 0, 0, 0, "Unknown session %d\n", session); return PLUGIN_RET_STOP; } *(short *)(data->response + 2) = ntohs(PKT_RESP_OK); @@ -80,9 +106,33 @@ int plugin_control(struct param_control *data) sprintf((data->response + data->response_length), "%s", errormsg); data->response_length += strlen(errormsg) + 1; } + return PLUGIN_RET_STOP; } +int plugin_become_master(void) +{ + int i; + iam_master = 1; // We just became the master. Wow! + + for (i = 0; up_commands[i] && *up_commands[i]; i++) + { + p->log(3, 0, 0, 0, "Running %s\n", up_commands[i]); + system(up_commands[i]); + } + + return PLUGIN_RET_OK; +} + +// Called for each active session after becoming master +int plugin_new_session_master(sessiont * s) +{ + if (s->walled_garden) + garden_session(s, 1); + + return PLUGIN_RET_OK; +} + int garden_session(sessiont *s, int flag) { char cmd[2048]; @@ -90,34 +140,11 @@ 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); + p->log(2, 0, 0, s->tunnel, "Garden user %s (%s)\n", s->user, p->inet_toa(htonl(s->ip))); + snprintf(cmd, 2048, "iptables -t nat -A garden_users -s %s -j garden", p->inet_toa(htonl(s->ip))); + p->log(3, 0, 0, s->tunnel, "%s\n", cmd); system(cmd); s->walled_garden = 1; } @@ -127,18 +154,18 @@ int garden_session(sessiont *s, int flag) 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))); + p->log(2, 0, 0, s->tunnel, "Un-Garden user %s (%s)\n", s->user, p->inet_toa(htonl(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 un-gardened"); + 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 released from walled garden"); } /* Clean up counters */ s->cin = s->cout = 0; s->pin = s->pout = 0; - snprintf(cmd, 2048, "iptables -t nat -D garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip))); - p.log(3, 0, 0, s->tunnel, "%s\n", cmd); + snprintf(cmd, 2048, "iptables -t nat -D garden_users -s %s -j garden", p->inet_toa(htonl(s->ip))); + p->log(3, 0, 0, s->tunnel, "%s\n", cmd); while (--count) { int status = system(cmd); @@ -149,8 +176,8 @@ int garden_session(sessiont *s, int flag) if (!s->die) { /* OK, we're up! */ - u16 r = p.radiusnew(p.get_id_by_session(s)); - p.radiussend(r, RADIUSSTART); + u16 r = p->radiusnew(p->get_id_by_session(s)); + p->radiussend(r, RADIUSSTART); } } s->walled_garden = flag; @@ -159,15 +186,32 @@ int garden_session(sessiont *s, int flag) int plugin_init(struct pluginfuncs *funcs) { - int i; + FILE *tables; + int found_nat = 0; - if (!funcs) return 0; - memcpy(&p, funcs, sizeof(p)); + if (!funcs) + return 0; - for (i = 0; init_commands[i] && *init_commands[i]; i++) + p = funcs; + + if ((tables = fopen("/proc/net/ip_tables_names", "r"))) { - p.log(3, 0, 0, 0, "Running %s\n", init_commands[i]); - system(init_commands[i]); + char buf[1024]; + while (fgets(buf, sizeof(buf), tables) && !found_nat) + found_nat = !strcmp(buf, "nat\n"); + + fclose(tables); + } + + /* master killed/crashed? */ + if (found_nat) + { + int i; + for (i = 0; down_commands[i] && *down_commands[i]; i++) + { + p->log(3, 0, 0, 0, "Running %s\n", down_commands[i]); + system(down_commands[i]); + } } return 1; @@ -176,10 +220,14 @@ int plugin_init(struct pluginfuncs *funcs) void plugin_done() { int i; - for (i = 0; done_commands[i] && *done_commands[i]; i++) + + if (!iam_master) // Never became master. nothing to do. + return; + + for (i = 0; down_commands[i] && *down_commands[i]; i++) { - p.log(3, 0, 0, 0, "Running %s\n", done_commands[i]); - system(done_commands[i]); + p->log(3, 0, 0, 0, "Running %s\n", down_commands[i]); + system(down_commands[i]); } } diff --git a/icmp.c b/icmp.c index 0a56740..f7d73c6 100644 --- a/icmp.c +++ b/icmp.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -9,9 +10,8 @@ #include #include #include -#include "l2tpns.h" -extern ipt myip; +#include "l2tpns.h" __u16 _checksum(unsigned char *addr, int count); diff --git a/l2tpns.c b/l2tpns.c index 433cf69..ebf71ad 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -27,13 +26,16 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include +#include + #include "md5.h" #include "l2tpns.h" #include "cluster.h" @@ -42,6 +44,7 @@ #include "constants.h" #include "control.h" #include "util.h" +#include "tbf.h" // Globals struct configt *config = NULL; // all configuration @@ -53,7 +56,6 @@ int *radfds = NULL; // RADIUS requests file handles 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 @@ -61,21 +63,28 @@ int syslog_log = 0; // are we logging to syslog FILE *log_stream = NULL; struct sockaddr_in snoop_addr = {0}; extern int cluster_sockfd; -unsigned long last_sid = 0; +u32 last_sid = 0; int clifd = 0; sessionidt *cli_session_kill = NULL; tunnelidt *cli_tunnel_kill = NULL; 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_size = 0; -time_t time_now; +u32 udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0; +u32 eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0; +u32 ip_pool_size = 1; +time_t time_now = 0; char time_now_string[64] = {0}; char main_quit = 0; char *_program_name = NULL; linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; +#ifdef BGP +#include "bgp.h" +struct bgp_peer *bgp_peers = 0; +struct bgp_route_list *bgp_routes = 0; +int bgp_configured = 0; +#endif /* BGP */ + #define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER) #define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE } @@ -86,20 +95,29 @@ struct config_descriptt config_values[] = { 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("send_garp", send_garp, BOOL), CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG), CONFIG("accounting_dir", accounting_dir, STRING), CONFIG("setuid", target_uid, INT), CONFIG("dump_speed", dump_speed, BOOL), CONFIG("cleanup_interval", cleanup_interval, INT), CONFIG("multi_read_count", multi_read_count, INT), + CONFIG("scheduler_fifo", scheduler_fifo, BOOL), + CONFIG("icmp_rate", icmp_rate, INT), + CONFIG("cluster_address", cluster_address, IP), + CONFIG("cluster_interface", cluster_interface, STRING), +#ifdef BGP + CONFIG("as_number", as_number, SHORT), + CONFIG("bgp_peer1", bgp_peer[0], STRING), + CONFIG("bgp_peer1_as", bgp_peer_as[0], SHORT), + CONFIG("bgp_peer2", bgp_peer[1], STRING), + CONFIG("bgp_peer2_as", bgp_peer_as[1], SHORT), +#endif /* BGP */ { NULL, 0, 0, 0 }, }; @@ -114,11 +132,15 @@ char *plugin_functions[] = { "plugin_kill_session", "plugin_control", "plugin_radius_response", + "plugin_become_master", + "plugin_new_session_master", }; + #define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) tunnelt *tunnel = NULL; // 1000 * 45 = 45000 = 45k sessiont *session = NULL; // 5000 * 213 = 1065000 = 1 Mb +sessioncountt *sess_count = NULL; radiust *radius = NULL; ippoolt *ip_address_pool = NULL; controlt *controlfree = 0; @@ -126,7 +148,6 @@ struct Tstats *_statistics = NULL; #ifdef RINGBUFFER struct Tringbuffer *ringbuffer = NULL; #endif -tbft *filter_buckets = NULL; void sigalrm_handler(int); void sighup_handler(int); @@ -140,6 +161,9 @@ void tunnel_clean(); tunnelidt new_tunnel(); void update_config(); +static void cache_ipmap(ipt ip, int s); +static void uncache_ipmap(ipt ip); + // return internal time (10ths since run) clockt now(void) { @@ -157,8 +181,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}; + static char message[65536] = {0}; + static char message2[65536] = {0}; va_list ap; #ifdef RINGBUFFER @@ -187,7 +211,7 @@ void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, { vsnprintf(message2, 65535, format, ap); snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2); - fprintf(log_stream, message); + fprintf(log_stream, "%s", message); } else if (syslog_log) { @@ -201,7 +225,7 @@ 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) { int i, j; - unsigned const char *d = (unsigned const char *)data; + const u8 *d = (const u8 *)data; if (config->debug < level) return; @@ -251,9 +275,22 @@ void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *tit // Add a route -void routeset(ipt ip, ipt mask, ipt gw, u8 add) +// +// This adds it to the routing table, advertises it +// via iBGP if enabled, and stuffs it into the +// 'sessionbyip' cache. +// +// 'ip' and 'mask' must be in _host_ order. +// +void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) { struct rtentry r; + int i; + + if (!mask) mask = 0xffffffff; + + ip = ip & mask; // Force the ip to be the first one in the route. + memset(&r, 0, sizeof(r)); r.rt_dev = config->tapdevice; r.rt_dst.sa_family = AF_INET; @@ -261,14 +298,44 @@ void routeset(ipt ip, ipt mask, ipt gw, u8 add) 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 ? mask : 0xFFFFFFF); + *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask); r.rt_flags = (RTF_UP | RTF_STATIC); if (gw) r.rt_flags |= RTF_GATEWAY; - else + else if (mask == 0xffffffff) r.rt_flags |= RTF_HOST; - if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) perror("routeset"); - log(1, ip, 0, 0, "Route %s %u.%u.%u.%u/%u.%u.%u.%u %u.%u.%u.%u\n", add ? "Add" : "Del", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, mask >> 24, mask >> 16 & 255, mask >> 8 & 255, mask & 255, gw >> 24, gw >> 16 & 255, gw >> 8 & 255, gw & 255); + if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) + log(0, 0, 0, 0, "routeset() error in ioctl: %s\n", strerror(errno)); + + log(1, ip, 0, 0, "Route %s %u.%u.%u.%u/%u.%u.%u.%u %u.%u.%u.%u\n", + add ? "add" : "del", + ip >> 24, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff, + mask >> 24, mask >> 16 & 0xff, mask >> 8 & 0xff, mask & 0xff, + gw >> 24, gw >> 16 & 0xff, gw >> 8 & 0xff, gw & 0xff); + +#ifdef BGP + if (add) + bgp_add_route(htonl(ip), htonl(mask)); + else + bgp_del_route(htonl(ip), htonl(mask)); +#endif /* BGP */ + + // Add/Remove the IPs to the 'sessionbyip' cache. + // Note that we add the zero address in the case of + // a network route. Roll on CIDR. + + // Note that 's == 0' implies this is the address pool. + // We still cache it here, because it will pre-fill + // the malloc'ed tree. + + if (s) + { + if (!add) // Are we deleting a route? + s = 0; // Caching the session as '0' is the same as uncaching. + + for (i = ip; (i&mask) == (ip&mask) ; ++i) + cache_ipmap(i, s); + } } // Set up TAP interface @@ -283,7 +350,7 @@ void inittap(void) if (tapfd < 0) { // fatal log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); - exit(-1); + exit(1); } { int flags = fcntl(tapfd, F_GETFL, 0); @@ -292,7 +359,7 @@ void inittap(void) if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) { log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); - exit(-1); + exit(1); } assert(strlen(ifr.ifr_name) < sizeof(config->tapdevice)); strncpy(config->tapdevice, ifr.ifr_name, sizeof(config->tapdevice) - 1); @@ -304,32 +371,32 @@ void inittap(void) if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) { - perror("set tap addr"); - exit( -1); + log(0, 0, 0, 0, "Error setting tun address: %s\n", strerror(errno)); + exit(1); } /* Bump up the qlen to deal with bursts from the network */ ifr.ifr_qlen = 1000; if (ioctl(ifrfd, SIOCSIFTXQLEN, (void *) &ifr) < 0) { - perror("set tap qlen"); - exit( -1); + log(0, 0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno)); + exit(1); } ifr.ifr_flags = IFF_UP; if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) { - perror("set tap flags"); - exit( -1); + log(0, 0, 0, 0, "Error setting tun flags: %s\n", strerror(errno)); + exit(1); } if (ioctl(ifrfd, SIOCGIFHWADDR, (void *) &ifr) < 0) { - perror("get tap hwaddr"); - exit( -1); + log(0, 0, 0, 0, "Error setting tun hardware address: %s\n", strerror(errno)); + exit(1); } memcpy(&tapmac, 2 + (u8 *) & ifr.ifr_hwaddr, 6); if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) { - perror("get tap ifindex"); - exit( -1); + log(0, 0, 0, 0, "Error setting tun ifindex: %s\n", strerror(errno)); + exit(1); } tapidx = ifr.ifr_ifindex; } @@ -353,10 +420,11 @@ void initudp(void) } if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) { - perror("udp bind"); - exit( -1); + log(0, 0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); + exit(1); } snoopfd = socket(AF_INET, SOCK_DGRAM, UDP); + snoop_addr.sin_family = AF_INET; // Control memset(&addr, 0, sizeof(addr)); @@ -366,35 +434,62 @@ void initudp(void) setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) { - perror("bind"); - exit(-1); + log(0, 0, 0, 0, "Error in control bind: %s\n", strerror(errno)); + exit(1); } } -// Find session by IP, 0 for not found -sessionidt sessionbyip(ipt ip) -{ - unsigned char *a = (unsigned char *)&ip; - char **d = (char **) ip_hash; - sessionidt s; +// +// Find session by IP, < 1 for not found +// +// Confusingly enough, this 'ip' must be +// in _network_ order. This being the common +// case when looking it up from IP packet headers. +// +// We actually use this cache for two things. +// #1. For used IP addresses, this maps to the +// session ID that it's used by. +// #2. For un-used IP addresses, this maps to the +// index into the pool table that contains that +// IP address. +// -#ifdef STAT_CALLS - STAT(call_sessionbyip); -#endif +int lookup_ipmap(ipt ip) +{ + u8 *a = (u8 *)&ip; + char **d = (char **) ip_hash; + int s; if (!(d = (char **) d[(size_t) *a++])) return 0; if (!(d = (char **) d[(size_t) *a++])) return 0; if (!(d = (char **) d[(size_t) *a++])) return 0; s = (ipt) d[(size_t) *a]; - if (s && session[s].tunnel) + return s; +} + +sessionidt sessionbyip(ipt ip) +{ + int s = lookup_ipmap(ip); + +#ifdef STAT_CALLS + STAT(call_sessionbyip); +#endif + if (s > 0 && s < MAXSESSION && session[s].tunnel) return s; return 0; } -void cache_sessionid(ipt ip, sessionidt s) +// +// Take an IP address in HOST byte order and +// add it to the sessionid by IP cache. +// +// (It's actually cached in network order) +// +static void cache_ipmap(ipt ip, int s) { - unsigned char *a = (unsigned char *) &ip; + ipt nip = htonl(ip); // MUST be in network order. I.e. MSB must in be ((char*)(&ip))[0] + u8 *a = (u8 *) &nip; char **d = (char **) ip_hash; int i; @@ -409,44 +504,81 @@ void cache_sessionid(ipt ip, sessionidt s) d = (char **) d[(size_t) a[i]]; } - log(4, ip, s, session[s].tunnel, "Caching session ID %d for ip address\n", s); d[(size_t) a[3]] = (char *)((int)s); + + if (s > 0) + log(4, ip, s, session[s].tunnel, "Caching ip address %s\n", inet_toa(nip)); + else if (s == 0) + log(4, ip, 0, 0, "Un-caching ip address %s\n", inet_toa(nip)); + // else a map to an ip pool index. } -void uncache_sessionid(ipt ip) +static void uncache_ipmap(ipt ip) { - unsigned char *a = (unsigned char *) &ip; - char **d = (char **) ip_hash; - int i; - - for (i = 0; i < 3; i++) - { - if (!d[(size_t) a[i]]) return; - d = (char **) d[(size_t) a[i]]; - } - d[(size_t) a[3]] = NULL; + cache_ipmap(ip, 0); // Assign it to the NULL session. } +// +// CLI list to dump current ipcache. +// +int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) +{ + char **d = (char **) ip_hash, **e, **f, **g; + int i, j, k, l; + int count = 0; + + cli_print(cli, "%7s %s", "Sess#", "IP Address"); + + for (i = 0; i < 256; ++i) { + if (!d[i]) continue; + e = (char**) d[i]; + for (j = 0; j < 256; ++j) { + if (!e[j]) continue; + f = (char**) e[j]; + for (k = 0; k < 256; ++k) { + if (!f[k]) continue; + g = (char**)f[k]; + for (l = 0; l < 256; ++l) { + if (!g[l]) continue; + cli_print(cli, "%7d %d.%d.%d.%d", (int) g[l], i, j, k, l); + ++count; + } + } + } + } + cli_print(cli, "%d entries in cache", count); + return CLI_OK; +} + + // Find session by username, 0 for not found // walled garden users aren't authenticated, so the username is // reasonably useless. Ignore them to avoid incorrect actions +// +// This is VERY inefficent. Don't call it often. :) +// sessionidt sessionbyuser(char *username) { int s; #ifdef STAT_CALLS STAT(call_sessionbyuser); #endif - for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++); - if (s < MAXSESSION) - return s; - return 0; + for (s = 1; s < MAXSESSION ; ++s) { + if (session[s].walled_garden) + continue; // Skip walled garden users. + + if (!strncmp(session[s].user, username, 128)) + return s; + + } + return 0; // Not found. } void send_garp(ipt ip) { int s; struct ifreq ifr; - unsigned char mac[6]; + u8 mac[6]; s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) @@ -487,20 +619,6 @@ sessionidt sessionidtbysessiont(sessiont *s) return val; } -// send gratuitous ARP to set ARP table for newly allocated IP -void sessionsendarp(sessionidt s) -{ - unsigned char mac[6]; -#ifdef STAT_CALLS - STAT(call_sendarp); -#endif - *(u16 *) (mac + 0) = htons(tapmac[0]); // set source address - *(u16 *) (mac + 2) = htons(tapmac[1]); - *(u16 *) (mac + 4) = htons(tapmac[2]); - sendarp(tapidx, mac, session[s].ip); - STAT(arp_sent); -} - // Handle ARP requests void processarp(u8 * buf, int len) { @@ -553,27 +671,27 @@ void processarp(u8 * buf, int len) STAT(arp_errors); return; } - ip = ntohl(*(u32 *) (buf + 42)); + ip = *(u32 *) (buf + 42); // look up session - s = sessionbyip(htonl(ip)); + s = sessionbyip(ip); if (s) { - log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255); + log(3, ntohl(ip), s, session[s].tunnel, "ARP reply for %s\n", inet_toa(ip)); memcpy(buf + 4, buf + 10, 6); // set destination as source - *(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address + *(u16 *) (buf + 10) = htons(tapmac[0]); // set source address *(u16 *) (buf + 12) = htons(tapmac[1]); *(u16 *) (buf + 14) = htons(tapmac[2]); *(u16 *) (buf + 24) = htons(0x0002); // ARP reply memcpy(buf + 26, buf + 10, 6); // sender ethernet memcpy(buf + 36, buf + 4, 6); // target ethernet *(u32 *) (buf + 42) = *(u32 *) (buf + 32); // target IP - *(u32 *) (buf + 32) = htonl(ip); // sender IP + *(u32 *) (buf + 32) = ip; // sender IP write(tapfd, buf, len); STAT(arp_replies); } else { - log(3, ip, 0, 0, "ARP request for unknown IP %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255); + log(3, ntohl(ip), 0, 0, "ARP request for unknown IP %s\n", inet_toa(ip)); STAT(arp_discarded); } } @@ -636,14 +754,28 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t) INC_STAT(tunnel_tx_bytes, l); } +// +// Tiny helper function to write data to +// the 'tun' device. +// +int tun_write(u8 * data, int size) +{ + return write(tapfd, data, size); +} + // process outgoing (to tunnel) IP +// void processipout(u8 * buf, int len) { sessionidt s; sessiont *sp; tunnelidt t; ipt ip; - u8 b[MAXETHER]; + + char * data = buf; // Keep a copy of the originals. + int size = len; + + u8 b[MAXETHER + 20]; #ifdef STAT_CALLS STAT(call_processipout); #endif @@ -653,6 +785,12 @@ void processipout(u8 * buf, int len) STAT(tunnel_tx_errors); return; } + if (len >= MAXETHER) + { + log(1, 0, 0, 0, "Oversize IP packet %d bytes\n", len); + STAT(tunnel_tx_errors); + return; + } // Skip the tun header buf += 4; @@ -668,27 +806,112 @@ void processipout(u8 * buf, int len) ip = *(u32 *)(buf + 16); if (!(s = sessionbyip(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); + // Is this a packet for a session that doesn't exist? + static int rate = 0; // Number of ICMP packets we've sent this second. + static int last = 0; // Last time we reset the ICMP packet counter 'rate'. + + if (last != time_now) { + last = time_now; + rate = 0; + } + + if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second. + { + 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; sp = &session[s]; - // Snooping this session, send it to ASIO - if (sp->snoop) snoop_send_packet(buf, len); + if (sp->tbf_out) + { + // Are we throttling this session? + if (config->cluster_iam_master) + tbf_queue_packet(sp->tbf_out, data, size); + else + master_throttle_packet(sp->tbf_out, data, size); + return; + } + else if (sp->walled_garden && !config->cluster_iam_master) + { + // We are walled-gardening this + master_garden_packet(s, data, size); + return; + } + + // Snooping this session, send it to intercept box + if (sp->snoop_ip && sp->snoop_port) + snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); // Add on L2TP header { - u8 *p = makeppp(b, buf, len, t, s, PPPIP); + u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); + if (!p) { + log(3, session[s].ip, s, t, "failed to send packet in processipout.\n"); + return; + } tunnelsend(b, len + (p-b), t); // send it... - sp->cout += len; // byte count - sp->total_cout += len; // byte count - sp->pout++; - udp_tx += len; } + + sp->cout += len; // byte count + sp->total_cout += len; // byte count + sp->pout++; + udp_tx += len; + sess_count[s].cout += len; // To send to master.. +} + +// +// Helper routine for the TBF filters. +// Used to send queued data in to the user! +// +void send_ipout(sessionidt s, u8 *buf, int len) +{ + sessiont *sp; + tunnelidt t; + ipt ip; + + u8 b[MAXETHER + 20]; + + if (len < 0 || len > MAXETHER) { + log(1,0,0,0, "Odd size IP packet: %d bytes\n", len); + return; + } + + // Skip the tun header + buf += 4; + len -= 4; + + ip = *(u32 *)(buf + 16); + + if (!session[s].ip) + return; + t = session[s].tunnel; + sp = &session[s]; + + log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + + // Snooping this session, send it to ASIO + if (sp->snoop_ip && sp->snoop_port) + snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); + + // Add on L2TP header + { + u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); + if (!p) { + log(3, session[s].ip, s, t, "failed to send packet in send_ipout.\n"); + return; + } + tunnelsend(b, len + (p-b), t); // send it... + } + sp->cout += len; // byte count + sp->total_cout += len; // byte count + sp->pout++; + udp_tx += len; + sess_count[s].cout += len; // To send to master.. } // add an AVP (16 bit) @@ -755,11 +978,13 @@ controlt *controlnew(u16 mtype) } // send zero block if nothing is waiting +// (ZLB send). void controlnull(tunnelidt t) { u8 buf[12]; - if (tunnel[t].controlc) + if (tunnel[t].controlc) // Messages queued; They will carry the ack. return; + *(u16 *) (buf + 0) = htons(0xC802); // flags/ver *(u16 *) (buf + 2) = htons(12); // length *(u16 *) (buf + 4) = htons(tunnel[t].far); // tunnel @@ -792,6 +1017,61 @@ void controladd(controlt * c, tunnelidt t, sessionidt s) } } +// +// Throttle or Unthrottle a session +// +// Throttle the data folling through a session +// to be no more than 'throttle' kbit/sec each way. +// +int throttle_session(sessionidt s, int throttle) +{ + if (!session[s].tunnel) + return 0; // No-one home. + + if (!*session[s].user) + return 0; // User not logged in + + if (throttle) { + if (session[s].tbf_in || session[s].tbf_out) { + if (throttle == session[s].throttle) + return 1; + + // Currently throttled but the rate is changing. + + free_tbf(session[s].tbf_in); + free_tbf(session[s].tbf_out); + } + + session[s].tbf_in = new_tbf(s, throttle*1024/4, throttle*1024/8, send_ipin); + session[s].tbf_out = new_tbf(s, throttle*1024/4, throttle*1024/8, send_ipout); + + if (throttle != session[s].throttle) { // Changed. Flood to slaves. + session[s].throttle = throttle; + cluster_send_session(s); + } + + return 1; + } + + // else Unthrottling. + + if (!session[s].tbf_in && !session[s].tbf_out && !session[s].throttle) + return 0; + + free_tbf(session[s].tbf_in); + session[s].tbf_in = 0; + + free_tbf(session[s].tbf_out); + session[s].tbf_out = 0; + + if (throttle != session[s].throttle) { // Changed. Flood to slaves. + session[s].throttle = throttle; + cluster_send_session(s); + } + + return 0; +} + // start tidy shutdown of session void sessionshutdown(sessionidt s, char *reason) { @@ -840,21 +1120,23 @@ void sessionshutdown(sessionidt s, char *reason) if (session[s].ip) { // IP allocated, clear and unroute - u16 r; - if (session[s].route[0].ip) + int r; + for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) { - routeset(session[s].ip, 0, 0, 0); - for (r = 0; r < MAXROUTE; r++) - { - if (session[s].route[r].ip) - { - routeset(session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 0); - session[s].route[r].ip = 0; - } - } + routeset(s, session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 0); + session[s].route[r].ip = 0; } - if (session[s].throttle) throttle_session(s, 0); session[s].throttle = 0; - free_ip_address(s); + + if (session[s].ip_pool_index == -1) // static ip + { + routeset(s, session[s].ip, 0, 0, 0); // Delete route. + session[s].ip = 0; + } + else + free_ip_address(s); + + if (session[s].throttle) // Unthrottle if throttled. + throttle_session(s, 0); } { // Send CDN controlt *c = controlnew(14); // sending CDN @@ -886,14 +1168,21 @@ void sendipcp(tunnelidt t, sessionidt s) sessionshutdown(s, "No reply on IPCP"); return; } - q = makeppp(buf, 0, 0, t, s, PPPIPCP); + + q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPCP); + if (!q) { + log(3, session[s].ip, s, t, "failed to send packet in sendipcp.\n"); + return; + } + *q = ConfigReq; q[1] = r << RADIUS_SHIFT; // ID, dont care, we only send one type of request *(u16 *) (q + 2) = htons(10); q[4] = 3; q[5] = 6; - *(u32 *) (q + 6) = htonl(myip ? myip : session[s].ip); // send my IP (use theirs if I dont have one) + *(u32 *) (q + 6) = config->bind_address; // send my IP tunnelsend(buf, 10 + (q - buf), t); // send it + session[s].flags &= ~SF_IPCP_ACKED; // Clear flag. } // kill a session now @@ -905,8 +1194,12 @@ void sessionkill(sessionidt s, char *reason) sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed - log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason); + log(2, 0, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason); + + throttle_session(s, 0); // Force session to be un-throttle. Free'ing TBF structures. + memset(&session[s], 0, sizeof(session[s])); + session[s].tunnel = T_FREE; // Mark it as free. session[s].next = sessionfree; sessionfree = s; cluster_send_session(s); @@ -1025,12 +1318,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) STAT(tunnel_rx_errors); return; } - if (s && !session[s].tunnel) - { - log(1, ntohl(addr->sin_addr.s_addr), s, t, "UDP packet contains session %d but no session[%d].tunnel exists (LAC said tunnel = %d). Dropping packet.\n", s, s, t); - STAT(tunnel_rx_errors); - return; - } if (*buf & 0x08) { // ns/nr ns = ntohs(*(u16 *) p); @@ -1063,17 +1350,39 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) int requestchap = 0; // do we request PAP instead of original CHAP request? char called[MAXTEL] = ""; // called number char calling[MAXTEL] = ""; // calling number + + if (!config->cluster_iam_master) { + master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); + return; + } + if ((*buf & 0xCA) != 0xC8) { log(1, ntohl(addr->sin_addr.s_addr), s, t, "Bad control header %02X\n", *buf); STAT(tunnel_rx_errors); return; } - log(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): %d ns %d nr %d ns %d nr %d\n", + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n", l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); // if no tunnel specified, assign one if (!t) { + int i; + + // + // Is this a duplicate of the first packet? (SCCRQ) + // + for ( i = 1; i <= config->cluster_highest_tunnelid ; ++i) { + if (tunnel[t].state != TUNNELOPENING || + tunnel[t].ip != ntohl(*(ipt *) & addr->sin_addr) || + tunnel[t].port != ntohs(addr->sin_port) ) + continue; + t = i; + break; + } + } + + if (!t) { if (!(t = new_tunnel())) { log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "No more tunnels\n"); @@ -1094,29 +1403,36 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // 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 - while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) - { - controlt *c = tunnel[t].controls; - tunnel[t].controls = c->next; - tunnel[t].controlc--; - c->next = controlfree; - controlfree = c; - skip--; - tunnel[t].try = 0; // we have progress - } + // some to clear maybe? + while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000)) + { + controlt *c = tunnel[t].controls; + tunnel[t].controls = c->next; + tunnel[t].controlc--; + c->next = controlfree; + controlfree = c; + skip--; + tunnel[t].try = 0; // we have progress } - if (tunnel[t].nr < ns && tunnel[t].nr != 0) + + // If the 'ns' just received is not the 'nr' we're + // expecting, just send an ack and drop it. + // + // if 'ns' is less, then we got a retransmitted packet. + // if 'ns' is greater than missed a packet. Either way + // we should ignore it. + if (ns != tunnel[t].nr) { // is this the sequence we were expecting? - log(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d not %d)\n", t, ns, tunnel[t].nr); + log(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n", t, ns, tunnel[t].nr); STAT(tunnel_rx_errors); -// controlnull(t); + + if (l) // Is this not a ZLB? + controlnull(t); return; } // receiver advance (do here so quoted correctly in any sends below) - if (l) tunnel[t].nr++; + if (l) tunnel[t].nr = (ns + 1); if (skip < 0) skip = 0; if (skip < tunnel[t].controlc) { @@ -1323,7 +1639,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) memcpy(tmp, b, (n >= 30) ? 30 : n); session[s].tx_connect_speed = atol(tmp); } - log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%lu>\n", + log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%u>\n", session[s].tx_connect_speed); break; case 38: // rx connect speed @@ -1338,7 +1654,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) memcpy(tmp, b, (n >= 30) ? 30 : n); session[s].rx_connect_speed = atol(tmp); } - log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%lu>\n", + log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%u>\n", session[s].rx_connect_speed); break; case 25: // Physical Channel ID @@ -1493,10 +1809,14 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) sessionfree = session[s].next; memset(&session[s], 0, sizeof(session[s])); + if (s > config->cluster_highest_sessionid) + config->cluster_highest_sessionid = s; + // make a RADIUS session if (!(r = radiusnew(s))) { log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); +// sessionkill(s, "no free RADIUS sesions"); return; } @@ -1526,7 +1846,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) break; case 12: // ICCN session[s].magic = amagic; // set magic number - session[s].flags = aflags; // set flags received + session[s].l2tp_flags = aflags; // set flags received log(3, ntohl(addr->sin_addr.s_addr), s, t, "Magic %X Flags %X\n", amagic, aflags); controlnull(t); // ack // In CHAP state, request PAP instead @@ -1561,11 +1881,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) u16 prot; log_hex(5, "Receive Tunnel Data", p, l); - if (session[s].die) - { - log(3, ntohl(addr->sin_addr.s_addr), s, t, "Session %d is closing. Don't process PPP packets\n", s); - return; // closing session, PPP not processed - } if (l > 2 && p[0] == 0xFF && p[1] == 0x03) { // HDLC address header, discard p += 2; @@ -1588,34 +1903,83 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) p += 2; l -= 2; } + + if (s && !session[s].tunnel) // Is something wrong?? + { + if (!config->cluster_iam_master) + { + // Pass it off to the master to deal with.. + master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); + return; + } + + + log(1, ntohl(addr->sin_addr.s_addr), s, t, "UDP packet contains session %d " + "but no session[%d].tunnel exists (LAC said" + " tunnel = %d). Dropping packet.\n", s, s, t); + STAT(tunnel_rx_errors); + return; + } + + if (session[s].die) + { + log(3, ntohl(addr->sin_addr.s_addr), s, t, "Session %d is closing. Don't process PPP packets\n", s); +// I'm pretty sure this isn't right -- mo. +// return; // closing session, PPP not processed + } if (prot == PPPPAP) { session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processpap(t, s, p, l); } else if (prot == PPPCHAP) { session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processchap(t, s, p, l); } else if (prot == PPPLCP) { session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processlcp(t, s, p, l); } else if (prot == PPPIPCP) { session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processipcp(t, s, p, l); } else if (prot == PPPCCP) { session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processccp(t, s, p, l); } else if (prot == PPPIP) { - session[s].last_packet = time_now; + if (!config->cluster_iam_master) + { + // We're a slave. Should we forward this packet to the master? + + // Is this a walled garden session, or something that needs it's + // idle time updated?? + + // Maintain the idle timeouts on the master. If this would + // significantly reset the idletimeout, run it via the master + // to refresh the master's idle timer. + // Not sure this is ideal: It may re-order packets. + + if (session[s].walled_garden || (session[s].last_packet + (ECHO_TIMEOUT/2)) < time_now) + { + master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); + session[s].last_packet = time_now; + return; + } + // fall through to processipin. + } else + session[s].last_packet = time_now; processipin(t, s, p, l); } else @@ -1647,6 +2011,203 @@ void processtap(u8 * buf, int len) processarp(buf, len); else if (*(u16 *) (buf + 2) == htons(PKTIP)) // IP processipout(buf, len); + // Else discard. +} + +// +// Maximum number of actions to complete. +// This is to avoid sending out too many packets +// at once. +#define MAX_ACTIONS 500 + +int regular_cleanups(void) +{ + static sessionidt s = 0; // Next session to check for actions on. + tunnelidt t; + int count=0,i; + u16 r; + static clockt next_acct = 0; + + log(3, 0, 0, 0, "Begin regular cleanup\n"); + + for (r = 1; r < MAXRADIUS; r++) + { + if (!radius[r].state) + continue; + if (radius[r].retry) + { + if (radius[r].retry <= config->current_time) + radiusretry(r); + } else + radius[r].retry = backoff(radius[r].try+1); // Is this really needed? --mo + } + for (t = 1; t < config->cluster_highest_tunnelid; t++) + { + // check for expired tunnels + if (tunnel[t].die && tunnel[t].die <= config->current_time) + { + STAT(tunnel_timeout); + tunnelkill(t, "Expired"); + continue; + } + // check for message resend + if (tunnel[t].retry && tunnel[t].controlc) + { + // resend pending messages as timeout on reply + if (tunnel[t].retry <= config->current_time) + { + controlt *c = tunnel[t].controls; + u8 w = tunnel[t].window; + tunnel[t].try++; // another try + if (tunnel[t].try > 5) + tunnelkill(t, "Timeout on control message"); // game over + else + while (c && w--) + { + tunnelsend(c->buf, c->length, t); + c = c->next; + } + } + } + // Send hello + if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < config->current_time + 600) + { + controlt *c = controlnew(6); // sending HELLO + controladd(c, t, 0); // send the message + log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n"); + } + } + + // 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++) + { + log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); + sessionshutdown(cli_session_kill[i], "Requested by administrator"); + cli_session_kill[i] = 0; + } + } + // Check for tunnels that have been killed from the CLI + if (cli_tunnel_kill[0]) + { + int 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 administrator"); + cli_tunnel_kill[i] = 0; + } + } + + count = 0; + for (i = 1; i < config->cluster_highest_sessionid; i++) + { + + s++; + if (s >= config->cluster_highest_sessionid) + s = 1; + + if (!session[s].tunnel) // Session isn't in use + continue; + + if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED) ) + { + // IPCP has not completed yet. Resend + log(3, session[s].ip, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n"); + sendipcp(session[s].tunnel, s); + } + + // check for expired sessions + if (session[s].die && session[s].die <= config->current_time) + { + sessionkill(s, "Expired"); + if (++count >= MAX_ACTIONS) break; + continue; + } + + // Drop sessions who have not responded within IDLE_TIMEOUT seconds + if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) + { + sessionkill(s, "No response to LCP ECHO requests"); + STAT(session_timeout); + if (++count >= MAX_ACTIONS) break; + continue; + } + + // No data in IDLE_TIMEOUT seconds, send LCP ECHO + if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) + { + u8 b[MAXCONTROL] = {0}; + + u8 *q = makeppp(b, sizeof(b), 0, 0, session[s].tunnel, s, PPPLCP); + if (!q) { + log(3, session[s].ip, s, t, "failed to send ECHO packet.\n"); + continue; + } + + *q = EchoReq; + *(u8 *)(q + 1) = (time_now % 255); // ID + *(u16 *)(q + 2) = htons(8); // Length + *(u32 *)(q + 4) = 0; // Magic Number (not supported) + + log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", + (int)(time_now - session[s].last_packet)); + tunnelsend(b, 24, session[s].tunnel); // send it + if (++count >= MAX_ACTIONS) break; + continue; + } + } + if (config->accounting_dir && next_acct <= config->current_time) + { + // Dump accounting data + next_acct = config->current_time + ACCT_TIME; + dump_acct_info(); + } + + if (count >= MAX_ACTIONS) + return 1; // Didn't finish! + + log(3, 0, 0, 0, "End regular cleanup (%d actions), next in %d seconds\n", count, config->cleanup_interval); + return 0; +} + + +// +// Are we in the middle of a tunnel update, or radius +// requests?? +// +int still_busy(void) +{ + int i; + static int last_talked = 0; + for (i = config->cluster_highest_tunnelid ; i > 0 ; --i) { + if (!tunnel[i].controlc) + continue; + + if (last_talked != config->current_time) { + log(2,0,0,0, "Tunnel %d still has an-acked control messages.\n", i); + last_talked = config->current_time; + } + return 1; + } + + for (i = 1; i < MAXRADIUS; i++) + { + if (radius[i].state == RADIUSNULL) + continue; + if (radius[i].state == RADIUSWAIT) + continue; + + if (last_talked != config->current_time) { + log(2,0,0,0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session); + last_talked = config->current_time; + } + return 1; + } + + return 0; } // main loop - gets packets on tap or udp and processes them @@ -1656,13 +2217,9 @@ void mainloop(void) int cn, i; u8 buf[65536]; struct timeval to; - - clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc - clockt next_acct = slow + ACCT_TIME; - clockt next_cluster_ping = slow + 50; + time_t next_cluster_ping = 0; // default 1 second pings. clockt next_clean = time_now + config->cleanup_interval; - to.tv_sec = 1; - to.tv_usec = 0; + log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, cluster_sockfd=%d, controlfd=%d\n", udpfd, tapfd, cluster_sockfd, controlfd); @@ -1685,10 +2242,14 @@ void mainloop(void) cn = radfds[i]; } - while (!main_quit) + while (!main_quit || still_busy()) { fd_set r; int n = cn; +#ifdef BGP + fd_set w; + int bgp_set[BGP_NUM_PEERS]; +#endif /* BGP */ if (config->reload_config) { @@ -1697,14 +2258,43 @@ void mainloop(void) } memcpy(&r, &cr, sizeof(fd_set)); + to.tv_sec = 0; + to.tv_usec = 100000; // 1/10th of a second. + +#ifdef BGP + FD_ZERO(&w); + for (i = 0; i < BGP_NUM_PEERS; i++) + { + bgp_set[i] = bgp_select_state(&bgp_peers[i]); + if (bgp_set[i] & 1) + { + FD_SET(bgp_peers[i].sock, &r); + if (bgp_peers[i].sock > n) + n = bgp_peers[i].sock; + } + + if (bgp_set[i] & 2) + { + FD_SET(bgp_peers[i].sock, &w); + if (bgp_peers[i].sock > n) + n = bgp_peers[i].sock; + } + } + + n = select(n + 1, &r, &w, 0, &to); +#else /* BGP */ n = select(n + 1, &r, 0, 0, &to); +#endif /* BGP */ + + config->current_time = now(); if (n < 0) { - if (errno != EINTR) - { - perror("select"); - exit( -1); - } + if (errno == EINTR) + continue; + + log(0, 0, 0, 0, "Error returned from select(): %s\n", strerror(errno)); + main_quit++; + break; } else if (n) { @@ -1735,8 +2325,11 @@ void mainloop(void) for (i = 0; i < config->num_radfds; i++) if (FD_ISSET(radfds[i], &r)) processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i); - if (FD_ISSET(cluster_sockfd, &r)) - processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)); + if (FD_ISSET(cluster_sockfd, &r)) { + int size; + size = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen); + processcluster(buf, size, addr.sin_addr.s_addr); + } if (FD_ISSET(controlfd, &r)) processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); if (FD_ISSET(clifd, &r)) @@ -1758,152 +2351,65 @@ void mainloop(void) } } + // Runs on every machine (master and slaves). + if (cluster_sockfd && next_cluster_ping <= time_now) + { + // Check to see which of the cluster is still alive.. + next_cluster_ping = time_now + 1; + cluster_send_ping(basetime); + + cluster_check_master(); + + cluster_heartbeat(config->cluster_highest_sessionid, sessionfree, config->cluster_highest_tunnelid); // Only does anything if we're a master. + master_update_counts(); // If we're a slave, send our byte counters to our master. + } + + // Run token bucket filtering queue.. + // Only run it every 1/10th of a second. + // Runs on all machines both master and slave. + { + static clockt last_run = 0; + if (last_run != config->current_time) { + last_run = config->current_time; + tbf_run_timer(); + } + } + /* Handle timeouts. Make sure that this gets run anyway, even if there was * something to read, else under load this will never actually run.... + * */ - if (n == 0 || next_clean <= time_now) { - clockt when = now(); - clockt best = when + 100; // default timeout - sessionidt s; - tunnelidt t; - int count; - u16 r; - - log(3, 0, 0, 0, "Begin regular cleanup\n"); - for (r = 1; r < MAXRADIUS; r++) - { - if (radius[r].state && radius[r].retry) - { - if (radius[r].retry <= when) - radiusretry(r); - if (radius[r].retry && radius[r].retry < best) - best = radius[r].retry; - } - else if (radius[r].state && !radius[r].retry) - radius[r].retry = backoff(radius[r].try+1); + if (config->cluster_iam_master && next_clean <= time_now) { + if (regular_cleanups()) { // Did it finish? + next_clean = time_now + 1 ; // Didn't finish. Check quickly. + } else { + next_clean = time_now + config->cleanup_interval; // Did. Move to next interval. } - for (t = 1; t < MAXTUNNEL; t++) - { - // check for expired tunnels - if (tunnel[t].die && tunnel[t].die <= when) - { - STAT(tunnel_timeout); - tunnelkill(t, "Expired"); - continue; - } - // check for message resend - if (tunnel[t].retry && tunnel[t].controlc) - { - // resend pending messages as timeout on reply - if (tunnel[t].retry <= when) - { - controlt *c = tunnel[t].controls; - u8 w = tunnel[t].window; - tunnel[t].try++; // another try - if (tunnel[t].try > 5) - tunnelkill(t, "Timeout on control message"); // game over - else - while (c && w--) - { - tunnelsend(c->buf, c->length, t); - c = c->next; - } - } - if (tunnel[t].retry && tunnel[t].retry < best) - best = tunnel[t].retry; - } - // Send hello - if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < when + 600) - { - controlt *c = controlnew(6); // sending HELLO - controladd(c, t, 0); // send the message - log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n"); - } - } - - // 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++) - { - log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); - sessionshutdown(cli_session_kill[i], "Requested by administrator"); - cli_session_kill[i] = 0; - } - } - // Check for tunnels that have been killed from the CLI - if (cli_tunnel_kill[0]) - { - int 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 administrator"); - cli_tunnel_kill[i] = 0; - } - } - - count = 0; - for (s = 1; s < MAXSESSION; s++) - { - // check for expired sessions - if (session[s].die && session[s].die <= when) - { - sessionkill(s, "Expired"); - if (++count >= 1000) break; - continue; - } - - // Drop sessions who have not responded within IDLE_TIMEOUT seconds - if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) - { - sessionkill(s, "No response to LCP ECHO requests"); - STAT(session_timeout); - if (++count >= 1000) break; - continue; - } - - // No data in IDLE_TIMEOUT seconds, send LCP ECHO - if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) - { - u8 b[MAXCONTROL] = {0}; - u8 *q = makeppp(b, 0, 0, session[s].tunnel, s, PPPLCP); - - *q = EchoReq; - *(u8 *)(q + 1) = (time_now % 255); // ID - *(u16 *)(q + 2) = htons(8); // Length - *(u32 *)(q + 4) = 0; // Magic Number (not supported) - - log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", - (int)(time_now - session[s].last_packet)); - tunnelsend(b, 24, session[s].tunnel); // send it - if (++count >= 1000) break; - continue; - } - } - if (config->accounting_dir && next_acct <= when) - { - // Dump accounting data - next_acct = when + ACCT_TIME; - dump_acct_info(); - } - - if (cluster_sockfd && next_cluster_ping <= when) - { - // Dump accounting data - next_cluster_ping = when + 50; - cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname)); - } - - if (best < when + config->cleanup_interval) - best = when + config->cleanup_interval; // Throttle to at most once per 10 seconds - next_clean = time_now + config->cleanup_interval; - to.tv_sec = config->cleanup_interval; - to.tv_usec = 0; - log(3, 0, 0, 0, "End regular cleanup, next in %d seconds\n", config->cleanup_interval); } + +#ifdef BGP + for (i = 0; i < BGP_NUM_PEERS; i++) + { + bgp_process(&bgp_peers[i], + bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0, + bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0); + } +#endif /* BGP */ } + + // Are we the master and shutting down?? + if (config->cluster_iam_master) { + + cluster_heartbeat(config->cluster_highest_sessionid, sessionfree, + config->cluster_highest_tunnelid); // Flush any queued changes.. + } + + // Ok. Notify everyone we're shutting down. If we're + // the master, this will force an election. + cluster_send_ping(0); + + // + // Important!!! We MUST not process any packets past this point! } // Init data structures @@ -1938,6 +2444,14 @@ void initdata(void) log(0, 0, 0, 0, "Error doing mmap for sessions: %s\n", strerror(errno)); exit(1); } + + sess_count = mmap(NULL, sizeof(sessioncountt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (sess_count == MAP_FAILED) + { + log(0, 0, 0, 0, "Error doing mmap for sessions_count: %s\n", strerror(errno)); + exit(1); + } + radius = mmap(NULL, sizeof(radiust) * MAXRADIUS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); if (radius == MAP_FAILED) { @@ -1975,48 +2489,52 @@ 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 == 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); - 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 < MAXSESSION - 1; i++) + + // Put all the sessions on the free list marked as undefined. + for (i = 1; i < MAXSESSION - 1; i++) { session[i].next = i + 1; + session[i].tunnel = T_UNDEF; // mark it as not filled in. + } session[MAXSESSION - 1].next = 0; sessionfree = 1; + + // Mark all the tunnels as undefined (waiting to be filled in by a download). + for (i = 1; i < MAXTUNNEL- 1; i++) { + tunnel[i].state = TUNNELUNDEF; // mark it as not filled in. + } + if (!*hostname) { - char *p; // Grab my hostname unless it's been specified gethostname(hostname, sizeof(hostname)); - { - struct hostent *h = gethostbyname(hostname); - 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); + +#ifdef BGP + bgp_peers = mmap(NULL, sizeof(struct bgp_peer) * BGP_NUM_PEERS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (bgp_peers == MAP_FAILED) + { + log(0, 0, 0, 0, "Error doing mmap for bgp: %s\n", strerror(errno)); + exit(1); + } +#endif /* BGP */ } void initiptables(void) { /* Flush the tables here so that we have a clean slate */ - system("iptables -t nat -F l2tpns"); - system("iptables -t mangle -F l2tpns"); + +// Not needed. 'nat' is setup by garden.c +// mangle isn't used (as throttling is done by tbf inhouse). } int assign_ip_address(sessionidt s) { - unsigned i; + u32 i; int best = -1; clockt best_time = time_now; char *u = session[s].user; @@ -2025,7 +2543,7 @@ int assign_ip_address(sessionidt s) #ifdef STAT_CALLS STAT(call_assign_ip_address); #endif - for (i = 0; i < ip_pool_size; i++) + for (i = 1; i < ip_pool_size; i++) { if (!ip_address_pool[i].address || ip_address_pool[i].assigned) continue; @@ -2051,7 +2569,7 @@ int assign_ip_address(sessionidt s) return 0; } - session[s].ip = ntohl(ip_address_pool[best].address); + session[s].ip = ip_address_pool[best].address; session[s].ip_pool_index = best; ip_address_pool[best].assigned = 1; ip_address_pool[best].last = time_now; @@ -2075,40 +2593,150 @@ void free_ip_address(sessionidt s) { int i = session[s].ip_pool_index; -#ifdef STAT_CALLS - STAT(call_free_ip_address); -#endif - if (!session[s].ip) return; // what the? + if (i < 0) // Is this actually part of the ip pool? + i = 0; + STAT(ip_freed); - uncache_sessionid(session[s].ip); + cache_ipmap(session[s].ip, -i); // Change the mapping to point back to the ip pool index. session[s].ip = 0; ip_address_pool[i].assigned = 0; ip_address_pool[i].session = 0; ip_address_pool[i].last = time_now; + +#ifdef STAT_CALLS + STAT(call_free_ip_address); +#endif +} + +// +// Fsck the address pool against the session table. +// Normally only called when we become a master. +// +// This isn't perfect: We aren't keep tracking of which +// users used to have an IP address. +// +void rebuild_address_pool(void) +{ + int i; + + // + // Zero the IP pool allocation, and build + // a map from IP address to pool index. + for (i = 1; i < MAXIPPOOL; ++i) { + ip_address_pool[i].assigned = 0; + ip_address_pool[i].session = 0; + if (!ip_address_pool[i].address) + continue; + + cache_ipmap(ip_address_pool[i].address, -i); // Map pool IP to pool index. + } + + for (i = 0; i < MAXSESSION; ++i) { + int ipid; + if (!session[i].ip || !session[i].tunnel) + continue; + ipid = - lookup_ipmap(htonl(session[i].ip)); + + if (session[i].ip_pool_index < 0) { // Not allocated out of the pool. + if (ipid < 1) // Not found in the pool either? good. + continue; + + log(0, 0, i, 0, "Session %d has an IP address (%s) that was marked static, but is in the pool (%d)!", + i, inet_toa(session[i].ip), ipid); + + // Fall through and process it as part of the pool. + } + + + if (ipid > MAXIPPOOL || ipid < 0) { + log(0, 0, i, 0, "Session %d has a pool IP that's not found in the pool! (%d)\n", i, ipid); + ipid = -1; + session[i].ip_pool_index = ipid; + continue; + } + + ip_address_pool[ipid].assigned = 1; + ip_address_pool[ipid].session = i; + ip_address_pool[ipid].last = time_now; + strncpy(ip_address_pool[ipid].user, session[i].user, sizeof(ip_address_pool[ipid].user) - 1); + session[i].ip_pool_index = ipid; + cache_ipmap(session[i].ip, i); // Fix the ip map. + } +} + +// +// Fix the address pool to match a changed session. +// (usually when the master sends us an update). +void fix_address_pool(int sid) +{ + int ipid; + + ipid = session[sid].ip_pool_index; + + if (ipid > ip_pool_size) + return; // Ignore it. rebuild_address_pool will fix it up. + + if (ip_address_pool[ipid].address != session[sid].ip) + return; // Just ignore it. rebuild_address_pool will take care of it. + + ip_address_pool[ipid].assigned = 1; + ip_address_pool[ipid].session = sid; + ip_address_pool[ipid].last = time_now; + strncpy(ip_address_pool[ipid].user, session[sid].user, sizeof(ip_address_pool[ipid].user) - 1); +} + +// +// Add a block of addresses to the IP pool to hand out. +// +void add_to_ip_pool(u32 addr, u32 mask) +{ + int i; + if (mask == 0) + mask = 0xffffffff; // Host route only. + + addr &= mask; + + if (ip_pool_size >= MAXIPPOOL) // Pool is full! + return ; + + for (i = addr ;(i & mask) == addr; ++i) + { + if ((i & 0xff) == 0 || (i&0xff) == 255) + continue; // Skip 0 and broadcast addresses. + + ip_address_pool[ip_pool_size].address = i; + ip_address_pool[ip_pool_size].assigned = 0; + ++ip_pool_size; + if (ip_pool_size >= MAXIPPOOL) + { + log(0,0,0,0, "Overflowed IP pool adding %s\n", inet_toa(htonl(addr)) ); + return; + } + } } // Initialize the IP address pool void initippool() { FILE *f; - char *buf, *p; - int pi = 0; + char *p; + char buf[4096]; memset(ip_address_pool, 0, sizeof(ip_address_pool)); if (!(f = fopen(IPPOOLFILE, "r"))) { log(0, 0, 0, 0, "Can't load pool file " IPPOOLFILE ": %s\n", strerror(errno)); - exit(-1); + exit(1); } - buf = (char *)malloc(4096); - - while (pi < MAXIPPOOL && fgets(buf, 4096, f)) + while (ip_pool_size < MAXIPPOOL && fgets(buf, 4096, f)) { - char* pool = buf; + char *pool = buf; + buf[4095] = 0; // Force it to be zero terminated/ + if (*buf == '#' || *buf == '\n') continue; // Skip comments / blank lines if ((p = (char *)strrchr(buf, '\n'))) *p = 0; @@ -2120,7 +2748,7 @@ void initippool() if (src == INADDR_NONE) { log(0, 0, 0, 0, "Invalid address pool IP %s", buf); - exit(-1); + exit(1); } // This entry is for a specific IP only if (src != config->bind_address) @@ -2132,8 +2760,7 @@ void initippool() { // It's a range int numbits = 0; - unsigned long start = 0, end = 0, mask = 0, ip; - struct rtentry r; + u32 start = 0, mask = 0; log(2, 0, 0, 0, "Adding IP address range %s\n", buf); *p++ = 0; @@ -2142,51 +2769,35 @@ void initippool() log(0, 0, 0, 0, "Invalid pool range %s\n", buf); continue; } - start = end = ntohl(inet_addr(pool)); - mask = (unsigned long)(pow(2, numbits) - 1) << (32 - numbits); - start &= mask; - end = start + (int)(pow(2, (32 - numbits))) - 1; - for (ip = (start + 1); ip < end && pi < MAXIPPOOL; ip++) - { - if ((ip & 0xFF) == 0 || (ip & 0xFF) == 255) - continue; - ip_address_pool[pi++].address = htonl(ip); - } + start = ntohl(inet_addr(pool)); + mask = (u32)(pow(2, numbits) - 1) << (32 - numbits); // Add a static route for this pool - log(5, 0, 0, 0, "Adding route for address pool %s/%lu\n", inet_toa(htonl(start)), 32 + mask); - memset(&r, 0, sizeof(r)); - 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; - *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask); - r.rt_flags = (RTF_UP | RTF_STATIC); - if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0) - { - log(0, 0, 0, 0, "Error adding ip address pool route %s/%lu: %s\n", - inet_toa(start), mask, strerror(errno)); - } + log(5, 0, 0, 0, "Adding route for address pool %s/%u\n", inet_toa(htonl(start)), 32 + mask); + routeset(0, start, mask, 0, 1); + + add_to_ip_pool(start, mask); } else { // It's a single ip address - ip_address_pool[pi++].address = inet_addr(pool); + add_to_ip_pool(inet_addr(pool), 0); } } - - free(buf); fclose(f); - log(1, 0, 0, 0, "IP address pool is %d addresses\n", pi); - ip_pool_size = pi; + log(1, 0, 0, 0, "IP address pool is %d addresses\n", ip_pool_size - 1); } -void snoop_send_packet(char *packet, u16 size) +void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port) { - if (!snoop_addr.sin_port || snoopfd <= 0 || size <= 0 || !packet) + if (!destination || !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)); + snoop_addr.sin_addr.s_addr = destination; + snoop_addr.sin_port = ntohs(port); + + 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); @@ -2230,12 +2841,12 @@ void dump_acct_info() } log(4, 0, 0, 0, "Dumping accounting information for %s\n", session[i].user); - fprintf(f, "%s %s %d %lu %lu\n", + fprintf(f, "%s %s %d %u %u\n", session[i].user, // username inet_toa(htonl(session[i].ip)), // ip (session[i].throttle) ? 2 : 1, // qos - (unsigned long)session[i].cin, // uptxoctets - (unsigned long)session[i].cout); // downrxoctets + (u32)session[i].cin, // uptxoctets + (u32)session[i].cout); // downrxoctets session[i].pin = session[i].cin = 0; session[i].pout = session[i].cout = 0; @@ -2252,9 +2863,9 @@ int main(int argc, char *argv[]) _program_name = strdup(argv[0]); time(&basetime); // start clock - // scan args - while ((o = getopt(argc, argv, "vc:h:a:d")) >= 0) + // scan args + while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) { switch (o) { @@ -2271,7 +2882,13 @@ int main(int argc, char *argv[]) break; case '?': default: - printf("Args are:\n\t-d\tDetach from terminal\n\t-c \tConfig file\n\t-h \tForce hostname\n\t-a
\tUse specific address\n\t-v\t\tDebug\n"); + printf("Args are:\n" + "\t-d\tDetach from terminal\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; } @@ -2286,9 +2903,13 @@ int main(int argc, char *argv[]) initiptables(); initplugins(); initdata(); + init_tbf(); init_cli(); read_config_file(); - log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.7 2004-05-24 04:42:50 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + + log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.8 2004-06-23 03:52:24 fred_nerk Exp $\n" + "(c) Copyright 2003, 2004 Optus Internet Engineering\n" + "(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; @@ -2300,12 +2921,47 @@ int main(int argc, char *argv[]) chdir("/tmp"); } - /* 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(config->bind_address, 0); - cluster_send_message(config->cluster_address, config->bind_address, C_HELLO, hostname, strlen(hostname)); + if (config->scheduler_fifo) + { + int ret; + struct sched_param params = {0}; + params.sched_priority = 1; + + if (get_nprocs() < 2) + { + log(0, 0, 0, 0, "Not using FIFO scheduler, there is only 1 processor in the system.\n"); + config->scheduler_fifo = 0; + } + else + { + if ((ret = sched_setscheduler(0, SCHED_FIFO, ¶ms)) == 0) + { + log(1, 0, 0, 0, "Using FIFO scheduler. Say goodbye to any other processes running\n"); + } + else + { + log(0, 0, 0, 0, "Error setting scheduler to FIFO: %s\n", strerror(errno)); + config->scheduler_fifo = 0; + } + } + } + + /* Set up the cluster communications port. */ + if (cluster_init(config->bind_address) < 0) + exit(1); + +#ifdef BGP + signal(SIGPIPE, SIG_IGN); + bgp_setup(config->as_number); + bgp_add_route(config->bind_address, 0xffffffff); + if (*config->bgp_peer[0]) + bgp_start(&bgp_peers[0], config->bgp_peer[0], + config->bgp_peer_as[0], 0); /* 0 = routing disabled */ + + if (*config->bgp_peer[1]) + bgp_start(&bgp_peers[1], config->bgp_peer[1], + config->bgp_peer_as[1], 0); +#endif /* BGP */ inittap(); log(1, 0, 0, 0, "Set up on interface %s\n", config->tapdevice); @@ -2313,13 +2969,6 @@ int main(int argc, char *argv[]) initudp(); initrad(); initippool(); - init_rl(); - if (config->bind_address) - send_garp(config->bind_address); - - // 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(); @@ -2336,6 +2985,24 @@ int main(int argc, char *argv[]) setuid(config->target_uid); mainloop(); + +#ifdef BGP + /* try to shut BGP down cleanly; with luck the sockets will be + writable since we're out of the select */ + { + int i; + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established) + bgp_stop(&bgp_peers[i]); + } +#endif /* BGP */ + + /* remove plugins (so cleanup code gets run) */ + plugins_done(); + + /* kill CLI children */ + signal(SIGTERM, SIG_IGN); + kill(0, SIGTERM); return 0; } @@ -2355,7 +3022,7 @@ void sigalrm_handler(int junk) // Log current traffic stats 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-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u", (udp_rx / 1024.0 / 1024.0 * 8), (eth_tx / 1024.0 / 1024.0 * 8), (eth_rx / 1024.0 / 1024.0 * 8), @@ -2388,12 +3055,12 @@ void sigterm_handler(int junk) log(1, 0, 0, 0, "Shutting down cleanly\n"); 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"); @@ -2408,12 +3075,6 @@ void sigquit_handler(int junk) 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++; } @@ -2433,17 +3094,16 @@ void read_state() u32 buf[2]; if (!config->save_state) - return; - - // Ignore saved state if NOSTATEFILE exists - if (config->ignore_cluster_updates) { unlink(STATEFILE); return; } if (stat(STATEFILE, &sb) < 0) + { + unlink(STATEFILE); return; + } if (sb.st_mtime < (time(NULL) - 60)) { @@ -2535,12 +3195,13 @@ void read_state() for (i = 0; i < MAXSESSION; i++) { - session[i].tbf = 0; + session[i].tbf_in = 0; + session[i].tbf_out = 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) - sessionsetup(session[i].tunnel, i, 0); + if (session[i].ip) + sessionsetup(session[i].tunnel, i); } } @@ -2628,10 +3289,6 @@ void update_config() { int i; - 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); - // Update logging closelog(); syslog_log = 0; @@ -2703,6 +3360,10 @@ void update_config() memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->cleanup_interval) config->cleanup_interval = 10; if (!config->multi_read_count) config->multi_read_count = 1; + if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); + if (!*config->cluster_interface) + strncpy(config->cluster_interface, DEFAULT_MCAST_INTERFACE, sizeof(config->cluster_interface) - 1); + config->reload_config = 0; } @@ -2723,81 +3384,165 @@ void read_config_file() update_config(); } -int sessionsetup(tunnelidt t, sessionidt s, u8 routes) +int sessionsetup(tunnelidt t, sessionidt s) { // A session now exists, set it up ipt ip; char *user; sessionidt i; + int r; #ifdef STAT_CALLS STAT(call_sessionsetup); #endif + log(3, session[s].ip, s, t, "Doing session setup for session\n"); - if (!session[s].ip) { - log(0, session[s].ip, s, t, "VERY VERY BAD! sessionsetup() called with no session[s].ip\n"); - return 1; + + if (!session[s].ip || session[s].ip == 0xFFFFFFFE) + { + assign_ip_address(s); + if (session[s].ip) + log(3, 0, s, t, " No IP allocated. Assigned %s from pool\n", + inet_toa(htonl(session[s].ip))); + else + log(0, 0, s, t, " No IP allocated. The IP address pool is FULL!\n"); } + // Make sure this is right session[s].tunnel = t; + // zap old sessions with same IP and/or username // Don't kill gardened 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 = 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) - sessionkill(i, "Duplicate session for user"); - } - - if (routes) - { - if (session[s].route[routes].ip && session[s].route[routes].mask) + ip = session[s].ip; + user = session[s].user; + for (i = 1; i <= config->cluster_highest_sessionid; i++) { - log(2, session[s].ip, s, t, "Routing session\n"); - routeset(session[s].ip, 0, 0, 1); - while (routes--) - routeset(session[s].route[routes].ip, session[s].route[routes].mask, - session[s].ip, 1); + 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) + sessionkill(i, "Duplicate session for users"); } } - sessionsendarp(s); - if (!session[s].sid) + + // Add the route for this session. + // + // Static IPs need to be routed. Anything else + // is part of the IP address pool and is already routed, + // it just needs to be added to the IP cache. + if (session[s].ip_pool_index == -1) // static ip + routeset(s, session[s].ip, 0, 0, 1); + else + cache_ipmap(session[s].ip, s); + + for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++) + routeset(s, session[s].route[r].ip, session[s].route[r].mask, session[s].ip, 1); + + if (!session[s].sid) { // did this session just finish radius? + log(3, session[s].ip, s, t, "Sending initial IPCP to client\n"); sendipcp(t, s); + session[s].sid = ++last_sid; + } - // Force throttling on or off - // This has the advantage of cleaning up after another throttled user who may have left - // firewall rules lying around - session[s].throttle = throttle_session(s, session[s].throttle); - + // Run the plugin's against this new session. { struct param_new_session data = { &tunnel[t], &session[s] }; run_plugins(PLUGIN_NEW_SESSION, &data); } - if (!session[s].sid) - session[s].sid = ++last_sid; + // Force throttling on or off (Actually : refresh the current throttling status) + // This has the advantage of cleaning up after another throttled user who may have left + // firewall rules lying around + throttle_session(s, session[s].throttle); - cache_sessionid(htonl(session[s].ip), s); - - cluster_send_session(s); session[s].last_packet = time_now; + { char *sessionip, *tunnelip; - sessionip = strdup(inet_toa(ntohl(session[s].ip))); - tunnelip = strdup(inet_toa(ntohl(tunnel[t].ip))); + sessionip = strdup(inet_toa(htonl(session[s].ip))); + tunnelip = strdup(inet_toa(htonl(tunnel[t].ip))); log(2, session[s].ip, s, t, "Login by %s at %s from %s (%s)\n", session[s].user, sessionip, tunnelip, tunnel[t].hostname); if (sessionip) free(sessionip); if (tunnelip) free(tunnelip); } + cluster_send_session(s); // Mark it as dirty, and needing to the flooded to the cluster. + return 1; // RADIUS OK and IP allocated, done... } +// +// This session just got dropped on us by the master or something. +// Make sure our tables up up to date... +// +int load_session(sessionidt s, sessiont *new) +{ + int i; + + // Sanity checks. + if (new->ip_pool_index >= MAXIPPOOL || + new->tunnel >= MAXTUNNEL) { + log(0,0,s,0, "Strange session update received!\n"); + // FIXME! What to do here? + return 0; + } + + // + // Ok. All sanity checks passed. Now we're committed to + // loading the new session. + // + + session[s].tunnel = new->tunnel; // For logging in cache_ipmap + + + if (new->ip != session[s].ip) // Changed ip. fix up hash tables. + { + if (session[s].ip) // If there's an old one, remove it. + { + if (session[s].ip_pool_index == -1) // static IP + routeset(s, session[s].ip, 0, 0, 0); + else // It's part of the IP pool, add it manually. + uncache_ipmap(session[s].ip); + } + + if (new->ip) { // If there's a new one, add it. + if (new->ip_pool_index == -1) + routeset(s, new->ip, 0, 0, 1); + else + cache_ipmap(new->ip, s); + } + } + + // Add routes for the session if they're new. + for (i = 0; i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++) + { + if (new->route[i].ip == session[s].route[i].ip && + new->route[i].mask == session[s].route[i].mask) + continue; + + if (session[s].route[i].ip) // Remove the old one if it exists. + routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].route[i].ip, 0); + + if (new->route[i].ip) // Add the new one if it exists. + routeset(s, new->route[i].ip, new->route[i].mask, new->ip, 1); + } + + + + if (new->tunnel && s > config->cluster_highest_sessionid) // Maintain this in the slave. It's used + // for walking the sessions to forward byte counts to the master. + config->cluster_highest_sessionid = s; + + memcpy(&session[s], new, sizeof(session[s])); // Copy over.. + + // Do fixups into address pool. + if (new->ip_pool_index != -1) + fix_address_pool(s); + return 1; +} + #ifdef RINGBUFFER void ringbuffer_dump(FILE *stream) { @@ -2823,28 +3568,33 @@ void initplugins() plugins[i] = ll_init(); } +static void *open_plugin(char *plugin_name, int load) +{ + char path[256] = ""; + + snprintf(path, 256, PLUGINDIR "/%s.so", plugin_name); + log(2, 0, 0, 0, "%soading plugin from %s\n", load ? "L" : "Un-l", path); + return dlopen(path, RTLD_NOW); +} + void add_plugin(char *plugin_name) { - void *p; + static struct pluginfuncs funcs = { + _log, + _log_hex, + inet_toa, + sessionbyuser, + sessiontbysessionidt, + sessionidtbysessiont, + sessionkill, + radiusnew, + radiussend, + }; + + void *p = open_plugin(plugin_name, 1); int (*initfunc)(struct pluginfuncs *); - char path[256] = {0}; int i; - struct pluginfuncs funcs; - funcs._log = _log; - funcs._log_hex = _log_hex; - funcs.inet_toa = inet_toa; - funcs.get_session_by_username = sessionbyuser; - funcs.get_session_by_id = sessiontbysessionidt; - funcs.get_id_by_session = sessionidtbysessiont; - funcs.sessionkill = sessionkill; - funcs.radiusnew = radiusnew; - funcs.radiussend = radiussend; - - snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); - - log(2, 0, 0, 0, "Loading plugin from %s\n", path); - p = dlopen(path, RTLD_NOW); if (!p) { log(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror()); @@ -2867,64 +3617,60 @@ void add_plugin(char *plugin_name) } } - initfunc = dlsym(p, "plugin_init"); - if (!initfunc) + if ((initfunc = dlsym(p, "plugin_init"))) { - log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist: %s\n", dlerror()); - dlclose(p); - return; + if (!initfunc(&funcs)) + { + log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror()); + dlclose(p); + return; + } } - if (!initfunc(&funcs)) - { - log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror()); - dlclose(p); - return; - } + ll_push(loaded_plugins, p); for (i = 0; i < max_plugin_functions; i++) { void *x; - if (!plugin_functions[i]) continue; - if ((x = dlsym(p, plugin_functions[i]))) + if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i]))) { log(3, 0, 0, 0, " Supports function \"%s\"\n", plugin_functions[i]); ll_push(plugins[i], x); } } + log(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name); } +static void run_plugin_done(void *plugin) +{ + int (*donefunc)(void) = dlsym(plugin, "plugin_done"); + + if (donefunc) + donefunc(); +} + void remove_plugin(char *plugin_name) { - void *p; - int (*donefunc)(); - char path[256] = {0}; + void *p = open_plugin(plugin_name, 0); int i; - snprintf(path, 256, "%s/%s.so", LIBDIR, plugin_name); - - log(2, 0, 0, 0, "Removing plugin %s\n", plugin_name); - // Get the existing pointer - p = dlopen(path, RTLD_LAZY); - if (!p) return; + if (!p) + return; for (i = 0; i < max_plugin_functions; i++) { void *x; - if (!plugin_functions[i]) continue; - if ((x = dlsym(p, plugin_functions[i]))) ll_delete(plugins[i], x); + if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i]))) + ll_delete(plugins[i], x); } if (ll_contains(loaded_plugins, p)) { ll_delete(loaded_plugins, p); - - donefunc = dlsym(p, "plugin_done"); - if (donefunc) donefunc(); + run_plugin_done(p); } - dlclose(p); dlclose(p); log(2, 0, 0, 0, "Removed plugin %s\n", plugin_name); } @@ -2945,6 +3691,15 @@ int run_plugins(int plugin_type, void *data) return 1; } +void plugins_done() +{ + void *p; + + ll_reset(loaded_plugins); + while ((p = ll_next(loaded_plugins))) + run_plugin_done(p); +} + void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) { char *resp; @@ -3013,6 +3768,8 @@ tunnelidt new_tunnel() if (tunnel[i].state == TUNNELFREE) { log(4, 0, 0, i, "Assigning tunnel ID %d\n", i); + if (i > config->cluster_highest_tunnelid) + config->cluster_highest_tunnelid = i; return i; } } @@ -3020,3 +3777,91 @@ tunnelidt new_tunnel() return 0; } +// +// We're becoming the master. Do any required setup.. +// +// This is principally telling all the plugins that we're +// now a master, and telling them about all the sessions +// that are active too.. +// +void become_master(void) +{ + int s; + run_plugins(PLUGIN_BECOME_MASTER, NULL); + + for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + if (!session[s].tunnel) // Not an in-use session. + continue; + + run_plugins(PLUGIN_NEW_SESSION_MASTER, &session[s]); + } +} + + + +int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc) +{ + int s, i; + int count = 0; + int buckets[64]; + + time(&time_now); + for (i = 0; i < 64;++i) buckets[i] = 0; + + for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + int idle; + if (!session[s].tunnel) + continue; + + idle = time_now - session[s].last_packet; + idle /= 5 ; // In multiples of 5 seconds. + if (idle < 0) + idle = 0; + if (idle > 63) + idle = 63; + + ++count; + ++buckets[idle]; + } + + for (i = 0; i < 63; ++i) { + cli_print(cli, "%3d seconds : %7.2f%% (%6d)", i * 5, (double) buckets[i] * 100.0 / count , buckets[i]); + } + cli_print(cli, "lots of secs : %7.2f%% (%6d)", (double) buckets[63] * 100.0 / count , buckets[i]); + cli_print(cli, "%d total sessions open.", count); + return CLI_OK; +} + +int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc) +{ + int s, i; + int count = 0; + int buckets[64]; + + time(&time_now); + for (i = 0; i < 64;++i) buckets[i] = 0; + + for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + int open = 0, d; + if (!session[s].tunnel) + continue; + + d = time_now - session[s].opened; + if (d < 0) + d = 0; + while (d > 1 && open < 32) { + ++open; + d >>= 1; // half. + } + ++count; + ++buckets[open]; + } + + s = 1; + for (i = 0; i < 30; ++i) { + cli_print(cli, " < %8d seconds : %7.2f%% (%6d)", s, (double) buckets[i] * 100.0 / count , buckets[i]); + s <<= 1; + } + cli_print(cli, "%d total sessions open.", count); + return CLI_OK; +} diff --git a/l2tpns.h b/l2tpns.h index f007154..653364c 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,20 +1,35 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.6 2004-05-24 04:33:35 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.7 2004-06-23 03:52:24 fred_nerk Exp $ + +#ifndef __L2TPNS_H__ +#define __L2TPNS_H__ #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "config.h" -#define VERSION "1.2.0" +#define VERSION "2.0.0" // Limits #define MAXTUNNEL 500 // could be up to 65535 #define MAXSESSION 50000 // could be up to 65535 +#define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session. + #define RADIUS_SHIFT 5 #define RADIUS_MASK ((unsigned short)(((unsigned short)~0) >> (16 - RADIUS_SHIFT))) #define MAXRADIUS ((2 << (RADIUS_SHIFT - 1)) * 255) +#define T_UNDEF (0xffff) // A tunnel ID that won't ever be used. Mark session as undefined. +#define T_FREE (0) // A tunnel ID that won't ever be used. Mark session as free. + #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 @@ -28,20 +43,28 @@ #define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation // Constants -#define STATISTICS -#define STAT_CALLS -#define RINGBUFFER +#ifndef PLUGINDIR +#define PLUGINDIR LIBDIR // Plugins +#endif + +#ifndef PLUGINCONF +#define PLUGINCONF ETCDIR // Plugin config dir +#endif + +#ifndef DATADIR +#define DATADIR "/tmp" +#endif + +#ifndef FLASHDIR +#define FLASHDIR ETCDIR +#endif + #define TAPDEVICE "/dev/net/tun" #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 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 STATEFILE DATADIR "/state.dump" // State dump file +#define CONFIGFILE FLASHDIR "/startup-config" // Configuration file +#define CLIUSERS FLASHDIR "/users" // CLI Users file +#define IPPOOLFILE FLASHDIR "/ip_pool" // Address pool configuration #define ACCT_TIME 3000 // 5 minute accounting interval #define L2TPPORT 1701 // L2TP port #define RADPORT 1645 // old radius port... @@ -56,7 +79,7 @@ #define PPPCCP 0x80FD #define PPPIP 0x0021 #define PPPMP 0x003D -#define MIN_IP_SIZE 0x20 +#define MIN_IP_SIZE 0x19 enum { ConfigReq = 1, @@ -102,20 +125,13 @@ typedef struct controls // control message } controlt; -typedef struct stbft -{ - char handle[10]; - char in_use; -} tbft; - - // 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 + ipt ip; // IP of session set by RADIUS response (host byte order). int ip_pool_index; // index to IP pool unsigned long sid; // session id for hsddb u16 nr; // next receive @@ -126,28 +142,40 @@ typedef struct sessions 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 + u32 throttle; // non-zero if this session is throttled. 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 u16 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 gardened? u16 mru; // maximum receive unit - u16 tbf; // filter bucket for throttling + u16 tbf_in; // filter bucket for throttling in from the user. + u16 tbf_out; // filter bucket for throttling out to the user. + u8 l2tp_flags; // various bit flags from the ICCN on the l2tp tunnel. + u8 walled_garden; // is this session gardened? + u8 flags1; // additional flags (currently unused); char random_vector[MAXTEL]; int random_vector_length; - char user[129]; // user (needed in seesion for radius stop messages) + char user[129]; // user (needed in seesion for radius stop messages) (can we reduce this? --mo) char called[MAXTEL]; // called number char calling[MAXTEL]; // calling number - unsigned long tx_connect_speed; - unsigned long rx_connect_speed; + u32 tx_connect_speed; + u32 rx_connect_speed; + u32 flags; // Various session flags. + ipt snoop_ip; // Interception destination IP + u16 snoop_port; // Interception destination port + char reserved[28]; // Space to expand structure without changing HB_VERSION } sessiont; +#define SF_IPCP_ACKED (1<<0) // Has this session seen an IPCP Ack? + +typedef struct { + u32 cin; + u32 cout; +} sessioncountt; + #define SESSIONPFC 1 // PFC negotiated flags #define SESSIONACFC 2 // ACFC negotiated flags @@ -191,7 +219,7 @@ radiust; typedef struct { - ipt address; + ipt address; // Host byte order.. char assigned; // 1 if assigned, 0 if free sessionidt session; clockt last; // last used @@ -223,7 +251,8 @@ enum TUNNELFREE, // Not in use TUNNELOPEN, // Active tunnel TUNNELDIE, // Currently closing - TUNNELOPENING // Busy opening + TUNNELOPENING, // Busy opening + TUNNELUNDEF, // Undefined }; enum @@ -234,7 +263,8 @@ enum 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 + RADIUSWAIT, // waiting timeout before available, in case delayed replies + RADIUSDEAD, // errored while talking to radius server. }; struct Tstats @@ -278,6 +308,9 @@ struct Tstats unsigned long ip_allocated; unsigned long ip_freed; + + unsigned long c_forwarded; + unsigned long recv_forward; #ifdef STAT_CALLS unsigned long call_processtap; unsigned long call_processarp; @@ -326,6 +359,7 @@ struct configt int debug; // debugging level time_t start_time; // time when l2tpns was started char bandwidth[256]; // current bandwidth + clockt current_time; char config_file[128]; int reload_config; // flag to re-read config (set by cli) @@ -344,21 +378,43 @@ struct configt 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 send_garp; // Set to true to garp for vip address on startup + int target_uid; int dump_speed; char plugins[64][MAXPLUGINS]; char old_plugins[64][MAXPLUGINS]; int next_tbf; // Next HTB id available to use + int scheduler_fifo; // If 1, will force scheduler to use SCHED_FIFO. + // Don't use this unless you have a dual processor machine! + int icmp_rate; // Max number of ICMP unreachable per second to send + + u32 cluster_address; // Multicast address of cluster. + // Send to this address to have everyone hear. + char cluster_interface[64]; // Which interface to listen for multicast on. + int cluster_iam_master; // Are we the cluster master??? + int cluster_iam_uptodate; // Set if we've got a full set of state from the master. + u32 cluster_master_address; // The network address of the cluster master. + // Zero if i am the cluster master. + int cluster_seq_number; // Sequence number of the next heartbeat we'll send out + // (or the seq number we're next expecting if we're a slave). + int cluster_undefined_sessions; // How many sessions we're yet to receive from the master. + int cluster_undefined_tunnels; // How many tunnels we're yet to receive from the master. + int cluster_highest_sessionid; + int cluster_highest_tunnelid; + clockt cluster_last_hb; // Last time we saw a heartbeat from the master. + int cluster_num_changes; // Number of changes queued. + +#ifdef BGP + u16 as_number; + char bgp_peer[2][64]; + u16 bgp_peer_as[2]; +#endif }; struct config_descriptt @@ -381,10 +437,10 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l); void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l); void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l); void sendchap(tunnelidt t, sessionidt s); -u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype); +u8 *makeppp(u8 * b, int size, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype); u8 *findppp(u8 * b, u8 mtype); void initlcp(tunnelidt t, sessionidt s); -void dumplcp(char *p, int l); +void dumplcp(u8 *p, int l); // radius.c @@ -410,7 +466,7 @@ void rl_destroy_tbf(u16 t); // l2tpns.c clockt now(void); clockt backoff(u8 try); -void routeset(ipt ip, ipt mask, ipt gw, u8 add); +void routeset(sessionidt, ipt ip, ipt mask, ipt gw, u8 add); void inittap(void); void initudp(void); void initdata(void); @@ -439,9 +495,13 @@ void processtap(u8 * buf, int len); void processcontrol(u8 * buf, int len, struct sockaddr_in *addr); int assign_ip_address(sessionidt s); void free_ip_address(sessionidt s); -void snoop_send_packet(char *packet, u16 size); +void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port); void dump_acct_info(); void mainloop(void); +int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc); +int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc); + #define log _log #ifndef log_hex #define log_hex(a,b,c,d) do{if (a <= config->debug) _log_hex(a,0,0,0,b,c,d);}while (0) @@ -449,7 +509,7 @@ void mainloop(void); void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) __attribute__((format (printf, 5, 6))); void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize); void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response); -int sessionsetup(tunnelidt t, sessionidt s, u8 routes); +int sessionsetup(tunnelidt t, sessionidt s); int cluster_send_session(int s); int cluster_send_tunnel(int t); int cluster_send_goodbye(); @@ -459,15 +519,23 @@ void cli_do(int sockfd); #ifdef RINGBUFFER void ringbuffer_dump(FILE *stream); #endif -void initplugins(); +void initplugins(void); int run_plugins(int plugin_type, void *data); void add_plugin(char *plugin_name); void remove_plugin(char *plugin_name); +void plugins_done(void); void tunnelclear(tunnelidt t); void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int packet_len); - +void fix_address_pool(int sid); +void rebuild_address_pool(void); +void send_ipin(sessionidt s, u8 * buf, int len); +int throttle_session(sessionidt s, int throttle); +int load_session(sessionidt, sessiont *); +void become_master(void); // We're the master; kick off any required master initializations. extern tunnelt *tunnel; extern sessiont *session; +extern sessioncountt *sess_count; +extern ippoolt *ip_address_pool; #define sessionfree (session[0].next) #define log_backtrace(count, max) \ @@ -485,3 +553,12 @@ if (count++ < max) { \ free(strings); \ } + +extern struct configt *config; +extern time_t basetime; // Time when this process started. +extern time_t time_now; // Seconds since EPOCH. +extern u32 last_sid; +extern struct Tstats *_statistics; +extern ipt my_address; +extern int tun_write(u8 *data, int size); +#endif /* __L2TPNS_H__ */ diff --git a/ll.c b/ll.c index fc18fa5..6793e6e 100644 --- a/ll.c +++ b/ll.c @@ -1,5 +1,5 @@ // L2TPNS Linked List Stuff -// $Id: ll.c,v 1.2 2004-03-05 00:09:03 fred_nerk Exp $ +// $Id: ll.c,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ #include #include diff --git a/ll.h b/ll.h index ad4d30c..f4d2d88 100644 --- a/ll.h +++ b/ll.h @@ -25,4 +25,4 @@ void *ll_next(linked_list *l); int ll_size(linked_list *l); int ll_contains(linked_list *l, void *search); -#endif +#endif /* __LL_H__ */ diff --git a/machines.cfg b/machines.cfg deleted file mode 100644 index a10a694..0000000 --- a/machines.cfg +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/perl -w -# vim:ft=perl - -$m{$_class} = { - IPfilter => "iptables", - - File_append => [ - [ "/etc/modules.conf", "alias char-major-10-200 tun\n", "char-major-10-200", "depmod -a" ], - ], - inittab_include => [ - "$_path/src/l2tpns", - ], - rpm_check => [ - "$_path/rpm/libcli-1.2.0-1.i386.rpm", - 'iproute', - 'perl-Compress-Zlib', - 'perl-MLDBM', - 'perl-Storable', - ], - Firewall => { - 'all' => '1701:udp', - }, - F_Firewall => { - all => 'all', - }, - Sysctl => { - 'net.ipv4.ip_forward' => 1, - 'net.ipv4.conf.all.proxy_arp' => 1, - 'net.core.rmem_max' => 8388608, - 'net.core.wmem_max' => 8388608, - 'net.core.rmem_default' => 8388608, - 'net.core.wmem_default' => 8388608, - 'net.ipv4.tcp_rmem' => '4096 65530 128388607', - 'net.ipv4.tcp_wmem' => '4096 65530 128388607', - }, - File_install => [ - [ "/etc/logrotate.d/l2tpns", "$_path/etc/l2tpns.logrotate", undef, { mode => 0755 } ], - ], -}; diff --git a/md5.h b/md5.h index cf6eee8..5685fdb 100644 --- a/md5.h +++ b/md5.h @@ -1,6 +1,9 @@ -/* GLOBAL.H - RSAREF types and constants +/* RSAREF types and constants */ +#ifndef __MD5_H__ +#define __MD5_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 @@ -68,3 +71,4 @@ void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, unsigned int)); void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); +#endif /* __MD5_H__ */ diff --git a/plugin.h b/plugin.h index e980579..896f08e 100644 --- a/plugin.h +++ b/plugin.h @@ -14,7 +14,9 @@ enum PLUGIN_NEW_SESSION, PLUGIN_KILL_SESSION, PLUGIN_CONTROL, - PLUGIN_RADIUS_RESPONSE + PLUGIN_RADIUS_RESPONSE, + PLUGIN_BECOME_MASTER, + PLUGIN_NEW_SESSION_MASTER, }; #define PLUGIN_RET_ERROR 0 @@ -115,4 +117,4 @@ struct param_radius_response char *value; }; -#endif +#endif /* __PLUGIN_H__ */ diff --git a/ppp.c b/ppp.c index 3136d30..deab284 100644 --- a/ppp.c +++ b/ppp.c @@ -1,5 +1,5 @@ // L2TPNS PPP Stuff -// $Id: ppp.c,v 1.4 2004-05-24 04:26:01 fred_nerk Exp $ +// $Id: ppp.c,v 1.5 2004-06-23 03:52:24 fred_nerk Exp $ #include #include @@ -10,14 +10,15 @@ #include "constants.h" #include "plugin.h" #include "util.h" +#include "tbf.h" +#include "cluster.h" extern tunnelt *tunnel; extern sessiont *session; extern radiust *radius; extern int tapfd; -extern char hostname[1000]; -extern struct Tstats *_statistics; -extern unsigned long eth_tx; +extern char hostname[]; +extern u32 eth_tx; extern time_t time_now; extern struct configt *config; @@ -66,7 +67,11 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) // respond now, either no RADIUS available or already authenticated u8 b[MAXCONTROL]; u8 id = p[1]; - u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + u8 *p = makeppp(b, sizeof(b), 0, 0, t, s, PPPPAP); + if (!p) { // Failed to make ppp header! + log(1,0,0,0, "Failed to make PPP header in process pap!\n"); + return; + } if (session[s].ip) *p = 2; // ACK else @@ -126,6 +131,9 @@ void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) if (!r) { log(1, 0, s, t, "Unexpected CHAP message\n"); + +// FIXME: Need to drop the session here. + STAT(tunnel_rx_errors); return; } @@ -206,21 +214,21 @@ char *ppp_lcp_types[] = { "DiscardRequest", }; -void dumplcp(char *p, int l) +void dumplcp(u8 *p, int l) { - signed int x = l - 3; - char *o = (p + 3); + signed int x = l - 4; + u8 *o = (p + 4); log_hex(5, "PPP LCP Packet", p, l); - log(4, 0, 0, 0, "PPP LCP Packet type %d (%s)\n", *p, ppp_lcp_types[(int)*p]); + log(4, 0, 0, 0, "PPP LCP Packet type %d (%s len %d)\n", *p, ppp_lcp_types[(int)*p], ntohs( ((u16 *) p)[1]) ); log(4, 0, 0, 0, "Length: %d\n", l); if (*p != ConfigReq && *p != ConfigRej && *p != ConfigAck) return; while (x > 2) { - int type = *(u8 *)(o); - int length = *(u8 *)(o + 1); + int type = o[0]; + int length = o[1]; if (length == 0) { log(4, 0, 0, 0, " Option length is 0...\n"); @@ -245,18 +253,18 @@ void dumplcp(char *p, int l) proto == 0xC223 ? "CHAP" : "PAP"); break; } - case 5: // Magic-Number - { - u32 magicno = ntohl(*(u32 *)(o + 2)); - log(4, 0, 0, 0, " %s %x\n", lcp_types[type], magicno); - break; - } case 4: // Quality-Protocol { u32 qp = ntohl(*(u32 *)(o + 2)); log(4, 0, 0, 0, " %s %x\n", lcp_types[type], qp); break; } + case 5: // Magic-Number + { + u32 magicno = ntohl(*(u32 *)(o + 2)); + log(4, 0, 0, 0, " %s %x\n", lcp_types[type], magicno); + break; + } case 7: // Protocol-Field-Compression { u32 pfc = ntohl(*(u32 *)(o + 2)); @@ -300,15 +308,16 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) } else if (*p == ConfigReq) { - signed int x = l - 1; - char *o = (p + 1); + signed int x = l - 4; + u8 *o = (p + 4); log(3, session[s].ip, s, t, "LCP: ConfigReq (%d bytes)...\n", l); + dumplcp(p, l); while (x > 2) { - int type = *(u8 *)(o); - int length = *(u8 *)(o + 1); + int type = o[0]; + int length = o[1]; if (length == 0 || type == 0) break; switch (type) { @@ -324,7 +333,11 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) if (!q) { - q = makeppp(b, p, l, t, s, PPPLCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPLCP); + if (!q) { + log(2, session[s].ip, s, t, " Failed to send packet.\n"); + break; + } *q++ = ConfigNak; } memcpy(q, o, length); @@ -365,7 +378,11 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) { // Send back a ConfigAck log(3, session[s].ip, s, t, "ConfigReq accepted, sending as Ack\n"); - q = makeppp(b, p, l, t, s, PPPLCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPLCP); + if (!q) { + log(3, session[s].ip, s, t, " failed to create packet.\n"); + return; + } *q = ConfigAck; tunnelsend(b, l + (q - b), t); } @@ -376,7 +393,7 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) tunnelsend(b, l + (q - b), t); log(3, session[s].ip, s, t, "Sending ConfigReq, requesting PAP login\n"); - q = makeppp(b, NULL, 0, t, s, PPPLCP); + q = makeppp(b, sizeof(b), NULL, 0, t, s, PPPLCP); *q++ = ConfigReq; *(u8 *)(q++) = 3; *(u8 *)(q++) = 4; @@ -393,7 +410,11 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) else if (*p == TerminateReq) { *p = TerminateAck; // close - q = makeppp(b, p, l, t, s, PPPLCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPLCP); + if (!q) { + log(3, session[s].ip, s, t, "Failed to create PPP packet in processlcp.\n"); + return; + } log(3, session[s].ip, s, t, "LCP: Received TerminateReq. Sending TerminateAck\n"); sessionshutdown(s, "Remote end closed connection."); tunnelsend(b, l + (q - b), t); // send it @@ -406,7 +427,11 @@ 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); + q = makeppp(b, sizeof(b), p, l, t, s, PPPLCP); + if (!q) { + log(3, session[s].ip, s, t, " failed to send EchoReply.\n"); + return; + } log(5, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n"); tunnelsend(b, l + (q - b), t); // send it } @@ -438,11 +463,14 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) if (*p == ConfigAck) { // happy with our IPCP u16 r = session[s].radius; - if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) + if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden) { if (!r) r = radiusnew(s); if (r) radiussend(r, RADIUSSTART); // send radius start, having got IPCP at last + } + session[s].flags |= SF_IPCP_ACKED; + return ; // done } if (*p != ConfigReq) @@ -460,7 +488,7 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) if (!session[s].ip) { log(3, 0, s, t, "Waiting on radius reply\n"); - return ; // have to wait on RADIUS eply + return ; // have to wait on RADIUS reply } // form a config reply quoting the IP in the session { @@ -480,7 +508,11 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) { // reject u16 n = 4; i = p + l; - q = makeppp(b, p, l, t, s, PPPIPCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPIPCP); + if (!q) { + log(2, 0, s, t, "Failed to send IPCP.\n"); + return; + } *q = ConfigRej; p += 4; while (p < i && p[1]) @@ -529,54 +561,109 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) *(u32 *) (i + 2) = htonl(session[s].ip); *p = ConfigNak; } - q = makeppp(b, p, l, t, s, PPPIPCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPIPCP); + if (!q) { + log(2, 0, s, t, " Failed to send IPCP packet.\n"); + return; + } tunnelsend(b, l + (q - b), t); // send it } } } // process IP packet received +// +// This MUST be called with at least 4 byte behind 'p'. +// (i.e. this routine writes to p[-4]). void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) { + ipt ip; + #ifdef STAT_CALLS STAT(call_processipin); #endif log_hex(5, "IP", p, l); + ip = ntohl(*(u32 *)(p + 12)); + if (l > MAXETHER) { - log(1, *(u32 *)(p + 12), s, t, "IP packet too long %d\n", l); + log(1, ip, s, t, "IP packet too long %d\n", l); STAT(tunnel_rx_errors); return ; } - session[s].cin += l; - session[s].total_cin += l; - session[s].pin++; - eth_tx += l; + // no spoof (do sessionbyip to handled statically routed subnets) + if (ip != session[s].ip && sessionbyip(htonl(ip)) != s) + { + log(5, ip, s, t, "Dropping packet with spoofed IP %s\n", inet_toa(htonl(ip))); + return; + } // Add on the tun header p -= 4; *(u32 *)p = htonl(0x00000800); l += 4; + if (session[s].tbf_in && !config->cluster_iam_master) { // Are we throttled and a slave? + master_throttle_packet(session[s].tbf_in, p, l); // Pass it to the master for handling. + return; + } + + session[s].cin += l - 4; + session[s].total_cin += l - 4; + sess_count[s].cin += l - 4; + + session[s].pin++; + eth_tx += l - 4; + + if (session[s].snoop_ip && session[s].snoop_port) + { + // Snooping this session, send it to ASIO + snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); + } + STAT(tap_tx_packets); + INC_STAT(tap_tx_bytes, l); + + if (session[s].tbf_in && config->cluster_iam_master) { // Are we throttled and a master?? actually handle the throttled packets. + tbf_queue_packet(session[s].tbf_in, p, l); + return; + } + // send to ethernet - if (write(tapfd, p, l) < 0) + if (tun_write(p, l) < 0) { STAT(tap_tx_errors); log(0, 0, s, t, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", l, strerror(errno), tapfd, p); } - if (session[s].snoop) - { - // Snooping this session, send it to ASIO - snoop_send_packet(p, l); - } - STAT(tap_tx_packets); - INC_STAT(tap_tx_bytes, l); } +// +// Helper routine for the TBF filters. +// Used to send queued data in from the user. +// +void send_ipin(sessionidt s, u8 *buf, int len) +{ + log_hex(5, "IP in throttled", buf, len); + if (write(tapfd, buf, len) < 0) + { + STAT(tap_tx_errors); + log(0, 0, 0, 0, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", + len, strerror(errno), tapfd, buf); + } + + // Increment packet counters + session[s].cin += len - 4; + session[s].total_cin += len - 4; + sess_count[s].cin += len - 4; + + session[s].pin++; + eth_tx += len - 4; +} + + // Process LCP messages void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) { @@ -607,7 +694,11 @@ void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) } else *p = TerminateAck; // close - q = makeppp(b, p, l, t, s, PPPCCP); + q = makeppp(b, sizeof(b), p, l, t, s, PPPCCP); + if (!q) { + log(1,0,0,0, "Failed to send CCP packet.\n"); + return; + } tunnelsend(b, l + (q - b), t); // send it } } @@ -645,7 +736,11 @@ void sendchap(tunnelidt t, sessionidt s) STAT(tunnel_tx_errors); return ; } - q = makeppp(b, 0, 0, t, s, PPPCHAP); + q = makeppp(b, sizeof(b), 0, 0, t, s, PPPCHAP); + if (!q) { + log(1, 0, s, t, "failed to send CHAP challenge.\n"); + return; + } *q = 1; // challenhe q[1] = radius[r].id; // ID q[4] = 16; // length @@ -658,24 +753,33 @@ void sendchap(tunnelidt t, sessionidt s) // fill in a L2TP message with a PPP frame, // copies existing PPP message and changes magic number if seen // returns start of PPP frame -u8 *makeppp(u8 * b, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype) +u8 *makeppp(u8 * b, int size, u8 * p, int l, tunnelidt t, sessionidt s, u16 mtype) { + + if (size < 12) + return NULL; // Need more space than this!! + *(u16 *) (b + 0) = htons(0x0002); // L2TP with no options *(u16 *) (b + 2) = htons(tunnel[t].far); // tunnel *(u16 *) (b + 4) = htons(session[s].far); // session b += 6; - if (mtype != PPPLCP && !(session[s].flags & SESSIONACFC)) + if (mtype != PPPLCP && !(session[s].l2tp_flags & SESSIONACFC)) { *(u16 *) b = htons(0xFF03); // HDLC header b += 2; } - if (mtype < 0x100 && session[s].flags & SESSIONPFC) + if (mtype < 0x100 && session[s].l2tp_flags & SESSIONPFC) *b++ = mtype; else { *(u16 *) b = htons(mtype); b += 2; } + + if (l + 12 > size) { + log(3,0,0,0, "Would have overflowed the buffer in makeppp: size %d, len %d.\n", size, l); + return NULL; // Run out of room to hold the packet! + } if (p && l) memcpy(b, p, l); return b; @@ -706,7 +810,11 @@ void initlcp(tunnelidt t, sessionidt s) { char b[500] = {0}, *q; - q = makeppp(b, NULL, 0, t, s, PPPLCP); + q = makeppp(b, sizeof(b), NULL, 0, t, s, PPPLCP); + if (!q) { + log(1, 0, s, t, "Failed to send LCP ConfigReq.\n"); + return; + } log(4, 0, s, t, "Sending LCP ConfigReq for PAP\n"); *q = ConfigReq; *(u8 *)(q + 1) = (time_now % 255) + 1; // ID diff --git a/radius.c b/radius.c index a90fe13..32eafe7 100644 --- a/radius.c +++ b/radius.c @@ -1,5 +1,5 @@ // L2TPNS Radius Stuff -// $Id: radius.c,v 1.3 2004-05-24 04:27:11 fred_nerk Exp $ +// $Id: radius.c,v 1.4 2004-06-23 03:52:24 fred_nerk Exp $ #include #include @@ -21,7 +21,6 @@ extern radiust *radius; extern sessiont *session; extern tunnelt *tunnel; extern u32 sessionid; -extern struct Tstats *_statistics; extern struct configt *config; extern int *radfds; @@ -95,6 +94,9 @@ u16 radiusnew(sessionidt s) session[s].radius = r; radius[r].session = s; radius[r].state = RADIUSWAIT; + radius[r].retry = config->current_time + 1200; // Wait at least 120 seconds to re-claim this. + + log(3,0,s, session[s].tunnel, "Allocated radius %d\n", r); return r; } @@ -352,6 +354,11 @@ void processrad(u8 *buf, int len, char socket_index) hasht hash; u8 routes = 0; + int r_code, r_id ; // Radius code. + + r_code = buf[0]; // First byte in radius packet. + r_id = buf[1]; // radius reply indentifier. + #ifdef STAT_CALLS STAT(call_processrad); #endif @@ -362,10 +369,10 @@ void processrad(u8 *buf, int len, char socket_index) return ; } len = ntohs(*(u16 *) (buf + 2)); - r = socket_index | (buf[1] << RADIUS_SHIFT); + r = socket_index | (r_id << RADIUS_SHIFT); s = radius[r].session; - log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u\n", - radius_states[radius[r].state], r, s); + log(3, 0, s, session[s].tunnel, "Received %s, radius %d response for session %u (code %d, id %d)\n", + radius_states[radius[r].state], r, s, r_code, r_id); if (!s && radius[r].state != RADIUSSTOP) { log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response\n"); @@ -386,16 +393,21 @@ void processrad(u8 *buf, int len, char socket_index) do { if (memcmp(hash, buf + 4, 16)) { - log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response\n"); - radius[r].state = RADIUSWAIT; - break; + log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response!! (wrong secret in radius config?)\n"); +// radius[r].state = RADIUSWAIT; + + return; // Do nothing. On timeout, it will try the next radius server. } if ((radius[r].state == RADIUSAUTH && *buf != 2 && *buf != 3) || ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && *buf != 5)) { log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response %d\n", *buf); - radius[r].state = RADIUSWAIT; - break; + + return; // We got something we didn't expect. Let the timeouts take + // care off finishing the radius session if that's really correct. +// old code. I think incorrect. --mo +// radius[r].state = RADIUSWAIT; +// break; // Finish the radius sesssion. } if (radius[r].state == RADIUSAUTH) { @@ -404,8 +416,10 @@ void processrad(u8 *buf, int len, char socket_index) if (radius[r].chap) { // CHAP - u8 *p = makeppp(b, 0, 0, t, s, PPPCHAP); - + u8 *p = makeppp(b, sizeof(b), 0, 0, t, s, PPPCHAP); + if (!p) { + return; // Abort! + } { struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPCHAP }; run_plugins(PLUGIN_POST_AUTH, &packet); @@ -422,7 +436,9 @@ void processrad(u8 *buf, int len, char socket_index) else { // PAP - u8 *p = makeppp(b, 0, 0, t, s, PPPPAP); + u8 *p = makeppp(b, sizeof(b), 0, 0, t, s, PPPPAP); + if (!p) + return; // Abort! { struct param_post_auth packet = { &tunnel[t], &session[s], session[s].user, (*buf == 2), PPPPAP }; @@ -453,17 +469,18 @@ void processrad(u8 *buf, int len, char socket_index) // Statically assigned address log(3, 0, s, session[s].tunnel, " Radius reply contains IP address %s\n", inet_toa(*(u32 *) (p + 2))); session[s].ip = ntohl(*(u32 *) (p + 2)); + session[s].ip_pool_index = -1; } else if (*p == 135) { // DNS address - log(3, 0, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + log(3, 0, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", inet_toa(*(u32 *) (p + 2))); session[s].dns1 = ntohl(*(u32 *) (p + 2)); } else if (*p == 136) { // DNS address - log(3, 0, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", inet_toa(ntohl(*(u32 *) (p + 2)))); + log(3, 0, s, session[s].tunnel, " Radius reply contains secondary DNS address %s\n", inet_toa(*(u32 *) (p + 2))); session[s].dns2 = ntohl(*(u32 *) (p + 2)); } else if (*p == 22) @@ -503,11 +520,11 @@ void processrad(u8 *buf, int len, char socket_index) { log(1, 0, s, session[s].tunnel, " Too many routes\n"); } - else + else if (ip) { char *ips, *masks; - ips = strdup(inet_toa(ip)); - masks = strdup(inet_toa(mask)); + ips = strdup(inet_toa(htonl(ip))); + masks = strdup(inet_toa(htonl(mask))); log(3, 0, s, session[s].tunnel, " Radius reply contains route for %s/%s\n", ips, masks); free(ips); free(masks); @@ -564,19 +581,10 @@ void processrad(u8 *buf, int len, char socket_index) else if (*buf == 3) { log(2, 0, s, session[s].tunnel, " Authentication denied for %s\n", session[s].user); +//FIXME: We should tear down the session here! break; } - // Check for Assign-IP-Address - if (!session[s].ip || session[s].ip == 0xFFFFFFFE) - { - 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(0, 0, s, t, " No IP allocated by radius. The IP address pool is FULL!\n"); - } if (!session[s].dns1 && config->default_dns1) { session[s].dns1 = htonl(config->default_dns1); @@ -588,21 +596,15 @@ void processrad(u8 *buf, int len, char socket_index) log(3, 0, s, t, " Sending dns2 = %s\n", inet_toa(config->default_dns2)); } - if (session[s].ip) - { - // Valid Session, set it up - session[s].sid = 0; - sessionsetup(t, s, routes); - } - else - { - log(0, 0, s, t, " End of processrad(), but no valid session exists.\n"); - sessionkill(s, "Can't create valid session"); - } + // Valid Session, set it up + session[s].sid = 0; + sessionsetup(t, s); } else { - log(3, 0, s, t, " RADIUS response in state %s\n", radius_states[radius[r].state]); + // An ack for a stop or start record. + log(3, 0, s, t, " RADIUS accounting ack recv in state %s\n", radius_states[radius[r].state]); + break; } } while (0); diff --git a/rl.c b/rl.c deleted file mode 100644 index 9f2f14a..0000000 --- a/rl.c +++ /dev/null @@ -1,121 +0,0 @@ -// L2TPNS Rate Limiting Stuff -// $Id: rl.c,v 1.4 2004-05-24 04:28:41 fred_nerk Exp $ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "l2tpns.h" - -extern radiust *radius; -extern sessiont *session; -extern u32 sessionid; -extern tbft *filter_buckets; -extern struct configt *config; - -#define DEVICE "tun0" - -void init_rl() -{ - char *commands[] = { - "tc qdisc add dev " DEVICE " root handle 1: htb", - "tc filter del dev " DEVICE " protocol ip pref 1 fw", - "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; - - log(2, 0, 0, 0, "Initializing HTB\n"); - for (i = 0; commands[i] && *commands[i]; i++) - { - log(3, 0, 0, 0, "Running \"%s\"\n", commands[i]); - system(commands[i]); - } - log(2, 0, 0, 0, "Done initializing HTB\n"); -} - -u16 rl_create_tbf() -{ - u16 t; - char cmd[2048]; - if (!config->rl_rate) return 0; - - t = ++config->next_tbf; - if (config->next_tbf >= MAXSESSION) return 0; - snprintf(filter_buckets[t].handle, 9, "1:%d0", t); - - 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, config->rl_rate); - log(3, 0, 0, 0, "%s\n", cmd); - if (WEXITSTATUS(system(cmd)) != 0) - { - memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); - log(0, 0, 0, 0, "tc returned an error creating a token bucket\n"); - return 0; - } - - snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid %s", - t, filter_buckets[t].handle); - log(3, 0, 0, 0, "%s\n", cmd); - if (WEXITSTATUS(system(cmd)) != 0) - { - memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); - log(0, 0, 0, 0, "tc returned an error creating a filter\n"); - return 0; - } - - return t; -} - -u16 rl_get_tbf() -{ - int i; - if (!config->rl_rate) return 0; - - for (i = 1; i < MAXSESSION; i++) - { - if (!*filter_buckets[i].handle) continue; - if (filter_buckets[i].in_use) continue; - - filter_buckets[i].in_use = 1; - log(2, 0, 0, 0, "Returning tbf %s\n", filter_buckets[i].handle); - return i; - } - i = rl_create_tbf(); - if (i) filter_buckets[i].in_use = 1; - return i; -} - -void rl_done_tbf(u16 t) -{ - if (!t) 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 (!config->rl_rate) return; - if (filter_buckets[t].in_use) - { - log(0, 0, 0, 0, "Trying to destroy an in-use HTB %s\n", filter_buckets[t].handle); - return; - } - snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle); - if (WEXITSTATUS(system(cmd)) != 0) - log(0, 0, 0, 0, "tc returned an error deleting a token bucket\n"); - memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle)); -} - diff --git a/tbf.c b/tbf.c new file mode 100644 index 0000000..c024c67 --- /dev/null +++ b/tbf.c @@ -0,0 +1,400 @@ +#include +#include +#include +#include + +#include "l2tpns.h" +#include "tbf.h" + +// Need a time interval. + +#define TBF_MAX_QUEUE 2 // Maximum of 2 queued packet per +#define TBF_MAX_SIZE 3000 // Maxiumum queued packet size is 2048. + +#define TBF_MAX_CREDIT 6000 // Maximum 6000 bytes of credit. +#define TBF_RATE 360 // 360 bytes per 1/10th of a second. + +typedef struct { + int credit; + int lasttime; + int queued; + int oldest; // Position of packet in the ring buffer. + sessionidt sid; // associated session ID. + int max_credit; // Maximum amount of credit available (burst size). + int rate; // How many bytes of credit per second we get? (sustained rate) + void (*send)(sessionidt s, u8 *, int); // Routine to actually send out the data. + int prev; // Timer chain position. + int next; // Timer chain position. + + u32 b_queued; // Total bytes sent through this TBF + u32 b_sent; // Total bytes sucessfully made it to the network. + u32 p_queued; // ditto packets. + u32 p_sent; // ditto packets. + u32 b_dropped; // Total bytes dropped. + u32 p_dropped; // Total packets dropped. + u32 p_delayed; // Total packets not sent immediately. + + int sizes[TBF_MAX_QUEUE]; + char packets[TBF_MAX_QUEUE][TBF_MAX_SIZE]; +} tbft; + + +tbft * filter_list = NULL; +int filter_list_size = 0; + +static int timer_chain = -1; // Head of timer chain. + +static void tbf_run_queue(int tbf_id); + +void init_tbf(void) +{ + filter_list = mmap(NULL, sizeof(*filter_list) * MAXTBFS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (!filter_list) + return; + + filter_list_size = MAXTBFS; + filter_list[0].sid = -1; // Reserved. +} +// +// Put a TBF on the timer list. +// This is a doubly linked list.. +// We put ourselves on the tail of the list. +// +static void add_to_timer(int id) +{ + if (!filter_list) + return; + + if (timer_chain == -1) { + filter_list[id].next = filter_list[id].prev = id; + timer_chain = id; + return; + } + + filter_list[id].next = timer_chain; + filter_list[id].prev = filter_list[timer_chain].prev; + filter_list[filter_list[timer_chain].prev].next = id; + filter_list[timer_chain].prev = id; +} + +// +// Remove a TBF from the timer list. +// This is a doubly linked list. +static void del_from_timer(int id) +{ + if (!filter_list) + return; + + if (filter_list[id].next == id) { // Last element in chain? + if (timer_chain != id) { // WTF? + log(0,0,0,0, "Removed a singleton element from TBF, but tc didn't point to it!\n"); + } else + timer_chain = -1; + filter_list[id].next = filter_list[id].prev = 0; + return; + } + + filter_list[filter_list[id].next].prev = filter_list[id].prev; + filter_list[filter_list[id].prev].next = filter_list[id].next; + if (timer_chain == id) + timer_chain = filter_list[id].next; + + filter_list[id].next = filter_list[id].prev = 0; // Mark as off the timer chain. +} + +// +// Free a token bucket filter structure for re-use. +// + +int free_tbf(int tid) +{ + if (tid < 1) // Make sure we don't free id # 0 + return -1; + + if (!filter_list) // WTF? + return -1; + + if (filter_list[tid].next) + del_from_timer(tid); + filter_list[tid].sid = 0; + + return 0; // Done! +} + +// +// Allocate a new token bucket filter. +// +int new_tbf(int sid, int max_credit, int rate, void (*f)(sessionidt, u8 *, int)) +{ + int i; + static int p = 0; + + log(3,0,0,0, "Allocating new TBF (sess %d, rate %d, helper %p)\n", sid, rate, f); + + if (!filter_list) + return 0; // Couldn't alloc memory! + +// again: + + for (i = 0 ; i < filter_list_size ; ++i, p = (p+1)%filter_list_size ) { + if (filter_list[p].sid) + continue; + + memset((void*) &filter_list[p], 0, sizeof(filter_list[p]) ); // Clear counters and data. + filter_list[p].sid = sid; + filter_list[p].credit = max_credit; + filter_list[p].queued = 0; + filter_list[p].max_credit = max_credit; + filter_list[p].rate = rate; + filter_list[p].oldest = 0; + filter_list[p].send = f; + return p; + } + + log(0,0,0,0, "Ran out of token bucket filters! Sess %d will be un-throttled\n", sid); + return 0; + +#if 0 + // Not using. Disasterous if called via the CLI! :) + // All allocated filters are used! Increase the size of the allocated + // filters. + + i = filter_list_size; + filter_list_size = filter_list_size * 2 + 1; + + filter_list = realloc(filter_list, filter_list_size * sizeof(*filter_list) ); + + for (; i < filter_list_size; ++i) + filter_list[i].sid = 0; + + goto again; +#endif +} + +// +// Sanity check all the TBF records. This is +// typically done when we become a master.. +// +void fsck_tbfs(void) +{ + int i , sid; + + if (!filter_list) + return; + + for (i = 1; i < filter_list_size; ++i) { + if (!filter_list[i].sid) // Is it used?? + continue; + + sid = filter_list[i].sid; + if (i != session[sid].tbf_in && + i != session[sid].tbf_out) { // Ooops. + + free_tbf(i); // Mark it as free... + } + } + + for (i = 0; i < config->cluster_highest_sessionid ; ++i) { + if (session[i].tbf_in && filter_list[session[i].tbf_in].sid != i) { + filter_list[session[i].tbf_in].sid = i; // Ouch!? FIXME. What to do here? + } + if (session[i].tbf_out && filter_list[session[i].tbf_out].sid != i) { + filter_list[session[i].tbf_out].sid = i; // Ouch!? FIXME. What to do here? + } + } +} + + +// +// Run a packet through a token bucket filter. +// If we can send it right away, we do. Else we +// try and queue it to send later. Else we drop it. +// +int tbf_queue_packet(int tbf_id, char * data, int size) +{ + int i; + tbft * f; + + if (!filter_list) + return -1; + + if (tbf_id > filter_list_size || tbf_id < 1) { // Out of range ID?? + // Very bad. Just drop it. + return -1; + } + + f = &filter_list[tbf_id]; + + if (!f->sid) // Is this a real structure?? + return -1; + + tbf_run_queue(tbf_id); // Caculate credit and send any queued packets if possible.. + + f->b_queued += size; + f->p_queued ++; + + if (!f->queued && f->credit > size) { // If the queue is empty, and we have + // enough credit, just send it now. + f->credit -= size; + if (f->send) { + f->send(f->sid, data, size); + f->b_sent += size; + f->p_sent ++; + } else { + f->b_dropped += size; + f->p_dropped ++; + } + return size; + } + + // Not enough credit. Can we have room in the queue? + if (f->queued >= TBF_MAX_QUEUE) { + f->p_dropped ++; + f->b_dropped += size; + return -1; // No, just drop it. + } + + // Is it too big to fit into a queue slot? + if (size >= TBF_MAX_SIZE) { + f->p_dropped ++; + f->b_dropped += size; + return -1; // Yes, just drop it. + } + + // Ok. We have a slot, and it's big enough to + // contain the packet, so queue the packet! + i = ( f->oldest + f->queued ) % TBF_MAX_QUEUE; + memcpy(f->packets[i], data, size); + + f->sizes[i] = size; + f->queued ++; + f->p_delayed ++; + + if (!f->next) // Are we off the timer chain? + add_to_timer(tbf_id); // Put ourselves on the timer chain. + + return 0; // All done. +} + +// +// Send queued packets from the filter if possible. +// (We're normally only called if this is possible.. ) +static void tbf_run_queue(int tbf_id) +{ + tbft * f; + + if (!filter_list) + return; + + f = &filter_list[tbf_id]; + + // Calculate available credit... + f->credit += (config->current_time - f->lasttime) * f->rate / 10; // current time is 1/10th of a second. + if (f->credit > f->max_credit) + f->credit = f->max_credit; + f->lasttime = config->current_time; + + while (f->queued > 0 && f->credit >= f->sizes[f->oldest]) { // While we have enough credit.. + + if (f->send) { + f->send(f->sid, f->packets[f->oldest], f->sizes[f->oldest]); + f->b_sent += f->sizes[f->oldest]; + f->p_sent ++; + } else { + f->b_dropped += f->sizes[f->oldest]; + f->p_dropped ++; + } + + f->credit -= f->sizes[f->oldest]; + + f->oldest = (f->oldest + 1 ) % TBF_MAX_QUEUE; + f->queued--; // One less queued packet.. + } + + if (f->queued) // Still more to do. Hang around on the timer list. + return; + + if (f->next) // Are we on the timer list?? + del_from_timer(tbf_id); // Nothing more to do. Get off the timer list. +} + +// +// Periodically walk the timer list.. +// +int tbf_run_timer(void) +{ + int i = timer_chain; + int count = filter_list_size + 1; // Safety check. + int last = -1; + int tbf_id; // structure being processed. + + if (timer_chain < 0) + return 0; // Nothing to do... + + if (!filter_list) // No structures built yet. + return 0; + + last = filter_list[i].prev; // last element to process. + + do { + tbf_id = i; + i = filter_list[i].next; // Get the next in the queue. + + tbf_run_queue(tbf_id); // Run the timer queue.. + } while ( timer_chain > 0 && i && tbf_id != last && --count > 0); + + +#if 0 // Debugging. + for (i = 0; i < filter_list_size; ++i) { + if (!filter_list[i].next) + continue; + if (filter_list[i].lasttime == config->current_time) // Did we just run it? + continue; + + log(1,0,0,0, "Missed tbf %d! Not on the timer chain?(n %d, p %d, tc %d)\n", i, + filter_list[i].next, filter_list[i].prev, timer_chain); + tbf_run_queue(i); + } +#endif + + return 1; +} + +int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc) +{ + int i; + int count = 0; + + if (!config->cluster_iam_master) { + cli_print(cli, "Command can't be run on a slave."); + return CLI_OK; + } + if (!filter_list) + return CLI_OK; + + cli_print(cli,"%6s %5s %5s %6s %6s | %7s %7s %8s %8s %8s %8s", "TBF#", "Sid", "Rate", "Credit", "Queued", + "ByteIn","PackIn","ByteSent","PackSent", "PackDrop", "PackDelay"); + + for (i = 1; i < filter_list_size; ++i) { + if (!filter_list[i].sid) // Is it used? + continue; // No. + + cli_print(cli, "%5d%1s %5d %5d %6d %6d | %7d %7d %8d %8d %8d %8d", + i, (filter_list[i].next ? "*" : " "), + filter_list[i].sid, + filter_list[i].rate * 8, + filter_list[i].credit, + filter_list[i].queued, + + filter_list[i].b_queued, + filter_list[i].p_queued, + filter_list[i].b_sent, + filter_list[i].p_sent, + filter_list[i].p_dropped, + filter_list[i].p_delayed); + ++count; + } + cli_print(cli, "%d tbf entries used, %d total", count, filter_list_size); + return CLI_OK; +} + diff --git a/tbf.h b/tbf.h new file mode 100644 index 0000000..3d98c1b --- /dev/null +++ b/tbf.h @@ -0,0 +1,13 @@ +#ifndef __TBF_H__ +#define __TBF_H__ + +void init_tbf(void); +int tbf_run_timer(void); +int tbf_queue_packet(int tbf_id, char * data, int size); +int new_tbf(int sid, int max_credit, int rate, void (*f)(sessionidt, u8 *, int)); +int free_tbf(int tid); +void fsck_tbfs(void); + +int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc); + +#endif /* __TBF_H__ */ diff --git a/throttle.c b/throttle.c deleted file mode 100644 index fd24712..0000000 --- a/throttle.c +++ /dev/null @@ -1,77 +0,0 @@ -// L2TPNS Throttle Stuff -// $Id: throttle.c,v 1.3 2004-05-24 04:29:21 fred_nerk Exp $ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "l2tpns.h" -#include "util.h" - -extern radiust *radius; -extern sessiont *session; -extern u32 sessionid; -extern tbft *filter_buckets; -extern struct configt *config; - -// Throttle or Unthrottle a session -int throttle_session(sessionidt s, int throttle) -{ - if (!config->rl_rate) return 0; - - if (!*session[s].user) - return 0; // User not logged in - - if (throttle) - { - // Throttle them - char cmd[2048] = {0}; - if (!session[s].tbf) session[s].tbf = rl_get_tbf(); - 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 (bucket %s)\n", s, session[s].user, filter_buckets[session[s].tbf].handle); - 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); - if (WEXITSTATUS(system(cmd)) != 0) - { - log(2, 0, s, session[s].tunnel, "iptables returned an error. Session is not throttled\n"); - return 0; - } - } - else - { - char cmd[2048] = {0}; - log(2, 0, s, session[s].tunnel, "Unthrottling session %d for user %s\n", s, session[s].user); - if (session[s].tbf) - { - int count = 10; - snprintf(cmd, 2048, "iptables -t mangle -D 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); - while (--count) - { - int status = system(cmd); - if (WEXITSTATUS(status) != 0) break; - } - system(cmd); - - rl_done_tbf(session[s].tbf); - session[s].tbf = 0; - } - } - session[s].throttle = throttle; - return session[s].throttle; -} - diff --git a/util.h b/util.h index ec2c017..be797f8 100644 --- a/util.h +++ b/util.h @@ -1 +1,6 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + char *inet_toa(unsigned long addr); + +#endif /* __UTIL_H__ */ From df561af44eac3ceb122be39fe570e257e006f02a Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 28 Jun 2004 02:21:20 +0000 Subject: [PATCH 030/482] Add INTERNALS file --- INTERNALS | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 INTERNALS diff --git a/INTERNALS b/INTERNALS new file mode 100644 index 0000000..e199981 --- /dev/null +++ b/INTERNALS @@ -0,0 +1,180 @@ +Documentation on various internal structures. + +Most important structure use an anonymous shared mmap() +so that child processes can watch them. (All the cli connections +are handled in child processes). + +TODO: Re-investigate threads to see if we can use a thread to handle +cli connections without killing forwarding performance. + +session[] + An array of session structures. This is one of the two + major data structures that are sync'ed across the cluster. + + This array is statically allocated at startup time to a + compile time size (currently 50k sessions). This sets a + hard limit on the number of sessions a cluster can handle. + + There is one element per l2tp session. (I.e. each active user). + + The zero'th session is always invalid. + +tunnel[] + An array of tunnel structures. This is the other major data structure + that's actively sync'ed across the cluster. + + As per sessions, this is statically allocated at startup time + to a compile time size limit. + + There is one element per l2tp tunnel. (normally one per BRAS + that this cluster talks to). + + The zero'th tunnel is always invalid. + +ip_pool[] + + A table holding all the IP address in the pool. As addresses + are used, they are tagged with the username of the session, + and the session index. + + When they are free'd the username tag ISN'T cleared. This is + to ensure that were possible we re-allocate the same IP + address back to the same user. + +radius[] + A table holding active radius session. Whenever a radius + conversation is needed (login, accounting et al), a radius + session is allocated. + +char **ip_hash + + A mapping of IP address to session structure. This is a + tenary tree (each byte of the IP address is used in turn + to index that level of the tree). + + If the value is postive, it's considered to be an index + into the session table. + + If it's negative, it's considered to be an index into + the ip_pool[] table. + + If it's zero, then there is no associated value. + + + +============================================================ + +Clustering: How it works. + + At a high level, the various members of the cluster elect +a master. All other machines become slaves. Slaves handle normal +packet forwarding. Whenever a slave get a 'state changing' packet +(i.e. tunnel setup/teardown, session setup etc) it _doesn't_ handle +it, but instead forwards it to the master. + + 'State changing' it defined to be "a packet that would cause +a change in either a session or tunnel structure that isn't just +updating the idle time or byte counters". In practise, this means +also all LCP, IPCP, and L2TP control packets. + + The master then handles the packet normally, updating +the session/tunnel structures. The changed structures are then +flooded out to the slaves via a multicast packet. + + +Heartbeat'ing: + The master sends out a multicast 'heartbeat' packet +at least once every second. This packet contains a sequence number, +and any changes to the session/tunnel structures that have +been queued up. If there is room in the packet, it also sends +out a number of extra session/tunnel structures. + + The sending out of 'extra' structures means that the +master will slowly walk the entire session and tunnel tables. +This allows a new slave to catch-up on cluster state. + + + Each heartbeat has an in-order sequence number. If a +slave receives a heartbeat with a sequence number other than +the one it was expecting, it drops the unexpected packet and +unicasts C_LASTSEEN to tell the master the last heartbeast it +had seen. The master normally than unicasts the missing packets +to the slave. If the master doesn't have the old packet any more +(i.e. it's outside the transmission window) then the master +unicasts C_KILL to the slave asking it to die. (it should then +restart, and catchup on state via the normal process). + +Ping'ing: + All slaves send out a 'ping' once per second as a +multicast packet. This 'ping' contains the slave's ip address, +and most importantly: The number of seconds from epoch +that the slave started up. (I.e. the value of time(2) at +that the process started). + + +Elections: + + All machines start up as slaves. + + Each slave listens for a heartbeat from the master. +If a slave fails to hear a heartbeat for N seconds then it +checks to see if it should become master. + + A slave will become master if: + * It hasn't heard from a master for N seconds. + * It is the oldest of all it's peers (the other slaves). + * In the event of a tie, the machine with the + lowest IP address will win. + + A 'peer' is any other slave machine that's send out a + ping in the last N seconds. (i.e. we must have seen + a recent ping from that slave for it to be considered). + + The upshot of this is that no special communication + takes place when a slave becomes a master. + + On initial cluster startup, the process would be (for example) + + * 3 machines startup simultaneously, all as slaves. + * each machine sends out a multicast 'ping' every second. + * 15 seconds later, the machine with the lowest IP + address becomes master, and starts sending + out heartbeats. + * The remaining two machine hear the heartbeat and + set that machine as their master. + +Becoming master: + + When a slave become master, the only structure maintained up + to date are the tunnel and session structures. This means + the master will rebuild a number of mappings. + + #0. All the session and table structures are marked as + defined. (Even if we weren't fully up to date, it's + too late now). + + #1. All the token bucket filters are re-build from scratch + with the associated session to tbf pointers being re-built. + +TODO: These changed tbf pointers aren't flooded to the slave right away! +Throttled session could take a couple of minutes to start working again +on master failover! + + #2. The ipcache to session hash is rebuilt. (This isn't + strictly needed, but it's a safety measure). + + #3. The mapping from the ippool into the session table + (and vice versa) is re-built. + + +Becoming slave: + + At startup the entire session and table structures are + marked undefined. + + As it seens updates from the master, the updated structures + are marked as defined. + + When there are no undefined tunnel or session structures, the + slave marks itself as 'up-to-date' and starts advertising routes + (if BGP is enabled). From 7aa420ce9f2c55049b061c7df836ccc990303ebf Mon Sep 17 00:00:00 2001 From: David Parrish Date: Mon, 28 Jun 2004 02:43:13 +0000 Subject: [PATCH 031/482] * Update cli callbacks to work with libcli 1.6. This supports privileged and unprivileged commands, as well as a configuration mode * Add help for all cli commands * Add "show version" command * Fix uptime counter display * Fix nasty bug where cluster basetime can be set to 0 when sending initial heartbeat * Don't rmmod ip_conntrack, as this can take a lot of time * Re-order logging in routeset such that the action is given before any error * Use the correct gateway address when deleting routes * Remove any routes when address changes * Require authentication if telnet from remote ip * Require enable password always * Return error if show pool done on slave * We MUST immediately exit if we're the wrong master! --- Makefile | 4 +- arp.c | 4 + bgp.c | 26 ++- bgp.h | 4 +- cli.c | 633 ++++++++++++++++++++++++++++++++++++++-------------- cluster.c | 41 ++-- constants.c | 4 + control.c | 4 + garden.c | 8 +- icmp.c | 4 + l2tpns.c | 189 +++++++++------- l2tpns.h | 52 ++++- ll.c | 3 +- md5.c | 2 + ppp.c | 51 ++--- radius.c | 49 ++-- tbf.c | 17 +- util.c | 2 + 18 files changed, 765 insertions(+), 332 deletions(-) diff --git a/Makefile b/Makefile index 04bf448..8af2656 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,7 @@ install: all ## Dependencies: (autogenerated) ## md5.o: md5.c md5.h icmp.o: icmp.c l2tpns.h config.h -cli.o: cli.c l2tpns.h config.h util.h cluster.h tbf.h bgp.h +cli.o: cli.c l2tpns.h config.h util.h cluster.h tbf.h ll.h bgp.h l2tpns.o: l2tpns.c md5.h l2tpns.h config.h cluster.h plugin.h ll.h \ constants.h control.h util.h tbf.h bgp.h ppp.o: ppp.c l2tpns.h config.h constants.h plugin.h util.h tbf.h \ @@ -85,7 +85,7 @@ constants.o: constants.c constants.h ll.o: ll.c ll.h control.o: control.c control.h util.o: util.c l2tpns.h config.h -tbf.o: tbf.c l2tpns.h config.h tbf.h +tbf.o: tbf.c l2tpns.h config.h util.h tbf.h bgp.o: bgp.c l2tpns.h config.h bgp.h util.h garden.so: garden.c l2tpns.h config.h plugin.h control.h autothrottle.so: autothrottle.c l2tpns.h config.h plugin.h control.h diff --git a/arp.c b/arp.c index 05929cd..68a4bba 100644 --- a/arp.c +++ b/arp.c @@ -1,3 +1,7 @@ +// L2TPNS: arp + +char const *cvs_id_arp = "$Id: arp.c,v 1.3 2004-06-28 02:43:13 fred_nerk Exp $"; + #include #include #include diff --git a/bgp.c b/bgp.c index ec6d9c2..23255ee 100644 --- a/bgp.c +++ b/bgp.c @@ -10,7 +10,7 @@ * nor RFC2385 (which requires a kernel patch on 2.4 kernels). */ -/* $Id: bgp.c,v 1.1 2004-06-23 03:52:24 fred_nerk Exp $ */ +char const *cvs_id_bgp = "$Id: bgp.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; #include #include @@ -1188,6 +1188,12 @@ int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc) if (!bgp_configured) return CLI_OK; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "A.B.C.D", "BGP peer address", + "NAME", "BGP peer name", + NULL); + cli_print(cli, "BGPv%d router identifier %s, local AS number %d, " "hold time %ds", BGP_VERSION, inet_toa(my_address), (int) our_as, BGP_HOLD_TIME); @@ -1235,6 +1241,12 @@ int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc) if (!bgp_configured) return CLI_OK; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "A.B.C.D", "BGP peer address", + "NAME", "BGP peer name", + NULL); + for (i = 0; i < BGP_NUM_PEERS; i++) { if (bgp_peers[i].state != Established) @@ -1262,6 +1274,12 @@ int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc if (!bgp_configured) return CLI_OK; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "A.B.C.D", "BGP peer address", + "NAME", "BGP peer name", + NULL); + for (i = 0; i < BGP_NUM_PEERS; i++) { if (bgp_peers[i].state != Established) @@ -1290,6 +1308,12 @@ int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc) if (!bgp_configured) return CLI_OK; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "A.B.C.D", "BGP peer address", + "NAME", "BGP peer name", + NULL); + for (i = 0; i < BGP_NUM_PEERS; i++) { if (!*bgp_peers[i].name) diff --git a/bgp.h b/bgp.h index 635a65d..1c33ce6 100644 --- a/bgp.h +++ b/bgp.h @@ -1,5 +1,5 @@ /* BGPv4 (RFC1771) */ -/* $Id: bgp.h,v 1.1 2004-06-23 03:52:24 fred_nerk Exp $ */ +/* $Id: bgp.h,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $ */ #ifndef __BGP_H__ #define __BGP_H__ @@ -199,4 +199,6 @@ int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc); int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc); int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc); +extern char const *cvs_id_bgp; + #endif /* __BGP_H__ */ diff --git a/cli.c b/cli.c index 892b8d1..902add0 100644 --- a/cli.c +++ b/cli.c @@ -1,14 +1,18 @@ // L2TPNS Command Line Interface -// $Id: cli.c,v 1.5 2004-06-23 03:52:24 fred_nerk Exp $ // vim: sw=4 ts=8 +char const *cvs_name = "$Name: $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.6 2004-06-28 02:43:13 fred_nerk Exp $"; + #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -17,11 +21,13 @@ #include #include #include +#include #include #include "l2tpns.h" #include "util.h" #include "cluster.h" #include "tbf.h" +#include "ll.h" #ifdef BGP #include "bgp.h" #endif @@ -39,13 +45,10 @@ extern sessionidt *cli_session_kill; extern tunnelidt *cli_tunnel_kill; 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.5 2004-06-23 03:52:24 fred_nerk Exp $"; - char *debug_levels[] = { "CRIT", "ERROR", @@ -109,71 +112,72 @@ void init_cli() struct sockaddr_in addr; cli = cli_init(); + cli_set_hostname(cli, "l2tpns"); - c = cli_register_command(cli, NULL, "show", NULL, NULL); - cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana"); + c = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "banana", cmd_show_banana, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a banana"); #ifdef BGP - cli_register_command(cli, c, "bgp", cmd_show_bgp, "Show BGP status"); + cli_register_command(cli, c, "bgp", cmd_show_bgp, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show BGP status"); #endif /* BGP */ - cli_register_command(cli, c, "cluster", cmd_show_cluster, "Show cluster information"); - cli_register_command(cli, c, "ipcache", cmd_show_ipcache, "Show contents of the IP cache"); - cli_register_command(cli, c, "plugins", cmd_show_plugins, "List all installed plugins"); - cli_register_command(cli, c, "pool", cmd_show_pool, "Show the IP address allocation pool"); - cli_register_command(cli, c, "radius", cmd_show_radius, "Show active radius queries"); - cli_register_command(cli, c, "running-config", cmd_show_run, "Show the currently running configuration"); - 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, "tbf", cmd_show_tbf, "List all token bucket filters in use"); - cli_register_command(cli, c, "throttle", cmd_show_throttle, "List all throttled sessions and associated TBFs"); - 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 or details of selected user"); - cli_register_command(cli, c, "version", cmd_show_version, "Show currently running software version"); + cli_register_command(cli, c, "cluster", cmd_show_cluster, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show cluster information"); + cli_register_command(cli, c, "ipcache", cmd_show_ipcache, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show contents of the IP cache"); + cli_register_command(cli, c, "plugins", cmd_show_plugins, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all installed plugins"); + cli_register_command(cli, c, "pool", cmd_show_pool, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the IP address allocation pool"); + cli_register_command(cli, c, "radius", cmd_show_radius, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show active radius queries"); + cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the currently running configuration"); + cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session"); + cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use"); + cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs"); + cli_register_command(cli, c, "tunnels", cmd_show_tunnels, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of tunnels or details for a single tunnel"); + cli_register_command(cli, c, "users", cmd_show_users, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of all connected users or details of selected user"); + cli_register_command(cli, c, "version", cmd_show_version, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show currently running software version"); - c2 = cli_register_command(cli, c, "histogram", NULL, NULL); - cli_register_command(cli, c2, "idle", cmd_show_hist_idle, "Show histogram of session idle times"); - cli_register_command(cli, c2, "open", cmd_show_hist_open, "Show histogram of session durations"); + c2 = cli_register_command(cli, c, "histogram", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c2, "idle", cmd_show_hist_idle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show histogram of session idle times"); + cli_register_command(cli, c2, "open", cmd_show_hist_open, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show histogram of session durations"); #ifdef STATISTICS - cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals"); + cli_register_command(cli, c, "counters", cmd_show_counters, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "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, "Clear internal counters"); + c = cli_register_command(cli, NULL, "clear", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "counters", cmd_clear_counters, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Clear internal counters"); #endif - cli_register_command(cli, NULL, "uptime", cmd_uptime, "Show uptime and bandwidth utilisation"); + cli_register_command(cli, NULL, "uptime", cmd_uptime, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "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"); + c = cli_register_command(cli, NULL, "write", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "memory", cmd_write_memory, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Save the running config to flash"); + cli_register_command(cli, c, "terminal", cmd_show_run, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "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"); - cli_register_command(cli, NULL, "debug", cmd_debug, "Set the level of logging that is shown on the console"); + cli_register_command(cli, NULL, "snoop", cmd_snoop, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Temporarily enable interception for a user"); + cli_register_command(cli, NULL, "throttle", cmd_throttle, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Temporarily enable throttling for a user"); + cli_register_command(cli, NULL, "debug", cmd_debug, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Set the level of logging that is shown on the console"); - c = cli_register_command(cli, NULL, "suspend", NULL, NULL); - cli_register_command(cli, c, "bgp", cmd_suspend_bgp, "Withdraw routes from BGP peer"); + c = cli_register_command(cli, NULL, "suspend", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "bgp", cmd_suspend_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Withdraw routes from BGP peer"); - c = cli_register_command(cli, NULL, "no", NULL, 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"); - c2 = cli_register_command(cli, c, "suspend", NULL, NULL); - cli_register_command(cli, c2, "bgp", cmd_no_suspend_bgp, "Advertise routes to BGP peer"); + c = cli_register_command(cli, NULL, "no", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "snoop", cmd_no_snoop, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Temporarily disable interception for a user"); + cli_register_command(cli, c, "throttle", cmd_no_throttle, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Temporarily disable throttling for a user"); + cli_register_command(cli, c, "debug", cmd_no_debug, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Turn off logging of a certain level of debugging"); + c2 = cli_register_command(cli, c, "suspend", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c2, "bgp", cmd_no_suspend_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Advertise routes to BGP peer"); - c = cli_register_command(cli, NULL, "drop", NULL, 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"); + c = cli_register_command(cli, NULL, "drop", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "user", cmd_drop_user, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a user"); + cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a tunnel and all sessions on that tunnel"); + cli_register_command(cli, c, "session", cmd_drop_session, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Disconnect a session"); - c = cli_register_command(cli, NULL, "restart", NULL, NULL); - cli_register_command(cli, c, "bgp", cmd_restart_bgp, "Restart BGP"); + c = cli_register_command(cli, NULL, "restart", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "bgp", cmd_restart_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Restart BGP"); - 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, "load", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "plugin", cmd_load_plugin, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Load a plugin"); - c = cli_register_command(cli, NULL, "remove", NULL, NULL); - cli_register_command(cli, c, "plugin", cmd_remove_plugin, "Remove a plugin"); + c = cli_register_command(cli, NULL, "remove", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); + cli_register_command(cli, c, "plugin", cmd_remove_plugin, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Remove a plugin"); - cli_register_command(cli, NULL, "set", cmd_set, "Set a configuration variable"); + cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Set a configuration variable"); // Enable regular processing cli_regular(cli, regular_stuff); @@ -193,9 +197,17 @@ void init_cli() if (!*buf) continue; if (!(p = strchr((char *)buf, ':'))) continue; *p++ = 0; + if (!strcmp(buf, "enable")) + { + cli_allow_enable(cli, p); + log(3, 0, 0, 0, "Setting enable password\n"); + } + else + { cli_allow_user(cli, buf, p); log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf); } + } fclose(f); } @@ -263,7 +275,33 @@ void cli_do(int sockfd) close(bgp_peers[i].sock); #endif /* BGP */ - log(3, 0, 0, 0, "Accepted connection to CLI\n"); + { + int require_auth = 1; + struct sockaddr_in addr; + int l = sizeof(addr); + if (getpeername(sockfd, (struct sockaddr *)&addr, &l) == 0) + { + log(3, 0, 0, 0, "Accepted connection to CLI from %s\n", inet_toa(addr.sin_addr.s_addr)); + require_auth = addr.sin_addr.s_addr != inet_addr("127.0.0.1"); + } + else + log(0, 0, 0, 0, "getpeername() failed on cli socket. Requiring authentication: %s\n", strerror(errno)); + + if (require_auth) + { + log(3, 0, 0, 0, "CLI is remote, requiring authentication\n"); + if (!cli->users) /* paranoia */ + { + log(0, 0, 0, 0, "No users for remote authentication! Exiting CLI\n"); + exit(0); + } + } + else + { + /* no username/pass required */ + cli->users = 0; + } + } debug_session = 0; debug_tunnel = 0; @@ -293,15 +331,56 @@ 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_file(cli, fh, PRIVILEGE_PRIVILEGED); cli_print_callback(cli, NULL); } +int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...) +{ + va_list ap; + char *desc; + char buf[16]; + char *p; + + va_start(ap, entry); + while (entry) + { + /* allow one %d */ + if ((p = strchr(entry, '%')) && !strchr(p+1, '%') && p[1] == 'd') + { + int v = va_arg(ap, int); + snprintf(buf, sizeof(buf), entry, v); + p = buf; + } + else + p = entry; + + desc = va_arg(ap, char *); + if (desc && *desc) + cli_print(cli, " %-20s %s", p, desc); + else + cli_print(cli, " %s", p); + + entry = desc ? va_arg(ap, char *) : 0; + } + + va_end(ap); + if (cr_ok) + cli_print(cli, " "); + + return CLI_OK; +} + int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) { int i; time_t time_now; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "<1-%d>", MAXSESSION-1, "Show specific session by id", + NULL); + time(&time_now); if (argc > 0) { @@ -310,7 +389,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) { unsigned int s; s = atoi(argv[i]); - if (!s || s > MAXSESSION) + if (s <= 0 || s >= MAXSESSION) { cli_print(cli, "Invalid session id \"%s\"", argv[i]); continue; @@ -395,6 +474,19 @@ int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc) "Opening", }; + if (CLI_HELP_REQUESTED) + { + if (argc > 1) + return cli_arg_help(cli, 1, + "<1-%d>", MAXTUNNEL-1, "Show specific tunnel by id", + NULL); + + return cli_arg_help(cli, 1, + "all", "Show all tunnels, including unused", + "<1-%d>", MAXTUNNEL-1, "Show specific tunnel by id", + NULL); + } + time(&time_now); if (argc > 0) { @@ -410,7 +502,7 @@ int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc) char s[65535] = {0}; unsigned int t; t = atoi(argv[i]); - if (!t || t > MAXTUNNEL) + if (t <= 0 || t >= MAXTUNNEL) { cli_print(cli, "Invalid tunnel id \"%s\"", argv[i]); continue; @@ -464,6 +556,12 @@ int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc) char *sargv[32]; int sargc = 0; int i; + + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "USER", "Show details for specific username", + NULL); + for (i = 0; i < MAXSESSION; i++) { if (!session[i].opened) continue; @@ -495,6 +593,9 @@ int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc) int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) { + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "%-10s %-8s %-10s %-8s", "Ethernet", "Bytes", "Packets", "Errors"); cli_print(cli, "%-10s %8lu %8lu %8lu", "RX", GET_STAT(tap_rx_bytes), @@ -542,7 +643,7 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "%-30s%lu", "recv_forward", GET_STAT(recv_forward)); -#ifdef STAT_CALLS +#ifdef STATISTICS cli_print(cli, "\n%-30s%-10s", "Counter", "Value"); cli_print(cli, "-----------------------------------------"); cli_print(cli, "%-30s%lu", "call_processtap", GET_STAT(call_processtap)); @@ -578,8 +679,73 @@ 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 tag = 0; + int file = 0; + int i = 0; + + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "tag", "Include CVS release tag", + "file", "Include file versions", + NULL); + + for (i = 0; i < argc; i++) + if (!strcmp(argv[i], "tag")) + tag++; + else if (!strcmp(argv[i], "file")) + file++; + cli_print(cli, "L2TPNS %s", VERSION); - cli_print(cli, "ID: %s", rcs_id); + if (tag) + { + char const *p = strchr(cvs_name, ':'); + char const *e; + if (p) + { + p++; + while (isspace(*p)) + p++; + } + + if (!p || *p == '$') + p = "HEAD"; + + e = strpbrk(p, " \t$"); + cli_print(cli, "Tag: %.*s", e ? e - p + 1 : strlen(p), p); + } + + if (file) + { + extern linked_list *loaded_plugins; + void *p; + + cli_print(cli, "Files:"); + cli_print(cli, " %s", cvs_id_arp); +#ifdef BGP + cli_print(cli, " %s", cvs_id_bgp); +#endif /* BGP */ + cli_print(cli, " %s", cvs_id_cli); + cli_print(cli, " %s", cvs_id_cluster); + cli_print(cli, " %s", cvs_id_constants); + cli_print(cli, " %s", cvs_id_control); + cli_print(cli, " %s", cvs_id_icmp); + cli_print(cli, " %s", cvs_id_l2tpns); + cli_print(cli, " %s", cvs_id_ll); + cli_print(cli, " %s", cvs_id_md5); + cli_print(cli, " %s", cvs_id_ppp); + cli_print(cli, " %s", cvs_id_radius); + cli_print(cli, " %s", cvs_id_tbf); + cli_print(cli, " %s", cvs_id_util); + + ll_reset(loaded_plugins); + while ((p = ll_next(loaded_plugins))) + { + char const **id = dlsym(p, "cvs_id"); + if (id) + cli_print(cli, " %s", *id); + } + } + return CLI_OK; } @@ -589,6 +755,22 @@ int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) int used = 0, free = 0, show_all = 0; time_t time_now; + if (!config->cluster_iam_master) + { + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + return CLI_OK; + } + + if (CLI_HELP_REQUESTED) + { + if (argc > 1) + return cli_arg_help(cli, 1, NULL); + + return cli_arg_help(cli, 1, + "all", "Show all pool addresses, including unused", + NULL); + } + if (argc > 0 && strcmp(argv[0], "all") == 0) show_all = 1; @@ -632,6 +814,9 @@ void print_save_config(struct cli_def *cli, char *string) int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc) { + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + if ((save_config_fh = fopen(config->config_file, "w"))) { cli_print(cli, "Writing configuration"); @@ -651,6 +836,9 @@ int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "# Current configuration:"); for (i = 0; config_values[i].key; i++) @@ -697,6 +885,16 @@ int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc) int i, free = 0, used = 0, show_all = 0; time_t time_now; + if (CLI_HELP_REQUESTED) + { + if (argc > 1) + return cli_arg_help(cli, 1, NULL); + + return cli_arg_help(cli, 1, + "all", "Show all RADIUS sessions, including unused", + NULL); + } + cli_print(cli, "%6s%5s%6s%9s%9s%4s", "Radius", "Sock", "State", "Session", "Retry", "Try"); time(&time_now); @@ -730,6 +928,10 @@ int cmd_show_radius(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 i; + + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "Plugins currently loaded:"); for (i = 0; i < MAXPLUGINS; i++) { @@ -744,6 +946,10 @@ int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc) int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc) { int i; + + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "Token bucket filters:"); cli_print(cli, "%-6s %8s %-4s", "ID", "Handle", "Used"); for (i = 0; i < MAXSESSION; i++) @@ -761,6 +967,9 @@ int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc) int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc) { + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, " _\n" "//\\\n" "V \\\n" @@ -781,6 +990,9 @@ int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc) int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc) { + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "Counters cleared"); SET_STAT(last_reset, time(NULL)); return CLI_OK; @@ -791,6 +1003,10 @@ int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "USER", "Username of session to drop", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -801,14 +1017,6 @@ int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a user to drop"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "username ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { @@ -842,6 +1050,10 @@ int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) int i; tunnelidt tid; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "<1-%d>", MAXTUNNEL-1, "Tunnel id to drop", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -852,22 +1064,14 @@ int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a tunnel to drop"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "tunnel_id ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { int x; - if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL)) + if ((tid = atol(argv[i])) <= 0 || (tid >= MAXTUNNEL)) { - cli_print(cli, "Invalid tunnel ID (%d - %d)", 0, MAXTUNNEL); + cli_print(cli, "Invalid tunnel ID (1-%d)", MAXTUNNEL-1); continue; } @@ -902,6 +1106,10 @@ int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "<1-%d>", MAXSESSION-1, "Session id to drop", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -912,20 +1120,12 @@ int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a session id to drop"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "session_id ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION)) { - cli_print(cli, "Invalid session ID (%d - %d)", 0, MAXSESSION); + cli_print(cli, "Invalid session ID (1-%d)", MAXSESSION-1); continue; } @@ -953,14 +1153,38 @@ 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 i; ipt ip; u16 port; sessionidt s; + if (CLI_HELP_REQUESTED) + { + switch (argc) + { + case 1: + return cli_arg_help(cli, 0, + "USER", "Username of session to snoop", NULL); + + case 2: + return cli_arg_help(cli, 0, + "A.B.C.D", "IP address of snoop destination", NULL); + + case 3: + return cli_arg_help(cli, 0, + "N", "Port of snoop destination", NULL); + + case 4: + if (!argv[3][1]) + return cli_arg_help(cli, 1, NULL); + + default: + return CLI_OK; + } + } + if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } @@ -970,16 +1194,6 @@ int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "username ip port"); - return CLI_OK; - } - } - - if (!(s = sessionbyuser(argv[0]))) { cli_print(cli, "User %s is not connected", argv[0]); @@ -1012,6 +1226,10 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "USER", "Username of session to un-snoop", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -1023,14 +1241,6 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a user"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "username ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { @@ -1052,6 +1262,10 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "USER", "Username of session to throttle", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -1062,14 +1276,6 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a user"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "username ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { @@ -1091,6 +1297,10 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) int i; sessionidt s; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "USER", "Username of session to un-throttle", NULL); + if (!config->cluster_iam_master) { cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); @@ -1101,14 +1311,6 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "Specify a user"); return CLI_OK; } - for (i = 0; i < argc; i++) - { - if (strchr(argv[i], '?')) - { - cli_print(cli, "username ..."); - return CLI_OK; - } - } for (i = 0; i < argc; i++) { @@ -1119,8 +1321,9 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) } throttle_session(s, 0); - cli_print(cli, "unthrottling user %s", argv[i]); + cli_print(cli, "Unthrottling user %s", argv[i]); } + return CLI_OK; } @@ -1128,47 +1331,61 @@ int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "all", "Enable debugging for all except \"data\"", + "critical", "", // FIXME: add descriptions + "error", "", + "warning", "", + "info", "", + "calls", "", + "data", "", + NULL); + if (!argc) { - 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, ""); + char *p = (char *) &debug_flags; + for (i = 0; i < sizeof(debug_flags); i++) + { + if (p[i]) + { + cli_print(cli, "Currently debugging:%s%s%s%s%s%s", + (debug_flags.critical) ? " critical" : "", + (debug_flags.error) ? " error" : "", + (debug_flags.warning) ? " warning" : "", + (debug_flags.info) ? " info" : "", + (debug_flags.calls) ? " calls" : "", + (debug_flags.data) ? " data" : ""); + + return CLI_OK; + } + } + + cli_print(cli, "Debugging off"); return CLI_OK; } for (i = 0; i < argc; i++) { - if (*argv[i] == '?') - { - 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; - } - } + int len = strlen(argv[i]); - for (i = 0; i < argc; i++) - { - if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 1; - if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 1; - if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 1; - if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 1; - if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 1; - if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 1; - if (strcasecmp(argv[i], "all") == 0) + if (argv[i][0] == 'c' && len < 2) + len = 2; /* distinguish [cr]itical from [ca]lls */ + + if (!strncasecmp(argv[i], "critical", len)) { debug_flags.critical = 1; continue; } + if (!strncasecmp(argv[i], "error", len)) { debug_flags.error = 1; continue; } + if (!strncasecmp(argv[i], "warning", len)) { debug_flags.warning = 1; continue; } + if (!strncasecmp(argv[i], "info", len)) { debug_flags.info = 1; continue; } + if (!strncasecmp(argv[i], "calls", len)) { debug_flags.calls = 1; continue; } + if (!strncasecmp(argv[i], "data", len)) { debug_flags.data = 1; continue; } + if (!strncasecmp(argv[i], "all", len)) { memset(&debug_flags, 1, sizeof(debug_flags)); debug_flags.data = 0; + continue; } + + cli_print(cli, "Invalid debugging flag \"%s\"", argv[i]); } return CLI_OK; @@ -1178,15 +1395,43 @@ int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, 1, + "all", "Disable all debugging", + "critical", "", // FIXME: add descriptions + "error", "", + "warning", "", + "info", "", + "calls", "", + "data", "", + NULL); + + if (!argc) + { + memset(&debug_flags, 0, sizeof(debug_flags)); + return CLI_OK; + } + for (i = 0; i < argc; i++) { - if (strcasecmp(argv[i], "critical") == 0) debug_flags.critical = 0; - if (strcasecmp(argv[i], "error") == 0) debug_flags.error = 0; - if (strcasecmp(argv[i], "warning") == 0) debug_flags.warning = 0; - if (strcasecmp(argv[i], "info") == 0) debug_flags.info = 0; - if (strcasecmp(argv[i], "calls") == 0) debug_flags.calls = 0; - if (strcasecmp(argv[i], "data") == 0) debug_flags.data = 0; - if (strcasecmp(argv[i], "all") == 0) memset(&debug_flags, 0, sizeof(debug_flags)); + int len = strlen(argv[i]); + + if (argv[i][0] == 'c' && len < 2) + len = 2; /* distinguish [cr]itical from [ca]lls */ + + if (!strncasecmp(argv[i], "critical", len)) { debug_flags.critical = 0; continue; } + if (!strncasecmp(argv[i], "error", len)) { debug_flags.error = 0; continue; } + if (!strncasecmp(argv[i], "warning", len)) { debug_flags.warning = 0; continue; } + if (!strncasecmp(argv[i], "info", len)) { debug_flags.info = 0; continue; } + if (!strncasecmp(argv[i], "calls", len)) { debug_flags.calls = 0; continue; } + if (!strncasecmp(argv[i], "data", len)) { debug_flags.data = 0; continue; } + if (!strncasecmp(argv[i], "all", len)) + { + memset(&debug_flags, 0, sizeof(debug_flags)); + continue; + } + + cli_print(cli, "Invalid debugging flag \"%s\"", argv[i]); } return CLI_OK; @@ -1195,6 +1440,11 @@ int cmd_no_debug(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 i, firstfree = 0; + + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "PLUGIN", "Name of plugin to load", NULL); + if (argc != 1) { cli_print(cli, "Specify a plugin to load"); @@ -1226,6 +1476,10 @@ int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + return cli_arg_help(cli, argc > 1, + "PLUGIN", "Name of plugin to unload", NULL); + if (argc != 1) { cli_print(cli, "Specify a plugin to remove"); @@ -1246,17 +1500,36 @@ int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } -char *duration(time_t seconds) +char *duration(time_t secs) { static char *buf = NULL; + int p = 0; + 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); + if (secs >= 86400) + { + int days = secs / 86400; + p = sprintf(buf, "%d day%s, ", days, days > 1 ? "s" : ""); + secs %= 86400; + } + + if (secs >= 3600) + { + int mins = secs / 60; + int hrs = mins / 60; + + mins %= 60; + sprintf(buf + p, "%d:%02d", hrs, mins); + } + else if (secs >= 60) + { + int mins = secs / 60; + sprintf(buf + p, "%d min%s", mins, mins > 1 ? "s" : ""); + } else - sprintf(buf, "%lu sec", seconds); + sprintf(buf, "%ld sec%s", secs, secs > 1 ? "s" : ""); + return buf; } @@ -1267,6 +1540,9 @@ int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc) int i, num_sessions = 0; time_t time_now; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + fh = fopen("/proc/loadavg", "r"); fgets(buf, 100, fh); fclose(fh); @@ -1282,7 +1558,7 @@ int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "%s up %s, %d users, load average: %s, %s, %s", buf, - duration(abs(time_now - config->start_time)), + duration(time_now - config->start_time), num_sessions, loads[0], loads[1], loads[2] ); @@ -1298,9 +1574,36 @@ int cmd_set(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + { + switch (argc) + { + case 1: + { + int len = strlen(argv[0])-1; + for (i = 0; config_values[i].key; i++) + if (!len || !strncmp(argv[0], config_values[i].key, len)) + cli_print(cli, " %s", config_values[i].key); + } + + return CLI_OK; + + case 2: + return cli_arg_help(cli, 0, + "VALUE", "Value for variable", NULL); + + case 3: + if (!argv[2][1]) + return cli_arg_help(cli, 1, NULL); + + default: + return CLI_OK; + } + } + if (argc != 2) { - cli_print(cli, "Usage: set "); + cli_print(cli, "Specify variable and value"); return CLI_OK; } diff --git a/cluster.c b/cluster.c index 6f4695d..4c4efde 100644 --- a/cluster.c +++ b/cluster.c @@ -1,5 +1,6 @@ // L2TPNS Clustering Stuff -// $Id: cluster.c,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ + +char const *cvs_id_cluster = "$Id: cluster.c,v 1.4 2004-06-28 02:43:13 fred_nerk Exp $"; #include #include @@ -134,7 +135,7 @@ int cluster_init() return -1; } - config->cluster_last_hb = config->current_time; + config->cluster_last_hb = TIME; config->cluster_seq_number = -1; return cluster_sockfd; @@ -435,18 +436,18 @@ void cluster_check_master(void) int i, count, tcount, high_sid = 0; int last_free = 0; int had_peers = have_peers; - clockt t = config->current_time; + clockt t = TIME; - if (config->current_time < (config->cluster_last_hb + HB_TIMEOUT) ) + if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout) ) return; // Everything's ok. return. if (!config->cluster_iam_master) log(0,0,0,0, "Master timed out! Holding election...\n"); - config->cluster_last_hb = config->current_time + 1; + config->cluster_last_hb = TIME + 1; for (i = have_peers = 0; i < num_peers ; ++i) { - if ((peers[i].timestamp + HB_TIMEOUT) < t) + if ((peers[i].timestamp + config->cluster_hb_timeout) < t) continue; // Stale peer! Skip them. if (!peers[i].basetime) @@ -721,7 +722,7 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) } if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? - log(0,0,0,0, "Overrun the heartbeat buffer! This is fatal. Exiting. (size %d)\n", p - buff); + log(0,0,0,0, "FATAL: Overran the heartbeat buffer! This is fatal. Exiting. (size %d)\n", p - buff); kill(0, SIGTERM); } @@ -744,6 +745,9 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) // // Fill out the packet with tunnels from the tunnel table... + // This effectively means we walk the tunnel table more quickly + // than the session table. This is good because stuffing up a + // tunnel is a much bigger deal than stuffing up a session. // while ( (p + sizeof(u32) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE) ) { @@ -891,7 +895,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) // This peer already exists. Just update the timestamp. peers[i].basetime = basetime; - peers[i].timestamp = config->current_time; + peers[i].timestamp = TIME; break; } @@ -904,7 +908,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) { if (peers[i].peer != peer) continue; - if ((peers[i].timestamp + HB_TIMEOUT * 10) < config->current_time) // Stale. + if ((peers[i].timestamp + config->cluster_hb_timeout * 10) < TIME) // Stale. break; } @@ -917,7 +921,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) peers[i].peer = peer; peers[i].basetime = basetime; - peers[i].timestamp = config->current_time; + peers[i].timestamp = TIME; if (i == num_peers) ++num_peers; @@ -1081,14 +1085,14 @@ static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u if (config->cluster_seq_number == -1) // Don't have one. Just align to the master... config->cluster_seq_number = h->seq; - config->cluster_last_hb = config->current_time; // Reset to ensure that we don't become master!! + config->cluster_last_hb = TIME; // Reset to ensure that we don't become master!! if (config->cluster_seq_number != h->seq) { // Out of sequence heartbeat! log(1,0,0,0, "HB: Got seq# %d but was expecting %d. asking for resend.\n", h->seq, config->cluster_seq_number); peer_send_message(addr, C_LASTSEEN, config->cluster_seq_number, NULL, 0); - config->cluster_last_hb = config->current_time; // Reset to ensure that we don't become master!! + config->cluster_last_hb = TIME; // Reset to ensure that we don't become master!! // Just drop the packet. The master will resend it as part of the catchup. @@ -1187,7 +1191,7 @@ static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u } config->cluster_master_address = addr; - config->cluster_last_hb = config->current_time; // Successfully received a heartbeat! + config->cluster_last_hb = TIME; // Successfully received a heartbeat! return 0; shortpacket: @@ -1282,12 +1286,12 @@ int processcluster(char * data, int size, u32 addr) } if (addr != config->cluster_master_address) { - log(0,0,0,0, "Received a C_KILL from %s which doesn't match config->cluster_master_address (%x)", + log(0,0,0,0, "Received a C_KILL from %s which doesn't match config->cluster_master_address (%x)\n", inet_toa(addr), config->cluster_master_address); // We can only warn about it. The master might really have switched! } - log(0,0,0,0, "Received a valid C_KILL: I'm going to die now."); + log(0,0,0,0, "Received a valid C_KILL: I'm going to die now.\n"); kill(0, SIGTERM); exit(0); // Lets be paranoid; return -1; // Just signalling the compiler. @@ -1313,6 +1317,9 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc) { int i; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "Cluster status : %s", config->cluster_iam_master ? "Master" : "Slave" ); cli_print(cli, "My address : %s", inet_toa(my_address)); cli_print(cli, "VIP address : %s", inet_toa(config->bind_address)); @@ -1322,7 +1329,7 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { cli_print(cli, "My master : %s (last heartbeat %.1f seconds old)", config->cluster_master_address ? inet_toa(config->cluster_master_address) : "Not defined", - 0.1 * (config->current_time - config->cluster_last_hb)); + 0.1 * (TIME - config->cluster_last_hb)); cli_print(cli, "Uptodate : %s", config->cluster_iam_uptodate ? "Yes" : "No"); cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number); cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid); @@ -1339,7 +1346,7 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "%20s %10s %8s", "Address", "Basetime", "Age"); for (i = 0; i < num_peers; ++i) { cli_print(cli, "%20s %10d %8d", inet_toa(peers[i].peer), - peers[i].basetime, config->current_time - peers[i].timestamp); + peers[i].basetime, TIME - peers[i].timestamp); } return CLI_OK; } diff --git a/constants.c b/constants.c index c53a26f..c7609d2 100644 --- a/constants.c +++ b/constants.c @@ -1,3 +1,7 @@ +// L2TPNS: constants + +char const *cvs_id_constants = "$Id: constants.c,v 1.3 2004-06-28 02:43:13 fred_nerk Exp $"; + #include "constants.h" #include diff --git a/control.c b/control.c index 9d83522..db86e2d 100644 --- a/control.c +++ b/control.c @@ -1,3 +1,7 @@ +// L2TPNS: control + +char const *cvs_id_control = "$Id: control.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; + #include #include #include diff --git a/garden.c b/garden.c index 7477089..5e3d368 100644 --- a/garden.c +++ b/garden.c @@ -7,6 +7,8 @@ #include "plugin.h" #include "control.h" +char const *cvs_id = "$Id: garden.c,v 1.7 2004-06-28 02:43:13 fred_nerk Exp $"; + int __plugin_api_version = 1; static struct pluginfuncs *p = 0; @@ -19,6 +21,7 @@ char *up_commands[] = { "iptables -t nat -N garden_users >/dev/null 2>&1",// Empty chain, users added/removed by garden_session "iptables -t nat -F garden_users", "iptables -t nat -A PREROUTING -j garden_users", // DNAT any users on the garden_users chain + "sysctl -w net.ipv4.ip_conntrack_max=256000 >/dev/null", // lots of entries NULL, }; @@ -28,7 +31,10 @@ char *down_commands[] = { "iptables -t nat -X garden_users", "iptables -t nat -F garden", "iptables -t nat -X garden", - "rmmod iptable_nat ip_conntrack", + "rmmod iptable_nat", // Should also remove ip_conntrack, but + // doing so can take hours... literally. + // If a master is re-started as a slave, + // either rmmod manually, or reboot. NULL, }; diff --git a/icmp.c b/icmp.c index f7d73c6..853d665 100644 --- a/icmp.c +++ b/icmp.c @@ -1,3 +1,7 @@ +// L2TPNS: icmp + +char const *cvs_id_icmp = "$Id: icmp.c,v 1.3 2004-06-28 02:43:13 fred_nerk Exp $"; + #include #include #include diff --git a/l2tpns.c b/l2tpns.c index ebf71ad..9ce2c79 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -1,8 +1,11 @@ // L2TP Network Server // Adrian Kennard 2002 -// (c) Copyrigth 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) +// Copyright (c) 2003, 2004 Optus Internet Engineering +// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.9 2004-06-28 02:43:13 fred_nerk Exp $"; + #include #include #include @@ -111,6 +114,8 @@ struct config_descriptt config_values[] = { CONFIG("icmp_rate", icmp_rate, INT), CONFIG("cluster_address", cluster_address, IP), CONFIG("cluster_interface", cluster_interface, STRING), + CONFIG("cluster_hb_interval", cluster_hb_interval, INT), + CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), #ifdef BGP CONFIG("as_number", as_number, SHORT), CONFIG("bgp_peer1", bgp_peer[0], STRING), @@ -289,7 +294,7 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) if (!mask) mask = 0xffffffff; - ip = ip & mask; // Force the ip to be the first one in the route. + ip &= mask; // Force the ip to be the first one in the route. memset(&r, 0, sizeof(r)); r.rt_dev = config->tapdevice; @@ -304,8 +309,6 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) r.rt_flags |= RTF_GATEWAY; else if (mask == 0xffffffff) r.rt_flags |= RTF_HOST; - if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) - log(0, 0, 0, 0, "routeset() error in ioctl: %s\n", strerror(errno)); log(1, ip, 0, 0, "Route %s %u.%u.%u.%u/%u.%u.%u.%u %u.%u.%u.%u\n", add ? "add" : "del", @@ -313,6 +316,9 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) mask >> 24, mask >> 16 & 0xff, mask >> 8 & 0xff, mask & 0xff, gw >> 24, gw >> 16 & 0xff, gw >> 8 & 0xff, gw & 0xff); + if (ioctl(ifrfd, add ? SIOCADDRT : SIOCDELRT, (void *) &r) < 0) + log(0, 0, 0, 0, "routeset() error in ioctl: %s\n", strerror(errno)); + #ifdef BGP if (add) bgp_add_route(htonl(ip), htonl(mask)); @@ -471,10 +477,8 @@ int lookup_ipmap(ipt ip) sessionidt sessionbyip(ipt ip) { int s = lookup_ipmap(ip); + CSTAT(call_sessionbyip); -#ifdef STAT_CALLS - STAT(call_sessionbyip); -#endif if (s > 0 && s < MAXSESSION && session[s].tunnel) return s; return 0; @@ -527,6 +531,9 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) int i, j, k, l; int count = 0; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + cli_print(cli, "%7s %s", "Sess#", "IP Address"); for (i = 0; i < 256; ++i) { @@ -560,9 +567,8 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) sessionidt sessionbyuser(char *username) { int s; -#ifdef STAT_CALLS - STAT(call_sessionbyuser); -#endif + CSTAT(call_sessionbyuser); + for (s = 1; s < MAXSESSION ; ++s) { if (session[s].walled_garden) continue; // Skip walled garden users. @@ -625,10 +631,9 @@ void processarp(u8 * buf, int len) ipt ip; sessionidt s; -#ifdef STAT_CALLS - STAT(call_processarp); -#endif + CSTAT(call_processarp); STAT(arp_recv); + if (len != 46) { log(0, 0, 0, 0, "Unexpected length ARP %d bytes\n", len); @@ -701,9 +706,8 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t) { struct sockaddr_in addr; -#ifdef STAT_CALLS - STAT(call_tunnelsend); -#endif + CSTAT(call_tunnelsend); + if (!t) { static int backtrace_count = 0; @@ -776,9 +780,9 @@ void processipout(u8 * buf, int len) int size = len; u8 b[MAXETHER + 20]; -#ifdef STAT_CALLS - STAT(call_processipout); -#endif + + CSTAT(call_processipout); + if (len < MIN_IP_SIZE) { log(1, 0, 0, 0, "Short IP, %d bytes\n", len); @@ -892,12 +896,12 @@ void send_ipout(sessionidt s, u8 *buf, int len) t = session[s].tunnel; sp = &session[s]; - log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); - - // Snooping this session, send it to ASIO + // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); + log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + // Add on L2TP header { u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); @@ -1078,9 +1082,9 @@ void sessionshutdown(sessionidt s, char *reason) int dead = session[s].die; int walled_garden = session[s].walled_garden; -#ifdef STAT_CALLS - STAT(call_sessionshutdown); -#endif + + CSTAT(call_sessionshutdown); + if (!session[s].tunnel) { log(3, session[s].ip, s, session[s].tunnel, "Called sessionshutdown on a session with no tunnel.\n"); @@ -1152,9 +1156,9 @@ void sendipcp(tunnelidt t, sessionidt s) u8 buf[MAXCONTROL]; u16 r = session[s].radius; u8 *q; -#ifdef STAT_CALLS - STAT(call_sendipcp); -#endif + + CSTAT(call_sendipcp); + if (!r) r = radiusnew(s); if (radius[r].state != RADIUSIPCP) @@ -1188,9 +1192,9 @@ void sendipcp(tunnelidt t, sessionidt s) // kill a session now void sessionkill(sessionidt s, char *reason) { -#ifdef STAT_CALLS - STAT(call_sessionkill); -#endif + + CSTAT(call_sessionkill); + sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed @@ -1210,9 +1214,8 @@ void tunnelkill(tunnelidt t, char *reason) { sessionidt s; controlt *c; -#ifdef STAT_CALLS - STAT(call_tunnelkill); -#endif + + CSTAT(call_tunnelkill); tunnel[t].state = TUNNELDIE; @@ -1242,9 +1245,9 @@ void tunnelkill(tunnelidt t, char *reason) void tunnelshutdown(tunnelidt t, char *reason) { sessionidt s; -#ifdef STAT_CALLS - STAT(call_tunnelshutdown); -#endif + + CSTAT(call_tunnelshutdown); + if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE) { // never set up, can immediately kill @@ -1277,9 +1280,9 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) u16 l = len, t = 0, s = 0, ns = 0, nr = 0; u8 *p = buf + 2; -#ifdef STAT_CALLS - STAT(call_processudp); -#endif + + CSTAT(call_processudp); + udp_rx += len; udp_rx_pkt++; log_hex(5, "UDP Data", buf, len); @@ -1996,9 +1999,9 @@ void processtap(u8 * buf, int len) log_hex(5, "Receive TAP Data", buf, len); STAT(tap_rx_packets); INC_STAT(tap_rx_bytes, len); -#ifdef STAT_CALLS - STAT(call_processtap); -#endif + + CSTAT(call_processtap); + eth_rx_pkt++; eth_rx += len; if (len < 22) @@ -2036,7 +2039,7 @@ int regular_cleanups(void) continue; if (radius[r].retry) { - if (radius[r].retry <= config->current_time) + if (radius[r].retry <= TIME) radiusretry(r); } else radius[r].retry = backoff(radius[r].try+1); // Is this really needed? --mo @@ -2044,7 +2047,7 @@ int regular_cleanups(void) for (t = 1; t < config->cluster_highest_tunnelid; t++) { // check for expired tunnels - if (tunnel[t].die && tunnel[t].die <= config->current_time) + if (tunnel[t].die && tunnel[t].die <= TIME) { STAT(tunnel_timeout); tunnelkill(t, "Expired"); @@ -2054,7 +2057,7 @@ int regular_cleanups(void) if (tunnel[t].retry && tunnel[t].controlc) { // resend pending messages as timeout on reply - if (tunnel[t].retry <= config->current_time) + if (tunnel[t].retry <= TIME) { controlt *c = tunnel[t].controls; u8 w = tunnel[t].window; @@ -2070,7 +2073,7 @@ int regular_cleanups(void) } } // Send hello - if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < config->current_time + 600) + if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < TIME + 600) { controlt *c = controlnew(6); // sending HELLO controladd(c, t, 0); // send the message @@ -2102,11 +2105,11 @@ int regular_cleanups(void) } count = 0; - for (i = 1; i < config->cluster_highest_sessionid; i++) + for (i = 1; i <= config->cluster_highest_sessionid; i++) { s++; - if (s >= config->cluster_highest_sessionid) + if (s > config->cluster_highest_sessionid) s = 1; if (!session[s].tunnel) // Session isn't in use @@ -2120,7 +2123,7 @@ int regular_cleanups(void) } // check for expired sessions - if (session[s].die && session[s].die <= config->current_time) + if (session[s].die && session[s].die <= TIME) { sessionkill(s, "Expired"); if (++count >= MAX_ACTIONS) break; @@ -2159,10 +2162,10 @@ int regular_cleanups(void) continue; } } - if (config->accounting_dir && next_acct <= config->current_time) + if (config->accounting_dir && next_acct <= TIME) { // Dump accounting data - next_acct = config->current_time + ACCT_TIME; + next_acct = TIME + ACCT_TIME; dump_acct_info(); } @@ -2186,9 +2189,9 @@ int still_busy(void) if (!tunnel[i].controlc) continue; - if (last_talked != config->current_time) { - log(2,0,0,0, "Tunnel %d still has an-acked control messages.\n", i); - last_talked = config->current_time; + if (last_talked != TIME) { + log(2,0,0,0, "Tunnel %d still has un-acked control messages.\n", i); + last_talked = TIME; } return 1; } @@ -2200,9 +2203,9 @@ int still_busy(void) if (radius[i].state == RADIUSWAIT) continue; - if (last_talked != config->current_time) { + if (last_talked != TIME) { log(2,0,0,0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session); - last_talked = config->current_time; + last_talked = TIME; } return 1; } @@ -2286,7 +2289,7 @@ void mainloop(void) n = select(n + 1, &r, 0, 0, &to); #endif /* BGP */ - config->current_time = now(); + TIME = now(); if (n < 0) { if (errno == EINTR) @@ -2369,8 +2372,8 @@ void mainloop(void) // Runs on all machines both master and slave. { static clockt last_run = 0; - if (last_run != config->current_time) { - last_run = config->current_time; + if (last_run != TIME) { + last_run = TIME; tbf_run_timer(); } } @@ -2540,9 +2543,9 @@ int assign_ip_address(sessionidt s) char *u = session[s].user; char reuse = 0; -#ifdef STAT_CALLS - STAT(call_assign_ip_address); -#endif + + CSTAT(call_assign_ip_address); + for (i = 1; i < ip_pool_size; i++) { if (!ip_address_pool[i].address || ip_address_pool[i].assigned) @@ -2606,9 +2609,9 @@ void free_ip_address(sessionidt s) ip_address_pool[i].session = 0; ip_address_pool[i].last = time_now; -#ifdef STAT_CALLS - STAT(call_free_ip_address); -#endif + + CSTAT(call_free_ip_address); + } // @@ -2644,7 +2647,7 @@ void rebuild_address_pool(void) if (ipid < 1) // Not found in the pool either? good. continue; - log(0, 0, i, 0, "Session %d has an IP address (%s) that was marked static, but is in the pool (%d)!", + log(0, 0, i, 0, "Session %d has an IP address (%s) that was marked static, but is in the pool (%d)!\n", i, inet_toa(session[i].ip), ipid); // Fall through and process it as part of the pool. @@ -2747,7 +2750,7 @@ void initippool() src = inet_addr(buf); if (src == INADDR_NONE) { - log(0, 0, 0, 0, "Invalid address pool IP %s", buf); + log(0, 0, 0, 0, "Invalid address pool IP %s\n", buf); exit(1); } // This entry is for a specific IP only @@ -2811,9 +2814,9 @@ void dump_acct_info() int i; FILE *f = NULL; -#ifdef STAT_CALLS - STAT(call_dump_acct_info); -#endif + + CSTAT(call_dump_acct_info); + strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t)); snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr); @@ -2907,9 +2910,9 @@ int main(int argc, char *argv[]) init_cli(); read_config_file(); - log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.8 2004-06-23 03:52:24 fred_nerk Exp $\n" - "(c) Copyright 2003, 2004 Optus Internet Engineering\n" - "(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); + log(0, 0, 0, 0, "L2TPNS version " VERSION "\n"); + log(0, 0, 0, 0, "Copyright (c) 2003, 2004 Optus Internet Engineering\n"); + log(0, 0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; rlim.rlim_cur = RLIM_INFINITY; @@ -3359,11 +3362,17 @@ void update_config() } memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->cleanup_interval) config->cleanup_interval = 10; - if (!config->multi_read_count) config->multi_read_count = 1; + if (!config->multi_read_count) config->multi_read_count = 10; if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); if (!*config->cluster_interface) strncpy(config->cluster_interface, DEFAULT_MCAST_INTERFACE, sizeof(config->cluster_interface) - 1); + if (!config->cluster_hb_interval) + config->cluster_hb_interval = PING_INTERVAL; // Heartbeat every 0.5 seconds. + + if (!config->cluster_hb_timeout) + config->cluster_hb_timeout = HB_TIMEOUT; // 10 missed heartbeat triggers an election. + config->reload_config = 0; } @@ -3391,9 +3400,9 @@ int sessionsetup(tunnelidt t, sessionidt s) char *user; sessionidt i; int r; -#ifdef STAT_CALLS - STAT(call_sessionsetup); -#endif + + CSTAT(call_sessionsetup); + log(3, session[s].ip, s, t, "Doing session setup for session\n"); @@ -3501,9 +3510,16 @@ int load_session(sessionidt s, sessiont *new) { if (session[s].ip) // If there's an old one, remove it. { + // Remove any routes if the IP has changed + for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++) + { + routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].ip, 0); + session[s].route[i].ip = 0; + } + if (session[s].ip_pool_index == -1) // static IP routeset(s, session[s].ip, 0, 0, 0); - else // It's part of the IP pool, add it manually. + else // It's part of the IP pool, remove it manually. uncache_ipmap(session[s].ip); } @@ -3515,7 +3531,7 @@ int load_session(sessionidt s, sessiont *new) } } - // Add routes for the session if they're new. + // Update routed networks for (i = 0; i < MAXROUTE && (session[s].route[i].ip || new->route[i].ip); i++) { if (new->route[i].ip == session[s].route[i].ip && @@ -3523,7 +3539,7 @@ int load_session(sessionidt s, sessiont *new) continue; if (session[s].route[i].ip) // Remove the old one if it exists. - routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].route[i].ip, 0); + routeset(s, session[s].route[i].ip, session[s].route[i].mask, session[s].ip, 0); if (new->route[i].ip) // Add the new one if it exists. routeset(s, new->route[i].ip, new->route[i].mask, new->ip, 1); @@ -3706,9 +3722,12 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) int l; struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 }; + + if (log_stream && config->debug >= 4) + { log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); - if (log_stream) dump_packet(buf, log_stream); + } resp = calloc(1400, 1); l = new_packet(PKT_RESP_ERROR, resp); @@ -3805,6 +3824,9 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc int count = 0; int buckets[64]; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; @@ -3838,6 +3860,9 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc int count = 0; int buckets[64]; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; diff --git a/l2tpns.h b/l2tpns.h index 653364c..97f3a8a 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.7 2004-06-23 03:52:24 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.8 2004-06-28 02:43:13 fred_nerk Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -125,7 +125,6 @@ typedef struct controls // control message } controlt; -// 336 bytes per session typedef struct sessions { sessionidt next; // next session in linked list @@ -311,7 +310,7 @@ struct Tstats unsigned long c_forwarded; unsigned long recv_forward; -#ifdef STAT_CALLS +#ifdef STATISTICS unsigned long call_processtap; unsigned long call_processarp; unsigned long call_processipout; @@ -343,11 +342,19 @@ struct Tstats }; #ifdef STATISTICS -#define STAT(x) _statistics->x++ -#define INC_STAT(x,y) _statistics->x += y -#define GET_STAT(x) _statistics->x -#define SET_STAT(x, y) _statistics->x = y + +#ifdef STAT_CALLS +#define CSTAT(x) STAT(x) #else +#define CSTAT(x) +#endif + +#define STAT(x) (_statistics->x++) +#define INC_STAT(x,y) (_statistics->x += (y)) +#define GET_STAT(x) (_statistics->x) +#define SET_STAT(x, y) (_statistics->x = (y)) +#else +#define CSTAT(x) #define STAT(x) #define INC_STAT(x,y) #define GET_STAT(x) 0 @@ -359,7 +366,9 @@ struct configt int debug; // debugging level time_t start_time; // time when l2tpns was started char bandwidth[256]; // current bandwidth - clockt current_time; + clockt current_time; // 1/10ths of a second since the process started. + // means that we can only run a given process + // for 13 years without re-starting! char config_file[128]; int reload_config; // flag to re-read config (set by cli) @@ -410,6 +419,9 @@ struct configt clockt cluster_last_hb; // Last time we saw a heartbeat from the master. int cluster_num_changes; // Number of changes queued. + int cluster_hb_interval; // How often to send a heartbeat. + int cluster_hb_timeout; // How many missed heartbeats trigger an election. + #ifdef BGP u16 as_number; char bgp_peer[2][64]; @@ -516,6 +528,7 @@ int cluster_send_goodbye(); void init_cli(); void cli_do_file(FILE *fh); void cli_do(int sockfd); +int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...); #ifdef RINGBUFFER void ringbuffer_dump(FILE *stream); #endif @@ -561,4 +574,27 @@ extern u32 last_sid; extern struct Tstats *_statistics; extern ipt my_address; extern int tun_write(u8 *data, int size); + + +#define TIME (config->current_time) + +// macros for handling help in cli commands +#define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?') +#define CLI_HELP_NO_ARGS (argc > 1 || argv[0][1]) ? CLI_OK : cli_arg_help(cli, 1, NULL) + +// CVS identifiers (for "show version file") +extern char const *cvs_id_arp; +extern char const *cvs_id_cli; +extern char const *cvs_id_cluster; +extern char const *cvs_id_constants; +extern char const *cvs_id_control; +extern char const *cvs_id_icmp; +extern char const *cvs_id_l2tpns; +extern char const *cvs_id_ll; +extern char const *cvs_id_md5; +extern char const *cvs_id_ppp; +extern char const *cvs_id_radius; +extern char const *cvs_id_tbf; +extern char const *cvs_id_util; + #endif /* __L2TPNS_H__ */ diff --git a/ll.c b/ll.c index 6793e6e..0831152 100644 --- a/ll.c +++ b/ll.c @@ -1,5 +1,6 @@ // L2TPNS Linked List Stuff -// $Id: ll.c,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ + +char const *cvs_id_ll = "$Id: ll.c,v 1.4 2004-06-28 02:43:13 fred_nerk Exp $"; #include #include diff --git a/md5.c b/md5.c index e0691c8..1911dc5 100644 --- a/md5.c +++ b/md5.c @@ -1,6 +1,8 @@ /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ +char const *cvs_id_md5 = "$Id: md5.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; + /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. diff --git a/ppp.c b/ppp.c index deab284..474c30a 100644 --- a/ppp.c +++ b/ppp.c @@ -1,5 +1,6 @@ // L2TPNS PPP Stuff -// $Id: ppp.c,v 1.5 2004-06-23 03:52:24 fred_nerk Exp $ + +char const *cvs_id_ppp = "$Id: ppp.c,v 1.6 2004-06-28 02:43:13 fred_nerk Exp $"; #include #include @@ -28,13 +29,13 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l) char user[129]; char pass[129]; -#ifdef STAT_CALLS - STAT(call_processpap); -#endif + + CSTAT(call_processpap); + log_hex(5, "PAP", p, l); if (l < 4) { - log(1, 0, s, t, "Short PAP %u bytes", l); + log(1, 0, s, t, "Short PAP %u bytes\n", l); STAT(tunnel_rx_errors); return ; } @@ -123,9 +124,9 @@ void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l) u16 r; u16 len; -#ifdef STAT_CALLS - STAT(call_processchap); -#endif + + CSTAT(call_processchap); + log_hex(5, "CHAP", p, l); r = session[s].radius; if (!r) @@ -292,13 +293,13 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) u8 b[MAXCONTROL]; u8 *q = NULL; -#ifdef STAT_CALLS - STAT(call_processlcp); -#endif + + CSTAT(call_processlcp); + log_hex(5, "LCP", p, l); if (l < 4) { - log(1, session[s].ip, s, t, "Short LCP %d bytes", l); + log(1, session[s].ip, s, t, "Short LCP %d bytes\n", l); STAT(tunnel_rx_errors); return ; } @@ -450,13 +451,13 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) // Process IPCP messages void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l) { -#ifdef STAT_CALLS - STAT(call_processipcp); -#endif + + CSTAT(call_processipcp); + log_hex(5, "IPCP", p, l); if (l < 5) { - log(1, 0, s, t, "Short IPCP %d bytes", l); + log(1, 0, s, t, "Short IPCP %d bytes\n", l); STAT(tunnel_rx_errors); return ; } @@ -579,9 +580,9 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) { ipt ip; -#ifdef STAT_CALLS - STAT(call_processipin); -#endif + + CSTAT(call_processipin); + log_hex(5, "IP", p, l); ip = ntohl(*(u32 *)(p + 12)); @@ -667,9 +668,9 @@ void send_ipin(sessionidt s, u8 *buf, int len) // Process LCP messages void processccp(tunnelidt t, sessionidt s, u8 * p, u16 l) { -#ifdef STAT_CALLS - STAT(call_processccp); -#endif + + CSTAT(call_processccp); + log_hex(5, "CCP", p, l); if (l < 2 || (*p != ConfigReq && *p != TerminateReq)) { @@ -709,9 +710,9 @@ void sendchap(tunnelidt t, sessionidt s) u8 b[MAXCONTROL]; u16 r = session[s].radius; u8 *q; -#ifdef STAT_CALLS - STAT(call_sendchap); -#endif + + CSTAT(call_sendchap); + if (!r) { log(1, 0, s, t, "No RADIUS to send challenge\n"); diff --git a/radius.c b/radius.c index 32eafe7..db49387 100644 --- a/radius.c +++ b/radius.c @@ -1,5 +1,6 @@ // L2TPNS Radius Stuff -// $Id: radius.c,v 1.4 2004-06-23 03:52:24 fred_nerk Exp $ + +char const *cvs_id_radius = "$Id: radius.c,v 1.5 2004-06-28 02:43:13 fred_nerk Exp $"; #include #include @@ -57,28 +58,26 @@ void radiusclear(u16 r, sessionidt s) memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL; } -int next_radius_id = 1; static u16 new_radius() { - u16 i; - int loops = 0; - for (i = next_radius_id; ; i = (i + 1) % MAXRADIUS) + int count; + static u32 next_radius_id = 0; + + for (count = MAXRADIUS; count > 0 ; --count) { - if (radius[i].state == RADIUSNULL) + ++next_radius_id; // Find the next ID to check. + if (next_radius_id >= MAXRADIUS) + next_radius_id = 1; + + if (radius[next_radius_id].state == RADIUSNULL) { - next_radius_id = (next_radius_id + 1) % MAXRADIUS; - return i; + return next_radius_id; + } + } - if (next_radius_id == i) - { - if (++loops == 2) - { log(0, 0, 0, 0, "Can't find a free radius session! This is very bad!\n"); return 0; - } - } - } } u16 radiusnew(sessionidt s) @@ -94,7 +93,7 @@ u16 radiusnew(sessionidt s) session[s].radius = r; radius[r].session = s; radius[r].state = RADIUSWAIT; - radius[r].retry = config->current_time + 1200; // Wait at least 120 seconds to re-claim this. + radius[r].retry = TIME + 1200; // Wait at least 120 seconds to re-claim this. log(3,0,s, session[s].tunnel, "Allocated radius %d\n", r); return r; @@ -109,9 +108,9 @@ void radiussend(u16 r, u8 state) int pl; u8 *p; sessionidt s; -#ifdef STAT_CALLS - STAT(call_radiussend); -#endif + + CSTAT(call_radiussend); + s = radius[r].session; if (!config->numradiusservers) { @@ -359,9 +358,9 @@ void processrad(u8 *buf, int len, char socket_index) r_code = buf[0]; // First byte in radius packet. r_id = buf[1]; // radius reply indentifier. -#ifdef STAT_CALLS - STAT(call_processrad); -#endif + + CSTAT(call_processrad); + log_hex(5, "RADIUS Response", buf, len); if (len < 20 || len < ntohs(*(u16 *) (buf + 2))) { @@ -617,9 +616,9 @@ void radiusretry(u16 r) { sessionidt s = radius[r].session; tunnelidt t = 0; -#ifdef STAT_CALLS - STAT(call_radiusretry); -#endif + + CSTAT(call_radiusretry); + if (s) t = session[s].tunnel; radius[r].retry = backoff(radius[r].try + 1); diff --git a/tbf.c b/tbf.c index c024c67..0232996 100644 --- a/tbf.c +++ b/tbf.c @@ -1,9 +1,14 @@ +// L2TPNS: token bucket filters + +char const *cvs_id_tbf = "$Id: tbf.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; + #include #include #include #include #include "l2tpns.h" +#include "util.h" #include "tbf.h" // Need a time interval. @@ -289,10 +294,10 @@ static void tbf_run_queue(int tbf_id) f = &filter_list[tbf_id]; // Calculate available credit... - f->credit += (config->current_time - f->lasttime) * f->rate / 10; // current time is 1/10th of a second. + f->credit += (TIME - f->lasttime) * f->rate / 10; // current time is 1/10th of a second. if (f->credit > f->max_credit) f->credit = f->max_credit; - f->lasttime = config->current_time; + f->lasttime = TIME; while (f->queued > 0 && f->credit >= f->sizes[f->oldest]) { // While we have enough credit.. @@ -348,7 +353,7 @@ int tbf_run_timer(void) for (i = 0; i < filter_list_size; ++i) { if (!filter_list[i].next) continue; - if (filter_list[i].lasttime == config->current_time) // Did we just run it? + if (filter_list[i].lasttime == TIME) // Did we just run it? continue; log(1,0,0,0, "Missed tbf %d! Not on the timer chain?(n %d, p %d, tc %d)\n", i, @@ -365,10 +370,14 @@ int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc) int i; int count = 0; + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + if (!config->cluster_iam_master) { - cli_print(cli, "Command can't be run on a slave."); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!filter_list) return CLI_OK; diff --git a/util.c b/util.c index 343e956..b0589f1 100644 --- a/util.c +++ b/util.c @@ -1,5 +1,7 @@ /* Misc util functions */ +char const *cvs_id_util = "$Id: util.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; + #include "l2tpns.h" #include From f706b16ca17c637a74613f8443b216d26ed98461 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 2 Jul 2004 07:26:18 +0000 Subject: [PATCH 032/482] rename l2tpns.cfg to startup-config fix installation of plugins use DESTDIR rather than PREFIX --- Makefile | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 8af2656..e3b7855 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ -PREFIX= -bindir = $(PREFIX)/usr/sbin -etcdir = $(PREFIX)/etc/l2tpns -libdir = $(PREFIX)/usr/lib/l2tpns +DESTDIR = +bindir = $(DESTDIR)/usr/sbin +etcdir = $(DESTDIR)/etc/l2tpns +libdir = $(DESTDIR)/usr/lib/l2tpns CC = gcc -DEFINES= -DBGP -DRINGBUFFER -DSTAT_CALLS -DSTATISTICS -OPTIM=-g -O3 -funroll-loops -fomit-frame-pointer -finline-functions -CFLAGS=-Wall $(OPTIM) $(DEFINES) +DEFINES = -DBGP -DRINGBUFFER -DSTAT_CALLS -DSTATISTICS +OPTIM = -g -O3 -funroll-loops -fomit-frame-pointer -finline-functions +CFLAGS = -Wall $(OPTIM) $(DEFINES) LDFLAGS = LIBS = -lm -ldl -lcli INSTALL = /usr/bin/install -c @@ -50,11 +50,11 @@ depend: install: all $(INSTALL) -D -o root -g root -m 0755 l2tpns $(bindir)/l2tpns $(INSTALL) -D -o root -g root -m 0755 nsctl $(bindir)/nsctl - $(INSTALL) -D -o root -g root -m 0600 etc/l2tpns.cfg.default $(etcdir)/l2tpns.cfg + $(INSTALL) -D -o root -g root -m 0600 etc/startup-config.default $(etcdir)/startup-config $(INSTALL) -D -o root -g root -m 0644 etc/ip_pool.default $(etcdir)/l2tpns.ip_pool $(INSTALL) -D -o root -g root -m 0600 etc/users.default $(etcdir)/l2tpns.users - for PLUGIN in $(PLUGINS); do \ - $(INSTALL) -D -o root -g root -m 0755 $(PLUGIN) $(libdir)/$(PLUGIN); \ + for plugin in $(PLUGINS); do \ + $(INSTALL) -D -o root -g root -m 0755 $$plugin $(libdir)/$$plugin; \ done if [ ! -e /dev/net/tun ]; then \ mkdir /dev/net; \ From e5a798077051b479e7d99c1296f8c116384f31a9 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 2 Jul 2004 07:29:07 +0000 Subject: [PATCH 033/482] rename l2tpns.cfg as startup-config to match CONFIGFILE --- etc/{l2tpns.cfg.default => startup-config.default} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename etc/{l2tpns.cfg.default => startup-config.default} (100%) diff --git a/etc/l2tpns.cfg.default b/etc/startup-config.default similarity index 100% rename from etc/l2tpns.cfg.default rename to etc/startup-config.default From f4ff96a03ea38ee8019ef2b94962074838a64a8a Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 2 Jul 2004 07:30:43 +0000 Subject: [PATCH 034/482] fold back in changes from production --- cli.c | 48 +++++++++++++++++++++++++++--------------------- cluster.c | 13 ++++++++----- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/cli.c b/cli.c index 902add0..777a754 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=4 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.6 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.7 2004-07-02 07:30:43 bodea Exp $"; #include #include @@ -171,13 +171,13 @@ void init_cli() c = cli_register_command(cli, NULL, "restart", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "bgp", cmd_restart_bgp, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Restart BGP"); - c = cli_register_command(cli, NULL, "load", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); - cli_register_command(cli, c, "plugin", cmd_load_plugin, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Load a plugin"); + c = cli_register_command(cli, NULL, "load", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); + cli_register_command(cli, c, "plugin", cmd_load_plugin, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Load a plugin"); - c = cli_register_command(cli, NULL, "remove", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL); - cli_register_command(cli, c, "plugin", cmd_remove_plugin, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Remove a plugin"); + c = cli_register_command(cli, NULL, "remove", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL); + cli_register_command(cli, c, "plugin", cmd_remove_plugin, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Remove a plugin"); - cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Set a configuration variable"); + cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set a configuration variable"); // Enable regular processing cli_regular(cli, regular_stuff); @@ -204,9 +204,9 @@ void init_cli() } else { - cli_allow_user(cli, buf, p); - log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf); - } + cli_allow_user(cli, buf, p); + log(3, 0, 0, 0, "Allowing user %s to connect to the CLI\n", buf); + } } fclose(f); } @@ -331,7 +331,7 @@ 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, PRIVILEGE_PRIVILEGED); + cli_file(cli, fh, PRIVILEGE_PRIVILEGED, MODE_CONFIG); cli_print_callback(cli, NULL); } @@ -455,7 +455,7 @@ int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) (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, + tunnelip, session[i].calling[0] ? session[i].calling : "*"); if (userip) free(userip); if (tunnelip) free(tunnelip); @@ -1009,9 +1009,10 @@ int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!argc) { cli_print(cli, "Specify a user to drop"); @@ -1056,9 +1057,10 @@ int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!argc) { cli_print(cli, "Specify a tunnel to drop"); @@ -1112,9 +1114,10 @@ int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!argc) { cli_print(cli, "Specify a session id to drop"); @@ -1232,7 +1235,7 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } @@ -1268,9 +1271,10 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!argc) { cli_print(cli, "Specify a user"); @@ -1289,6 +1293,7 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) else cli_print(cli, "Throttling user %s", argv[i]); } + return CLI_OK; } @@ -1303,9 +1308,10 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) if (!config->cluster_iam_master) { - cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); + cli_print(cli, "Can't do this on a slave. Do it on %s", inet_toa(config->cluster_master_address)); return CLI_OK; } + if (!argc) { cli_print(cli, "Specify a user"); @@ -1346,9 +1352,9 @@ int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc) { char *p = (char *) &debug_flags; for (i = 0; i < sizeof(debug_flags); i++) - { - if (p[i]) { + if (p[i]) + { cli_print(cli, "Currently debugging:%s%s%s%s%s%s", (debug_flags.critical) ? " critical" : "", (debug_flags.error) ? " error" : "", @@ -1357,9 +1363,9 @@ int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc) (debug_flags.calls) ? " calls" : "", (debug_flags.data) ? " data" : ""); - return CLI_OK; + return CLI_OK; + } } - } cli_print(cli, "Debugging off"); return CLI_OK; diff --git a/cluster.c b/cluster.c index 4c4efde..3e40d45 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.4 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.5 2004-07-02 07:30:43 bodea Exp $"; #include #include @@ -343,6 +343,7 @@ static void send_heartbeat(int seq, char * data, int size) if (size > sizeof(past_hearts[0].data)) { log(0,0,0,0, "Tried to heartbeat something larger than the maximum packet!\n"); kill(0, SIGTERM); + exit(1); } i = seq % HB_HISTORY_SIZE; past_hearts[i].seq = seq; @@ -770,8 +771,8 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) kill(0, SIGTERM); } - log(3,0,0,0, "Sending heartbeat with %d changes (%d x-sess, %d x-tunnels, %d highsess, %d hightun size %d)\n", - config->cluster_num_changes, count, tcount, config->cluster_highest_sessionid, + log(3,0,0,0, "Sending heartbeat #%d with %d changes (%d x-sess, %d x-tunnels, %d highsess, %d hightun size %d)\n", + h.seq, config->cluster_num_changes, count, tcount, config->cluster_highest_sessionid, config->cluster_highest_tunnelid, (p-buff)); config->cluster_num_changes = 0; @@ -1068,16 +1069,18 @@ static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u log(0,0,0,0, "I just got a packet claiming to be from a master but _I_ am the master!\n"); if (!h->basetime) { - log(0,0,0,0, "Heartbeat from addr %s with zero basetime!\n", inet_toa(htonl(addr)) ); + log(0,0,0,0, "Heartbeat from addr %s with zero basetime!\n", inet_toa(addr) ); return -1; // Skip it. } if (basetime > h->basetime) { - log(0,0,0,0, "They're (%s) an older master than me so I'm gone!\n", inet_toa(htonl(addr))); + log(0,0,0,0, "They're (%s) an older master than me so I'm gone!\n", inet_toa(addr)); kill(0, SIGTERM); + exit(1); } if (basetime == h->basetime && my_address < addr) { // Tie breaker. log(0,0,0,0, "They're a higher IP address than me, so I'm gone!\n"); kill(0, SIGTERM); + exit(1); } return -1; // Skip it. } From a5848e393c20bd3cbeec4c553dd899d010ebda3b Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 2 Jul 2004 07:31:23 +0000 Subject: [PATCH 035/482] fluff to simplify diffs --- l2tpns.c | 11 +++++------ l2tpns.h | 8 ++++---- radius.c | 10 +++++----- tbf.c | 3 +-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 9ce2c79..7bd7d18 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.9 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.10 2004-07-02 07:31:23 bodea Exp $"; #include #include @@ -896,12 +896,12 @@ void send_ipout(sessionidt s, u8 *buf, int len) t = session[s].tunnel; sp = &session[s]; + log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); - // Add on L2TP header { u8 *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIP); @@ -3545,8 +3545,6 @@ int load_session(sessionidt s, sessiont *new) routeset(s, new->route[i].ip, new->route[i].mask, new->ip, 1); } - - if (new->tunnel && s > config->cluster_highest_sessionid) // Maintain this in the slave. It's used // for walking the sessions to forward byte counts to the master. config->cluster_highest_sessionid = s; @@ -3556,6 +3554,7 @@ int load_session(sessionidt s, sessiont *new) // Do fixups into address pool. if (new->ip_pool_index != -1) fix_address_pool(s); + return 1; } @@ -3725,7 +3724,7 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr) if (log_stream && config->debug >= 4) { - log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); + log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); dump_packet(buf, log_stream); } diff --git a/l2tpns.h b/l2tpns.h index 97f3a8a..7be0911 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.8 2004-06-28 02:43:13 fred_nerk Exp $ +// $Id: l2tpns.h,v 1.9 2004-07-02 07:31:23 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -14,7 +14,6 @@ #include #include #include -#include "config.h" #define VERSION "2.0.0" @@ -43,6 +42,7 @@ #define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation // Constants +#include "config.h" #ifndef PLUGINDIR #define PLUGINDIR LIBDIR // Plugins #endif @@ -403,12 +403,12 @@ struct configt // Don't use this unless you have a dual processor machine! int icmp_rate; // Max number of ICMP unreachable per second to send - u32 cluster_address; // Multicast address of cluster. + u32 cluster_address; // Multicast address of cluster. // Send to this address to have everyone hear. char cluster_interface[64]; // Which interface to listen for multicast on. int cluster_iam_master; // Are we the cluster master??? int cluster_iam_uptodate; // Set if we've got a full set of state from the master. - u32 cluster_master_address; // The network address of the cluster master. + u32 cluster_master_address; // The network address of the cluster master. // Zero if i am the cluster master. int cluster_seq_number; // Sequence number of the next heartbeat we'll send out // (or the seq number we're next expecting if we're a slave). diff --git a/radius.c b/radius.c index db49387..b276d28 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.5 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.6 2004-07-02 07:31:23 bodea Exp $"; #include #include @@ -74,10 +74,10 @@ static u16 new_radius() { return next_radius_id; } - - } - log(0, 0, 0, 0, "Can't find a free radius session! This is very bad!\n"); - return 0; + } + + log(0, 0, 0, 0, "Can't find a free radius session! This is very bad!\n"); + return 0; } u16 radiusnew(sessionidt s) diff --git a/tbf.c b/tbf.c index 0232996..5d4bbfc 100644 --- a/tbf.c +++ b/tbf.c @@ -1,6 +1,6 @@ // L2TPNS: token bucket filters -char const *cvs_id_tbf = "$Id: tbf.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_tbf = "$Id: tbf.c,v 1.3 2004-07-02 07:31:23 bodea Exp $"; #include #include @@ -406,4 +406,3 @@ int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "%d tbf entries used, %d total", count, filter_list_size); return CLI_OK; } - From d78201b5d724fc1927b2eb803c65a5198356ea77 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 5 Jul 2004 06:54:01 +0000 Subject: [PATCH 036/482] roll in Michael's "limp along" fix for when a slave drops temporarily from the mcast group --- cluster.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cluster.c b/cluster.c index 3e40d45..d9c619e 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.5 2004-07-02 07:30:43 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.6 2004-07-05 06:54:01 bodea Exp $"; #include #include @@ -438,6 +438,20 @@ void cluster_check_master(void) int last_free = 0; int had_peers = have_peers; clockt t = TIME; + static int probed = 0; + + // Is the master late? If so, try probing it... + if (TIME > (config->cluster_last_hb + config->cluster_hb_timeout/8 + 11)) { + if (!probed) { + if (config->cluster_master_address) { + peer_send_message(config->cluster_master_address, + C_LASTSEEN, config->cluster_seq_number, NULL, 0); + probed = 1; + } + } + } else { // We got a recent heartbeat; reset the probe flag. + probed = 0; + } if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout) ) return; // Everything's ok. return. From 4ad75362584e275243a7b70bc64f1762ebc552d9 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Wed, 7 Jul 2004 09:09:53 +0000 Subject: [PATCH 037/482] - set hostname in CLI prompt - add assertions to help identify odd LASTSEEN breakage - make cluster_hb_interval work; include interval/timeout in heartbeats so that a change on the master is propagated immediately to the slaves - use fast heartbeats when there are slaves not up to date - ensure basetime of shut down master is set to zero (prevent delayed election) - fix radius session leak on IPCP timeout - fix some off-by-one errors in tunnel/session loops --- cli.c | 20 +++--- cluster.c | 211 ++++++++++++++++++++++++++++++++++++------------------ cluster.h | 14 ++-- l2tpns.c | 80 ++++++++++++++------- l2tpns.h | 5 +- radius.c | 25 ++++--- 6 files changed, 224 insertions(+), 131 deletions(-) diff --git a/cli.c b/cli.c index 777a754..0f7a848 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=4 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.7 2004-07-02 07:30:43 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.8 2004-07-07 09:09:53 bodea Exp $"; #include #include @@ -102,7 +102,7 @@ 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() +void init_cli(char *hostname) { FILE *f; char buf[4096]; @@ -112,7 +112,10 @@ void init_cli() struct sockaddr_in addr; cli = cli_init(); - cli_set_hostname(cli, "l2tpns"); + if (hostname && *hostname) + cli_set_hostname(cli, hostname); + else + cli_set_hostname(cli, "l2tpns"); c = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "banana", cmd_show_banana, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a banana"); @@ -374,7 +377,6 @@ int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...) int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc) { int i; - time_t time_now; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, 1, @@ -466,7 +468,6 @@ 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 i, x, show_all = 0; - time_t time_now; char *states[] = { "Free", "Open", @@ -528,7 +529,7 @@ int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc) } // Show tunnel summary - cli_print(cli, "%s %s %s %s %s", + cli_print(cli, "%4s %20s %20s %6s %s", "TID", "Hostname", "IP", @@ -540,7 +541,7 @@ int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc) 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++; - cli_print(cli, "%d %s %s %s %d", + cli_print(cli, "%4d %20s %20s %6s %6d", i, *tunnel[i].hostname ? tunnel[i].hostname : "(null)", inet_toa(htonl(tunnel[i].ip)), @@ -753,7 +754,6 @@ int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc) { int i; int used = 0, free = 0, show_all = 0; - time_t time_now; if (!config->cluster_iam_master) { @@ -873,6 +873,7 @@ int cmd_show_run(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 i, free = 0, used = 0, show_all = 0; char *states[] = { "NULL", "CHAP", @@ -882,8 +883,6 @@ int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc) "STOP", "WAIT", }; - int i, free = 0, used = 0, show_all = 0; - time_t time_now; if (CLI_HELP_REQUESTED) { @@ -1544,7 +1543,6 @@ 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; if (CLI_HELP_REQUESTED) return CLI_HELP_NO_ARGS; diff --git a/cluster.c b/cluster.c index d9c619e..a251a77 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.6 2004-07-05 06:54:01 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.7 2004-07-07 09:09:53 bodea Exp $"; #include #include @@ -44,8 +44,6 @@ ipt my_address = 0; // The network address of my ethernet port. static int walk_session_number = 0; // The next session to send when doing the slow table walk. static int walk_tunnel_number = 0; // The next tunnel to send when doing the slow table walk. -static int hsess, fsess; // Saved copies of the highest used session id, and the first free one. - #define MAX_HEART_SIZE (8192) // Maximum size of heartbeat packet. Must be less than max IP packet size :) #define MAX_CHANGES (MAX_HEART_SIZE/(sizeof(sessiont) + sizeof(int) ) - 2) // Assumes a session is the biggest type! @@ -68,7 +66,7 @@ static struct { int uptodate; } peers[CLUSTER_MAX_SIZE]; // List of all the peers we've heard from. static int num_peers; // Number of peers in list. -static int have_peers; // At least one peer +static int have_peers; // At least one up to date peer int rle_decompress(u8 ** src_p, int ssize, u8 *dst, int dsize); int rle_compress(u8 ** src_p, int ssize, u8 *dst, int dsize); @@ -94,7 +92,7 @@ int cluster_init() if (!*config->cluster_interface) return 0; - cluster_sockfd = socket(AF_INET, SOCK_DGRAM, UDP); + cluster_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; @@ -158,8 +156,6 @@ int cluster_send_data(void *data, int datalen) addr.sin_port = htons(CLUSTERPORT); addr.sin_family = AF_INET; -// log_hex(4, "Cluster send", data, datalen); // VERY big data packets. How about we don't.. - log(5,0,0,0, "Cluster send data: %d bytes\n", datalen); if (sendto(cluster_sockfd, data, datalen, MSG_NOSIGNAL, (void *) &addr, sizeof(addr)) < 0) @@ -202,18 +198,13 @@ void cluster_uptodate(void) log(0,0,0,0, "Now uptodate with master.\n"); - // If we're not a master, or if we have no slaves - // then start taking traffic.. - if (!config->cluster_iam_master || !have_peers) - { #ifdef BGP - if (bgp_configured) - bgp_enable_routing(1); - else + if (bgp_configured) + bgp_enable_routing(1); + else #endif /* BGP */ - if (config->send_garp) - send_garp(config->bind_address); // Start taking traffic. - } + if (config->send_garp) + send_garp(config->bind_address); // Start taking traffic. } // @@ -339,6 +330,13 @@ int master_garden_packet(sessionidt s, char *data, int size) static void send_heartbeat(int seq, char * data, int size) { int i; + static int last_seq = -1; + + if (last_seq != -1 && (seq != (last_seq+1)%HB_MAX_SEQ) ) { + log(0,0,0,0, "FATAL: Sequence number skipped! (%d != %d)\n", + seq, last_seq); + } + last_seq = seq; if (size > sizeof(past_hearts[0].data)) { log(0,0,0,0, "Tried to heartbeat something larger than the maximum packet!\n"); @@ -440,35 +438,62 @@ void cluster_check_master(void) clockt t = TIME; static int probed = 0; - // Is the master late? If so, try probing it... - if (TIME > (config->cluster_last_hb + config->cluster_hb_timeout/8 + 11)) { - if (!probed) { - if (config->cluster_master_address) { - peer_send_message(config->cluster_master_address, - C_LASTSEEN, config->cluster_seq_number, NULL, 0); + if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout)) + { + // If the master is late (missed 2 hearbeats by a second and a + // hair) it may be that the switch has dropped us from the + // multicast group, try unicasting one probe to the master + // which will hopefully respond with a unicast heartbeat that + // will allow us to limp along until the querier next runs. + if (config->cluster_master_address + && TIME > (config->cluster_last_hb + 2 * config->cluster_hb_interval + 11)) + { + if (!probed) + { probed = 1; + log(1, 0, 0, 0, "Heartbeat from master %.1fs late, probing...\n", + TIME - (config->cluster_last_hb + config->cluster_hb_interval)); + + peer_send_message(config->cluster_master_address, + C_LASTSEEN, config->cluster_seq_number, NULL, 0); } + } else { // We got a recent heartbeat; reset the probe flag. + probed = 0; } - } else { // We got a recent heartbeat; reset the probe flag. - probed = 0; + + if (!config->cluster_iam_master) + return; // Everything's ok. return. + + // Master needs to check peer state } - if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout) ) - return; // Everything's ok. return. - - if (!config->cluster_iam_master) - log(0,0,0,0, "Master timed out! Holding election...\n"); - config->cluster_last_hb = TIME + 1; - for (i = have_peers = 0; i < num_peers ; ++i) { + if (config->cluster_iam_master) + config->cluster_iam_uptodate = 1; // cleared in loop below + else + log(0,0,0,0, "Master timed out! Holding election...\n"); + + + for (i = have_peers = 0; i < num_peers; i++) + { if ((peers[i].timestamp + config->cluster_hb_timeout) < t) continue; // Stale peer! Skip them. if (!peers[i].basetime) continue; // Shutdown peer! Skip them. - have_peers = 1; + if (peers[i].uptodate) + have_peers = 1; + + if (config->cluster_iam_master) + { + if (!peers[i].uptodate) + config->cluster_iam_uptodate = 0; // Start fast heartbeats + + continue; + } + if (peers[i].basetime < basetime) { log(1,0,0,0, "Expecting %s to become master\n", inet_toa(peers[i].peer) ); return; // They'll win the election. Get out of here. @@ -586,12 +611,11 @@ void cluster_check_master(void) config->cluster_undefined_sessions = 0; config->cluster_undefined_tunnels = 0; + config->cluster_iam_uptodate = 1; // assume all peers are up-to-date - // - // FIXME. We need to fix up the tunnel control message - // queue here! There's a number of other variables we - // should also update. - cluster_uptodate(); + // FIXME. We need to fix up the tunnel control message + // queue here! There's a number of other variables we + // should also update. } @@ -699,6 +723,7 @@ int hb_add_type(char **p, int type, int id) default: log(0,0,0,0, "Found an invalid type in heart queue! (%d)\n", type); kill(0, SIGTERM); + exit(1); } return 0; } @@ -706,31 +731,33 @@ int hb_add_type(char **p, int type, int id) // // Send a heartbeat, incidently sending out any queued changes.. // -void cluster_heartbeat(int highsession, int freesession, int hightunnel) +void cluster_heartbeat() { int i, count = 0, tcount = 0; char buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ]; heartt h; - char * p = buff; + char *p = buff; if (!config->cluster_iam_master) // Only the master does this. return; - hsess = highsession; - fsess = freesession; - // Fill out the heartbeat header. + // Fill out the heartbeat header. + memset(&h, 0, sizeof(h)); + h.version = HB_VERSION; h.seq = config->cluster_seq_number; h.basetime = basetime; h.clusterid = config->bind_address; // Will this do?? h.basetime = basetime; - h.highsession = highsession; - h.freesession = freesession; - h.hightunnel = hightunnel; + h.highsession = config->cluster_highest_sessionid; + h.freesession = sessionfree; + h.hightunnel = config->cluster_highest_tunnelid; h.size_sess = sizeof(sessiont); // Just in case. h.size_tunn = sizeof(tunnelt); + h.interval = config->cluster_hb_interval; + h.timeout = config->cluster_hb_timeout; - add_type(&p, C_HEARTBEAT, HB_VERSION, (char*) &h, sizeof(h) ); + add_type(&p, C_HEARTBEAT, HB_VERSION, (char*) &h, sizeof(h)); for (i = 0; i < config->cluster_num_changes; ++i) { hb_add_type(&p, cluster_changes[i].type, cluster_changes[i].id); @@ -739,6 +766,7 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? log(0,0,0,0, "FATAL: Overran the heartbeat buffer! This is fatal. Exiting. (size %d)\n", p - buff); kill(0, SIGTERM); + exit(1); } // @@ -749,11 +777,11 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) if (!walk_session_number) // session #0 isn't valid. ++walk_session_number; - if (count >= highsession) // If we're a small cluster, don't go wild. + if (count >= config->cluster_highest_sessionid) // If we're a small cluster, don't go wild. break; hb_add_type(&p, C_CSESSION, walk_session_number); - walk_session_number = (1+walk_session_number)%(highsession+1); // +1 avoids divide by zero. + walk_session_number = (1+walk_session_number)%(config->cluster_highest_sessionid+1); // +1 avoids divide by zero. ++count; // Count the number of extra sessions we're sending. } @@ -783,6 +811,7 @@ void cluster_heartbeat(int highsession, int freesession, int hightunnel) if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer? log(0,0,0,0, "Overran the heartbeat buffer now! This is fatal. Exiting. (size %d)\n", p - buff); kill(0, SIGTERM); + exit(1); } log(3,0,0,0, "Sending heartbeat #%d with %d changes (%d x-sess, %d x-tunnels, %d highsess, %d hightun size %d)\n", @@ -813,7 +842,7 @@ int type_changed(int type, int id) ++config->cluster_num_changes; if (config->cluster_num_changes > MAX_CHANGES) - cluster_heartbeat(config->cluster_highest_sessionid, fsess, config->cluster_highest_tunnelid); + cluster_heartbeat(); // flush now return 1; } @@ -868,8 +897,13 @@ int cluster_catchup_slave(int seq, u32 slave) while (seq != config->cluster_seq_number) { s = seq%HB_HISTORY_SIZE; if (seq != past_hearts[s].seq) { + int i; log(0,0,0,0, "Tried to re-send heartbeat for %s but %d doesn't match %d! (%d,%d)\n", inet_toa(slave), seq, past_hearts[s].seq, s, config->cluster_seq_number); + + for (i = 0; i < HB_HISTORY_SIZE; ++i) { + log(0,0,0,0, "\tentry %3d: seq %d (size %d)\n", i, past_hearts[s].seq, past_hearts[s].size); + } return -1; // What to do here!? } peer_send_data(slave, past_hearts[s].data, past_hearts[s].size); @@ -895,14 +929,6 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) return 0; } - // Is this the master shutting down?? - if (peer == config->cluster_master_address && !basetime) { - config->cluster_master_address = 0; - config->cluster_last_hb = 0; // Force an election. - cluster_check_master(); - return 0; - } - for (i = 0; i < num_peers ; ++i) { if (peers[i].peer != peer) @@ -911,9 +937,18 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) // This peer already exists. Just update the timestamp. peers[i].basetime = basetime; peers[i].timestamp = TIME; + peers[i].uptodate = !p->undef; break; } + // Is this the master shutting down?? + if (peer == config->cluster_master_address && !basetime) { + config->cluster_master_address = 0; + config->cluster_last_hb = 0; // Force an election. + cluster_check_master(); + return 0; + } + if (i >= num_peers) { log(4,0,0,0, "Adding %s as a peer\n", inet_toa(peer)); @@ -921,8 +956,9 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) // Not found. Is there a stale slot to re-use? for (i = 0; i < num_peers ; ++i) { - if (peers[i].peer != peer) - continue; + if (!peers[i].basetime) // Shutdown + break; + if ((peers[i].timestamp + config->cluster_hb_timeout * 10) < TIME) // Stale. break; } @@ -937,19 +973,27 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) peers[i].peer = peer; peers[i].basetime = basetime; peers[i].timestamp = TIME; + peers[i].uptodate = !p->undef; if (i == num_peers) ++num_peers; log(1,0,0,0, "Added %s as a new peer. Now %d peers\n", inet_toa(peer), num_peers); } + if (peers[i].uptodate) + { #ifdef BGP - /* drop routes if we've now got a peer */ - if (bgp_configured && config->cluster_iam_master && !have_peers) - bgp_enable_routing(0); + /* drop routes if we've now got a peer */ + if (config->cluster_iam_master && bgp_configured && !have_peers) + bgp_enable_routing(0); #endif /* BGP */ + have_peers = 1; + } + else if (config->cluster_iam_master) + { + config->cluster_iam_uptodate = 0; // increase heart-rate... + } - have_peers = 1; return 1; } @@ -1017,6 +1061,7 @@ static int cluster_recv_session(int more , u8 * p) if (!config->cluster_iam_uptodate) cluster_uptodate(); // Check to see if we're up to date. + return 0; } @@ -1054,23 +1099,28 @@ static int cluster_recv_tunnel(int more, u8 *p) // -// Process a version one heartbeat.. +// Process a heartbeat.. // -static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u32 addr) +static int cluster_process_heartbeat(u8 * data, int size, int more, u8 * p, u32 addr) { heartt * h; int s = size - (p-data); int i, type; - if (more != HB_VERSION) { - log(0,0,0,0, "Received a heartbeat version that I don't understand!\n"); +#if HB_VERSION != 3 +# error "need to update cluster_process_heartbeat()" +#endif + + // we handle version 2+ + if (more < 2 || more > HB_VERSION) { + log(0,0,0,0, "Received a heartbeat version that I don't support (%d)!\n", more); return -1; // Ignore it?? } + // Ok. It's a heartbeat packet from a cluster master! if (s < sizeof(*h)) goto shortpacket; - h = (heartt*) p; p += sizeof(*h); s -= sizeof(*h); @@ -1128,6 +1178,25 @@ static int cluster_process_heartbeat_v2(u8 * data, int size, int more, u8 * p, u // that the free session pointer is correct. cluster_check_sessions(h->highsession, h->freesession, h->hightunnel); + if (more > 2) // reserved section of heartt was not initialized prior to v3 + { + if (h->interval != config->cluster_hb_interval) + { + log(2, 0, 0, 0, "Master set ping/heartbeat interval to %u (was %u)\n", + h->interval, config->cluster_hb_interval); + + config->cluster_hb_interval = h->interval; + } + + if (h->timeout != config->cluster_hb_timeout) + { + log(2, 0, 0, 0, "Master set heartbeat timeout to %u (was %u)\n", + h->timeout, config->cluster_hb_timeout); + + config->cluster_hb_timeout = h->timeout; + } + } + // Ok. process the packet... while ( s > 0) { @@ -1315,14 +1384,14 @@ int processcluster(char * data, int size, u32 addr) case C_HEARTBEAT: log(4,0,0,0, "Got a heartbeat from %s\n", inet_toa(addr)); - - return cluster_process_heartbeat_v2(data, size, more, p, addr); + return cluster_process_heartbeat(data, size, more, p, addr); default: log(0,0,0,0, "Strange type packet received on cluster socket (%d)\n", type); return -1; } return 0; + shortpacket: log(0,0,0,0, "I got an cluster heartbeat packet! This means I'm probably out of sync!!\n"); return -1; diff --git a/cluster.h b/cluster.h index 9d2e273..042d41a 100644 --- a/cluster.h +++ b/cluster.h @@ -1,5 +1,5 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.3 2004-06-23 03:52:24 fred_nerk Exp $ +// $Id: cluster.h,v 1.4 2004-07-07 09:09:53 bodea Exp $ #ifndef __CLUSTER_H__ #define __CLUSTER_H__ @@ -20,7 +20,7 @@ #define C_CTUNNEL 13 // Compressed tunnel structure. #define C_GARDEN 14 // Gardened packet -#define HB_VERSION 2 // Protocol version number.. +#define HB_VERSION 3 // Protocol version number.. #define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!) #define HB_HISTORY_SIZE 64 // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ) @@ -28,10 +28,6 @@ #define HB_TIMEOUT (15*2*PING_INTERVAL) // 15 seconds without heartbeat triggers an election.. #define CLUSTERPORT 32792 -#define UDP 17 -#define TIMEOUT 20 -#define IL sizeof(int) - #define CLUSTER_MAX_SIZE 32 // No more than 32 machines in a cluster! #define DEFAULT_MCAST_ADDR "239.192.13.13" // Need an assigned number! @@ -49,8 +45,10 @@ typedef struct { u32 size_sess; // Size of the session structure. u32 size_tunn; // size of the tunnel structure. + u32 interval; // ping/heartbeat interval (if changed) + u32 timeout; // heartbeat timeout (if changed) - char reserved[128 - 9*sizeof(u32)]; // Pad out to 128 bytes. + char reserved[128 - 11*sizeof(u32)]; // Pad out to 128 bytes. } heartt; typedef struct { /* Used to update byte counters on the */ @@ -78,7 +76,7 @@ int master_garden_packet(sessionidt s, char * data, int size); void master_update_counts(void); void cluster_send_ping(time_t basetime); -void cluster_heartbeat(int highsession, int freesession, int hightunnel); +void cluster_heartbeat(void); void cluster_check_master(void); int show_cluster(struct cli_def *cli, char *command, char **argv, int argc); diff --git a/l2tpns.c b/l2tpns.c index 7bd7d18..5133807 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.10 2004-07-02 07:31:23 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.11 2004-07-07 09:09:53 bodea Exp $"; #include #include @@ -418,7 +418,7 @@ void initudp(void) addr.sin_family = AF_INET; addr.sin_port = htons(L2TPPORT); addr.sin_addr.s_addr = config->bind_address; - udpfd = socket(AF_INET, SOCK_DGRAM, UDP); + udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); { int flags = fcntl(udpfd, F_GETFL, 0); @@ -429,7 +429,7 @@ void initudp(void) log(0, 0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); } - snoopfd = socket(AF_INET, SOCK_DGRAM, UDP); + snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); snoop_addr.sin_family = AF_INET; // Control @@ -1161,14 +1161,17 @@ void sendipcp(tunnelidt t, sessionidt s) if (!r) r = radiusnew(s); + if (radius[r].state != RADIUSIPCP) { radius[r].state = RADIUSIPCP; radius[r].try = 0; } + radius[r].retry = backoff(radius[r].try++); if (radius[r].try > 10) { + radiusclear(r, s); // Clear radius session. sessionshutdown(s, "No reply on IPCP"); return; } @@ -1198,6 +1201,7 @@ void sessionkill(sessionidt s, char *reason) sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed + log(2, 0, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason); throttle_session(s, 0); // Force session to be un-throttle. Free'ing TBF structures. @@ -1585,8 +1589,8 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // TBA - to send to RADIUS break; case 8: // vendor name - memset(tunnel[t].vendor, 0, 128); - memcpy(tunnel[t].vendor, b, (n >= 127) ? 127 : n); + memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor)); + memcpy(tunnel[t].vendor, b, (n >= sizeof(tunnel[t].vendor) - 1) ? sizeof(tunnel[t].vendor) - 1 : n); log(4, ntohl(addr->sin_addr.s_addr), s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor); break; case 9: // assigned tunnel @@ -1819,7 +1823,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) if (!(r = radiusnew(s))) { log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); -// sessionkill(s, "no free RADIUS sesions"); + sessionkill(s, "no free RADIUS sesions"); return; } @@ -2044,7 +2048,7 @@ int regular_cleanups(void) } else radius[r].retry = backoff(radius[r].try+1); // Is this really needed? --mo } - for (t = 1; t < config->cluster_highest_tunnelid; t++) + for (t = 1; t <= config->cluster_highest_tunnelid; t++) { // check for expired tunnels if (tunnel[t].die && tunnel[t].die <= TIME) @@ -2220,8 +2224,8 @@ void mainloop(void) int cn, i; u8 buf[65536]; struct timeval to; - time_t next_cluster_ping = 0; // default 1 second pings. - clockt next_clean = time_now + config->cleanup_interval; + clockt next_cluster_ping = 0; // send initial ping immediately + time_t next_clean = time_now + config->cleanup_interval; log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, cluster_sockfd=%d, controlfd=%d\n", udpfd, tapfd, cluster_sockfd, controlfd); @@ -2355,16 +2359,17 @@ void mainloop(void) } // Runs on every machine (master and slaves). - if (cluster_sockfd && next_cluster_ping <= time_now) + if (cluster_sockfd && next_cluster_ping <= TIME) { // Check to see which of the cluster is still alive.. - next_cluster_ping = time_now + 1; cluster_send_ping(basetime); - cluster_check_master(); - - cluster_heartbeat(config->cluster_highest_sessionid, sessionfree, config->cluster_highest_tunnelid); // Only does anything if we're a master. + cluster_heartbeat(); // Only does anything if we're a master. master_update_counts(); // If we're a slave, send our byte counters to our master. + if (config->cluster_iam_master && !config->cluster_iam_uptodate) + next_cluster_ping = TIME + 1; // out-of-date slaves, do fast updates + else + next_cluster_ping = TIME + config->cluster_hb_interval; } // Run token bucket filtering queue.. @@ -2401,11 +2406,8 @@ void mainloop(void) } // Are we the master and shutting down?? - if (config->cluster_iam_master) { - - cluster_heartbeat(config->cluster_highest_sessionid, sessionfree, - config->cluster_highest_tunnelid); // Flush any queued changes.. - } + if (config->cluster_iam_master) + cluster_heartbeat(); // Flush any queued changes.. // Ok. Notify everyone we're shutting down. If we're // the master, this will force an election. @@ -2464,14 +2466,14 @@ void initdata(void) ip_address_pool = mmap(NULL, sizeof(ippoolt) * MAXIPPOOL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); if (ip_address_pool == MAP_FAILED) { - log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + log(0, 0, 0, 0, "Error doing mmap for ip_address_pool: %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 == MAP_FAILED) { - log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno)); + log(0, 0, 0, 0, "Error doing mmap for ringbuffer: %s\n", strerror(errno)); exit(1); } memset(ringbuffer, 0, sizeof(struct Tringbuffer)); @@ -2512,8 +2514,10 @@ void initdata(void) if (!*hostname) { + char *p; // Grab my hostname unless it's been specified gethostname(hostname, sizeof(hostname)); + if ((p = strchr(hostname, '.'))) *p = 0; } _statistics->start_time = _statistics->last_reset = time(NULL); @@ -2539,7 +2543,7 @@ int assign_ip_address(sessionidt s) { u32 i; int best = -1; - clockt best_time = time_now; + time_t best_time = time_now; char *u = session[s].user; char reuse = 0; @@ -2881,7 +2885,7 @@ int main(int argc, char *argv[]) config->debug++; break; case 'h': - strncpy(hostname, optarg, 999); + snprintf(hostname, sizeof(hostname), "%s", optarg); break; case '?': default: @@ -2907,7 +2911,7 @@ int main(int argc, char *argv[]) initplugins(); initdata(); init_tbf(); - init_cli(); + init_cli(hostname); read_config_file(); log(0, 0, 0, 0, "L2TPNS version " VERSION "\n"); @@ -3291,6 +3295,8 @@ static int facility_value(char *name) void update_config() { int i; + static int timeout = 0; + static int interval = 0; // Update logging closelog(); @@ -3373,6 +3379,26 @@ void update_config() if (!config->cluster_hb_timeout) config->cluster_hb_timeout = HB_TIMEOUT; // 10 missed heartbeat triggers an election. + if (interval != config->cluster_hb_interval || timeout != config->cluster_hb_timeout) + { + // Paranoia: cluster_check_master() treats 2 x interval + 1 sec as + // late, ensure we're sufficiently larger than that + int t = 4 * config->cluster_hb_interval + 11; + + if (config->cluster_hb_timeout < t) + { + log(0,0,0,0, "Heartbeat timeout %d too low, adjusting to %d\n", config->cluster_hb_timeout, t); + config->cluster_hb_timeout = t; + } + + // Push timing changes to the slaves immediately if we're the master + if (config->cluster_iam_master) + cluster_heartbeat(); + + interval = config->cluster_hb_interval; + timeout = config->cluster_hb_timeout; + } + config->reload_config = 0; } @@ -3807,7 +3833,7 @@ void become_master(void) int s; run_plugins(PLUGIN_BECOME_MASTER, NULL); - for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { if (!session[s].tunnel) // Not an in-use session. continue; @@ -3829,7 +3855,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; - for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int idle; if (!session[s].tunnel) continue; @@ -3865,7 +3891,7 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc time(&time_now); for (i = 0; i < 64;++i) buckets[i] = 0; - for (s = 0; s < config->cluster_highest_sessionid ; ++s) { + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int open = 0, d; if (!session[s].tunnel) continue; diff --git a/l2tpns.h b/l2tpns.h index 7be0911..ea13670 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.9 2004-07-02 07:31:23 bodea Exp $ +// $Id: l2tpns.h,v 1.10 2004-07-07 09:09:53 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -60,7 +60,6 @@ #endif #define TAPDEVICE "/dev/net/tun" -#define UDP 17 #define STATEFILE DATADIR "/state.dump" // State dump file #define CONFIGFILE FLASHDIR "/startup-config" // Configuration file #define CLIUSERS FLASHDIR "/users" // CLI Users file @@ -525,7 +524,7 @@ int sessionsetup(tunnelidt t, sessionidt s); int cluster_send_session(int s); int cluster_send_tunnel(int t); int cluster_send_goodbye(); -void init_cli(); +void init_cli(char *hostname); void cli_do_file(FILE *fh); void cli_do(int sockfd); int cli_arg_help(struct cli_def *cli, int cr_ok, char *entry, ...); diff --git a/radius.c b/radius.c index b276d28..2eeb940 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.6 2004-07-02 07:31:23 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.7 2004-07-07 09:09:53 bodea Exp $"; #include #include @@ -46,7 +46,7 @@ void initrad(void) for (i = 0; i < config->num_radfds; i++) { int flags; - if (!radfds[i]) radfds[i] = socket(AF_INET, SOCK_DGRAM, UDP); + if (!radfds[i]) radfds[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); flags = fcntl(radfds[i], F_GETFL, 0); fcntl(radfds[i], F_SETFL, flags | O_NONBLOCK); } @@ -59,7 +59,7 @@ void radiusclear(u16 r, sessionidt s) } -static u16 new_radius() +static u16 get_free_radius() { int count; static u32 next_radius_id = 0; @@ -82,13 +82,22 @@ static u16 new_radius() u16 radiusnew(sessionidt s) { - u16 r; - if (!(r = new_radius())) + u16 r = session[s].radius; + + /* re-use */ + if (r) + { + log(3, 0, s, session[s].tunnel, "Re-used radius %d\n", r); + return r; + } + + if (!(r = get_free_radius())) { log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n"); STAT(radius_overflow); return 0; }; + memset(&radius[r], 0, sizeof(radius[r])); session[s].radius = r; radius[r].session = s; @@ -393,20 +402,14 @@ void processrad(u8 *buf, int len, char socket_index) if (memcmp(hash, buf + 4, 16)) { log(0, 0, s, session[s].tunnel, " Incorrect auth on RADIUS response!! (wrong secret in radius config?)\n"); -// radius[r].state = RADIUSWAIT; - return; // Do nothing. On timeout, it will try the next radius server. } if ((radius[r].state == RADIUSAUTH && *buf != 2 && *buf != 3) || ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP) && *buf != 5)) { log(1, 0, s, session[s].tunnel, " Unexpected RADIUS response %d\n", *buf); - return; // We got something we didn't expect. Let the timeouts take // care off finishing the radius session if that's really correct. -// old code. I think incorrect. --mo -// radius[r].state = RADIUSWAIT; -// break; // Finish the radius sesssion. } if (radius[r].state == RADIUSAUTH) { From 44b941b89cc40d1ca1d3bd102cc9f5a0483a0d1a Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 8 Jul 2004 16:14:12 +0000 Subject: [PATCH 038/482] update for new clustering --- INSTALL | 65 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/INSTALL b/INSTALL index cade5e5..283e332 100644 --- a/INSTALL +++ b/INSTALL @@ -2,40 +2,39 @@ Brief Installation guide for L2TPNS 1. Requirements - * libcli 1.5.0 or greater - You can get it from http://sourceforge.net/projects/libcli. + * libcli 1.7.0 or greater + You can get it from http://sourceforge.net/projects/libcli. - * A kernel with iptables support - - * If you want to use throttling, you must have a kernel and a tc (iproute) which supports HTB. + * A kernel with iptables support. 2. Compile -make + * make 3. Install - * make install. This does: - * Install the binaries into /usr/bin (l2tpns, cluster_master and nsctl) - * Create config dir /etc/l2tpns and create default config files - * Ensures that /dev/net/tun exists + * make install. This process: + - Installs the binaries into /usr/sbin (l2tpns and nsctl). + - Creates the config dir /etc/l2tpns installs default config files. + - Ensures that /dev/net/tun exists. - * Modify config file. You probably need to change most of the config options. + * Modify config file. You probably need to change most of the config + options. - * Set up basic firewall rules. This should be done in an init script. + * Set up basic firewall rules. The l2tpns process listens on a bunch of + ports: - iptables -t nat -N l2tpns - iptables -t nat -A PREROUTING -j l2tpns - iptables -t mangle -N l2tpns - iptables -t mangle -A PREROUTING -j l2tpns + 23/tcp command line interface + 1701/udp l2tp (on bind_address) + 1702/udp control port (nsctl) + 32792/udp clustering messages - * Set up walled garden firewall rules. This should be done in an init - script. This is not required unless you are using the garden plugin. + * If you are using the garden plugin, setup the walled garden firewall + rules. These should be in /etc/l2tpns/build-garden, which is run by the + plugin after creating/flushing the "garden" nat table. - iptables -t nat -N garden >/dev/null 2>&1 - iptables -t nat -F garden iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1 iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1 iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1 @@ -46,24 +45,28 @@ make iptables -t nat -A garden -p icmp -j ACCEPT iptables -t nat -A garden -j DROP - * Set up IP address pools in /etc/l2tpns/ip_pool + * Set up IP address pools in /etc/l2tpns/ip_pool - * Set up clustering + * Set up routing. + - If you are running a single instance, you can simply statically route + the IP pools to the bind_address (l2tpns will send a gratuitous arp). - * Run cluster_master on a separate machine - * Set the "cluster master" and "bind address" parameters in /etc/l2tpns/l2tpns.cfg + - For a cluster, configure the members as BGP neighbours on your router + and configure multi-path load-balancing (on Cisco use "maximum-paths"). - * Make l2tpns run on startup + * Make l2tpns run on startup. In a clustered environment running from + inittab is recomended: - * Test it out + l2tp:2345:respawn:/home/l2tpns/src/l2tpns >/dev/null 2>&1 + + * Test it out. - -This software is quite stable and is being used in a production -environment at a quite large ISP. However, you may have problems -setting it up, and if so, I would appreciate it if you would file -useful bug reports on the Source Forge page: +This software is quite stable and is being used in a production environment at +a quite large ISP. However, you may have problems setting it up, and if so, I +would appreciate it if you would file useful bug reports on the Source Forge +page: http://sourceforge.net/projects/l2tpns/ From 3a12cd7519f0367ac06e6b00c3938d35ca758b8e Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 8 Jul 2004 16:16:48 +0000 Subject: [PATCH 039/482] doc updates from mo --- INTERNALS | 63 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/INTERNALS b/INTERNALS index e199981..5f26608 100644 --- a/INTERNALS +++ b/INTERNALS @@ -59,7 +59,23 @@ char **ip_hash the ip_pool[] table. If it's zero, then there is no associated value. + +config->cluster_iam_master + + If true, indicates that this node is the master for + the cluster. This has many consequences... +config->cluster_iam_uptodate + + On the slaves, this indicates if it's seen a full run + of sessions from the master, and thus it's safe to be + taking traffic. + + On the master, this indicates that all slaves are + up to date. If any of the slaves aren't up to date, + this variable is false, and indicates that we should + shift to more rapid heartbeats to bring the slave + back up to date. ============================================================ @@ -67,15 +83,16 @@ char **ip_hash Clustering: How it works. At a high level, the various members of the cluster elect -a master. All other machines become slaves. Slaves handle normal -packet forwarding. Whenever a slave get a 'state changing' packet -(i.e. tunnel setup/teardown, session setup etc) it _doesn't_ handle -it, but instead forwards it to the master. +a master. All other machines become slaves as soon as they hear +a heartbeat from the master. Slaves handle normal packet forwarding. +Whenever a slave get a 'state changing' packet (i.e. tunnel setup/teardown, +session setup etc) it _doesn't_ handle it, but instead forwards it +to the master. 'State changing' it defined to be "a packet that would cause a change in either a session or tunnel structure that isn't just updating the idle time or byte counters". In practise, this means -also all LCP, IPCP, and L2TP control packets. +almost all LCP, IPCP, and L2TP control packets. The master then handles the packet normally, updating the session/tunnel structures. The changed structures are then @@ -101,16 +118,30 @@ unicasts C_LASTSEEN to tell the master the last heartbeast it had seen. The master normally than unicasts the missing packets to the slave. If the master doesn't have the old packet any more (i.e. it's outside the transmission window) then the master -unicasts C_KILL to the slave asking it to die. (it should then -restart, and catchup on state via the normal process). +unicasts C_KILL to the slave asking it to die. (The slave should +then restart, and catchup on state via the normal process). + + If a slave goes for more than a few seconds without +hearing from the master, it sends out a preemptive C_LASTSEEN. +If the master still exists, this forces to the master to unicast +the missed heartbeats. This is a work around for a temporary +multicast problem. (i.e. if an IGMP probe is missed, the slave +will temporarily stop seeing the multicast heartbeats. This +work around prevents the slave from becoming master with +horrible consequences). Ping'ing: All slaves send out a 'ping' once per second as a multicast packet. This 'ping' contains the slave's ip address, -and most importantly: The number of seconds from epoch +and most importantly, the number of seconds from epoch that the slave started up. (I.e. the value of time(2) at -that the process started). +that the process started). (This is the 'basetime'). +Obviously, this is never zero. + There is a special case. The master can send a single +ping on shutdown to indicate that it is dead and that an +immediate election should be held. This special ping is +send from the master with a 'basetime' of zero. Elections: @@ -178,3 +209,17 @@ Becoming slave: When there are no undefined tunnel or session structures, the slave marks itself as 'up-to-date' and starts advertising routes (if BGP is enabled). + +STONITH: + + Currently, there is very minimal protection from split brain. +In particular, there is no real STONITH protocol to stop two masters +appearing in the event of a network problem. + + + +TODO: + Should slaves that have undefined sessions, and receive +a packet from a non-existant session then forward it to the master?? +In normal practice, a slave with undefined session shouldn't be +handling packets, but ... From 904838ad0c1da22a05467e9cc4e8bff3bad0d31a Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 8 Jul 2004 16:19:09 +0000 Subject: [PATCH 040/482] update counters --- arp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arp.c b/arp.c index 68a4bba..78ba856 100644 --- a/arp.c +++ b/arp.c @@ -1,6 +1,6 @@ // L2TPNS: arp -char const *cvs_id_arp = "$Id: arp.c,v 1.3 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_arp = "$Id: arp.c,v 1.4 2004-07-08 16:19:09 bodea Exp $"; #include #include @@ -29,6 +29,9 @@ void sendarp(int ifr_idx, const unsigned char* mac, ipt ip) struct sockaddr_ll sll; struct arp_buf buf; + CSTAT(call_sendarp); + STAT(arp_sent); + /* Ethernet */ memset(buf.eth.ether_dhost, 0xFF, ETH_ALEN); memcpy(buf.eth.ether_shost, mac, ETH_ALEN); From 6adc660b4946e81de5f659ea4217fee43496a733 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 8 Jul 2004 16:54:35 +0000 Subject: [PATCH 041/482] - s/tap/tun/ - fix for LASTSEEN breakage: don't do anything in the CLI other than flag changes to be made by the parent - split out master parts from cluster_check_master() into cluster_check_slaves() --- cli.c | 109 +++++++---------- cluster.c | 161 ++++++++++++-------------- cluster.h | 3 +- l2tpns.c | 341 +++++++++++++++++++++++++----------------------------- l2tpns.h | 53 ++++++--- ppp.c | 22 ++-- tbf.c | 36 +++--- 7 files changed, 341 insertions(+), 384 deletions(-) diff --git a/cli.c b/cli.c index 0f7a848..2707e17 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=4 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.8 2004-07-07 09:09:53 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.9 2004-07-08 16:54:35 bodea Exp $"; #include #include @@ -39,15 +39,15 @@ extern ippoolt *ip_address_pool; extern struct Tstats *_statistics; struct cli_def *cli = NULL; int cli_quit = 0; -extern int clifd, udpfd, tapfd, snoopfd, ifrfd, cluster_sockfd; +extern int clifd, udpfd, tunfd, snoopfd, ifrfd, cluster_sockfd; extern int *radfds; -extern sessionidt *cli_session_kill; -extern tunnelidt *cli_tunnel_kill; extern struct configt *config; extern struct config_descriptt config_values[]; #ifdef RINGBUFFER extern struct Tringbuffer *ringbuffer; #endif +extern struct cli_session_actions *cli_session_actions; +extern struct cli_tunnel_actions *cli_tunnel_actions; char *debug_levels[] = { "CRIT", @@ -265,7 +265,7 @@ void cli_do(int sockfd) // Close sockets if (udpfd) close(udpfd); udpfd = 0; - if (tapfd) close(tapfd); tapfd = 0; + if (tunfd) close(tunfd); tunfd = 0; if (snoopfd) close(snoopfd); snoopfd = 0; for (i = 0; i < config->num_radfds; i++) if (radfds[i]) close(radfds[i]); @@ -599,13 +599,13 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) 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)); + GET_STAT(tun_rx_bytes), + GET_STAT(tun_rx_packets), + GET_STAT(tun_rx_errors)); cli_print(cli, "%-10s %8lu %8lu %8lu", "TX", - GET_STAT(tap_tx_bytes), - GET_STAT(tap_tx_packets), - GET_STAT(tap_tx_errors)); + GET_STAT(tun_tx_bytes), + GET_STAT(tun_tx_packets), + GET_STAT(tun_tx_errors)); cli_print(cli, ""); cli_print(cli, "%-10s %-8s %-10s %-8s %-8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries"); @@ -624,11 +624,7 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) 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)); @@ -647,8 +643,7 @@ int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc) #ifdef STATISTICS 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_processtun", GET_STAT(call_processtun)); 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)); @@ -1028,17 +1023,8 @@ int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc) if (session[s].ip && session[s].opened && !session[s].die) { - int x; - cli_print(cli, "Dropping user %s", session[s].user); - for (x = 0; x < MAXSESSION; x++) - { - if (!cli_session_kill[x]) - { - cli_session_kill[x] = s; - break; - } - } + cli_session_actions[s].action |= CLI_SESS_KILL; } } @@ -1048,7 +1034,7 @@ 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 i; - tunnelidt tid; + tunnelidt t; if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 1, @@ -1068,35 +1054,26 @@ int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc) for (i = 0; i < argc; i++) { - int x; - - if ((tid = atol(argv[i])) <= 0 || (tid >= MAXTUNNEL)) + if ((t = atol(argv[i])) <= 0 || (t >= MAXTUNNEL)) { cli_print(cli, "Invalid tunnel ID (1-%d)", MAXTUNNEL-1); continue; } - if (!tunnel[tid].ip) + if (!tunnel[t].ip) { - cli_print(cli, "Tunnel %d is not connected", tid); + cli_print(cli, "Tunnel %d is not connected", t); continue; } - if (tunnel[tid].die) + if (tunnel[t].die) { - cli_print(cli, "Tunnel %d is already being shut down", tid); + cli_print(cli, "Tunnel %d is already being shut down", t); continue; } - for (x = 0; x < MAXTUNNEL; x++) - { - if (!cli_tunnel_kill[x]) - { - cli_tunnel_kill[x] = tid; - cli_print(cli, "Tunnel %d shut down (%s)", tid, tunnel[tid].hostname); - break; - } - } + cli_print(cli, "Tunnel %d shut down (%s)", t, tunnel[t].hostname); + cli_tunnel_actions[t].action |= CLI_TUN_KILL; } return CLI_OK; @@ -1131,18 +1108,10 @@ int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc) continue; } - if (session[s].opened && !session[s].die) + if (session[s].ip && session[s].opened && !session[s].die) { - int x; - for (x = 0; x < MAXSESSION; x++) - { - if (!cli_session_kill[x]) - { - cli_session_kill[x] = s; - break; - } - } cli_print(cli, "Dropping session %d", s); + cli_session_actions[s].action |= CLI_SESS_KILL; } else { @@ -1216,10 +1185,11 @@ int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc) return CLI_OK; } - session[s].snoop_ip = ip; - session[s].snoop_port = port; - cli_print(cli, "Snooping user %s to %s:%d", argv[0], inet_toa(session[s].snoop_ip), session[s].snoop_port); + cli_session_actions[s].snoop_ip = ip; + cli_session_actions[s].snoop_port = port; + cli_session_actions[s].action |= CLI_SESS_SNOOP; + return CLI_OK; } @@ -1251,10 +1221,9 @@ int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - session[s].snoop_ip = 0; - session[s].snoop_port = 0; cli_print(cli, "Not snooping user %s", argv[i]); + cli_session_actions[s].action |= CLI_SESS_NOSNOOP; } return CLI_OK; } @@ -1287,10 +1256,16 @@ int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - if (!throttle_session(s, config->rl_rate)) - cli_print(cli, "Error throttling %s", argv[i]); - else - cli_print(cli, "Throttling user %s", argv[i]); + + if (session[s].throttle) + { + cli_print(cli, "User %s already throttled", argv[i]); + continue; + } + + cli_print(cli, "Throttling user %s", argv[i]); + cli_session_actions[s].throttle = config->rl_rate; // could be configurable at some stage + cli_session_actions[s].action |= CLI_SESS_THROTTLE; } return CLI_OK; @@ -1324,9 +1299,15 @@ int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc) cli_print(cli, "User %s is not connected", argv[i]); continue; } - throttle_session(s, 0); + + if (!session[s].throttle) + { + cli_print(cli, "User %s not throttled", argv[i]); + continue; + } cli_print(cli, "Unthrottling user %s", argv[i]); + cli_session_actions[s].action |= CLI_SESS_NOTHROTTLE; } return CLI_OK; diff --git a/cluster.c b/cluster.c index a251a77..d99da26 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.7 2004-07-07 09:09:53 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.8 2004-07-08 16:54:35 bodea Exp $"; #include #include @@ -66,7 +66,6 @@ static struct { int uptodate; } peers[CLUSTER_MAX_SIZE]; // List of all the peers we've heard from. static int num_peers; // Number of peers in list. -static int have_peers; // At least one up to date peer int rle_decompress(u8 ** src_p, int ssize, u8 *dst, int dsize); int rle_compress(u8 ** src_p, int ssize, u8 *dst, int dsize); @@ -330,13 +329,6 @@ int master_garden_packet(sessionidt s, char *data, int size) static void send_heartbeat(int seq, char * data, int size) { int i; - static int last_seq = -1; - - if (last_seq != -1 && (seq != (last_seq+1)%HB_MAX_SEQ) ) { - log(0,0,0,0, "FATAL: Sequence number skipped! (%d != %d)\n", - seq, last_seq); - } - last_seq = seq; if (size > sizeof(past_hearts[0].data)) { log(0,0,0,0, "Tried to heartbeat something larger than the maximum packet!\n"); @@ -427,53 +419,22 @@ void master_update_counts(void) } // -// Check that we have a master. If it's been too -// long since we heard from a master then hold an election. +// On the master, check how our slaves are going. If +// one of them's not up-to-date we'll heartbeat faster. +// If we don't have any of them, then we need to turn +// on our own packet handling! // -void cluster_check_master(void) +void cluster_check_slaves(void) { - int i, count, tcount, high_sid = 0; - int last_free = 0; + int i; + static int have_peers = 0; int had_peers = have_peers; clockt t = TIME; - static int probed = 0; - if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout)) - { - // If the master is late (missed 2 hearbeats by a second and a - // hair) it may be that the switch has dropped us from the - // multicast group, try unicasting one probe to the master - // which will hopefully respond with a unicast heartbeat that - // will allow us to limp along until the querier next runs. - if (config->cluster_master_address - && TIME > (config->cluster_last_hb + 2 * config->cluster_hb_interval + 11)) - { - if (!probed) - { - probed = 1; - log(1, 0, 0, 0, "Heartbeat from master %.1fs late, probing...\n", - TIME - (config->cluster_last_hb + config->cluster_hb_interval)); - - peer_send_message(config->cluster_master_address, - C_LASTSEEN, config->cluster_seq_number, NULL, 0); - } - } else { // We got a recent heartbeat; reset the probe flag. - probed = 0; - } - - if (!config->cluster_iam_master) - return; // Everything's ok. return. - - // Master needs to check peer state - } - - config->cluster_last_hb = TIME + 1; - - if (config->cluster_iam_master) - config->cluster_iam_uptodate = 1; // cleared in loop below - else - log(0,0,0,0, "Master timed out! Holding election...\n"); + if (!config->cluster_iam_master) + return; // Only runs on the master... + config->cluster_iam_uptodate = 1; // cleared in loop below for (i = have_peers = 0; i < num_peers; i++) { @@ -486,13 +447,67 @@ void cluster_check_master(void) if (peers[i].uptodate) have_peers = 1; - if (config->cluster_iam_master) - { - if (!peers[i].uptodate) - config->cluster_iam_uptodate = 0; // Start fast heartbeats + if (!peers[i].uptodate) + config->cluster_iam_uptodate = 0; // Start fast heartbeats + } - continue; +#ifdef BGP + // master lost all slaves, need to handle traffic ourself + if (bgp_configured && had_peers && !have_peers) + bgp_enable_routing(1); + else if (bgp_configured && !had_peers && have_peers) + bgp_enable_routing(0); +#endif /* BGP */ +} + +// +// Check that we have a master. If it's been too +// long since we heard from a master then hold an election. +// +void cluster_check_master(void) +{ + int i, count, tcount, high_sid = 0; + int last_free = 0; + clockt t = TIME; + static int probed = 0; + + if (config->cluster_iam_master) + return; // Only runs on the slaves... + + // If the master is late (missed 2 hearbeats by a second and a + // hair) it may be that the switch has dropped us from the + // multicast group, try unicasting one probe to the master + // which will hopefully respond with a unicast heartbeat that + // will allow us to limp along until the querier next runs. + if (TIME > (config->cluster_last_hb + 2 * config->cluster_hb_interval + 11)) + { + if (!probed && config->cluster_master_address) + { + probed = 1; + log(1, 0, 0, 0, "Heartbeat from master %.1fs late, probing...\n", + 0.1 * (TIME - (config->cluster_last_hb + config->cluster_hb_interval))); + + peer_send_message(config->cluster_master_address, + C_LASTSEEN, config->cluster_seq_number, NULL, 0); } + } else { // We got a recent heartbeat; reset the probe flag. + probed = 0; + } + + if (TIME < (config->cluster_last_hb + config->cluster_hb_timeout)) + return; // Everything's ok! + + config->cluster_last_hb = TIME + 1; // Just the one election thanks. + + log(0,0,0,0, "Master timed out! Holding election...\n"); + + for (i = 0; i < num_peers; i++) + { + if ((peers[i].timestamp + config->cluster_hb_timeout) < t) + continue; // Stale peer! Skip them. + + if (!peers[i].basetime) + continue; // Shutdown peer! Skip them. if (peers[i].basetime < basetime) { log(1,0,0,0, "Expecting %s to become master\n", inet_toa(peers[i].peer) ); @@ -506,16 +521,6 @@ void cluster_check_master(void) } } - if (config->cluster_iam_master) // If we're the master, we've already won - { -#ifdef BGP - // master lost all slaves, need to handle traffic ourself - if (bgp_configured && had_peers && !have_peers) - bgp_enable_routing(1); -#endif /* BGP */ - return; - } - // Wow. it's been ages since I last heard a heartbeat // and I'm better than an of my peers so it's time // to become a master!!! @@ -525,11 +530,6 @@ void cluster_check_master(void) log(0,0,0,0, "I am declaring myself the master!\n"); -#ifdef BGP - if (bgp_configured && have_peers) - bgp_enable_routing(0); /* stop handling traffic */ -#endif /* BGP */ - if (config->cluster_seq_number == -1) config->cluster_seq_number = 0; @@ -897,13 +897,8 @@ int cluster_catchup_slave(int seq, u32 slave) while (seq != config->cluster_seq_number) { s = seq%HB_HISTORY_SIZE; if (seq != past_hearts[s].seq) { - int i; log(0,0,0,0, "Tried to re-send heartbeat for %s but %d doesn't match %d! (%d,%d)\n", inet_toa(slave), seq, past_hearts[s].seq, s, config->cluster_seq_number); - - for (i = 0; i < HB_HISTORY_SIZE; ++i) { - log(0,0,0,0, "\tentry %3d: seq %d (size %d)\n", i, past_hearts[s].seq, past_hearts[s].size); - } return -1; // What to do here!? } peer_send_data(slave, past_hearts[s].data, past_hearts[s].size); @@ -943,6 +938,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) // Is this the master shutting down?? if (peer == config->cluster_master_address && !basetime) { + log(3,0,0,0, "Master %s shutting down...\n", inet_toa(config->cluster_master_address)); config->cluster_master_address = 0; config->cluster_last_hb = 0; // Force an election. cluster_check_master(); @@ -980,21 +976,6 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) log(1,0,0,0, "Added %s as a new peer. Now %d peers\n", inet_toa(peer), num_peers); } - if (peers[i].uptodate) - { -#ifdef BGP - /* drop routes if we've now got a peer */ - if (config->cluster_iam_master && bgp_configured && !have_peers) - bgp_enable_routing(0); -#endif /* BGP */ - have_peers = 1; - } - else if (config->cluster_iam_master) - { - config->cluster_iam_uptodate = 0; // increase heart-rate... - } - - return 1; } diff --git a/cluster.h b/cluster.h index 042d41a..8eba2d5 100644 --- a/cluster.h +++ b/cluster.h @@ -1,5 +1,5 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.4 2004-07-07 09:09:53 bodea Exp $ +// $Id: cluster.h,v 1.5 2004-07-08 16:54:35 bodea Exp $ #ifndef __CLUSTER_H__ #define __CLUSTER_H__ @@ -78,6 +78,7 @@ void master_update_counts(void); void cluster_send_ping(time_t basetime); void cluster_heartbeat(void); void cluster_check_master(void); +void cluster_check_slaves(void); int show_cluster(struct cli_def *cli, char *command, char **argv, int argc); #endif /* __CLUSTER_H__ */ diff --git a/l2tpns.c b/l2tpns.c index 5133807..45d652e 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.11 2004-07-07 09:09:53 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.12 2004-07-08 16:54:35 bodea Exp $"; #include #include @@ -22,7 +22,6 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.11 2004-07-07 09:09:53 bodea Exp #include #include #include -#define __USE_GNU #include #include #include @@ -51,7 +50,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.11 2004-07-07 09:09:53 bodea Exp // Globals struct configt *config = NULL; // all configuration -int tapfd = -1; // tap interface file handle +int tunfd = -1; // tun interface file handle. (network device) int udpfd = -1; // UDP file handle int controlfd = -1; // Control signal handle int snoopfd = -1; // UDP file handle for sending out intercept data @@ -59,24 +58,25 @@ int *radfds = NULL; // RADIUS requests file handles int ifrfd = -1; // File descriptor for routing, etc time_t basetime = 0; // base clock char hostname[1000] = ""; // us. -u16 tapmac[3]; // MAC of tap interface -int tapidx; // ifr_ifindex of tap device +int tunidx; // ifr_ifindex of tun device u32 sessionid = 0; // session id for radius accounting int syslog_log = 0; // are we logging to syslog -FILE *log_stream = NULL; -struct sockaddr_in snoop_addr = {0}; -extern int cluster_sockfd; -u32 last_sid = 0; -int clifd = 0; -sessionidt *cli_session_kill = NULL; -tunnelidt *cli_tunnel_kill = NULL; -static void *ip_hash[256]; -u32 udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0; +FILE *log_stream = NULL; // file handle for direct logging (i.e. direct into file, not via syslog). +extern int cluster_sockfd; // Intra-cluster communications socket. +u32 last_sid = 0; // Last used PPP SID. Can I kill this?? -- mo +int clifd = 0; // Socket listening for CLI connections. + +struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI +struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI + +static void *ip_hash[256]; // Mapping from IP address to session structures. + +u32 udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0; // Global traffic counters. u32 eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0; -u32 ip_pool_size = 1; -time_t time_now = 0; -char time_now_string[64] = {0}; -char main_quit = 0; +u32 ip_pool_size = 1; // Size of the pool of addresses used for dynamic address allocation. +time_t time_now = 0; // Current time in seconds since epoch. +char time_now_string[64] = {0}; // Current time as a string. +char main_quit = 0; // True if we're in the process of exiting. char *_program_name = NULL; linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -143,11 +143,11 @@ char *plugin_functions[] = { #define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) -tunnelt *tunnel = NULL; // 1000 * 45 = 45000 = 45k -sessiont *session = NULL; // 5000 * 213 = 1065000 = 1 Mb -sessioncountt *sess_count = NULL; -radiust *radius = NULL; -ippoolt *ip_address_pool = NULL; +tunnelt *tunnel = NULL; // Array of tunnel structures. +sessiont *session = NULL; // Array of session structures. +sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters. +radiust *radius = NULL; // Array of radius structures. +ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER @@ -169,7 +169,7 @@ void update_config(); static void cache_ipmap(ipt ip, int s); static void uncache_ipmap(ipt ip); -// return internal time (10ths since run) +// return internal time (10ths since process startup) clockt now(void) { struct timeval t; @@ -178,12 +178,18 @@ clockt now(void) } // work out a retry time based on try number +// This is a straight bounded exponential backoff. +// Maximum re-try time is 32 seconds. (2^5). clockt backoff(u8 try) { if (try > 5) try = 5; // max backoff return now() + 10 * (1 << try); } + +// +// Log a debug message. +// void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...) { static char message[65536] = {0}; @@ -297,7 +303,7 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) ip &= mask; // Force the ip to be the first one in the route. memset(&r, 0, sizeof(r)); - r.rt_dev = config->tapdevice; + r.rt_dev = config->tundevice; 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; @@ -344,31 +350,32 @@ void routeset(sessionidt s, ipt ip, ipt mask, ipt gw, u8 add) } } -// Set up TAP interface -void inittap(void) +// +// Set up TUN interface +void inittun(void) { struct ifreq ifr; struct sockaddr_in sin = {0}; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN; - tapfd = open(TAPDEVICE, O_RDWR); - if (tapfd < 0) + tunfd = open(TUNDEVICE, O_RDWR); + if (tunfd < 0) { // fatal - log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); + log(0, 0, 0, 0, "Can't open %s: %s\n", TUNDEVICE, strerror(errno)); exit(1); } { - int flags = fcntl(tapfd, F_GETFL, 0); - fcntl(tapfd, F_SETFL, flags | O_NONBLOCK); + int flags = fcntl(tunfd, F_GETFL, 0); + fcntl(tunfd, F_SETFL, flags | O_NONBLOCK); } - if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) + if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) { - log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); + log(0, 0, 0, 0, "Can't set tun interface: %s\n", strerror(errno)); exit(1); } - assert(strlen(ifr.ifr_name) < sizeof(config->tapdevice)); - strncpy(config->tapdevice, ifr.ifr_name, sizeof(config->tapdevice) - 1); + assert(strlen(ifr.ifr_name) < sizeof(config->tundevice)); + strncpy(config->tundevice, ifr.ifr_name, sizeof(config->tundevice) - 1); ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); sin.sin_family = AF_INET; @@ -393,18 +400,12 @@ void inittap(void) log(0, 0, 0, 0, "Error setting tun flags: %s\n", strerror(errno)); exit(1); } - if (ioctl(ifrfd, SIOCGIFHWADDR, (void *) &ifr) < 0) - { - log(0, 0, 0, 0, "Error setting tun hardware address: %s\n", strerror(errno)); - exit(1); - } - memcpy(&tapmac, 2 + (u8 *) & ifr.ifr_hwaddr, 6); if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) { log(0, 0, 0, 0, "Error setting tun ifindex: %s\n", strerror(errno)); exit(1); } - tapidx = ifr.ifr_ifindex; + tunidx = ifr.ifr_ifindex; } // set up UDP port @@ -430,7 +431,6 @@ void initudp(void) exit(1); } snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - snoop_addr.sin_family = AF_INET; // Control memset(&addr, 0, sizeof(addr)); @@ -625,82 +625,6 @@ sessionidt sessionidtbysessiont(sessiont *s) return val; } -// Handle ARP requests -void processarp(u8 * buf, int len) -{ - ipt ip; - sessionidt s; - - CSTAT(call_processarp); - STAT(arp_recv); - - if (len != 46) - { - log(0, 0, 0, 0, "Unexpected length ARP %d bytes\n", len); - STAT(arp_errors); - return; - } - if (*(u16 *) (buf + 16) != htons(PKTARP)) - { - log(0, 0, 0, 0, "Unexpected ARP type %04X\n", ntohs(*(u16 *) (buf + 16))); - STAT(arp_errors); - return; - } - if (*(u16 *) (buf + 18) != htons(0x0001)) - { - log(0, 0, 0, 0, "Unexpected ARP hard type %04X\n", ntohs(*(u16 *) (buf + 18))); - STAT(arp_errors); - return; - } - if (*(u16 *) (buf + 20) != htons(PKTIP)) - { - log(0, 0, 0, 0, "Unexpected ARP prot type %04X\n", ntohs(*(u16 *) (buf + 20))); - STAT(arp_errors); - return; - } - if (buf[22] != 6) - { - log(0, 0, 0, 0, "Unexpected ARP hard len %d\n", buf[22]); - STAT(arp_errors); - return; - } - if (buf[23] != 4) - { - log(0, 0, 0, 0, "Unexpected ARP prot len %d\n", buf[23]); - STAT(arp_errors); - return; - } - if (*(u16 *) (buf + 24) != htons(0x0001)) - { - log(0, 0, 0, 0, "Unexpected ARP op %04X\n", ntohs(*(u16 *) (buf + 24))); - STAT(arp_errors); - return; - } - ip = *(u32 *) (buf + 42); - // look up session - s = sessionbyip(ip); - if (s) - { - log(3, ntohl(ip), s, session[s].tunnel, "ARP reply for %s\n", inet_toa(ip)); - memcpy(buf + 4, buf + 10, 6); // set destination as source - *(u16 *) (buf + 10) = htons(tapmac[0]); // set source address - *(u16 *) (buf + 12) = htons(tapmac[1]); - *(u16 *) (buf + 14) = htons(tapmac[2]); - *(u16 *) (buf + 24) = htons(0x0002); // ARP reply - memcpy(buf + 26, buf + 10, 6); // sender ethernet - memcpy(buf + 36, buf + 4, 6); // target ethernet - *(u32 *) (buf + 42) = *(u32 *) (buf + 32); // target IP - *(u32 *) (buf + 32) = ip; // sender IP - write(tapfd, buf, len); - STAT(arp_replies); - } - else - { - log(3, ntohl(ip), 0, 0, "ARP request for unknown IP %s\n", inet_toa(ip)); - STAT(arp_discarded); - } -} - // actually send a control message for a specific tunnel void tunnelsend(u8 * buf, u16 l, tunnelidt t) { @@ -764,7 +688,7 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t) // int tun_write(u8 * data, int size) { - return write(tapfd, data, size); + return write(tunfd, data, size); } // process outgoing (to tunnel) IP @@ -898,7 +822,7 @@ void send_ipout(sessionidt s, u8 *buf, int len) log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); - // Snooping this session, send it to intercept box + // Snooping this session. if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); @@ -1210,6 +1134,7 @@ void sessionkill(sessionidt s, char *reason) session[s].tunnel = T_FREE; // Mark it as free. session[s].next = sessionfree; sessionfree = s; + cli_session_actions[s].action = 0; cluster_send_session(s); } @@ -1239,10 +1164,9 @@ void tunnelkill(tunnelidt t, char *reason) // free tunnel tunnelclear(t); - cluster_send_tunnel(t); log(1, 0, 0, t, "Kill tunnel %d: %s\n", t, reason); - tunnel[t].die = 0; - tunnel[t].state = TUNNELFREE; + cli_tunnel_actions[s].action = 0; + cluster_send_tunnel(t); } // shut down a tunnel cleanly @@ -1546,13 +1470,10 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error Code %d: %s\n", errcode, errdesc); } - if (n > 4) { - /* %*s doesn't work?? */ - 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); - } + if (n > 4) + log(4, ntohl(addr->sin_addr.s_addr), s, t, " Error String: %.*s\n", + n-4, b+4); + break; } break; @@ -1997,26 +1918,25 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) } } -// read and process packet on tap -void processtap(u8 * buf, int len) +// read and process packet on tun +void processtun(u8 * buf, int len) { - log_hex(5, "Receive TAP Data", buf, len); - STAT(tap_rx_packets); - INC_STAT(tap_rx_bytes, len); + log_hex(5, "Receive TUN Data", buf, len); + STAT(tun_rx_packets); + INC_STAT(tun_rx_bytes, len); - CSTAT(call_processtap); + CSTAT(call_processtun); eth_rx_pkt++; eth_rx += len; if (len < 22) { - log(1, 0, 0, 0, "Short tap packet %d bytes\n", len); - STAT(tap_rx_errors); + log(1, 0, 0, 0, "Short tun packet %d bytes\n", len); + STAT(tun_rx_errors); return; } - if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP - processarp(buf, len); - else if (*(u16 *) (buf + 2) == htons(PKTIP)) // IP + + if (*(u16 *) (buf + 2) == htons(PKTIP)) // IP processipout(buf, len); // Else discard. } @@ -2034,6 +1954,7 @@ int regular_cleanups(void) int count=0,i; u16 r; static clockt next_acct = 0; + int a; log(3, 0, 0, 0, "Begin regular cleanup\n"); @@ -2083,35 +2004,23 @@ int regular_cleanups(void) controladd(c, t, 0); // send the message log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n"); } - } - // 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++) + // Check for tunnel changes requested from the CLI + if ((a = cli_tunnel_actions[t].action)) { - log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); - sessionshutdown(cli_session_kill[i], "Requested by administrator"); - cli_session_kill[i] = 0; - } - } - // Check for tunnels that have been killed from the CLI - if (cli_tunnel_kill[0]) - { - int 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 administrator"); - cli_tunnel_kill[i] = 0; + cli_tunnel_actions[t].action = 0; + if (a & CLI_TUN_KILL) + { + log(2, tunnel[t].ip, 0, t, "Dropping tunnel by CLI\n"); + tunnelshutdown(t, "Requested by administrator"); + } } + } count = 0; for (i = 1; i <= config->cluster_highest_sessionid; i++) { - s++; if (s > config->cluster_highest_sessionid) s = 1; @@ -2165,7 +2074,60 @@ int regular_cleanups(void) if (++count >= MAX_ACTIONS) break; continue; } + + // Check for actions requested from the CLI + if ((a = cli_session_actions[s].action)) + { + int send = 0; + + cli_session_actions[s].action = 0; + if (a & CLI_SESS_KILL) + { + log(2, 0, s, session[s].tunnel, "Dropping session by CLI\n"); + sessionshutdown(s, "Requested by administrator"); + a = 0; // dead, no need to check for other actions + } + + if (a & CLI_SESS_SNOOP) + { + log(2, 0, s, session[s].tunnel, "Snooping session by CLI (to %s:%d)\n", + inet_toa(cli_session_actions[s].snoop_ip), cli_session_actions[s].snoop_port); + + session[s].snoop_ip = cli_session_actions[s].snoop_ip; + session[s].snoop_port = cli_session_actions[s].snoop_port; + send++; + } + + if (a & CLI_SESS_NOSNOOP) + { + log(2, 0, s, session[s].tunnel, "Unsnooping session by CLI\n"); + session[s].snoop_ip = 0; + session[s].snoop_port = 0; + send++; + } + + if (a & CLI_SESS_THROTTLE) + { + log(2, 0, s, session[s].tunnel, "Throttling session by CLI (to %d)\n", + cli_session_actions[s].throttle); + + throttle_session(s, cli_session_actions[s].throttle); + } + + if (a & CLI_SESS_NOTHROTTLE) + { + log(2, 0, s, session[s].tunnel, "Un-throttling session by CLI\n"); + throttle_session(s, 0); + } + + if (send) + cluster_send_session(s); + + if (++count >= MAX_ACTIONS) break; + continue; + } } + if (config->accounting_dir && next_acct <= TIME) { // Dump accounting data @@ -2217,7 +2179,7 @@ int still_busy(void) return 0; } -// main loop - gets packets on tap or udp and processes them +// main loop - gets packets on tun or udp and processes them void mainloop(void) { fd_set cr; @@ -2227,17 +2189,17 @@ void mainloop(void) clockt next_cluster_ping = 0; // send initial ping immediately time_t next_clean = time_now + config->cleanup_interval; - log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, cluster_sockfd=%d, controlfd=%d\n", - udpfd, tapfd, cluster_sockfd, controlfd); + log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n", + udpfd, tunfd, cluster_sockfd, controlfd); FD_ZERO(&cr); FD_SET(udpfd, &cr); - FD_SET(tapfd, &cr); + FD_SET(tunfd, &cr); FD_SET(controlfd, &cr); FD_SET(clifd, &cr); if (cluster_sockfd) FD_SET(cluster_sockfd, &cr); cn = udpfd; - if (cn < tapfd) cn = tapfd; + if (cn < tunfd) cn = tunfd; if (cn < controlfd) cn = controlfd; if (cn < clifd) cn = clifd; if (cn < cluster_sockfd) cn = cluster_sockfd; @@ -2318,13 +2280,13 @@ void mainloop(void) break; } } - if (FD_ISSET(tapfd, &r)) + if (FD_ISSET(tunfd, &r)) { int c, n; for (c = 0; c < config->multi_read_count; c++) { - if ((n = read(tapfd, buf, sizeof(buf))) > 0) - processtap(buf, n); + if ((n = read(tunfd, buf, sizeof(buf))) > 0) + processtun(buf, n); else break; } @@ -2362,10 +2324,15 @@ void mainloop(void) if (cluster_sockfd && next_cluster_ping <= TIME) { // Check to see which of the cluster is still alive.. - cluster_send_ping(basetime); - cluster_check_master(); + + cluster_send_ping(basetime); // Only does anything if we're a slave + cluster_check_master(); // ditto. + cluster_heartbeat(); // Only does anything if we're a master. + cluster_check_slaves(); // ditto. + master_update_counts(); // If we're a slave, send our byte counters to our master. + if (config->cluster_iam_master && !config->cluster_iam_uptodate) next_cluster_ping = TIME + 1; // out-of-date slaves, do fast updates else @@ -2479,20 +2446,20 @@ void initdata(void) memset(ringbuffer, 0, sizeof(struct Tringbuffer)); #endif - cli_session_kill = mmap(NULL, sizeof(sessionidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); - if (cli_session_kill == MAP_FAILED) + cli_session_actions = mmap(NULL, sizeof(struct cli_session_actions) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (cli_session_actions == MAP_FAILED) { - log(0, 0, 0, 0, "Error doing mmap for cli session kill: %s\n", strerror(errno)); + log(0, 0, 0, 0, "Error doing mmap for cli session actions: %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 == MAP_FAILED) + memset(cli_session_actions, 0, sizeof(struct cli_session_actions) * MAXSESSION); + cli_tunnel_actions = mmap(NULL, sizeof(struct cli_tunnel_actions) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (cli_tunnel_actions == MAP_FAILED) { - log(0, 0, 0, 0, "Error doing mmap for cli tunnel kill: %s\n", strerror(errno)); + log(0, 0, 0, 0, "Error doing mmap for cli tunnel actions: %s\n", strerror(errno)); exit(1); } - memset(cli_tunnel_kill, 0, sizeof(tunnelidt) * MAXSESSION); + memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION); memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL); memset(session, 0, sizeof(sessiont) * MAXSESSION); @@ -2797,9 +2764,11 @@ void initippool() void snoop_send_packet(char *packet, u16 size, ipt destination, u16 port) { + struct sockaddr_in snoop_addr = {0}; if (!destination || !port || snoopfd <= 0 || size <= 0 || !packet) return; + snoop_addr.sin_family = AF_INET; snoop_addr.sin_addr.s_addr = destination; snoop_addr.sin_port = ntohs(port); @@ -2903,7 +2872,7 @@ int main(int argc, char *argv[]) // Start the timer routine off time(&time_now); - strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); signal(SIGALRM, sigalrm_handler); siginterrupt(SIGALRM, 0); @@ -2970,8 +2939,8 @@ int main(int argc, char *argv[]) config->bgp_peer_as[1], 0); #endif /* BGP */ - inittap(); - log(1, 0, 0, 0, "Set up on interface %s\n", config->tapdevice); + inittun(); + log(1, 0, 0, 0, "Set up on interface %s\n", config->tundevice); initudp(); initrad(); @@ -3046,7 +3015,7 @@ void sigalrm_handler(int junk) // Update the internal time counter time(&time_now); - strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); alarm(1); { diff --git a/l2tpns.h b/l2tpns.h index ea13670..ce36a3c 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.10 2004-07-07 09:09:53 bodea Exp $ +// $Id: l2tpns.h,v 1.11 2004-07-08 16:54:35 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -15,7 +15,7 @@ #include #include -#define VERSION "2.0.0" +#define VERSION "2.0.1" // Limits #define MAXTUNNEL 500 // could be up to 65535 @@ -30,7 +30,7 @@ #define T_FREE (0) // A tunnel ID that won't ever be used. Mark session as free. #define MAXCONTROL 1000 // max length control message we ever send... -#define MAXETHER (1500+18) // max packet we try sending to tap +#define MAXETHER (1500+18) // max packet we try sending to tun #define MAXTEL 96 // telephone number #define MAXPLUGINS 20 // maximum number of plugins to load #define MAXRADSERVER 10 // max radius servers @@ -59,7 +59,7 @@ #define FLASHDIR ETCDIR #endif -#define TAPDEVICE "/dev/net/tun" +#define TUNDEVICE "/dev/net/tun" #define STATEFILE DATADIR "/state.dump" // State dump file #define CONFIGFILE FLASHDIR "/startup-config" // Configuration file #define CLIUSERS FLASHDIR "/users" // CLI Users file @@ -105,6 +105,26 @@ typedef u16 tunnelidt; typedef u32 clockt; typedef u8 hasht[16]; +// CLI actions +struct cli_session_actions { + char action; + ipt snoop_ip; + u16 snoop_port; + int throttle; +}; + +#define CLI_SESS_KILL 0x01 +#define CLI_SESS_SNOOP 0x02 +#define CLI_SESS_NOSNOOP 0x04 +#define CLI_SESS_THROTTLE 0x08 +#define CLI_SESS_NOTHROTTLE 0x10 + +struct cli_tunnel_actions { + char action; +}; + +#define CLI_TUN_KILL 0x01 + // dump header: update number if internal format changes #define DUMP_MAGIC "L2TPNS#" VERSION "#" @@ -270,12 +290,12 @@ struct Tstats time_t start_time; time_t last_reset; - unsigned long tap_rx_packets; - unsigned long tap_tx_packets; - unsigned long tap_rx_bytes; - unsigned long tap_tx_bytes; - unsigned long tap_rx_errors; - unsigned long tap_tx_errors; + unsigned long tun_rx_packets; + unsigned long tun_tx_packets; + unsigned long tun_rx_bytes; + unsigned long tun_tx_bytes; + unsigned long tun_rx_errors; + unsigned long tun_tx_errors; unsigned long tunnel_rx_packets; unsigned long tunnel_tx_packets; @@ -287,11 +307,7 @@ struct Tstats unsigned long tunnel_retries; unsigned long radius_retries; - unsigned long arp_errors; - unsigned long arp_replies; - unsigned long arp_discarded; unsigned long arp_sent; - unsigned long arp_recv; unsigned long packets_snooped; @@ -310,8 +326,7 @@ struct Tstats unsigned long c_forwarded; unsigned long recv_forward; #ifdef STATISTICS - unsigned long call_processtap; - unsigned long call_processarp; + unsigned long call_processtun; unsigned long call_processipout; unsigned long call_processudp; unsigned long call_sessionbyip; @@ -374,7 +389,7 @@ struct configt int cleanup_interval; // interval between regular cleanups (in seconds) int multi_read_count; // amount of packets to read per fd in processing loop - char tapdevice[10]; // tap device name + char tundevice[10]; // tun device name char log_filename[128]; char l2tpsecret[64]; @@ -478,7 +493,7 @@ void rl_destroy_tbf(u16 t); clockt now(void); clockt backoff(u8 try); void routeset(sessionidt, ipt ip, ipt mask, ipt gw, u8 add); -void inittap(void); +void inittun(void); void initudp(void); void initdata(void); void initippool(); @@ -502,7 +517,7 @@ void sendipcp(tunnelidt t, sessionidt s); void processipout(u8 * buf, int len); void processarp(u8 * buf, int len); void processudp(u8 * buf, int len, struct sockaddr_in *addr); -void processtap(u8 * buf, int len); +void processtun(u8 * buf, int len); void processcontrol(u8 * buf, int len, struct sockaddr_in *addr); int assign_ip_address(sessionidt s); void free_ip_address(sessionidt s); diff --git a/ppp.c b/ppp.c index 474c30a..6074e00 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.6 2004-06-28 02:43:13 fred_nerk Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.7 2004-07-08 16:54:35 bodea Exp $"; #include #include @@ -17,7 +17,7 @@ char const *cvs_id_ppp = "$Id: ppp.c,v 1.6 2004-06-28 02:43:13 fred_nerk Exp $"; extern tunnelt *tunnel; extern sessiont *session; extern radiust *radius; -extern int tapfd; +extern int tunfd; extern char hostname[]; extern u32 eth_tx; extern time_t time_now; @@ -623,8 +623,8 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) // Snooping this session, send it to ASIO snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); } - STAT(tap_tx_packets); - INC_STAT(tap_tx_bytes, l); + STAT(tun_tx_packets); + INC_STAT(tun_tx_bytes, l); if (session[s].tbf_in && config->cluster_iam_master) { // Are we throttled and a master?? actually handle the throttled packets. tbf_queue_packet(session[s].tbf_in, p, l); @@ -634,9 +634,9 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) // send to ethernet if (tun_write(p, l) < 0) { - STAT(tap_tx_errors); - log(0, 0, s, t, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", - l, strerror(errno), tapfd, p); + STAT(tun_tx_errors); + log(0, 0, s, t, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n", + l, strerror(errno), tunfd, p); } } @@ -648,11 +648,11 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l) void send_ipin(sessionidt s, u8 *buf, int len) { log_hex(5, "IP in throttled", buf, len); - if (write(tapfd, buf, len) < 0) + if (write(tunfd, buf, len) < 0) { - STAT(tap_tx_errors); - log(0, 0, 0, 0, "Error writing %d bytes to TAP device: %s (tapfd=%d, p=%p)\n", - len, strerror(errno), tapfd, buf); + STAT(tun_tx_errors); + log(0, 0, 0, 0, "Error writing %d bytes to TUN device: %s (tunfd=%d, p=%p)\n", + len, strerror(errno), tunfd, buf); } // Increment packet counters diff --git a/tbf.c b/tbf.c index 5d4bbfc..d3090ac 100644 --- a/tbf.c +++ b/tbf.c @@ -1,8 +1,9 @@ // L2TPNS: token bucket filters -char const *cvs_id_tbf = "$Id: tbf.c,v 1.3 2004-07-02 07:31:23 bodea Exp $"; +char const *cvs_id_tbf = "$Id: tbf.c,v 1.4 2004-07-08 16:54:35 bodea Exp $"; + +#define _GNU_SOURCE -#include #include #include #include @@ -44,8 +45,8 @@ typedef struct { } tbft; -tbft * filter_list = NULL; -int filter_list_size = 0; +static tbft *filter_list = NULL; +static int filter_list_size = 0; static int timer_chain = -1; // Head of timer chain. @@ -156,23 +157,32 @@ int new_tbf(int sid, int max_credit, int rate, void (*f)(sessionidt, u8 *, int)) return p; } - log(0,0,0,0, "Ran out of token bucket filters! Sess %d will be un-throttled\n", sid); - return 0; - #if 0 - // Not using. Disasterous if called via the CLI! :) - // All allocated filters are used! Increase the size of the allocated - // filters. + // All allocated filters are used! Increase the size of the allocated + // filters. - i = filter_list_size; - filter_list_size = filter_list_size * 2 + 1; + { + int new_size = filter_list_size * 2; + tbft *new = mremap(filter_list, filter_list_size * sizeof(*new), new_size * sizeof(*new), MREMAP_MAYMOVE); - filter_list = realloc(filter_list, filter_list_size * sizeof(*filter_list) ); + if (new == MAP_FAILED) + { + log(0,0,0,0, "Ran out of token bucket filters and mremap failed! Sess %d will be un-throttled\n", sid); + return 0; + } + + i = filter_list_size; + filter_list_size = new_size; + filter_list = new; + } for (; i < filter_list_size; ++i) filter_list[i].sid = 0; goto again; +#else + log(0,0,0,0, "Ran out of token bucket filters! Sess %d will be un-throttled\n", sid); + return 0; #endif } From eae3c0527fafa2113996e1d374a2ce62599159cd Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sun, 11 Jul 2004 07:57:33 +0000 Subject: [PATCH 042/482] - TerminateAck fix from Yuri - Adject cli_loop args for libcli 1.8.0 - Allow for backward compatabity in C_PING packets - Don't send RADIUS stop messages from sessionshutdown when called from sessionkill. --- cli.c | 8 ++------ cluster.c | 25 +++++++++++++++++++------ l2tpns.c | 13 +++++++------ ppp.c | 6 +++--- radius.c | 4 ++-- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/cli.c b/cli.c index 2707e17..5725ea5 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=4 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.9 2004-07-08 16:54:35 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.10 2004-07-11 07:57:33 bodea Exp $"; #include #include @@ -314,11 +314,7 @@ void cli_do(int sockfd) memset(&debug_flags, 0, sizeof(debug_flags)); debug_flags.critical = 1; - { - char prompt[1005]; - snprintf(prompt, 1005, "l2tpns> "); - cli_loop(cli, sockfd, prompt); - } + cli_loop(cli, sockfd); close(sockfd); log(3, 0, 0, 0, "Closed CLI connection\n"); diff --git a/cluster.c b/cluster.c index d99da26..91fc3b1 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.8 2004-07-08 16:54:35 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.9 2004-07-11 07:57:35 bodea Exp $"; #include #include @@ -911,12 +911,25 @@ int cluster_catchup_slave(int seq, u32 slave) // We've heard from another peer! Add it to the list // that we select from at election time. // -int cluster_add_peer(u32 peer, time_t basetime, pingt *p) +int cluster_add_peer(u32 peer, time_t basetime, pingt *pp, int size) { int i; u32 clusterid; + pingt p; + - clusterid = p->addr; + // Allow for backward compatability. + // Just the ping packet into a new structure to allow + // for the possibility that we might have received + // more or fewer elements than we were expecting. + if (size > sizeof(p)) + size = sizeof(p); + + memset( (void*) &p, 0, sizeof(p) ); + memcpy( (void*) &p, (void*) pp, size); + + + clusterid = p.addr; if (clusterid != config->bind_address) { // Is this for us? @@ -932,7 +945,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) // This peer already exists. Just update the timestamp. peers[i].basetime = basetime; peers[i].timestamp = TIME; - peers[i].uptodate = !p->undef; + peers[i].uptodate = !p.undef; break; } @@ -969,7 +982,7 @@ int cluster_add_peer(u32 peer, time_t basetime, pingt *p) peers[i].peer = peer; peers[i].basetime = basetime; peers[i].timestamp = TIME; - peers[i].uptodate = !p->undef; + peers[i].uptodate = !p.undef; if (i == num_peers) ++num_peers; @@ -1297,7 +1310,7 @@ int processcluster(char * data, int size, u32 addr) switch (type) { case C_PING: // Update the peers table. - return cluster_add_peer(addr, more, (pingt*)p); + return cluster_add_peer(addr, more, (pingt*)p, s); case C_LASTSEEN: // Catch up a slave (slave missed a packet). return cluster_catchup_slave(more, addr); diff --git a/l2tpns.c b/l2tpns.c index 45d652e..b305d9d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.12 2004-07-08 16:54:35 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.13 2004-07-11 07:57:35 bodea Exp $"; #include #include @@ -1015,7 +1015,7 @@ void sessionshutdown(sessionidt s, char *reason) return; // not a live session } - if (!session[s].die) + if (!dead) log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); session[s].die = now() + 150; // Clean up in 15 seconds @@ -1122,9 +1122,10 @@ void sessionkill(sessionidt s, char *reason) CSTAT(call_sessionkill); + session[s].die = now(); sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) - radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed + radiusclear(session[s].radius, s); // cant send clean accounting data, session is killed log(2, 0, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason); @@ -1304,9 +1305,9 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr) // Is this a duplicate of the first packet? (SCCRQ) // for ( i = 1; i <= config->cluster_highest_tunnelid ; ++i) { - if (tunnel[t].state != TUNNELOPENING || - tunnel[t].ip != ntohl(*(ipt *) & addr->sin_addr) || - tunnel[t].port != ntohs(addr->sin_port) ) + if (tunnel[i].state != TUNNELOPENING || + tunnel[i].ip != ntohl(*(ipt *) & addr->sin_addr) || + tunnel[i].port != ntohs(addr->sin_port) ) continue; t = i; break; diff --git a/ppp.c b/ppp.c index 6074e00..8e93f5d 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.7 2004-07-08 16:54:35 bodea Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.8 2004-07-11 07:57:35 bodea Exp $"; #include #include @@ -420,9 +420,9 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l) sessionshutdown(s, "Remote end closed connection."); tunnelsend(b, l + (q - b), t); // send it } - else if (*p == TerminateReq) + else if (*p == TerminateAck) { - sessionshutdown(s, "Remote end closed connection."); + sessionshutdown(s, "Connection closed."); } else if (*p == EchoReq) { diff --git a/radius.c b/radius.c index 2eeb940..7453211 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.7 2004-07-07 09:09:53 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.8 2004-07-11 07:57:35 bodea Exp $"; #include #include @@ -664,6 +664,6 @@ void radius_clean() || !session[radius[i].session].opened || session[radius[i].session].die || session[radius[i].session].tunnel == 0) - radiusclear(i, 0); + radiusclear(i, radius[i].session); } } From 6e1e4432fc6798fae60cb41cf75e93479b80c674 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 12 Jul 2004 08:19:46 +0000 Subject: [PATCH 043/482] more TODO stuff --- INTERNALS | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/INTERNALS b/INTERNALS index 5f26608..b7b0b8c 100644 --- a/INTERNALS +++ b/INTERNALS @@ -223,3 +223,43 @@ TODO: a packet from a non-existant session then forward it to the master?? In normal practice, a slave with undefined session shouldn't be handling packets, but ... + + There is far too much walking of large arrays (in the master +specifically). Although this is mitigated somewhat by the +cluster_high_{sess,tun}, this benefit is lost as that value gets +closer to MAX{SESSION,TUNNEL}. There are two issues here: + + * The tunnel, radius and tbf arrays should probably use a + mechanism like sessions, where grabbing a new one is a + single lookup rather than a walk. + + * A list structure (simillarly rooted at [0].interesting) is + required to avoid having to walk tables periodically. As a + back-stop the code in the master which *does* walk the + arrays can mark any entry it processes as "interesting" to + ensure it gets looked at even if a bug causes it to be + otherwiase overlooked. + + Support for more than 64k sessions per cluster. There is +currently a 64k session limit because each session gets an id that global +over the cluster (as opposed to local to the tunnel). Obviously, the tunnel +id needs to be used in conjunction with the session id to index into +the session table. But how? + + I think the best way is to use something like page tables. +for a given , the appropriate session index is +session[ tunnel[tid].page[sid>>10] + (sid & 1023) ] +Where tunnel[].page[] is a 64 element array. As a tunnel +fills up it's page block, it allocated a new 1024 session block +from the session table and fills in the appropriate .page[] +entry. + + This should be a reasonable compromise between wasting memory +(average 500 sessions per tunnel wasted) and speed. (Still a direct +index without searching, but extra lookups required). Obviously +the <6,10> split on the sid can be moved around to tune the size +of the page table v the session table block size. + + This unfortunately means that the tunnel structure HAS to +be filled on the slave before any of the sessions on it can be used. + From a4c14149f241e03e19ce9900f7ad5383e40fc2b4 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 12 Jul 2004 08:21:45 +0000 Subject: [PATCH 044/482] add lock_pages option --- l2tpns.c | 18 ++++++++++++++---- l2tpns.h | 8 ++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index b305d9d..9d1454d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.13 2004-07-11 07:57:35 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.14 2004-07-12 08:21:45 bodea Exp $"; #include #include @@ -111,6 +111,7 @@ struct config_descriptt config_values[] = { CONFIG("cleanup_interval", cleanup_interval, INT), CONFIG("multi_read_count", multi_read_count, INT), CONFIG("scheduler_fifo", scheduler_fifo, BOOL), + CONFIG("lock_pages", lock_pages, BOOL), CONFIG("icmp_rate", icmp_rate, INT), CONFIG("cluster_address", cluster_address, IP), CONFIG("cluster_interface", cluster_interface, STRING), @@ -143,10 +144,10 @@ char *plugin_functions[] = { #define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *)) -tunnelt *tunnel = NULL; // Array of tunnel structures. -sessiont *session = NULL; // Array of session structures. +tunnelt *tunnel = NULL; // Array of tunnel structures. +sessiont *session = NULL; // Array of session structures. sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters. -radiust *radius = NULL; // Array of radius structures. +radiust *radius = NULL; // Array of radius structures. ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. controlt *controlfree = 0; struct Tstats *_statistics = NULL; @@ -2955,6 +2956,15 @@ int main(int argc, char *argv[]) signal(SIGQUIT, sigquit_handler); signal(SIGCHLD, sigchild_handler); + // Prevent us from getting paged out + if (config->lock_pages) + { + if (!mlockall(MCL_CURRENT)) + log(1, 0, 0, 0, "Locking pages into memory\n"); + else + log(0, 0, 0, 0, "Can't lock pages: %s\n", strerror(errno)); + } + alarm(1); // Drop privileges here diff --git a/l2tpns.h b/l2tpns.h index ce36a3c..56cb9a5 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.11 2004-07-08 16:54:35 bodea Exp $ +// $Id: l2tpns.h,v 1.12 2004-07-12 08:21:45 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -413,9 +413,9 @@ struct configt char old_plugins[64][MAXPLUGINS]; int next_tbf; // Next HTB id available to use - int scheduler_fifo; // If 1, will force scheduler to use SCHED_FIFO. - // Don't use this unless you have a dual processor machine! - int icmp_rate; // Max number of ICMP unreachable per second to send + int scheduler_fifo; // If the system has multiple CPUs, use FIFO scheduling policy for this process. + int lock_pages; // Lock pages into memory. + int icmp_rate; // Max number of ICMP unreachable per second to send> u32 cluster_address; // Multicast address of cluster. // Send to this address to have everyone hear. From 60589c977c109666cbec754da1a589fb22cd3f6d Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 12 Jul 2004 14:15:11 +0000 Subject: [PATCH 045/482] Documentation update --- Docs/manual.html | 709 +++++++++++++++++++++++++++-------------------- INSTALL | 2 +- 2 files changed, 407 insertions(+), 304 deletions(-) diff --git a/Docs/manual.html b/Docs/manual.html index a1c398f..d1ce33c 100644 --- a/Docs/manual.html +++ b/Docs/manual.html @@ -1,7 +1,10 @@ + + L2TPNS Manual - - - -

L2TPNS Manual

-
    -
  1. Overview
  2. -
  3. Installation -
      -
    1. Requirements
    2. -
    3. Compile
    4. -
    5. Install
    6. -
    7. Running
    8. -
    -
  4. -
  5. Configuration -
      -
    1. startup-config
    2. -
    3. users
    4. -
    5. ip_pool
    6. -
    7. build-garden
    8. -
    -
  6. -
  7. Controlling the Process -
      -
    1. Command-Line Interface
    2. -
    3. nsctl
    4. -
    5. Signals
    6. -
    -
  8. -
  9. Throttling
  10. -
  11. Interception
  12. -
  13. Authentication
  14. -
  15. Plugins
  16. -
  17. Walled Garden
  18. -
  19. Filtering
  20. -
  21. Clustering
  22. -
  23. Routing
  24. -
  25. Performance
  26. -
- -

Overview

-l2tpns is half of a complete L2TP implementation. It supports only the -LNS side of the connection.

- -L2TP (Layer 2 Tunneling Protocol) is designed to allow any layer 2 -protocol (e.g. Ethernet, PPP) to be tunneled over an IP connection. l2tpns -implements PPP over L2TP only.

- -There are a couple of other L2TP implementations, of which l2tpd is probably the -most popular. l2tpd also will handle being either end of a tunnel, and -is a lot more configurable than l2tpns. However, due to the way it works, -it is nowhere near as scalable.

- -l2tpns uses the TUN/TAP interface provided by the Linux kernel to receive -and send packets. Using some packet manipulation it doesn't require a -single interface per connection, as l2tpd does.

- -This allows it to scale extremely well to very high loads and very high -numbers of connections.

- -It also has a plugin architecture which allows custom code to be run -during processing. An example of this is in the walled garden module -included.

- -
-Documentation is not my best skill. If you find any problems -with this document, or if you wish to contribute, please email the mailing list.

- -

Installation

-

Requirements

- -
    -
  1. Linux kernel version 2.4 or above, with the Tun/Tap interface either -compiled in, or as a module.
  2. - -
  3. libcli 1.8.0 or greater.
    You can get this from http://sourceforge.net/projects/libcli
  4. -
- -

Compile

- -You can generally get away with just running make from the source -directory. This will compile the daemon, associated tools and any modules -shipped with the distribution.

- -

Install

- -After you have successfully compiled everything, run make -install to install it. By default, the binaries are installed into -/usr/sbin, the configuration into /etc/l2tpns, and the -modules into /usr/lib/l2tpns.

- -You will definately need to edit the configuration files before you -start. See the Configuration section for -more information.

- -

Running

- -You only need to run /usr/sbin/l2tpns as root to start it. It does -not detach to a daemon process, so you should perhaps run it from init.

- -By default there is no log destination set, so all log messages will go to -stdout.

- -

Configuration

- -All configuration of the software is done from the files installed into -/etc/l2tpns. - -

startup-config

- -This is the main configuration file for l2tpns. The format of the file is a -list of commands that can be run through the command-line interface. This -file can also be written directly by the l2tpns process if a user runs the -write memory command, so any comments will be lost. However if your -policy is not to write the config by the program, then feel free to comment -the file with a # or ! at the beginning of the line.

- -A list of the possible configuration directives follows. Each of these -should be set by a line like:

-

-set configstring "value"
-set ipaddress 192.168.1.1
-set boolean true
-
- -

-

    -
  • debug (int)
    -Sets the level of messages that will be written to the log file. The value -should be between 0 and 5, with 0 being no debugging, and 5 being the -highest. A rough description of the levels is: -
      -
    1. Critical Errors - Things are probably broken
    2. -
    3. Errors - Things might have gone wrong, but probably will recover
    4. -
    5. Warnings - Just in case you care what is not quite perfect
    6. -
    7. Information - Parameters of control packets
    8. -
    9. Calls - For tracing the execution of the code
    10. -
    11. Packets - Everything, including a hex dump of all packets processed... probably twice
    12. -

    -Note that the higher you set the debugging level, the slower the program -will run. Also, at level 5 a LOT of information will be logged. This should -only ever be used for working out why it doesn't work at all. -

  • - -
  • log_file (string)
    -This will be where all logging and debugging information is written -to. This may be either a filename, such as /var/log/l2tpns, or -the special magic string syslog:facility, where facility -is any one of the syslog logging facilities, such as local5. -
  • - -
  • pid_file (string)
    -If set, the process id will be written to the specified file. The -value must be an absolute path. -
  • - -
  • l2tp_secret (string)
    -The secret used by l2tpns for authenticating tunnel request. Must be -the same as the LAC, or authentication will fail. Only actually be -used if the LAC requests authentication. -
  • - -
  • l2tp_mtu (int)
    -MTU of interface for L2TP traffic (default: 1500). Used to set link -MRU and adjust TCP MSS. -
  • - -
  • ppp_restart_time (int)
    -ppp_max_configure (int)
    -ppp_max_failure (int)
    -PPP counter and timer values, as described in §4.1 of -RFC1661. -
  • - -
  • primary_dns (ip address) -
  • secondary_dns (ip address)
    -Whenever a PPP connection is established, DNS servers will be sent to the -user, both a primary and a secondary. If either is set to 0.0.0.0, then that -one will not be sent. -
  • - -
  • primary_radius (ip address) -
  • secondary_radius (ip address)
    -Sets the RADIUS servers used for both authentication and accounting. -If the primary server does not respond, then the secondary RADIUS -server will be tried.
    -Note: in addition to the source IP address and -identifier, the RADIUS server must include the source -port when detecting duplicates to supress (in order to cope with a -large number of sessions comming on-line simultaneously l2tpns uses a -set of udp sockets, each with a seperate identifier). -
  • - -
  • primary_radius_port (short) -
  • secondary_radius_port (short)
    -Sets the authentication ports for the primary and secondary RADIUS -servers. The accounting port is one more than the authentication -port. If no RADIUS ports are given, the authentication port defaults -to 1645, and the accounting port to 1646. -
  • - -
  • radius_accounting (boolean)
    -If set to true, then RADIUS accounting packets will be sent. This -means that a Start record will be sent when the session is -successfully authenticated, and a Stop record will be sent when the -session is closed. -
  • - -
  • radius_secret (string)
    -This secret will be used in all RADIUS queries. If this is not set then -RADIUS queries will fail. -
  • - -
  • radius_authtypes (string)
    -A comma separated list of supported RADIUS authentication methods -(pap or chap), in order of preference (default pap). -
  • - -
  • radius_dae_port (short)
    -Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) -requests (default: 3799). -
  • - -
  • allow_duplicate_users (boolean)
    -Allow multiple logins with the same username. If false (the default), -any prior session with the same username will be dropped when a new -session is established. -
  • - -
  • bind_address (ip address)
    -When the tun interface is created, it is assigned the address -specified here. If no address is given, 1.1.1.1 is used. Packets -containing user traffic should be routed via this address if given, -otherwise the primary address of the machine. -
  • - -
  • peer_address (ip address)
    -Address to send to clients as the default gateway. - - -
  • send_garp (boolean)
    -Determines whether or not to send a gratuitous ARP for the -bind_address when the server is ready to handle traffic (default: -true).
    -This value is ignored if BGP is configured. -
  • - -
  • throttle_speed (int)
    -Sets the default speed (in kbits/s) which sessions will be limited to. -If this is set to 0, then throttling will not be used at all. Note: -You can set this by the CLI, but changes will not affect currently -connected users. -
  • - -
  • throttle_buckets (int)
    -Number of token buckets to allocate for throttling. Each throttled -session requires two buckets (in and out). -
  • - -
  • accounting_dir (string)
    -If set to a directory, then every 5 minutes the current usage for -every connected use will be dumped to a file in this directory. Each -file dumped begins with a header, where each line is prefixed by #. -Following the header is a single line for every connected user, fields -separated by a space.
    The fields are username, ip, qos, -uptxoctets, downrxoctets. The qos field is 1 if a standard user, and -2 if the user is throttled. -
  • - -
  • setuid (int)
    -After starting up and binding the interface, change UID to this. This -doesn't work properly. -
  • - -
  • dump_speed (boolean)
    -If set to true, then the current bandwidth utilization will be logged every -second. Even if this is disabled, you can see this information by running -the uptime command on the CLI. -
  • - -
  • multi_read_count (int)
    -Number of packets to read off each of the UDP and TUN fds when -returned as readable by select (default: 10). Avoids incurring the -unnecessary system call overhead of select on busy servers. -
  • - -
  • scheduler_fifo (boolean)
    -Sets the scheduling policy for the l2tpns process to SCHED_FIFO. This -causes the kernel to immediately preempt any currently running SCHED_OTHER -(normal) process in favour of l2tpns when it becomes runnable. -Ignored on uniprocessor systems. -
  • - -
  • lock_pages (boolean)
    -Keep all pages mapped by the l2tpns process in memory. -
  • - -
  • icmp_rate (int)
    -Maximum number of host unreachable ICMP packets to send per second. -
  • - -
  • packet_limit (int>
    -Maximum number of packets of downstream traffic to be handled each -tenth of a second per session. If zero, no limit is applied (default: -0). Intended as a DoS prevention mechanism and not a general -throttling control (packets are dropped, not queued). -
  • - -
  • cluster_address (ip address)
    -Multicast cluster address (default: 239.192.13.13). See the section -on Clustering for more information. -
  • - -
  • cluster_interface (string)
    -Interface for cluster packets (default: eth0). -
  • - -
  • cluster_mcast_ttl (int)
    -TTL for multicast packets (default: 1). -
  • - -
  • cluster_hb_interval (int)
    -Interval in tenths of a second between cluster heartbeat/pings. -
  • - -
  • cluster_hb_timeout (int)
    -Cluster heartbeat timeout in tenths of a second. A new master will be -elected when this interval has been passed without seeing a heartbeat -from the master. -
  • - -
  • cluster_master_min_adv (int)
    -Determines the minumum number of up to date slaves required before the -master will drop routes (default: 1). -
  • -
- -

BGP routing configuration is entered by the command: -The routing configuration section is entered by the command -

router bgp as
-where as specifies the local AS number. - -

Subsequent lines prefixed with -

neighbour peer
-define the attributes of BGP neighhbours. Valid commands are: -
-
neighbour peer remote-as as -
neighbout peer timers keepalive hold -
- -Where peer specifies the BGP neighbour as either a hostname or -IP address, as is the remote AS number and keepalive, -hold are the timer values in seconds. - -

Named access-lists are configured using one of the commands: -

-
ip access-list standard name -
ip access-list extended name -
- -

Subsequent lines prefixed with permit or deny -define the body of the access-list. Standard access-list syntax: -

-
{permit|deny} - {host|source source-wildcard|any} - [{host|destination destination-wildcard|any}] -
- -Extended access-lists: - -
-

{permit|deny} ip - {host|source source-wildcard|any} - {host|destination destination-wildcard|any} [fragments] -

{permit|deny} udp - {host|source source-wildcard|any} - [{eq|neq|gt|lt} port|range from to] - {host|destination destination-wildcard|any} - [{eq|neq|gt|lt} port|range from to] - [fragments] -

{permit|deny} tcp - {host|source source-wildcard|any} - [{eq|neq|gt|lt} port|range from to] - {host|destination destination-wildcard|any} - [{eq|neq|gt|lt} port|range from to] - [{established|{match-any|match-all} - {+|-}{fin|syn|rst|psh|ack|urg} - ...|fragments] -

- -

users

- -Usernames and passwords for the command-line interface are stored in -this file. The format is username:password where -password may either by plain text, an MD5 digest (prefixed by -$1salt$) or a DES password, distinguished from -plain text by the prefix {crypt}.

- -The username enable has a special meaning and is used to set -the enable password.

- -Note: If this file doesn't exist, then anyone who can get to -port 23 will be allowed access without a username / password.

- -

ip_pool

- -This file is used to configure the IP address pool which user -addresses are assigned from. This file should contain either an IP -address or a CIDR network per line. e.g.:

- -

-    192.168.1.1
-    192.168.1.2
-    192.168.1.3
-    192.168.4.0/24
-    172.16.0.0/16
-    10.0.0.0/8
-
- -Keep in mind that l2tpns can only handle 65535 connections per -process, so don't put more than 65535 IP addresses in the -configuration file. They will be wasted. - -

build-garden

- -The garden plugin on startup creates a NAT table called "garden" then -sources the build-garden script to populate that table. All -packets from gardened users will be sent through this table. Example: - -
-    iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p tcp -m tcp --dport 80 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p tcp -m tcp --dport 110 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p tcp -m tcp --dport 443 -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p icmp -m icmp --icmp-type echo-request -j DNAT --to 192.168.1.1
-    iptables -t nat -A garden -p icmp -j ACCEPT
-    iptables -t nat -A garden -j DROP
-
- -

Controlling the Process

- -A running l2tpns process can be controlled in a number of ways. The primary -method of control is by the Command-Line Interface (CLI).

- -You can also remotely send commands to modules via the nsctl client -provided.

- -Also, there are a number of signals that l2tpns understands and takes action -when it receives them. - -

Command-Line Interface

- -You can access the command line interface by telnet'ing to port 23. -There is no IP address restriction, so it's a good idea to firewall -this port off from anyone who doesn't need access to it. See -users for information on restricting access based -on a username and password.

- -The CLI gives you real-time control over almost everything in -the process. The interface is designed to look like a Cisco -device, and supports things like command history, line editing and -context sensitive help. This is provided by linking with the -libcli -library. Some general documentation of the interface is - -here.

- -After you have connected to the telnet port (and perhaps logged in), you -will be presented with a hostname> prompt.

- -Enter help to get a list of possible commands. A brief -overview of the more important commands follows: - -

    -
  • show session
    -Without specifying a session ID, this will list all tunnels currently -connected. If you specify a session ID, you will be given all -information on a single tunnel. Note that the full session list can -be around 185 columns wide, so you should probably use a wide terminal -to see the list properly.

    -The columns listed in the overview are: - - - - - - - - - - - - - - -
    SIDSession ID
    TIDTunnel ID - Use with show tunnel tid
    UsernameThe username given in the PPP - authentication. If this is *, then LCP authentication has not - completed.
    IPThe IP address given to the session. If - this is 0.0.0.0, LCP negotiation has not completed.
    IIntercept - Y or N depending on whether the - session is being snooped. See snoop.
    TThrottled - Y or N if the session is - currently throttled. See throttle.
    GWalled Garden - Y or N if the user is - trapped in the walled garden. This field is present even if the - garden module is not loaded.
    openedThe number of seconds since the - session started
    downloadedNumber of bytes downloaded by the user
    uploadedNumber of bytes uploaded by the user
    idleThe number of seconds since traffic was - detected on the session
    LACThe IP address of the LAC the session is - connected to.
    CLIThe Calling-Line-Identification field - provided during the session setup. This field is generated by the - LAC.
    -

    -

  • - -
  • show users
    -With no arguments, display a list of currently connected users. If an -argument is given, the session details for the given username are -displayed. -
  • - -
  • show tunnel
    -This will show all the open tunnels in a summary, or detail on a single -tunnel if you give a tunnel id.

    -The columns listed in the overview are: - - - - - - -
    TIDTunnel ID
    HostnameThe hostname for the tunnel as - provided by the LAC. This has no relation to DNS, it is just - a text field.
    IPThe IP address of the LAC
    StateTunnel state - Free, Open, Dieing, - Opening
    SessionsThe number of open sessions on the - tunnel
    -

    -

  • - -
  • show pool
    -Displays the current IP address pool allocation. This will only display -addresses that are in use, or are reserved for re-allocation to a -disconnected user.

    -If an address is not currently in use, but has been used, then in the User -column the username will be shown in square brackets, followed by the time -since the address was used: -

    -IP Address      Used  Session User
    -192.168.100.6     N           [joe.user] 1548s
    -
    -

    -

  • - -
  • show radius
    -Show a summary of the in-use RADIUS sessions. This list should not be very -long, as RADIUS sessions should be cleaned up as soon as they are used. The -columns listed are: - - - - - - -
    RadiusThe ID of the RADIUS request. This is - sent in the packet to the RADIUS server for identification.
    StateThe state of the request - WAIT, CHAP, - AUTH, IPCP, START, STOP, NULL.
    SessionThe session ID that this RADIUS - request is associated with
    RetryIf a response does not appear to the - request, it will retry at this time. This is a unix timestamp.
    TryRetry count. The RADIUS request is - discarded after 3 retries.
    -

    -

  • - -
  • show running-config
    -This will list the current running configuration. This is in a format that -can either be pasted into the configuration file, or run directly at the -command line. -

    -

  • - -
  • show counters
    -Internally, counters are kept of key values, such as bytes and packets -transferred, as well as function call counters. This function displays all -these counters, and is probably only useful for debugging.

    -You can reset these counters by running clear counters. -

    -

  • - -
  • show cluster
    -Show cluster status. Shows the cluster state for this server -(Master/Slave), information about known peers and (for slaves) the -master IP address, last packet seen and up-to-date status.

    -See Clustering for more information. -

    -

  • - -
  • write memory
    -This will write the current running configuration to the config file -startup-config, which will be run on a restart. -

    -

  • - -
  • snoop
    -You must specify a username, IP address and port. All packets for the -current session for that username will be forwarded to the given -host/port. Specify no snoop username to disable interception -for the session.

    - -If you want interception to be permanent, you will have to modify the RADIUS -response for the user. See Interception. -

    -

  • - -
  • throttle
    -You must specify a username, which will be throttled for the current -session. Specify no throttle username to disable throttling -for the current session.

    - -If you want throttling to be permanent, you will have to modify the -RADIUS response for the user. See Throttling. -

    -

  • - -
  • drop session
    -This will cleanly disconnect a session. You must specify a session id, which -you can get from show session. This will send a disconnect message -to the remote end. -

    -

  • - -
  • drop tunnel
    -This will cleanly disconnect a tunnel, as well as all sessions on that -tunnel. It will send a disconnect message for each session individually, and -after 10 seconds it will send a tunnel disconnect message. -

    -

  • - -
  • uptime
    -This will show how long the l2tpns process has been running, and the current -bandwidth utilization: -
    -17:10:35 up 8 days, 2212 users, load average: 0.21, 0.17, 0.16
    -Bandwidth: UDP-ETH:6/6  ETH-UDP:13/13  TOTAL:37.6   IN:3033 OUT:2569
    -
    -The bandwidth line contains 4 sets of values.
    -UDP-ETH is the current bandwidth going from the LAC to the ethernet -(user uploads), in mbits/sec.
    -ETH-UDP is the current bandwidth going from ethernet to the LAC (user -downloads).
    -TOTAL is the total aggregate bandwidth in mbits/s.
    -IN and OUT are packets/per-second going between UDP-ETH and ETH-UDP. -

    -These counters are updated every second. -

    -

  • - -
  • configure terminal
    -Enter configuration mode. Use exit or ^Z to exit this mode. -The following commands are valid in this mode:

    -

  • - -
  • load plugin
    -Load a plugin. You must specify the plugin name, and it will search in -/usr/lib/l2tpns for plugin.so. You can unload a loaded plugin with -remove plugin. -

    -

  • - -
  • set
    -Set a configuration variable. You must specify the variable name, and -the value. If the value contains any spaces, you should quote the -value with double (") or single (') quotes.

    - -You can set any startup-config value in -this way, although some may require a restart to take effect.

    -

  • -
- -

nsctl

- -nsctl allows messages to be passed to plugins.

- -Arguments are command and optional args. See -nsctl(8) for more details.

- -Built-in command are load_plugin, unload_plugin and -help. Any other commands are passed to plugins for processing. - -

Signals

- -While the process is running, you can send it a few different signals, using -the kill command. -
-killall -HUP l2tpns
-
- -The signals understood are: -
-
SIGHUP
Reload the config from disk and re-open log file.
-
SIGTERM, SIGINT
Stop process. Tunnels and sessions are not -terminated. This signal should be used to stop l2tpns on a -cluster node where there are other machines to -continue handling traffic.
-
SIGQUIT
Shut down tunnels and sessions, exit process when -complete.
-
- -

Throttling

- -l2tpns contains support for slowing down user sessions to whatever speed you -desire. You must first enable the global setting throttle_speed -before this will be activated.

- -If you wish a session to be throttled permanently, you should set the -Vendor-Specific RADIUS value Cisco-Avpair="throttle=yes", which -will be handled by the autothrottle module.

- -Otherwise, you can enable and disable throttling an active session using -the throttle CLI command.

- -

Interception

- -You may have to deal with legal requirements to be able to intercept a -user's traffic at any time. l2tpns allows you to begin and end interception -on the fly, as well as at authentication time.

- -When a user is being intercepted, a copy of every packet they send and -receive will be sent wrapped in a UDP packet to the IP address and port set -in the snoop_host and snoop_port configuration -variables.

- -The UDP packet contains just the raw IP frame, with no extra headers.

- -To enable interception on a connected user, use the snoop username -and no snoop username CLI commands. These will enable interception -immediately.

- -If you wish the user to be intercepted whenever they reconnect, you will -need to modify the RADIUS response to include the Vendor-Specific value -Cisco-Avpair="intercept=yes". For this feature to be enabled, -you need to have the autosnoop module loaded.

- -

Authentication

- -Whenever a session connects, it is not fully set up until authentication is -completed. The remote end must send a PPP CHAP or PPP PAP authentication -request to l2tpns.

- -This request is sent to the RADIUS server, which will hopefully respond with -Auth-Accept or Auth-Reject.

- -If Auth-Accept is received, the session is set up and an IP address is -assigned. The RADIUS server can include a Framed-IP-Address field in the -reply, and that address will be assigned to the client. It can also include -specific DNS servers, and a Framed-Route if that is required.

- -If Auth-Reject is received, then the client is sent a PPP AUTHNAK packet, -at which point they should disconnect. The exception to this is when the -walled garden module is loaded, in which case the user still receives the -PPP AUTHACK, but their session is flagged as being a garden'd user, and they -should not receive any service.

- -The RADIUS reply can also contain a Vendor-Specific attribute called -Cisco-Avpair. This field is a freeform text field that most Cisco -devices understand to contain configuration instructions for the session. In -the case of l2tpns it is expected to be of the form -

-key=value,key2=value2,key3=value3,keyn=value
-
- -Each key-value pair is separated and passed to any modules loaded. The -autosnoop and autothrottle understand the keys -intercept and throttle respectively. For example, to have -a user who is to be throttled and intercepted, the Cisco-Avpair value should -contain: -
-intercept=yes,throttle=yes
-
- -

Plugins

- -So as to make l2tpns as flexible as possible (I know the core code is pretty -difficult to understand), it includes a plugin API, which you can use to -hook into certain events.

- -There are a few example modules included - autosnoop, autothrottle and -garden.

- -When an event happens that has a hook, l2tpns looks for a predefined -function name in every loaded module, and runs them in the order the modules -were loaded.

- -The function should return PLUGIN_RET_OK if it is all OK. If it returns -PLUGIN_RET_STOP, then it is assumed to have worked, but that no further -modules should be run for this event.

-A return of PLUGIN_RET_ERROR means that this module failed, and -no further processing should be done for this event. Use this with care. - -Every event function called takes a specific structure named -param_event, which varies in content with each event. The -function name for each event will be plugin_event, -so for the event timer, the function declaration should look like: -

-int plugin_timer(struct param_timer *data);
-
- -A list of the available events follows, with a list of all the fields in the -supplied structure: -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventDescriptionParameters
pre_authThis is called after a RADIUS response has been - received, but before it has been processed by the - code. This will allow you to modify the response in - some way. - -
-
t
Tunnel -
s
Session -
username -
password -
protocol
0xC023 for PAP, 0xC223 for CHAP -
continue_auth
Set to 0 to stop processing authentication modules -
-
post_authThis is called after a RADIUS response has been - received, and the basic checks have been performed. This - is what the garden module uses to force authentication - to be accepted. - -
-
t
Tunnel -
s
Session -
username -
auth_allowed
This is already set to true or - false depending on whether authentication has been - allowed so far. You can set this to 1 or 0 to force - allow or disallow authentication -
protocol
0xC023 for PAP, 0xC223 for CHAP -
-
packet_rxThis is called whenever a session receives a - packet. Use this sparingly, as this will - seriously slow down the system. - -
-
t
Tunnel -
s
Session -
buf
The raw packet data -
len
The length of buf -
-
packet_txThis is called whenever a session sends a - packet. Use this sparingly, as this will - seriously slow down the system. - -
-
t
Tunnel -
s
Session -
buf
The raw packet data -
len
The length of buf -
-
timerThis is run every second, no matter what is happening. - This is called from a signal handler, so make sure anything - you do is reentrant. - -
-
time_now
The current unix timestamp -
-
new_sessionThis is called after a session is fully set up. The - session is now ready to handle traffic. - -
-
t
Tunnel -
s
Session -
-
kill_sessionThis is called when a session is about to be shut down. - This may be called multiple times for the same session. - -
-
t
Tunnel -
s
Session -
-
radius_responseThis is called whenever a RADIUS response includes a - Cisco-Avpair value. The value is split up into - key=value pairs, and each is processed through all - modules. - -
-
t
Tunnel -
s
Session -
key -
value -
-
radius_resetThis is called whenever a RADIUS CoA request is - received to reset any options to default values before - the new values are applied. - -
-
t
Tunnel -
s
Session -
-
controlThis is called in whenever a nsctl packet is received. - This should handle the packet and form a response if - required. - -
-
iam_master
Cluster master status -
argc
The number of arguments -
argv
Arguments -
response
Return value: NSCTL_RES_OK or NSCTL_RES_ERR -
additional
Extended response text -
-
-
- -

Walled Garden

- -Walled Garden is implemented so that you can provide perhaps limited service -to sessions that incorrectly authenticate.

- -Whenever a session provides incorrect authentication, and the -RADIUS server responds with Auth-Reject, the walled garden module -(if loaded) will force authentication to succeed, but set the flag -garden in the session structure, and adds an iptables rule to -the garden_users chain to force all packets for the session's IP -address to traverse the garden chain.

- -This doesn't just work. To set this all up, you will to -setup the garden nat table with the -build-garden script with rules to limit -user's traffic. For example, to force all traffic except DNS to be -forwarded to 192.168.1.1, add these entries to your -build-garden: -

-iptables -t nat -A garden -p tcp --dport ! 53 -j DNAT --to 192.168.1.1
-iptables -t nat -A garden -p udp --dport ! 53 -j DNAT --to 192.168.1.1
-
- -l2tpns will add entries to the garden_users chain as appropriate.

- -You can check the amount of traffic being captured using the following -command: -

-iptables -t nat -L garden -nvx
-
- -

Filtering

- -Sessions may be filtered by specifying Filter-Id attributes in -the RADIUS reply. filter.in specifies that the named -access-list filter should be applied to traffic from the -customer, filter.out specifies a list for traffic to the -customer. - -

Clustering

- -An l2tpns cluster consists of of one* or more servers configured with -the same configuration, notably the multicast cluster_address.

- -*A stand-alone server is simply a degraded cluster.

- -Initially servers come up as cluster slaves, and periodically (every -cluster_hb_interval/10 seconds) send out ping packets -containing the start time of the process to the multicast -cluster_address.

- -A cluster master sends heartbeat rather than ping packets, which -contain those session and tunnel changes since the last heartbeat.

- -When a slave has not seen a heartbeat within -cluster_hb_timeout/10 seconds it "elects" a new master by -examining the list of peers it has seen pings from and determines -which of these and itself is the "best" candidate to be master. -"Best" in this context means the server with the highest uptime (the -highest IP address is used as a tie-breaker in the case of equal -uptimes).

- -After discovering a master, and determining that it is up-to-date (has -seen an update for all in-use sessions and tunnels from heartbeat -packets) will raise a route (see Routing) for -the bind_address and for all addresses/networks in -ip_pool. Any packets recieved by the slave which would alter -the session state, as well as packets for throttled or gardened -sessions are forwarded to the master for handling. In addition, byte -counters for session traffic are periodically forwarded.

- -A master, when determining that it has at least one up-to-date slave -will drop all routes (raising them again if all slaves disappear) and -subsequently handle only packets forwarded to it by the slaves.

- -

Routing

-If you are running a single instance, you may simply statically route -the IP pools to the bind_address (l2tpns will send a gratuitous -arp).

- -For a cluster, configure the members as BGP neighbours on your router -and configure multi-path load-balancing. Cisco uses "maximum-paths -ibgp" for IBGP. If this is not supported by your IOS revision, you -can use "maximum-paths" (which works for EBGP) and set -as_number to a private value such as 64512.

- -

Performance

- -Performance is great.

- -I'd like to include some pretty graphs here that show a linear performance -increase, with no impact by number of connected sessions.

- -That's really what it looks like.

- -
-David Parrish
-l2tpns-users@lists.sourceforge.net - - diff --git a/Docs/manual/manual.xml b/Docs/manual/manual.xml new file mode 100644 index 0000000..cb26b54 --- /dev/null +++ b/Docs/manual/manual.xml @@ -0,0 +1,2150 @@ + + + +

+ + L2TPNS Manual + + + + Overview + + l2tpns is half of a complete L2TP + implementation. It supports only the LNS side of the + connection. + + + + L2TP (Layer 2 Tunneling Protocol) is designed to allow any layer + 2 protocol (e.g. Ethernet, PPP) to be tunneled over an IP + connection. l2tpns implements PPP over L2TP + only. + + + + There are a couple of other L2TP implementations, of which + l2tpd + is probably the most popular. l2tpd also will handle being + either end of a tunnel, and is a lot more configurable than + l2tpns. However, due to the way it works, it + is nowhere near as scalable. + + + + l2tpns uses the TUN/TAP interface provided by + the Linux kernel to receive and send packets. Using some packet + manipulation it doesn't require a single interface per + connection, as l2tpd does. + + + + This allows it to scale extremely well to very high loads and + very high numbers of connections. + + + + It also has a plugin architecture which allows custom code to be + run during processing. An example of this is in the walled + garden module included. + + + + + Installation + + Requirements + + + + Linux kernel version 2.4 or above, with the Tun/Tap + interface either compiled in, or as a module. + + + + + + libcli 1.8.5 or greater. You can get this from + + SourceForge + + + + + + + Compiling + + You can generally get away with just running + make from the source directory. This will + compile the daemon, associated tools and any modules shipped + with the distribution. + + + + + Installing + + After you have successfully compiled everything, run + make install to install it. By + default, the binaries are installed into + /usr/sbin, the configuration into + /etc/l2tpns, and the modules into + /usr/lib/l2tpns. + + + + You will definately need to edit the configuration files + before you start. See for + more information. + + + + + Running + + You only need to run /usr/sbin/l2tpns as + root to start it. It does not normally detach to a daemon + process (see the option), so you should + perhaps run it from init. + + + + By default there is no log destination set, so all log + messages will go to stdout. + + + + + + Configuration + + All configuration of the software is done from the files + installed into /etc/l2tpns. + + + + <filename>startup-config</filename> + + This is the main configuration file for + l2tpns. The format of the file is a list + of commands that can be run through the command-line + interface. This file can also be written directly by the + l2tpns process if a user runs the + write memory command, so any comments + will be lost. However if your policy is not to write the + config by the program, then feel free to comment the file with + a # or ! at the + beginning of the line. + + + + A list of the possible configuration directives follows. Each + of these should be set by a line like: + +set configstring "value" +set ipaddress 192.168.1.1 +set boolean true + + + + + + debug (int) + + + Sets the level of messages that will be written to the + log file. The value should be between 0 and 5, with 0 + being no debugging, and 5 being the highest. A rough + description of the levels is: + + + + 0: Critical Errors + + Things are probably broken + + + + + 1: Errors + + + Things might have gone wrong, but probably will + recover + + + + + + 2: Warnings + + + Just in case you care what is not quite perfect + + + + + + 3: Information + + Parameters of control packets + + + + + 4: Calls + + For tracing the execution of the code + + + + + 5: Packets + + + Everything, including a hex dump of all packets + processed... probably twice + + + + + + + + Note that the higher you set the debugging level, the + slower the program will run. Also, at level 5 a + lot of information will be logged. + This should only ever be used for working out why it + doesn't work at all. + + + + + + log_file (string) + + + This will be where all logging and debugging information + is written to. This may be either a filename, such as + /var/log/l2tpns, or the special + magic string + syslog:facility, + where facility is any one of + the syslog logging facilities, such as + local5. + + + + + + pid_file (string) + + + If set, the process id will be written to the specified + file. The value must be an absolute path. + + + + + + random_device (string) + + + Path to random data source (default + /dev/urandom). Use "" to use the + rand() library function. + + + + + + l2tp_secret (string) + + + The secret used by l2tpns for + authenticating tunnel request. Must be the same as the + LAC, or authentication will fail. Only actually be used + if the LAC requests authentication. + + + + + + l2tp_mtu (int) + + + MTU of interface for L2TP traffic (default: + 1500). Used to set link MRU and + adjust TCP MSS. + + + + + + ppp_restart_time (int) + ppp_max_configure (int) + ppp_max_failure (int) + + + PPP counter and timer values, as described in §4.1 + of + RFC1661. + + + + + + primary_dns (ip address) + econdary_dns (ip address) + + + Whenever a PPP connection is established, DNS servers + will be sent to the user, both a primary and a + secondary. If either is set to 0.0.0.0, then that one + will not be sent. + + + + + + primary_radius (ip address) + secondary_radius (ip address) + + + Sets the RADIUS servers used for both authentication and + accounting. If the primary server does not respond, + then the secondary RADIUS server will be tried. + + + + In addition to the source IP address and identifier, + the RADIUS server must include + the source port when detecting duplicates to supress + (in order to cope with a large number of sessions + comming on-line simultaneously + l2tpns uses a set of udp sockets, + each with a seperate identifier). + + + + + + + + primary_radius_port (short) + secondary_radius_port (short) + + + Sets the authentication ports for the primary and + secondary RADIUS servers. The accounting port is one + more than the authentication port. If no RADIUS ports + are given, the authentication port defaults to 1645, and + the accounting port to 1646. + + + + + + radius_accounting (boolean) + + + If set to true, then RADIUS accounting packets will be + sent. This means that a Start record will be sent when + the session is successfully authenticated, and a Stop + record will be sent when the session is closed. + + + + + + radius_interim (int) + + + If radius_accounting is on, defines + the interval between sending of RADIUS interim + accounting records (in seconds). + + + + + + radius_secret (string) + + + This secret will be used in all RADIUS queries. If this + is not set then RADIUS queries will fail. + + + + + + radius_authtypes (string) + + + A comma separated list of supported RADIUS + authentication methods (pap or + chap), in order of preference + (default pap). + + + + + + radius_dae_port (short) + + + Port for DAE RADIUS (Packet of Death/Disconnect, Change + of Authorization) requests (default: + 3799). + + + + + + allow_duplicate_users (boolean) + + + Allow multiple logins with the same username. If false + (the default), any prior session with the same username + will be dropped when a new session is established. + + + + + + guest_account (string) + + + Allow multiple logins matching this specific username. + + + + + + bind_address (ip address) + + + When the tun interface is created, it is assigned the + address specified here. If no address is given, 1.1.1.1 + is used. Packets containing user traffic should be + routed via this address if given, otherwise the primary + address of the machine. + + + + + + peer_address (ip address) + + Address to send to clients as the default gateway. + + + + + send_garp (boolean) + + + Determines whether or not to send a gratuitous ARP for + the bind_address when the server is ready to handle + traffic (default: true). This value + is ignored if BGP is configured. + + + + + + throttle_speed (int) + + + Sets the default speed (in kbits/s) which sessions will + be limited to. If this is set to 0, then throttling + will not be used at all. Note: You can set this by the + CLI, but changes will not affect currently connected + users. + + + + + + throttle_buckets (int) + + + Number of token buckets to allocate for throttling. + Each throttled session requires two buckets (in and + out). + + + + + + accounting_dir (string) + + + If set to a directory, then every 5 minutes the current + usage for every connected use will be dumped to a file + in this directory. Each file dumped begins with a + header, where each line is prefixed by #. + Following the header is a single line for every + connected user, fields separated by a space. + + + + The fields are username, ip, qos, uptxoctets, + downrxoctets. The qos field is 1 if a standard user, + and 2 if the user is throttled. + + + + + + dump_speed (boolean) + + + If set to true, then the current bandwidth utilization + will be logged every second. Even if this is disabled, + you can see this information by running the + uptime command on the CLI. + + + + + + multi_read_count (int) + + + Number of packets to read off each of the UDP and TUN + fds when returned as readable by select (default: 10). + Avoids incurring the unnecessary system call overhead of + select on busy servers. + + + + + + scheduler_fifo (boolean) + + + Sets the scheduling policy for the + l2tpns process to + SCHED_FIFO. This causes the kernel + to immediately preempt any currently running + SCHED_OTHER (normal) process in + favour of l2tpns when it becomes + runnable. Ignored on uniprocessor systems. + + + + + + lock_pages (boolean) + + + Keep all pages mapped by the l2tpns + process in memory. + + + + + + icmp_rate (int) + + + Maximum number of host unreachable ICMP packets to send + per second. + + + + + + packet_limit (int) + + + Maximum number of packets of downstream traffic to be + handled each tenth of a second per session. If zero, no + limit is applied (default: 0). Intended as a DoS + prevention mechanism and not a general throttling + control (packets are dropped, not queued). + + + + + + cluster_address (ip address) + + + Multicast cluster address (default: 239.192.13.13). + See for more information. + + + + + + cluster_interface (string) + + Interface for cluster packets (default: eth0) + + + + + cluster_mcast_ttl (int) + + TTL for multicast packets (default: 1). + + + + + cluster_hb_interval (int) + + + Interval in tenths of a second between cluster + heartbeat/pings. + + + + + + cluster_hb_timeout (int) + + + Cluster heartbeat timeout in tenths of a second. A new + master will be elected when this interval has been + passed without seeing a heartbeat from the master. + + + + + + cluster_master_min_adv (int) + + + Determines the minumum number of up to date slaves + required before the master will drop routes (default: 1). + + + + + + ipv6_prefix (ipv6 address) + + + Enable negotiation of IPv6. This forms the the first 64 + bits of the client allocated address. The remaining 64 + come from the allocated IPv4 address and 4 bytes of 0s. + + + + + + + BGP + + BGP routing configuration is entered by the command: + router bgp as + where as specifies the local AS + number. + + + + Subsequent lines prefixed with + neighbour peer + define the attributes of BGP neighhbours. Valid commands + are: + +neighbour peer remote-as as +neighbour peer timers keepalive hold + + + + + Where peer specifies the BGP + neighbour as either a hostname or IP address, + as is the remote AS number and + keepalive, + hold are the timer values in + seconds. + + + + + Access Lists + + Named access-lists are configured using one of the commands: + +ip access-list standard name +ip access-list extended name + + + + + Subsequent lines prefixed with permit or + deny define the body of the access-list. + Standard access-list syntax: + + + + {permit|deny} + {host|source + source-wildcard|any} + [{host|destination + destination-wildcard|any}] + + + Extended access-lists: + + {permit|deny} + ip + {host|source + source-wildcard|any} + {host|destination + destination-wildcard|any} + [fragments] + + + + {permit|deny} + udp + {host|source + source-wildcard|any} + [{eq|neq|gt|lt} + port|range + from + to] + {host|destination + destination-wildcard|any} + [{eq|neq|gt|lt} + port|range + from + to] + [fragments] + + + + {permit|deny} + tcp + {host|source + source-wildcard|any} + [{eq|neq|gt|lt} + port|range + from + to] + {host|destination + destination-wildcard|any} + [{eq|neq|gt|lt} + port|range + from + to] + [{established|{match-any|match-all} + {+|-}{fin|syn|rst|psh|ack|urg} + ...|fragments] + + + + + + <filename>users</filename> + + Usernames and passwords for the command-line interface are + stored in this file. The format is + +username:password + + where password may either by plain + text, an MD5 digest (prefixed by + $1salt$) + or a DES password, distinguished from plain text by the prefix + {crypt}. + + + + The username enable has a special meaning + and is used to set the enable password. + + + + + If this file doesn't exist, then anyone who can get to port + 23 will be allowed access without a username or password. + + + + + + <filename>ip_pool</filename> + + This file is used to configure the IP address pool which user + addresses are assigned from. This file should contain either + an IP address or a CIDR network per line. e.g.: + + +192.168.1.1 +192.168.1.2 +192.168.1.3 +192.168.4.0/24 +172.16.0.0/16 +10.0.0.0/8 + + + + + Keep in mind that l2tpns can only handle + 65535 connections per process, so don't put more than 65535 IP + addresses in the configuration file. They will be + wasted. + + + + + <filename>build-garden</filename> + + The garden plugin on startup creates a NAT table called + "garden" then sources the build-garden + script to populate that table. All packets from gardened + users will be sent through this table. Example: + + +iptables -t nat -A garden -p tcp -m tcp --dport 25 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p udp -m udp --dport 53 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p tcp -m tcp --dport 53 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p tcp -m tcp --dport 80 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p tcp -m tcp --dport 110 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p tcp -m tcp --dport 443 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p icmp -m icmp --icmp-type echo-request -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p icmp -j ACCEPT +iptables -t nat -A garden -j DROP + + + + + + + Operation + + A running l2tpns process can be controlled in a number of ways. + The primary method of control is by the Command-Line Interface + (CLI). + + + + You can also remotely send commands to modules via the + nsctl client provided. + + + + There are also a number of signals that l2tpns understands and + takes action when it receives them. + + + + Command-Line Interface + + You can access the command line interface by telneting to port + 23. There is no IP address restriction, so it's a good idea + to firewall this port off from anyone who doesn't need access + to it. See for information on + restricting access based on a username and password. + + + + The CLI gives you real-time control over almost everything in + the process. The interface is designed to look like a Cisco + device, and supports things like command history, line editing + and context sensitive help. This is provided by linking with + the + libcli library. Some general documentation of the + interface is + here. + + + + After you have connected to the telnet port (and perhaps + logged in), you will be presented with a + hostname> + prompt. + + + + Enter help to get a list of possible + commands, or press ? for + context-specific help. + + + + A brief overview of the more important commands + follows: + + + + + show session [ID] + + + + + + Detailed information for a specific session is + presented if you specify a session + ID argument. + + + + If no ID is given, a + summary of all connected sessions is produced. Note + that this summary list can be around 185 columns wide, + so you should probably use a wide + terminal. + + + + The columns listed in the summary are: + + + + + + + + + SID + Session ID + + + TID + Tunnel ID + + See also the CLI + command. + + + + Username + + The username given in the PPP authentication. + + + If this is *, then LCP authentication has + not completed. + + + + IP + The IP address given to the session. + + If this is 0.0.0.0, IPCP negotiation has not + completed + + + + I + Intercept + + Y or N: indicates whether the session is + being snooped. See also the + + CLI command. + + + + T + Throttled + + Y or N: indicates whether the session is + currently throttled. See also the + + CLI command. + + + + G + Walled Garden + + Y or N: indicates whether the user is + trapped in the walled garden. This field is + present even if the garden module is not + loaded. + + + + 6 + IPv6 + + Y or N: indicates whether the session has + IPv6 active (IPV6CP open) + + + + opened + + The number of seconds since the + session started + + + + downloaded + + Number of bytes downloaded by the user + + + + uploaded + + Number of bytes uploaded by the user + + + + idle + + The number of seconds since traffic was + detected on the session + + + + LAC + + The IP address of the LAC the session is + connected to. + + + + CLI + + The Calling-Line-Identification field + provided during the session setup. This + field is generated by the LAC. + + + + + + + + + + + + show users + + + show user username + + + + + + With no arguments, display a list of currently + connected users. If an argument is given, the session + details for the given + username are displayed. + + + + + + + show tunnel [ID] + + + + + Produce a summary list of all open tunnels, or detail + on a specific tunnel ID. + + + + The columns listed in the summary are: + + + + + + TID + Tunnel ID + + + Hostname + + The hostname for the tunnel as provided by + the LAC. This has no relation to DNS, it is + just a text field. + + + + IP + The IP address of the LAC + + + State + + Tunnel state: Free, Open, Dieing, Opening + + + + Sessions + The number of open sessions on the tunnel + + + + + + + + + + show pool + + + Displays the current IP address pool allocation. This + will only display addresses that are in use, or are + reserved for re-allocation to a disconnected user. + + + + If an address is not currently in use, but has been + used, then in the User column the username will be + shown in square brackets, followed by the time since + the address was used: + + +IP Address Used Session User +192.168.100.6 N [joe.user] 1548s + + + + + + + show radius + + + Show a summary of the in-use RADIUS sessions. This + list should not be very long, as RADIUS sessions + should be cleaned up as soon as they are used. The + columns listed are: + + + + + + Radius + + The ID of the RADIUS request. This is sent + in the packet to the RADIUS server for + identification + + + + State + + The state of the request: WAIT, CHAP, AUTH, + IPCP, START, STOP or NULL + + + + Session + + The session ID that this RADIUS request is + associated with + + + + Retry + + If a response does not appear to the + request, it will retry at this time. This + is a Unix timestamp + + + + Try + + Retry count. The RADIUS request is + discarded after 3 retries + + + + + + + + + + + show running-config + + + This will list the current running configuration. + This is in a format that can either be pasted into the + configuration file, or run directly at the command + line. + + + + + + show counters + + + Internally, counters are kept of key values, such as + bytes and packets transferred, as well as function + call counters. This function displays all these + counters, and is probably only useful for debugging. + + + + You can reset these counters by running + clear counters. + + + + + + show cluster + + + Show cluster status. Shows the cluster state for this + server (Master/Slave), information about known peers + and (for slaves) the master IP address, last packet + seen and up-to-date status. See + for more information. + + + + + + write memory + + + This will write the current running configuration to + the config file startup-config, + which will be run on a restart. + + + + + + + snoop user + IP + port + + + + You must specify a username, IP address and port. All + packets for the current session for that username will + be forwarded to the given host/port. Specify + no snoop + username to + disable interception for the session. + + + + If you want interception to be permanent, you will + have to modify the RADIUS response for the user. See + . + + + + + + + throttle user + [in|out] rate + + + + You must specify a username, which will be throttled + for the current session to + rate Kbps. Prefix + rate with + in or + out to set different upstream + and downstream rates. + + + + Specify no throttle + username to + disable throttling for the current session. + + + + If you want throttling to be permanent, you will have + to modify the RADIUS response for the user. See . + + + + + + + drop session + + + + This will cleanly disconnect the session specified by + session ID. + + + + + + + drop tunnel + + + + This will cleanly disconnect the tunnel specified by + tunnel ID, as well as all + sessions on that tunnel. + + + + + + uptime + + + This will show how long the l2tpns + process has been running, and the current bandwidth + utilization: + + +17:10:35 up 8 days, 2212 users, load average: 0.21, 0.17, 0.16 +Bandwidth: UDP-ETH:6/6 ETH-UDP:13/13 TOTAL:37.6 IN:3033 OUT:2569 + + + The bandwidth line contains 4 sets of values: + + + + + + UDP-ETH + + The current bandwidth going from the LAC to + the ethernet (user uploads), in mbits/sec. + + + + ETH-UDP + + The current bandwidth going from ethernet to + the LAC (user downloads). + + + + TOTAL + The total aggregate bandwidth in mbits/s. + + + IN and OUT + + Packets/per-second going between UDP-ETH and + ETH-UDP. + + + + + + + These counters are updated every second. + + + + + + configure terminal + + + Enter configuration mode. Use + exit or + ^Z to exit this mode. + + + + The following commands are valid in this mode: + + + + + load plugin + name + + + + Load a plugin. You must specify the plugin + name, and it will search in + /usr/lib/l2tpns for + name.so. + You can unload a loaded plugin with + remove plugin + name. + + + + + + set ... + + + Set a configuration variable. You must specify + the variable name, and the value. If the value + contains any spaces, you should quote the value + with double (") or single (') quotes. + + + + You can set any configuration value in this + way, although some may require a restart to + take effect. See . + + + + + + router bgp ... + + + Configure BGP. See . + + + + + + ip access-list ... + + + Configure a named access list. See . + + + + + + + + + + + + + nsctl + + nsctl sends messages to a running + l2tpns instance to be control plugins. + + + + Arguments are command and optional + args. See + nsctl(8). + + + + Built-in command are load_plugin, + unload_plugin and + help. Any other commands are passed to + plugins for processing by the + plugin_control function. + + + + + Signals + + While the process is running, you can send it a few different + signals, using the kill command. + + +killall -HUP l2tpns + + + The signals understood are: + + + + SIGHUP + + Reload the config from disk and re-open log file. + + + + + SIGTERM + SIGINT + + + Stop process. Tunnels and sessions are not + terminated. This signal should be used to stop + l2tpns on a cluster node where + there are other machines to continue handling traffic. + See + + + + + + SIGQUIT + + + Shut down tunnels and sessions, exit process when + complete. + + + + + + + + + + Throttling + + l2tpns contains support for slowing down user + sessions to whatever speed you desire. The global setting + throttle_speed defines the default throttle + rate. + + + + To throttle a sesion permanently, add a + Cisco-AVPair RADIUS attribute. The + autothrotle module interprets the following + attributes: + + + + + + throttle=yes + + Throttle upstream/downstream traffic to the configured + throttle_speed. + + + + + + throttle=rate + + + Throttle upstream/downstream traffic to the specified + rate Kbps. + + + + + + lcp:interface-config#1=service-policy input + rate + + + + Alternate (Cisco) format: throttle + upstream/downstream to specified + rate Kbps. + + + + + + lcp:interface-config#2=service-policy output + rate + + + + + + + + + You can also enable and disable throttling an active session + using the CLI command. + + + + + Interception + + You may have to deal with legal requirements to be able to + intercept a user's traffic at any time. + l2tpns allows you to begin and end + interception on the fly, as well as at authentication time. + + + + When a user is being intercepted, a copy of every packet they + send and receive will be sent wrapped in a UDP packet to a + specified host. + + + + The UDP packet contains just the raw IP frame, with no extra + headers. The script scripts/l2tpns-capture + may be used as the end-point for such intercepts, writing the + data in PCAP format (suitable for inspection with + tcpdump). + + + + To enable or disable interception of a connected user, use the + and no + snoop CLI commands. These will enable interception + immediately. + + + + If you wish the user to be intercepted whenever they reconnect, + you will need to modify the RADIUS response to include the + Vendor-Specific value + Cisco-AVPair="intercept=ip:port". + For this feature to be enabled, you need to have the + autosnoop module loaded. + + + + + Plugins + + So as to make l2tpns as flexible as possible, + a plugin API is include which you can use to hook into certain + events. + + + + There are a some standard modules included which may be used as + examples: autosnoop, + autothrottle, garden, + sessionctl, + setrxspeed, snoopctl, + stripdomain and + throttlectl. + + + + When an event occurs that has a hook, l2tpns + looks for a predefined function name in every loaded module, and + runs them in the order the modules were loaded. + + + + The function should return PLUGIN_RET_OK if it is + all OK. If it returns PLUGIN_RET_STOP, then it is + assumed to have worked, but that no further modules should be + run for this event. + + + + A return of PLUGIN_RET_ERROR means that this module + failed, and no further processing should be done for this event. + Use this with care. + + + + Most event functions take a specific structure named + param_event, which + varies in content with each event. The function name for each + event will be + plugin_event, so for the + event timer, the function declaration + should look like: + + +int plugin_timer(struct param_timer *data); + + + A list of the available events follows, with a list of all the + fields in the supplied structure: + + + + + + + + + + + Event + Description + Arguments + + + + + + plugin_init + + + Called when the plugin is loaded. A pointer to a + struct containing function pointers is passed as the + only argument, allowing the plugin to call back into + the main code. + + + Prior to loading the plugin, l2tpns + checks the API version the plugin was compiled + against. All plugins should contain: + +int plugin_api_version = PLUGIN_API_VERSION; + + + + struct pluginfuncs * + + + + See pluginfuncs structure in + plugin.h for available functions. + + + + + plugin_done + + Called when the plugin is unloaded or + l2tpns is shutdown. + + void + + + No arguments. + + + + plugin_pre_auth + + Called after a RADIUS response has been received, but + before it has been processed by the code. This will + allow you to modify the response in some way. + + + struct plugin param_pre_auth * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + char *username + User name. + + + char *password + Password. + + + int protocol + + Authentication protocol: 0xC023 for PAP, + 0xC223 for CHAP. + + + + int continue_auth + Set to 0 to stop processing authentication modules. + + + + plugin_post_auth + + Called after a RADIUS response has been received, and + the basic checks have been performed. This is what + the garden module uses to force + authentication to be accepted. + + + struct plugin param_post_auth * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + char *username + User name. + + + short auth_allowed + + Initially true or false depending on whether + authentication has been allowed so far. You can + set this to 1 or 0 to force authentication to be + accepted or rejected. + + + + int protocol + + Authentication protocol: 0xC023 for PAP, + 0xC223 for CHAP. + + + + + plugin_timer + + Run once per second. + + + struct plugin param_timer * + + + + time_t time_now + The current unix timestamp. + + + + plugin_new_session + + Called after a session is fully set up. The session + is now ready to handle traffic. + + + struct plugin param_new_session * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + + plugin_kill_session + + Called when a session is about to be shut down. This + may be called multiple times for the same session. + + + struct plugin param_kill_session * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + + plugin_control + + + Called in whenever a nsctl packet + is received. This should handle the packet and form a + response if required. + + + Plugin-specific help strings may be included in the + output of nsctl help by + defining a NULL terminated list of + strings as follows: + +char *plugin_control_help[] = { ..., NULL }; + + + + + struct plugin param_control * + + + + int iam_master + If true, this node is the cluster master. + + + int argc + nsctl arguments. + + + char **argc + + + int reponse + + Response from control message (if handled): should be + either NSCTL_RES_OK or + NSCTL_RES_ERR. + + + + char *additional + + Additional information, output by + nsctl on receiving the response. + + + + + plugin_radius_response + + Called whenever a RADIUS response includes a + Cisco-AVPair value. The value is + split into + key=value + pairs. Will be called once for each pair in the + response. + + + struct plugin param_radius_response * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + char *key + Key and value. + + + char *value + + + + plugin_radius_reset + + Called whenever a RADIUS CoA request is received to + reset any options to default values before the new + values are applied. + + + struct param_radius_reset * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + + plugin_radius_account + + Called when preparing a RADIUS accounting record to + allow additional data to be added to the packet. + + + struct param_radius_account * + + + + tunnelt *t + Tunnel. + + + sessiont *s + Session. + + + uint8_t **packet + + Pointer to the end of the currently assembled + packet buffer. The value should be incremented by the + length of any data added. + + + + + plugin_become_master + + Called when a node elects itself cluster master. + + void + + + No arguments. + + + + plugin_new_session_master + + Called once for each open session on becoming cluster + master. + + sessiont * + + + + Session. + + + + + + + + + + Walled Garden + + A "Walled Garden" is implemented so that you can provide perhaps + limited service to sessions that incorrectly authenticate. + + + + Whenever a session provides incorrect authentication, and the + RADIUS server responds with Auth-Reject, the walled garden + module (if loaded) will force authentication to succeed, but set + the walled_garden flag in the session + structure, and adds an iptables rule to the + garden_users chain to cause all packets for + the session to traverse the garden chain. + + + + This doesn't just work. To set this all + up, you will to setup the garden nat table + with the script with rules + to limit user's traffic. + + + + For example, to force all traffic except DNS to be forwarded to + 192.168.1.1, add these entries to your + build-garden script: + + +iptables -t nat -A garden -p tcp --dport ! 53 -j DNAT --to 192.168.1.1 +iptables -t nat -A garden -p udp --dport ! 53 -j DNAT --to 192.168.1.1 + + + + + l2tpns will add entries to the + garden_users chain as appropriate. + + + + You can check the amount of traffic being captured using the + following command: + + +iptables -t nat -L garden -nvx + + + + + + Filtering + + Sessions may be filtered by specifying + Filter-Id attributes in the RADIUS reply. + filter.in + specifies that the named access-list + filter should be applied to traffic + from the customer, + filter.out + specifies a list for traffic to the customer. + + + + + Clustering + + An l2tpns cluster consists of one* or more + servers configured with the same configuration, notably the + multicast cluster_address. + + + *A stand-alone server is simply a degraded cluster. + + + Initially servers come up as cluster slaves, and periodically + (every cluster_hb_interval/10 seconds) send + out ping packets containing the start time of the process to the + multicast cluster_address. + + + + A cluster master sends heartbeat rather than ping packets, which + contain those session and tunnel changes since the last + heartbeat. + + + + When a slave has not seen a heartbeat within + cluster_hb_timeout/10 seconds it "elects" a + new master by examining the list of peers it has seen pings from + and determines which of these and itself is the "best" candidate + to be master. "Best" in this context means the server with the + highest uptime (the highest IP address is used as a tie-breaker + in the case of equal uptimes). + + + + After discovering a master, and determining that it is + up-to-date (has seen an update for all in-use sessions and + tunnels from heartbeat packets) will raise a route (see ) for the bind_address and + for all addresses/networks in ip_pool. + + + + Any packets recieved by the slave which would alter the session + state, as well as packets for throttled or gardened sessions are + forwarded to the master for handling. In addition, byte + counters for session traffic are periodically forwarded. + + + + The master, when determining that it has at least one* up-to-date + slave will drop all routes (raising them again if all slaves + disappear) and subsequently handle only packets forwarded to it + by the slaves. + + + *Configurable with cluster_master_min_adv + + + + Routing + + If you are running a single instance, you may simply statically + route the IP pools to the bind_address + (l2tpns will send a gratuitous arp). + + + + For a cluster, configure the members as BGP neighbours on your + router and configure multi-path load-balancing. Cisco uses + maximum-paths ibgp for IBGP. If this is + not supported by your IOS revision, you can use + maximum-paths (which works for EBGP) and + set as_number to a private value such as + 64512. + + +
diff --git a/l2tpns.spec b/l2tpns.spec index c46d3ae..15fffac 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Fri Apr 28 2006 Brendan O'Dea 2.2.0-1 +* Wed May 24 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes From 67af795b70319ac8972c86060ed8346b421fc75b Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sat, 3 Jun 2006 08:16:46 +0000 Subject: [PATCH 453/482] kludge around problem with Netgear DM602 authentication --- Changes | 1 + ppp.c | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Changes b/Changes index 0029cfd..7927769 100644 --- a/Changes +++ b/Changes @@ -4,6 +4,7 @@ - Add Multilink support from Khaled Al Hamwi. - Remove non-working setuid option. - Convert manual.html to Docbook. +- Kludge around problem with Netgear DM602 authentication. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/ppp.c b/ppp.c index c369f37..8c1c9a3 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.100 2006-04-27 09:53:50 bodea Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.101 2006-06-03 08:16:46 bodea Exp $"; #include #include @@ -179,16 +179,24 @@ void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) return; } + if (session[s].ppp.phase != Authenticate) + { + LOG(2, s, t, "CHAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase)); + return; + } + r = sess_local[s].radius; if (!r) { LOG(3, s, t, "Unexpected CHAP message\n"); - return; - } - if (session[s].ppp.phase != Authenticate) - { - LOG(2, s, t, "CHAP ignored in %s phase\n", ppp_phase(session[s].ppp.phase)); + // Some modems (Netgear DM602, possibly others) persist in using CHAP even + // after ACKing our ConfigReq for PAP. + if (sess_local[s].lcp_authtype == AUTHPAP && config->radius_authtypes & AUTHCHAP) + { + sess_local[s].lcp_authtype = AUTHCHAP; + sendchap(s, t); + } return; } From 65270c4bd287b5a27c567ff1ebb595738d466b4d Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sun, 11 Jun 2006 12:46:18 +0000 Subject: [PATCH 454/482] add session/idle timeouts --- Changes | 3 +- THANKS | 1 + cli.c | 7 ++- cluster.c | 8 ++-- l2tpns.c | 40 +++++++++++++---- l2tpns.h | 5 ++- l2tpns.spec | 2 +- radius.c | 122 +++++++++++++++++++++++++++++++++++----------------- 8 files changed, 133 insertions(+), 55 deletions(-) diff --git a/Changes b/Changes index 7927769..046dfc6 100644 --- a/Changes +++ b/Changes @@ -1,10 +1,11 @@ -* Wed May 24 2006 Brendan O'Dea 2.2.0 +* Fri Jun 9 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. - Remove non-working setuid option. - Convert manual.html to Docbook. - Kludge around problem with Netgear DM602 authentication. +- Add session/idle timeouts (Graham Maltby). * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/THANKS b/THANKS index 046a513..5fbe93a 100644 --- a/THANKS +++ b/THANKS @@ -27,3 +27,4 @@ Paul Martin Jonathan Yarden Patrick Cole Khaled Al Hamwi +Graham Maltby diff --git a/cli.c b/cli.c index a5f067b..f01a0ae 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=8 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.73 2006-05-05 08:10:18 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.74 2006-06-11 12:46:18 bodea Exp $"; #include #include @@ -426,6 +426,11 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int cli_print(cli, "\tUnique SID:\t%u", session[s].unique_id); cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0); cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0); + if (session[s].session_timeout) + cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (session[s].opened ? abs(time_now - session[s].opened) : 0)); + if (session[s].idle_timeout) + cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); + cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin); if (session[s].timeout) { diff --git a/cluster.c b/cluster.c index 6e1bf57..51f34d4 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.51 2006-04-27 09:53:49 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.52 2006-06-11 12:46:18 bodea Exp $"; #include #include @@ -654,7 +654,7 @@ void cluster_check_master(void) } // Reset idle timeouts.. - session[i].last_packet = time_now; + session[i].last_packet = session[i].last_data = time_now; // Reset die relative to our uptime rather than the old master's if (session[i].die) session[i].die = TIME; @@ -1214,7 +1214,9 @@ static int cluster_handle_bytes(uint8_t *data, int size) session[b->sid].cout_delta += b->cout; if (b->cin) - session[b->sid].last_packet = time_now; // Reset idle timer! + session[b->sid].last_packet = session[b->sid].last_data = time_now; + else if (b->cout) + session[b->sid].last_data = time_now; size -= sizeof(*b); ++b; diff --git a/l2tpns.c b/l2tpns.c index 519a831..0795866 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.166 2006-05-16 06:46:37 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.167 2006-06-11 12:46:18 bodea Exp $"; #include #include @@ -1075,6 +1075,7 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e p += 2; l -= 2; } + if (proto == PPPIP) { if (session[s].die) @@ -1082,7 +1083,8 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } - session[s].last_packet = time_now; + + session[s].last_packet = session[s].last_data = time_now; processipin(s, t, p, l); } else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) @@ -1093,7 +1095,7 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; processipv6in(s, t, p, l); } else @@ -1166,6 +1168,7 @@ static void processipout(uint8_t *buf, int len) } t = session[s].tunnel; sp = &session[s]; + sp->last_data = time_now; // DoS prevention: enforce a maximum number of packets per 0.1s for a session if (config->max_packets > 0) @@ -1363,6 +1366,7 @@ static void processipv6out(uint8_t * buf, int len) } t = session[s].tunnel; sp = &session[s]; + sp->last_data = time_now; // FIXME: add DoS prevention/filters? @@ -2642,7 +2646,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) session[s].opened = time_now; session[s].tunnel = t; session[s].far = asession; - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far); control16(c, 14, s, 1); // assigned session controladd(c, asession, t); // send the reply @@ -2803,7 +2807,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); @@ -2820,7 +2824,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); @@ -2837,7 +2841,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); @@ -3195,6 +3199,24 @@ static void regular_cleanups(double period) s_actions++; } + // Drop sessions who have reached session_timeout seconds + if (session[s].session_timeout && (time_now - session[s].opened >= session[s].session_timeout)) + { + sessionshutdown(s, "Session Timeout Reached", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + STAT(session_timeout); + s_actions++; + continue; + } + + // Drop sessions who have reached idle_timeout seconds + if (session[s].last_data && session[s].idle_timeout && (time_now - session[s].last_data >= session[s].idle_timeout)) + { + sessionshutdown(s, "Idle Timeout Reached", CDN_ADMIN_DISC, TERM_IDLE_TIMEOUT); + STAT(session_timeout); + s_actions++; + continue; + } + // Check for actions requested from the CLI if ((a = cli_session_actions[s].action)) { @@ -4876,7 +4898,7 @@ int sessionsetup(sessionidt s, tunnelidt t) if (session[s].throttle_in || session[s].throttle_out) throttle_session(s, session[s].throttle_in, session[s].throttle_out); - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; LOG(2, s, t, "Login by %s at %s from %s (%s)\n", session[s].user, fmtaddr(htonl(session[s].ip), 0), @@ -5439,7 +5461,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc if (!session[s].opened) continue; - idle = time_now - session[s].last_packet; + idle = time_now - session[s].last_data; idle /= 5 ; // In multiples of 5 seconds. if (idle < 0) idle = 0; diff --git a/l2tpns.h b/l2tpns.h index 54bbed8..48e7b02 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.116 2006-04-27 14:37:28 bodea Exp $ +// $Id: l2tpns.h,v 1.117 2006-06-11 12:46:18 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -284,7 +284,10 @@ typedef struct uint16_t mru; // maximum receive unit clockt opened; // when started clockt die; // being closed, when to finally free + uint32_t session_timeout; // Maximum session time in seconds + uint32_t idle_timeout; // Maximum idle time in seconds time_t last_packet; // Last packet from the user (used for idle timeouts) + time_t last_data; // Last data packet to/from the user (used for idle timeouts) in_addr_t dns1, dns2; // DNS servers routet route[MAXROUTE]; // static routes uint16_t tbf_in; // filter bucket for throttling in from the user. diff --git a/l2tpns.spec b/l2tpns.spec index 15fffac..9507981 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Wed May 24 2006 Brendan O'Dea 2.2.0-1 +* Fri Jun 9 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes diff --git a/radius.c b/radius.c index bfeda40..a0a4068 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.50 2006-04-27 09:53:50 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.51 2006-06-11 12:46:18 bodea Exp $"; #include #include @@ -330,6 +330,7 @@ void radiussend(uint16_t r, uint8_t state) } } } + if (s) { *p = 5; // NAS-Port @@ -346,52 +347,73 @@ void radiussend(uint16_t r, uint8_t state) p[1] = 6; *(uint32_t *) (p + 2) = htonl(1); // PPP p += p[1]; - } - if (s && session[s].ip) - { - *p = 8; // Framed-IP-Address - p[1] = 6; - *(uint32_t *) (p + 2) = htonl(session[s].ip); - p += p[1]; - } - if (s && session[s].route[0].ip) - { - int r; - for (r = 0; s && r < MAXROUTE && session[s].route[r].ip; r++) + + if (session[s].ip) { - int width = 32; - if (session[s].route[r].mask) + *p = 8; // Framed-IP-Address + p[1] = 6; + *(uint32_t *) (p + 2) = htonl(session[s].ip); + p += p[1]; + } + + if (session[s].route[0].ip) + { + int r; + for (r = 0; s && r < MAXROUTE && session[s].route[r].ip; r++) { - int mask = session[s].route[r].mask; - while (!(mask & 1)) - { - width--; - mask >>= 1; - } + int width = 32; + if (session[s].route[r].mask) + { + int mask = session[s].route[r].mask; + while (!(mask & 1)) + { + width--; + mask >>= 1; + } + } + + *p = 22; // Framed-Route + p[1] = sprintf((char *) p + 2, "%s/%d %s 1", + fmtaddr(htonl(session[s].route[r].ip), 0), + width, fmtaddr(htonl(session[s].ip), 1)) + 2; + + p += p[1]; } + } - *p = 22; // Framed-Route - p[1] = sprintf((char *) p + 2, "%s/%d %s 1", - fmtaddr(htonl(session[s].route[r].ip), 0), - width, fmtaddr(htonl(session[s].ip), 1)) + 2; + if (session[s].session_timeout) + { + *p = 27; // Session-Timeout + p[1] = 6; + *(uint32_t *) (p + 2) = htonl(session[s].session_timeout); + p += p[1]; + } + if (session[s].idle_timeout) + { + *p = 28; // Idle-Timeout + p[1] = 6; + *(uint32_t *) (p + 2) = htonl(session[s].idle_timeout); + p += p[1]; + } + + if (*session[s].called) + { + *p = 30; // called + p[1] = strlen(session[s].called) + 2; + strcpy((char *) p + 2, session[s].called); + p += p[1]; + } + + if (*session[s].calling) + { + *p = 31; // calling + p[1] = strlen(session[s].calling) + 2; + strcpy((char *) p + 2, session[s].calling); p += p[1]; } } - if (*session[s].called) - { - *p = 30; // called - p[1] = strlen(session[s].called) + 2; - strcpy((char *) p + 2, session[s].called); - p += p[1]; - } - if (*session[s].calling) - { - *p = 31; // calling - p[1] = strlen(session[s].calling) + 2; - strcpy((char *) p + 2, session[s].calling); - p += p[1]; - } + // NAS-IP-Address *p = 4; p[1] = 6; @@ -699,6 +721,28 @@ void processrad(uint8_t *buf, int len, char socket_index) ip_filters[f].used++; } } + else if (*p == 27) + { + // Session-Timeout + uint32_t to = ntohl(*(uint32_t *)(p + 2)); + + LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", to); + if (to > 0) + { + session[s].session_timeout = to; + } + } + else if (*p == 28) + { + // Idle-Timeout + uint32_t to = ntohl(*(uint32_t *)(p + 2)); + + LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", to); + if (to > 0) + { + session[s].idle_timeout = to; + } + } else if (*p == 26 && p[1] >= 7) { // Vendor-Specific Attribute From d2f496f60306b167509b1562936fd9cc6ea8cc6e Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 22 Jun 2006 15:30:29 +0000 Subject: [PATCH 455/482] set acct-disconnect-cause from result code AVP if no disconnect cause AVP is present --- Changes | 4 +++- l2tpns.c | 31 +++++++++++++++++++++++++------ l2tpns.spec | 2 +- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Changes b/Changes index 046dfc6..319091e 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Fri Jun 9 2006 Brendan O'Dea 2.2.0 +* Fri Jun 23 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -6,6 +6,8 @@ - Convert manual.html to Docbook. - Kludge around problem with Netgear DM602 authentication. - Add session/idle timeouts (Graham Maltby). +- Use result code AVP to set Acct-Terminate-Cause is disconnect cause + AVP is not present. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/l2tpns.c b/l2tpns.c index 0795866..03e780c 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.167 2006-06-11 12:46:18 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.168 2006-06-22 15:30:29 bodea Exp $"; #include #include @@ -2184,9 +2184,10 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) int error = 0; char *msg = 0; - // default disconnect cause/message on receipt - // of CDN (set to more specific value from - // attribute 46 if present below). + // Default disconnect cause/message on receipt of CDN. Set to + // more specific value from attribute 1 (result code) or 46 + // (disconnect cause) if present below. + int disc_cause_set = 0; int disc_cause = TERM_NAS_REQUEST; char const *disc_reason = "Closed (Received CDN)."; @@ -2296,25 +2297,41 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 1: // result code { uint16_t rescode = ntohs(*(uint16_t *) b); - const char* resdesc = "(unknown)"; + char const *resdesc = "(unknown)"; + char const *errdesc = NULL; + int cause = 0; + if (message == 4) { /* StopCCN */ resdesc = l2tp_stopccn_result_code(rescode); + cause = TERM_LOST_SERVICE; } else if (message == 14) { /* CDN */ resdesc = l2tp_cdn_result_code(rescode); + if (rescode == 1) + cause = TERM_LOST_CARRIER; + else + cause = TERM_ADMIN_RESET; } LOG(4, s, t, " Result Code %u: %s\n", rescode, resdesc); if (n >= 4) { uint16_t errcode = ntohs(*(uint16_t *)(b + 2)); - LOG(4, s, t, " Error Code %u: %s\n", errcode, l2tp_error_code(errcode)); + errdesc = l2tp_error_code(errcode); + LOG(4, s, t, " Error Code %u: %s\n", errcode, errdesc); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); + if (cause && disc_cause_set < mtype) // take cause from attrib 46 in preference + { + disc_cause_set = mtype; + disc_reason = errdesc ? errdesc : resdesc; + disc_cause = cause; + } + break; } break; @@ -2506,6 +2523,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n", code, proto, dir, n - 5, b + 5); + disc_cause_set = mtype; + switch (code) { case 1: // admin disconnect diff --git a/l2tpns.spec b/l2tpns.spec index 9507981..e3b34c1 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Fri Jun 9 2006 Brendan O'Dea 2.2.0-1 +* Fri Jun 23 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes From e49d6736b53034705fc2f54a36e0965070accd69 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sat, 1 Jul 2006 12:40:10 +0000 Subject: [PATCH 456/482] add radius_bind_{min,max} options --- Changes | 3 ++- Docs/manual/manual.xml | 13 +++++++++++++ Docs/startup-config.5 | 13 +++++++++---- l2tpns.c | 10 ++++++---- l2tpns.h | 6 ++++-- l2tpns.spec | 2 +- radius.c | 42 +++++++++++++++++++++++++++++++++++++++++- 7 files changed, 76 insertions(+), 13 deletions(-) diff --git a/Changes b/Changes index 319091e..f125011 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Fri Jun 23 2006 Brendan O'Dea 2.2.0 +* Sat Jul 1 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -8,6 +8,7 @@ - Add session/idle timeouts (Graham Maltby). - Use result code AVP to set Acct-Terminate-Cause is disconnect cause AVP is not present. +- Add radius_bind_{min,max} to simplify firewalling of RADIUS ports. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/Docs/manual/manual.xml b/Docs/manual/manual.xml index cb26b54..38e7b81 100644 --- a/Docs/manual/manual.xml +++ b/Docs/manual/manual.xml @@ -390,6 +390,19 @@ set boolean true + + radius_bind_min (short) + radius_bind_max (short) + + + Define a port range in which to bind sockets used to + send and receive RADIUS packets. Must be at least + RADIUS_FDS (64) wide. Simplifies firewalling of RADIUS + ports (default: dynamically assigned). + + + + radius_dae_port (short) diff --git a/Docs/startup-config.5 b/Docs/startup-config.5 index 07fc79c..77af484 100644 --- a/Docs/startup-config.5 +++ b/Docs/startup-config.5 @@ -2,7 +2,7 @@ .de Id .ds Dt \\$4 \\$5 .. -.Id $Id: startup-config.5,v 1.17 2006-04-27 14:38:14 bodea Exp $ +.Id $Id: startup-config.5,v 1.18 2006-07-01 12:40:17 bodea Exp $ .TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions" .SH NAME startup\-config \- configuration file for l2tpns @@ -77,17 +77,17 @@ Number of configure requests to send before giving up (default: 10). Number of Configure-Nak requests to send before sending a Configure-Reject (default: 5). .TP -.BR primary_dns , " secondary_dns" +.BR primary_dns ", " secondary_dns Whenever a PPP connection is established, DNS servers will be sent to the user, both a primary and a secondary. If either is set to 0.0.0.0, then that one will not be sent. .TP -.BR primary_radius , " secondary_radius" +.BR primary_radius ", " secondary_radius Sets the RADIUS servers used for both authentication and accounting. If the primary server does not respond, then the secondary RADIUS server will be tried. .TP -.BR primary_radius_port , " secondary_radius_port" +.BR primary_radius_port ", " secondary_radius_port Sets the authentication ports for the primary and secondary RADIUS servers. The accounting port is one more than the authentication port. If no ports are given, authentication defaults to 1645, and @@ -118,6 +118,11 @@ A comma separated list of supported RADIUS authentication methods Port for DAE RADIUS (Packet of Death/Disconnect, Change of Authorization) requests (default: 3799). .TP +.BR radius_bind_min ", " radius_bind_max +Define a port range in which to bind sockets used to send and receive +RADIUS packets. Must be at least RADIUS_FDS (64) wide. Simplifies +firewalling of RADIUS ports (default: dynamically assigned). +.TP .B allow_duplicate_users Allow multiple logins with the same username. If false (the default), any prior session with the same username will be dropped when a new diff --git a/l2tpns.c b/l2tpns.c index 03e780c..0c6c1c0 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.168 2006-06-22 15:30:29 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.169 2006-07-01 12:40:17 bodea Exp $"; #include #include @@ -126,6 +126,8 @@ config_descriptt config_values[] = { CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("radius_dae_port", radius_dae_port, SHORT), + CONFIG("radius_bind_min", radius_bind_min, SHORT), + CONFIG("radius_bind_max", radius_bind_max, SHORT), CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), CONFIG("guest_account", guest_user, STRING), CONFIG("bind_address", bind_address, IPv4), @@ -625,7 +627,7 @@ static void initudp(void) int flags = fcntl(udpfd, F_GETFL, 0); fcntl(udpfd, F_SETFL, flags | O_NONBLOCK); } - if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) + if (bind(udpfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); @@ -638,7 +640,7 @@ static void initudp(void) controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto - if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) + if (bind(controlfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); @@ -651,7 +653,7 @@ static void initudp(void) daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto - if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) + if (bind(daefd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); exit(1); diff --git a/l2tpns.h b/l2tpns.h index 48e7b02..29f0710 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.117 2006-06-11 12:46:18 bodea Exp $ +// $Id: l2tpns.h,v 1.118 2006-07-01 12:40:17 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -638,7 +638,9 @@ typedef struct uint16_t radiusport[MAXRADSERVER]; // radius base ports uint8_t numradiusservers; // radius server count - uint16_t radius_dae_port; // local port for radius dae + uint16_t radius_dae_port; // port for radius DAE + uint16_t radius_bind_min; // port range for udp sockets used to send/recv radius packets + uint16_t radius_bind_max; char radius_authtypes_s[32]; // list of valid authentication types (chap, pap) in order of preference int radius_authtypes; diff --git a/l2tpns.spec b/l2tpns.spec index e3b34c1..e7fb9fb 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Fri Jun 23 2006 Brendan O'Dea 2.2.0-1 +* Sat Jul 1 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes diff --git a/radius.c b/radius.c index a0a4068..c804023 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.51 2006-06-11 12:46:18 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.52 2006-07-01 12:40:17 bodea Exp $"; #include #include @@ -45,6 +45,25 @@ static void calc_auth(const void *buf, size_t len, const uint8_t *in, uint8_t *o void initrad(void) { int i; + uint16_t port = 0; + uint16_t min = config->radius_bind_min; + uint16_t max = config->radius_bind_max; + int inc = 1; + struct sockaddr_in addr; + + if (min) + { + port = min; + if (!max) + max = ~0 - 1; + } + else if (max) /* no minimum specified, bind from max down */ + { + port = max; + min = 1; + inc = -1; + } + LOG(3, 0, 0, "Creating %d sockets for RADIUS queries\n", RADIUS_FDS); radfds = calloc(sizeof(int), RADIUS_FDS); for (i = 0; i < RADIUS_FDS; i++) @@ -53,6 +72,27 @@ void initrad(void) radfds[i] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); flags = fcntl(radfds[i], F_GETFL, 0); fcntl(radfds[i], F_SETFL, flags | O_NONBLOCK); + + if (port) + { + int b; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + + do { + addr.sin_port = htons(port); + if ((b = bind(radfds[i], (struct sockaddr *) &addr, sizeof(addr))) < 0) + { + if ((port += inc) < min || port > max) + { + LOG(0, 0, 0, "Can't bind RADIUS socket in range %u-%u\n", min, max); + exit(1); + } + } + } while (b < 0); + } } } From edd8999aed3478444c1fa662d442e4531d5d1980 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sat, 1 Jul 2006 14:07:35 +0000 Subject: [PATCH 457/482] resolve gcc 4.1 warning by changing ip_hash to a union --- l2tpns.c | 73 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 0c6c1c0..659a87d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.169 2006-07-01 12:40:17 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.170 2006-07-01 14:07:35 bodea Exp $"; #include #include @@ -82,9 +82,13 @@ uint16_t MSS = 0; // TCP MSS struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI -static void *ip_hash[256]; // Mapping from IP address to session structures. +union iphash { + sessionidt sess; + union iphash *idx; +} ip_hash[256]; // Mapping from IP address to session structures. + struct ipv6radix { - int sess; + sessionidt sess; struct ipv6radix *branch; } ipv6_hash[256]; // Mapping from IPv6 address to session structures. @@ -189,9 +193,9 @@ struct Tstats *_statistics = NULL; struct Tringbuffer *ringbuffer = NULL; #endif -static void cache_ipmap(in_addr_t ip, int s); +static void cache_ipmap(in_addr_t ip, sessionidt s); static void uncache_ipmap(in_addr_t ip); -static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s); +static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s); static void free_ip_address(sessionidt s); static void dump_acct_info(int all); static void sighup_handler(int sig); @@ -678,19 +682,19 @@ static void initudp(void) // IP address. // -static int lookup_ipmap(in_addr_t ip) +static sessionidt lookup_ipmap(in_addr_t ip) { uint8_t *a = (uint8_t *) &ip; - uint8_t **d = (uint8_t **) ip_hash; + union iphash *h = ip_hash; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; - return (int) (intptr_t) d[(size_t) *a]; + return h[*a].sess; } -static int lookup_ipv6map(struct in6_addr ip) +static sessionidt lookup_ipv6map(struct in6_addr ip) { struct ipv6radix *curnode; int i; @@ -718,18 +722,18 @@ static int lookup_ipv6map(struct in6_addr ip) sessionidt sessionbyip(in_addr_t ip) { - int s = lookup_ipmap(ip); + sessionidt s = lookup_ipmap(ip); CSTAT(sessionbyip); if (s > 0 && s < MAXSESSION && session[s].opened) - return (sessionidt) s; + return s; return 0; } sessionidt sessionbyipv6(struct in6_addr ip) { - int s; + sessionidt s; CSTAT(sessionbyipv6); if (!memcmp(&config->ipv6_prefix, &ip, 8) || @@ -755,25 +759,22 @@ sessionidt sessionbyipv6(struct in6_addr ip) // // (It's actually cached in network order) // -static void cache_ipmap(in_addr_t ip, int s) +static void cache_ipmap(in_addr_t ip, sessionidt s) { in_addr_t nip = htonl(ip); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0] uint8_t *a = (uint8_t *) &nip; - uint8_t **d = (uint8_t **) ip_hash; + union iphash *h = ip_hash; int i; for (i = 0; i < 3; i++) { - if (!d[(size_t) a[i]]) - { - if (!(d[(size_t) a[i]] = calloc(256, sizeof(void *)))) - return; - } + if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union iphash))))) + return; - d = (uint8_t **) d[(size_t) a[i]]; + h = h[a[i]].idx; } - d[(size_t) a[3]] = (uint8_t *) (intptr_t) s; + h[a[3]].sess = s; if (s > 0) LOG(4, s, session[s].tunnel, "Caching ip address %s\n", fmtaddr(nip, 0)); @@ -788,7 +789,7 @@ static void uncache_ipmap(in_addr_t ip) cache_ipmap(ip, 0); // Assign it to the NULL session. } -static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s) +static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s) { int i; int bytes; @@ -829,7 +830,7 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s) // int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) { - char **d = (char **) ip_hash, **e, **f, **g; + union iphash *d = ip_hash, *e, *f, *g; int i, j, k, l; int count = 0; @@ -840,24 +841,28 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) for (i = 0; i < 256; ++i) { - if (!d[i]) + if (!d[i].idx) continue; - e = (char **) d[i]; + + e = d[i].idx; for (j = 0; j < 256; ++j) { - if (!e[j]) + if (!e[j].idx) continue; - f = (char **) e[j]; + + f = e[j].idx; for (k = 0; k < 256; ++k) { - if (!f[k]) + if (!f[k].idx) continue; - g = (char **)f[k]; + + g = f[k].idx; for (l = 0; l < 256; ++l) { - if (!g[l]) + if (!g[l].sess) continue; - cli_print(cli, "%7d %d.%d.%d.%d", (int) (intptr_t) g[l], i, j, k, l); + + cli_print(cli, "%7d %d.%d.%d.%d", g[l].sess, i, j, k, l); ++count; } } From 082e3baf901ced203a10a8c9140033e1222fe53a Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 17 Jul 2006 07:53:08 +0000 Subject: [PATCH 458/482] fix log message --- cluster.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster.c b/cluster.c index 51f34d4..ab4d914 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.52 2006-06-11 12:46:18 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.53 2006-07-17 07:53:08 bodea Exp $"; #include #include @@ -1770,7 +1770,7 @@ int processcluster(uint8_t *data, int size, in_addr_t addr) case C_FORWARD_DAE: // Forwarded DAE packet. pass off to processdae. if (!config->cluster_iam_master) { - LOG(0, 0, 0, "I'm not the master, but I got a C_FORWARD_%s from %s?\n", + LOG(0, 0, 0, "I'm not the master, but I got a C_FORWARD%s from %s?\n", type == C_FORWARD_DAE ? "_DAE" : "", fmtaddr(addr, 0)); return -1; From 9448b44db31c07879e9bb2c089d0dc2acbd53eb5 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Wed, 2 Aug 2006 12:54:45 +0000 Subject: [PATCH 459/482] fix sign problem with reporting of unknown RADIUS VSAs --- Changes | 3 ++- l2tpns.spec | 2 +- radius.c | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Changes b/Changes index f125011..2a3167f 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Sat Jul 1 2006 Brendan O'Dea 2.2.0 +* Wed Aug 2 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -9,6 +9,7 @@ - Use result code AVP to set Acct-Terminate-Cause is disconnect cause AVP is not present. - Add radius_bind_{min,max} to simplify firewalling of RADIUS ports. +- Fix sign problem with reporting of unknown RADIUS VSAs. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/l2tpns.spec b/l2tpns.spec index e7fb9fb..6b66952 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Sat Jul 1 2006 Brendan O'Dea 2.2.0-1 +* Wed Aug 2 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes diff --git a/radius.c b/radius.c index c804023..d35a3f9 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.52 2006-07-01 12:40:17 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.53 2006-08-02 12:54:45 bodea Exp $"; #include #include @@ -786,11 +786,11 @@ void processrad(uint8_t *buf, int len, char socket_index) else if (*p == 26 && p[1] >= 7) { // Vendor-Specific Attribute - int vendor = ntohl(*(int *)(p + 2)); - char attrib = *(p + 6); + uint32_t vendor = ntohl(*(int *)(p + 2)); + uint8_t attrib = *(p + 6); int attrib_length = *(p + 7) - 2; - LOG(3, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%d Attrib=%d Length=%d\n", vendor, attrib, attrib_length); + LOG(3, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%u Attrib=%u Length=%d\n", vendor, attrib, attrib_length); if (vendor != 9 || attrib != 1) { LOG(3, s, session[s].tunnel, " Unknown vendor-specific\n"); From fb0d8b5fb60119975b25086428f2a0c099491f4b Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Wed, 2 Aug 2006 13:35:39 +0000 Subject: [PATCH 460/482] cleanup clash between timeout and session_timout --- cli.c | 31 ++++++++++----------------- l2tpns.c | 64 ++++++++++++++++++++++++-------------------------------- l2tpns.h | 3 +-- ppp.c | 4 ++-- radius.c | 35 +++++++------------------------ 5 files changed, 48 insertions(+), 89 deletions(-) diff --git a/cli.c b/cli.c index f01a0ae..d7a487e 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=8 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.74 2006-06-11 12:46:18 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.75 2006-08-02 13:35:39 bodea Exp $"; #include #include @@ -427,19 +427,18 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0); cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0); if (session[s].session_timeout) - cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (session[s].opened ? abs(time_now - session[s].opened) : 0)); + { + clockt opened = session[s].opened; + if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) + opened = bundle[session[s].bundle].online_time; + + cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (opened ? abs(time_now - opened) : 0)); + } + if (session[s].idle_timeout) cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin); - if (session[s].timeout) - { - cli_print(cli, "\tRemaing time:\t%u", - (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) - ? (unsigned) (session[s].timeout - bundle[session[s].bundle].online_time) - : (unsigned) (session[s].timeout - (time_now - session[s].opened))); - } - cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin); cli_print(cli, "\tMRU:\t\t%d", session[s].mru); cli_print(cli, "\tRx Speed:\t%u", session[s].rx_connect_speed); @@ -506,7 +505,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int } // Show Summary - cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s", + cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %-15s %s", "SID", "TID", "Username", @@ -519,20 +518,13 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int "downloaded", "uploaded", "idle", - "Rem.Time", "LAC", "CLI"); for (i = 1; i < MAXSESSION; i++) { - uint32_t rem_time; if (!session[i].opened) continue; - if (session[i].bundle && bundle[session[i].bundle].num_of_links > 1) - rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0; - else - rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0; - - cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s", + cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %-15s %s", i, session[i].tunnel, session[i].user[0] ? session[i].user : "*", @@ -545,7 +537,6 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int (unsigned long)session[i].cout, (unsigned long)session[i].cin, abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)), - (unsigned long)(rem_time), fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1), session[i].calling[0] ? session[i].calling : "*"); } diff --git a/l2tpns.c b/l2tpns.c index 659a87d..454f79b 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.170 2006-07-01 14:07:35 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.171 2006-08-02 13:35:39 bodea Exp $"; #include #include @@ -3053,37 +3053,6 @@ static void regular_cleanups(double period) continue; } - // check for timed out sessions - if (session[s].timeout) - { - bundleidt bid = session[s].bundle; - if (bid) - { - clockt curr_time = time_now; - if (curr_time - bundle[bid].last_check >= 1) - { - bundle[bid].online_time += (curr_time-bundle[bid].last_check)*bundle[bid].num_of_links; - bundle[bid].last_check = curr_time; - if (bundle[bid].online_time >= session[s].timeout) - { - int ses; - for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--) - { - sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); - s_actions++; - continue; - } - } - } - } - else if (session[s].timeout <= time_now - session[s].opened) - { - sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); - s_actions++; - continue; - } - } - // PPP timeouts if (sess_local[s].lcp.restart <= time_now) { @@ -3226,12 +3195,33 @@ static void regular_cleanups(double period) } // Drop sessions who have reached session_timeout seconds - if (session[s].session_timeout && (time_now - session[s].opened >= session[s].session_timeout)) + if (session[s].session_timeout) { - sessionshutdown(s, "Session Timeout Reached", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); - STAT(session_timeout); - s_actions++; - continue; + bundleidt bid = session[s].bundle; + if (bid) + { + if (time_now - bundle[bid].last_check >= 1) + { + bundle[bid].online_time += (time_now - bundle[bid].last_check) * bundle[bid].num_of_links; + bundle[bid].last_check = time_now; + if (bundle[bid].online_time >= session[s].session_timeout) + { + int ses; + for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--) + { + sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + s_actions++; + continue; + } + } + } + } + else if (time_now - session[s].opened >= session[s].session_timeout) + { + sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + s_actions++; + continue; + } } // Drop sessions who have reached idle_timeout seconds diff --git a/l2tpns.h b/l2tpns.h index 29f0710..21ec6fd 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.118 2006-07-01 12:40:17 bodea Exp $ +// $Id: l2tpns.h,v 1.119 2006-08-02 13:35:39 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -299,7 +299,6 @@ typedef struct char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; - clockt timeout; // Session timeout uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit uint8_t mssf; // Multilink Short Sequence Number Header Format epdist epdis; // Multilink Endpoint Discriminator diff --git a/ppp.c b/ppp.c index 8c1c9a3..a1ad56a 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.101 2006-06-03 08:16:46 bodea Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.102 2006-08-02 13:35:39 bodea Exp $"; #include #include @@ -373,7 +373,7 @@ void lcp_open(sessionidt s, tunnelidt t) session[s].ip = session[first_ses].ip; session[s].dns1 = session[first_ses].dns1; session[s].dns2 = session[first_ses].dns2; - session[s].timeout = session[first_ses].timeout; + session[s].session_timeout = session[first_ses].session_timeout; ipcp_open(s, t); } else diff --git a/radius.c b/radius.c index d35a3f9..c167cbe 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.53 2006-08-02 12:54:45 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.54 2006-08-02 13:35:39 bodea Exp $"; #include #include @@ -655,19 +655,6 @@ void processrad(uint8_t *buf, int len, char socket_index) LOG(3, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n", fmtaddr(htonl(session[s].dns1), 0)); } - else if (*p == 27) - { - // Session timeout - if (p[1] < 6) { - LOG(2, s, session[s].tunnel, "Error: Received Session timeout with length %d < 6\n", p[1]); - continue; - } - - session[s].timeout = ntohl(*(uint32_t *) (p + 2)); - LOG(3, s, session[s].tunnel, " Radius reply contains Session timeout %d\n", session[s].timeout); - if (!session[s].timeout) - sessionshutdown(s, "Session timeout is zero", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); - } else if (*p == 136) { // DNS address @@ -764,24 +751,16 @@ void processrad(uint8_t *buf, int len, char socket_index) else if (*p == 27) { // Session-Timeout - uint32_t to = ntohl(*(uint32_t *)(p + 2)); - - LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", to); - if (to > 0) - { - session[s].session_timeout = to; - } + if (p[1] < 6) continue; + session[s].session_timeout = ntohl(*(uint32_t *)(p + 2)); + LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", session[s].session_timeout); } else if (*p == 28) { // Idle-Timeout - uint32_t to = ntohl(*(uint32_t *)(p + 2)); - - LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", to); - if (to > 0) - { - session[s].idle_timeout = to; - } + if (p[1] < 6) continue; + session[s].idle_timeout = ntohl(*(uint32_t *)(p + 2)); + LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", session[s].idle_timeout); } else if (*p == 26 && p[1] >= 7) { From 534a9900a815e57e179b5e1cfec26662ae777bfc Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Wed, 2 Aug 2006 14:17:30 +0000 Subject: [PATCH 461/482] allow DNS servers to be specified using either old or new vendor-specific Ascend formats --- Changes | 4 +++- l2tpns.spec | 2 +- radius.c | 53 ++++++++++++++++++++++++++++++----------------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Changes b/Changes index 2a3167f..a37c50e 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Wed Aug 2 2006 Brendan O'Dea 2.2.0 +* Thu Aug 3 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -10,6 +10,8 @@ AVP is not present. - Add radius_bind_{min,max} to simplify firewalling of RADIUS ports. - Fix sign problem with reporting of unknown RADIUS VSAs. +- Allow DNS servers to be specified either using the old or new + vendor-specific Ascend formats. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/l2tpns.spec b/l2tpns.spec index 6b66952..d2a563b 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Wed Aug 2 2006 Brendan O'Dea 2.2.0-1 +* Thu Aug 3 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes diff --git a/radius.c b/radius.c index c167cbe..3da31f8 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.54 2006-08-02 13:35:39 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.55 2006-08-02 14:17:30 bodea Exp $"; #include #include @@ -635,6 +635,35 @@ void processrad(uint8_t *buf, int len, char socket_index) uint8_t *e = buf + len; for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1]) { + if (*p == 26 && p[1] >= 7) + { + // Vendor-Specific Attribute + uint32_t vendor = ntohl(*(int *)(p + 2)); + uint8_t attrib = *(p + 6); + int attrib_length = *(p + 7) - 2; + + LOG(4, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%u Attrib=%u Length=%d\n", vendor, attrib, attrib_length); + if (vendor == 9 && attrib == 1) // Cisco-AVPair + { + if (attrib_length < 0) continue; + LOG(3, s, session[s].tunnel, " Cisco-AVPair value: %.*s\n", + attrib_length, p + 8); + + handle_avpair(s, p + 8, attrib_length); + continue; + } + else if (vendor == 529 && attrib >= 135 && attrib <= 136) // Ascend + { + // handle old-format ascend DNS attributes below + p += 6; + } + else + { + LOG(3, s, session[s].tunnel, " Unknown vendor-specific\n"); + continue; + } + } + if (*p == 8) { // Framed-IP-Address @@ -762,28 +791,6 @@ void processrad(uint8_t *buf, int len, char socket_index) session[s].idle_timeout = ntohl(*(uint32_t *)(p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", session[s].idle_timeout); } - else if (*p == 26 && p[1] >= 7) - { - // Vendor-Specific Attribute - uint32_t vendor = ntohl(*(int *)(p + 2)); - uint8_t attrib = *(p + 6); - int attrib_length = *(p + 7) - 2; - - LOG(3, s, session[s].tunnel, " Radius reply contains Vendor-Specific. Vendor=%u Attrib=%u Length=%d\n", vendor, attrib, attrib_length); - if (vendor != 9 || attrib != 1) - { - LOG(3, s, session[s].tunnel, " Unknown vendor-specific\n"); - continue; - } - - if (attrib_length > 0) - { - LOG(3, s, session[s].tunnel, " Cisco-AVPair value: %.*s\n", - attrib_length, p + 8); - - handle_avpair(s, p + 8, attrib_length); - } - } else if (*p == 99) { // Framed-IPv6-Route From 4a2a55c66e0aa388ca8d5fe382eff16fa2726598 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 23 Oct 2006 02:51:53 +0000 Subject: [PATCH 462/482] fix comment --- l2tpns.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/l2tpns.h b/l2tpns.h index 21ec6fd..5614ae9 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.119 2006-08-02 13:35:39 bodea Exp $ +// $Id: l2tpns.h,v 1.120 2006-10-23 02:51:53 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -49,15 +49,13 @@ #define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate #define MAX_LOG_LENGTH 512 // Maximum size of log message #define ECHO_TIMEOUT 10 // Time between last packet sent and LCP ECHO generation -#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation +#define IDLE_TIMEOUT 240 // Time between last packet seen and session shutdown #define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown #define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header #define MP_END 0x40 // This value is used when (e)nd bit is set in MP header #define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header -#define DEFAULT_EPDIS_ADDRESS "L2TPNetServer" // Company name may goes here! - // Constants #ifndef ETCDIR #define ETCDIR "/etc/l2tpns" From dbaf3410c4be2a9ce6cf2f71e18b7ea4facb9bbb Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 4 Dec 2006 20:50:02 +0000 Subject: [PATCH 463/482] Security: Rhys Kidd identified a vulnerability in the handling of heartbeat packets. Drop oversize heartbeat packets. --- Changes | 4 +++- THANKS | 1 + cluster.c | 8 ++++++-- l2tpns.spec | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Changes b/Changes index a37c50e..05c4f9f 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Thu Aug 3 2006 Brendan O'Dea 2.2.0 +* Tue Dec 5 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -12,6 +12,8 @@ - Fix sign problem with reporting of unknown RADIUS VSAs. - Allow DNS servers to be specified either using the old or new vendor-specific Ascend formats. +- Security: Rhys Kidd identified a vulnerability in the handling of + heartbeat packets. Drop oversize heartbeat packets. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/THANKS b/THANKS index 5fbe93a..bc38c6d 100644 --- a/THANKS +++ b/THANKS @@ -28,3 +28,4 @@ Jonathan Yarden Patrick Cole Khaled Al Hamwi Graham Maltby +Rhys Kidd diff --git a/cluster.c b/cluster.c index ab4d914..fb71070 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.53 2006-07-17 07:53:08 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.54 2006-12-04 20:50:02 bodea Exp $"; #include #include @@ -1453,7 +1453,11 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t return -1; // Ignore it?? } - // Ok. It's a heartbeat packet from a cluster master! + if (size > sizeof(past_hearts[0].data)) { + LOG(0, 0, 0, "Received an oversize heartbeat from %s (%d)!\n", fmtaddr(addr, 0), size); + return -1; + } + if (s < sizeof(*h)) goto shortpacket; diff --git a/l2tpns.spec b/l2tpns.spec index d2a563b..dc03660 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Thu Aug 3 2006 Brendan O'Dea 2.2.0-1 +* Tue Dec 5 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes From 24ba2c5ae114305f082f675b0183bb4f841eecae Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 4 Dec 2006 20:54:51 +0000 Subject: [PATCH 464/482] fix heartt pad --- cluster.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster.h b/cluster.h index a568029..0849631 100644 --- a/cluster.h +++ b/cluster.h @@ -1,5 +1,5 @@ // L2TPNS Clustering Stuff -// $Id: cluster.h,v 1.15 2006-04-27 09:53:49 bodea Exp $ +// $Id: cluster.h,v 1.16 2006-12-04 20:54:51 bodea Exp $ #ifndef __CLUSTER_H__ #define __CLUSTER_H__ @@ -56,7 +56,7 @@ typedef struct { uint64_t table_version; // # state changes processed by cluster - char reserved[128 - 13*sizeof(uint32_t)]; // Pad out to 128 bytes. + char reserved[128 - 13*sizeof(uint32_t) - sizeof(uint64_t)]; // Pad out to 128 bytes. } heartt; typedef struct { /* Used to update byte counters on the */ From f67e4566a5708830ad9679d567aabb22d8eb8c76 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Tue, 5 Dec 2006 05:22:59 +0000 Subject: [PATCH 465/482] minor cleanup --- md5.c | 362 +++++++++++++++++++++++++++++----------------------------- md5.h | 19 +-- 2 files changed, 190 insertions(+), 191 deletions(-) diff --git a/md5.c b/md5.c index 9abecfb..184a876 100644 --- a/md5.c +++ b/md5.c @@ -15,10 +15,7 @@ * and avoid compile-time configuration. */ -#ifndef HAVE_OPENSSL - #include - #include "md5.h" /* @@ -27,18 +24,18 @@ * F is optimized compared to its RFC 1321 definition just like in Colin * Plumb's implementation. */ -#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, t, s) \ - (a) += f((b), (c), (d)) + (x) + (t); \ - (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ - (a) += (b); + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); /* * SET reads 4 input bytes in little-endian byte order and stores them @@ -49,19 +46,17 @@ * doesn't work. */ #if defined(__i386__) || defined(__vax__) -#define SET(n) \ - (*(MD5_u32plus *)&ptr[(n) * 4]) -#define GET(n) \ - SET(n) +# define SET(n) (*(MD5_u32plus *)&ptr[(n) * 4]) +# define GET(n) SET(n) #else -#define SET(n) \ - (ctx->block[(n)] = \ - (MD5_u32plus)ptr[(n) * 4] | \ - ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ - ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ - ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) -#define GET(n) \ - (ctx->block[(n)]) +# define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +# define GET(n) \ + (ctx->block[(n)]) #endif /* @@ -70,205 +65,208 @@ */ static void *body(MD5_CTX *ctx, void *data, unsigned long size) { - unsigned char *ptr; - MD5_u32plus a, b, c, d; - MD5_u32plus saved_a, saved_b, saved_c, saved_d; + unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; - ptr = data; + ptr = data; - a = ctx->a; - b = ctx->b; - c = ctx->c; - d = ctx->d; + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; - do { - saved_a = a; - saved_b = b; - saved_c = c; - saved_d = d; + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; -/* Round 1 */ - STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) - STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) - STEP(F, c, d, a, b, SET(2), 0x242070db, 17) - STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) - STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) - STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) - STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) - STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) - STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) - STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) - STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) - STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) - STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) - STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) - STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) - STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) -/* Round 2 */ - STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) - STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) - STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) - STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) - STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) - STEP(G, d, a, b, c, GET(10), 0x02441453, 9) - STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) - STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) - STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) - STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) - STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) - STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) - STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) - STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) - STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) - STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + /* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) -/* Round 3 */ - STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) - STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) - STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) - STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) - STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) - STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) - STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) - STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) - STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) - STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) - STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) - STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) - STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) - STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) - STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) - STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) + /* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) -/* Round 4 */ - STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) - STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) - STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) - STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) - STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) - STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) - STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) - STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) - STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) - STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) - STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) - STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) - STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) - STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) - STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) - STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + /* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) - a += saved_a; - b += saved_b; - c += saved_c; - d += saved_d; + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; - ptr += 64; - } while (size -= 64); + ptr += MD5_BLOCK_SZ; + } while (size -= MD5_BLOCK_SZ); - ctx->a = a; - ctx->b = b; - ctx->c = c; - ctx->d = d; + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; - return ptr; + return ptr; } void MD5_Init(MD5_CTX *ctx) { - ctx->a = 0x67452301; - ctx->b = 0xefcdab89; - ctx->c = 0x98badcfe; - ctx->d = 0x10325476; + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; - ctx->lo = 0; - ctx->hi = 0; + ctx->lo = 0; + ctx->hi = 0; } void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size) { - MD5_u32plus saved_lo; - unsigned long used, free; + MD5_u32plus saved_lo; + unsigned long used, free; - saved_lo = ctx->lo; - if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) - ctx->hi++; - ctx->hi += size >> 29; + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; - used = saved_lo & 0x3f; + ctx->hi += size >> 29; - if (used) { - free = 64 - used; + used = saved_lo & 0x3f; - if (size < free) { - memcpy(&ctx->buffer[used], data, size); - return; - } + if (used) + { + free = MD5_BLOCK_SZ - used; - memcpy(&ctx->buffer[used], data, free); - data = (unsigned char *)data + free; - size -= free; - body(ctx, ctx->buffer, 64); + if (size < free) + { + memcpy(&ctx->buffer[used], data, size); + return; } - if (size >= 64) { - data = body(ctx, data, size & ~(unsigned long)0x3f); - size &= 0x3f; - } + memcpy(&ctx->buffer[used], data, free); + data = (unsigned char *)data + free; + size -= free; + body(ctx, ctx->buffer, MD5_BLOCK_SZ); + } - memcpy(ctx->buffer, data, size); + if (size >= MD5_BLOCK_SZ) + { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); } void MD5_Final(unsigned char *result, MD5_CTX *ctx) { - unsigned long used, free; + unsigned long used, free; - used = ctx->lo & 0x3f; + used = ctx->lo & 0x3f; - ctx->buffer[used++] = 0x80; + ctx->buffer[used++] = 0x80; - free = 64 - used; + free = MD5_BLOCK_SZ - used; - if (free < 8) { - memset(&ctx->buffer[used], 0, free); - body(ctx, ctx->buffer, 64); - used = 0; - free = 64; - } + if (free < 8) + { + memset(&ctx->buffer[used], 0, free); + body(ctx, ctx->buffer, MD5_BLOCK_SZ); + used = 0; + free = MD5_BLOCK_SZ; + } - memset(&ctx->buffer[used], 0, free - 8); + memset(&ctx->buffer[used], 0, free - 8); - ctx->lo <<= 3; - ctx->buffer[56] = ctx->lo; - ctx->buffer[57] = ctx->lo >> 8; - ctx->buffer[58] = ctx->lo >> 16; - ctx->buffer[59] = ctx->lo >> 24; - ctx->buffer[60] = ctx->hi; - ctx->buffer[61] = ctx->hi >> 8; - ctx->buffer[62] = ctx->hi >> 16; - ctx->buffer[63] = ctx->hi >> 24; + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; - body(ctx, ctx->buffer, 64); + body(ctx, ctx->buffer, MD5_BLOCK_SZ); - result[0] = ctx->a; - result[1] = ctx->a >> 8; - result[2] = ctx->a >> 16; - result[3] = ctx->a >> 24; - result[4] = ctx->b; - result[5] = ctx->b >> 8; - result[6] = ctx->b >> 16; - result[7] = ctx->b >> 24; - result[8] = ctx->c; - result[9] = ctx->c >> 8; - result[10] = ctx->c >> 16; - result[11] = ctx->c >> 24; - result[12] = ctx->d; - result[13] = ctx->d >> 8; - result[14] = ctx->d >> 16; - result[15] = ctx->d >> 24; + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; - memset(ctx, 0, sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); } - -#endif diff --git a/md5.h b/md5.h index 1ff5045..25a59e0 100644 --- a/md5.h +++ b/md5.h @@ -6,23 +6,24 @@ * in the public domain. See md5.c for more information. */ -#ifdef HAVE_OPENSSL -#include -#elif !defined(_MD5_H) -#define _MD5_H +#ifndef __MD5_H__ +#define __MD5_H__ + +#define MD5_DIGEST_SZ 16 +#define MD5_BLOCK_SZ 64 /* Any 32-bit or wider unsigned integer data type will do */ typedef unsigned long MD5_u32plus; typedef struct { - MD5_u32plus lo, hi; - MD5_u32plus a, b, c, d; - unsigned char buffer[64]; - MD5_u32plus block[16]; + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[MD5_BLOCK_SZ]; + MD5_u32plus block[MD5_DIGEST_SZ]; } MD5_CTX; extern void MD5_Init(MD5_CTX *ctx); extern void MD5_Update(MD5_CTX *ctx, void *data, unsigned long size); extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); -#endif +#endif /* __MD5_H__ */ From c2152aa3785766ff9623b5de63e6dcd1f934dcac Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 7 Dec 2006 05:46:16 +0000 Subject: [PATCH 466/482] add CVE --- Changes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 05c4f9f..0c6fc40 100644 --- a/Changes +++ b/Changes @@ -12,8 +12,8 @@ - Fix sign problem with reporting of unknown RADIUS VSAs. - Allow DNS servers to be specified either using the old or new vendor-specific Ascend formats. -- Security: Rhys Kidd identified a vulnerability in the handling of - heartbeat packets. Drop oversize heartbeat packets. +- Security [CVE-2006-5873]: Rhys Kidd identified a vulnerability in the + handling of heartbeat packets. Drop oversize heartbeat packets. * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. From 36ddde96e1e6a58889663b06c24d78e03afc5c3f Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 18 Dec 2006 11:58:35 +0000 Subject: [PATCH 467/482] add Makefile, cleanup for -std=c99 --- test/Makefile | 11 ++++++++ test/bounce.c | 5 ++-- test/generateload.c | 62 +++++++++++++++++++++++++-------------------- 3 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 test/Makefile diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..ae9b79f --- /dev/null +++ b/test/Makefile @@ -0,0 +1,11 @@ +CFLAGS = -g -Wall -W -std=c99 -pedantic +TARGETS = bounce generateload radius + +all: $(TARGETS) +clean: + rm -f $(TARGETS) + +../md5.o: ../md5.c ../md5.h + $(MAKE) -C .. md5.o + +radius: radius.c ../md5.o diff --git a/test/bounce.c b/test/bounce.c index 5443b67..269d6c9 100644 --- a/test/bounce.c +++ b/test/bounce.c @@ -68,7 +68,8 @@ int main(int argc, char *argv[]) while (1) { struct sockaddr_in addr; - int alen = sizeof(addr), l; + socklen_t alen = sizeof(addr); + int l; unsigned int iseq; l = recvfrom(s, packet, 65535, 0, (void *) &addr, &alen); @@ -87,7 +88,7 @@ int main(int argc, char *argv[]) free(packet); } -void sigalarm(int junk) +void sigalarm(int unusedg __attribute__ ((unused))) { printf("Recv: %10llu %0.1fMbits/s (%lu pps) (%5ld dropped)\n", recv_count, (bytes / 1024.0 / 1024.0 * 8), pps, dropped); pps = bytes = 0; diff --git a/test/generateload.c b/test/generateload.c index 0b32199..b671b4f 100644 --- a/test/generateload.c +++ b/test/generateload.c @@ -1,3 +1,9 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#define __USE_XOPEN_EXTENDED +#define __USE_MISC #include #include #include @@ -6,11 +12,8 @@ #include #include #include -#include -#include #include #include -#include #include #include #include @@ -179,10 +182,10 @@ typedef struct } control_message; typedef struct { -unsigned long long send_count , recv_count ; -unsigned long long spkt , rpkt ; -unsigned int dropped; -unsigned long sbytes , rbytes ; +long long send_count, recv_count; +long long spkt, rpkt ; +int dropped; +long sbytes, rbytes ; int quitit; struct sessiont { @@ -210,7 +213,7 @@ void dump_control_message(control_message *c); u32 avp_get_32(control_message *c, int id); u16 avp_get_16(control_message *c, int id); char *avp_get_s(control_message *c, int id); -void reader_thread(int udpfd); +void reader_thread(void); void skip_zlb(); void cm_free(control_message *m); controlt *ppp_new(u16 session, int protocol); @@ -234,7 +237,7 @@ void print_report(); int ns = 0, nr = 0; int udpfd; int t = 0; -struct sockaddr_in gatewayaddr = {0}; +struct sockaddr_in gatewayaddr; int numsessions = NUM_SESSIONS; int packet_length = PACKET_LENGTH; int target_pps = TARGET_PPS; @@ -251,7 +254,7 @@ char *suffix = "@optusnet.com.au"; int main(int argc, char *argv[]) { int s; - char *packet; + unsigned char *packet; ss = (sharedt*) mmap(NULL, sizeof(*ss), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0); @@ -377,6 +380,7 @@ int main(int argc, char *argv[]) printf("Bound to port %d\n", htons(addr.sin_port)); }/*}}}*/ + memset(&gatewayaddr, 0, sizeof(gatewayaddr)); gatewayaddr.sin_family = AF_INET; gatewayaddr.sin_port = htons(1701); inet_aton(gwaddr, &gatewayaddr.sin_addr); @@ -400,7 +404,8 @@ int main(int argc, char *argv[]) // Receive reply/*{{{*/ { struct sockaddr_in addr; - int alen = sizeof(addr), l; + int l; + socklen_t alen = sizeof(addr); l = recvfrom(udpfd, packet, 4096, 0, (void *) &addr, &alen); if (l < 0) @@ -409,7 +414,7 @@ int main(int argc, char *argv[]) return -1; } printf("Received "); - r = parsecontrol(packet, l); + r = parsecontrol((char *) packet, l); if (!r->first) { printf("Invalid packet.. no first avp\n"); @@ -446,7 +451,7 @@ int main(int argc, char *argv[]) printf("All session create requests sent...\n");/*}}}*/ if ( fork() == 0) { - reader_thread(udpfd); + reader_thread(); exit(0); } @@ -529,7 +534,7 @@ int main(int argc, char *argv[]) *(u16 *)(c->buf + 4) = htons(ss->sessions[i].remote_session); // Session ID iph->saddr = ss->sessions[i].addr; iph->check = 0; - iph->check = ntohs(checksum((char *)iph, sizeof(struct iphdr))); + iph->check = ntohs(checksum((unsigned char *)iph, sizeof(struct iphdr))); *((unsigned int *) data) = seq++; ppp_send(c); @@ -546,7 +551,8 @@ int main(int argc, char *argv[]) nanosleep(&req, NULL); } - if (max_packets && ss->send_count >= max_packets) ss->quitit++; + if (max_packets && ss->send_count >= max_packets) + ss->quitit++; } } @@ -602,22 +608,24 @@ void clean_shutdown()/*{{{*/ } }/*}}}*/ -void sigint(int signal) +void sigint(int unused __attribute__ ((unused))) { ss->quitit++; } -void sigalarm(int junk) +void sigalarm(int unused __attribute__ ((unused))) { static unsigned long long last_rpkts[AVG_SIZE], last_spkts[AVG_SIZE]; static int last = 0, avg_count = 0; - register unsigned int avg_s = 0, avg_r = 0, i; + unsigned int avg_s = 0, avg_r = 0; + int i; float loss; last_rpkts[last] = ss->rpkt; last_spkts[last] = ss->spkt; last = (last + 1) % AVG_SIZE; - if (avg_count < AVG_SIZE) avg_count++; + if (avg_count < AVG_SIZE) + avg_count++; for (i = 0; i < avg_count; i++) { @@ -867,7 +875,7 @@ void cm_free(control_message *m) // }}} -void reader_thread(int updfd)/*{{{*/ +void reader_thread()/*{{{*/ { unsigned char *packet; unsigned int seq = 0; @@ -877,7 +885,7 @@ void reader_thread(int updfd)/*{{{*/ while (!ss->quitit) { struct sockaddr_in addr; - int alen = sizeof(addr); + socklen_t alen = sizeof(addr); control_message *m; int l; int s; @@ -906,7 +914,7 @@ void reader_thread(int updfd)/*{{{*/ { // Control Packet printf("Reader Received "); - m = parsecontrol(packet, l); + m = parsecontrol((char *) packet, l); printf("\n"); s = m->session; @@ -975,7 +983,7 @@ void reader_thread(int updfd)/*{{{*/ if (protocol != PPPIP) { printf("Received "); - dump_ppp_packet(packet + 6, l - 6); + dump_ppp_packet((char *) (packet + 6), l - 6); } if (protocol == PPPLCP) @@ -1073,13 +1081,13 @@ void reader_thread(int updfd)/*{{{*/ if (iph->protocol == 17) { - int iseq; + unsigned int iseq; ss->recv_count++; ss->rpkt++; iseq = *((unsigned int *) data); - if (seq != iseq) { + if (seq != iseq) ss->dropped += (iseq - seq) ; - } + seq = iseq + 1; // Next sequence number to expect. } } @@ -1095,7 +1103,7 @@ void reader_thread(int updfd)/*{{{*/ void skip_zlb() /*{{{*/ { struct sockaddr_in addr; - int alen = sizeof(addr); + socklen_t alen = sizeof(addr); char buf[1024]; int l; l = recvfrom(udpfd, buf, 1024, MSG_PEEK, (void *) &addr, &alen); From 3bea7b1e3e74035ee1eabd50396624edf4f070d4 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 18 Dec 2006 12:05:36 +0000 Subject: [PATCH 468/482] don't send interim records before session start (Daryl Tester) --- Changes | 3 ++- l2tpns.c | 5 +++-- l2tpns.spec | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Changes b/Changes index 0c6fc40..81a3337 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Tue Dec 5 2006 Brendan O'Dea 2.2.0 +* Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . - Add Multilink support from Khaled Al Hamwi. @@ -14,6 +14,7 @@ vendor-specific Ascend formats. - Security [CVE-2006-5873]: Rhys Kidd identified a vulnerability in the handling of heartbeat packets. Drop oversize heartbeat packets. +- Don't send interim records before session start (Daryl Tester). * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/l2tpns.c b/l2tpns.c index 454f79b..41f3dc9 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.171 2006-08-02 13:35:39 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.172 2006-12-18 12:05:36 bodea Exp $"; #include #include @@ -3311,7 +3311,8 @@ static void regular_cleanups(double period) if (config->radius_accounting && config->radius_interim > 0 && session[s].ip && !session[s].walled_garden && !sess_local[s].radius // RADIUS already in progress - && time_now - sess_local[s].last_interim >= config->radius_interim) + && time_now - sess_local[s].last_interim >= config->radius_interim + && session[s].flags & SESSION_STARTED) { int rad = radiusnew(s); if (!rad) diff --git a/l2tpns.spec b/l2tpns.spec index dc03660..021a848 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Tue Dec 5 2006 Brendan O'Dea 2.2.0-1 +* Mon Dec 18 2006 Brendan O'Dea 2.2.0-1 - 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes From 50da1f206fd464004a03c5a8ae9d3ac996c76af7 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 18 Dec 2006 12:08:28 +0000 Subject: [PATCH 469/482] add "shutdown" and "reload" CLI commands (Daryl Tester) --- Changes | 1 + cli.c | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Changes b/Changes index 81a3337..83b307a 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ - Security [CVE-2006-5873]: Rhys Kidd identified a vulnerability in the handling of heartbeat packets. Drop oversize heartbeat packets. - Don't send interim records before session start (Daryl Tester). +- Add "shutdown" and "reload" CLI commands (Daryl Tester). * Tue Apr 18 2006 Brendan O'Dea 2.1.18 - Don't shutdown on TerminateReq, wait for CDN. diff --git a/cli.c b/cli.c index d7a487e..12f4641 100644 --- a/cli.c +++ b/cli.c @@ -2,7 +2,7 @@ // vim: sw=8 ts=8 char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.75 2006-08-02 13:35:39 bodea Exp $"; +char const *cvs_id_cli = "$Id: cli.c,v 1.76 2006-12-18 12:08:28 bodea Exp $"; #include #include @@ -100,6 +100,9 @@ static int cmd_set(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc); static int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc); +static int cmd_shutdown(struct cli_def *cli, char *command, char **argv, int argc); +static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc); + static int regular_stuff(struct cli_def *cli); @@ -176,6 +179,8 @@ void init_cli(char *hostname) #endif cli_register_command(cli, NULL, "uptime", cmd_uptime, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show uptime and bandwidth utilisation"); + cli_register_command(cli, NULL, "shutdown", cmd_shutdown, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Shutdown l2tpns daemon and exit"); + cli_register_command(cli, NULL, "reload", cmd_reload, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Reload configuration"); c = cli_register_command(cli, NULL, "write", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "memory", cmd_write_memory, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Save the running config to flash"); @@ -426,17 +431,17 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int cli_print(cli, "\tUnique SID:\t%u", session[s].unique_id); cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0); cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0); - if (session[s].session_timeout) + if (session[s].session_timeout) { clockt opened = session[s].opened; if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) opened = bundle[session[s].bundle].online_time; - cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (opened ? abs(time_now - opened) : 0)); + cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (opened ? abs(time_now - opened) : 0)); } - if (session[s].idle_timeout) - cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); + if (session[s].idle_timeout) + cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0)); cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin); cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin); @@ -2029,7 +2034,7 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a int keepalive; int hold; - if (CLI_HELP_REQUESTED) + if (CLI_HELP_REQUESTED) { switch (argc) { @@ -2139,14 +2144,14 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a config->neighbour[i].keepalive = keepalive; config->neighbour[i].hold = hold; - return CLI_OK; + return CLI_OK; } static int cmd_router_bgp_no_neighbour(struct cli_def *cli, char *command, char **argv, int argc) { int i; - if (CLI_HELP_REQUESTED) + if (CLI_HELP_REQUESTED) return cli_arg_help(cli, argc > 0, "A.B.C.D", "BGP neighbour address", "NAME", "BGP neighbour name", @@ -2171,7 +2176,7 @@ static int cmd_router_bgp_no_neighbour(struct cli_def *cli, char *command, char } memset(&config->neighbour[i], 0, sizeof(config->neighbour[i])); - return CLI_OK; + return CLI_OK; } static int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc) @@ -3077,3 +3082,21 @@ static int cmd_show_access_list(struct cli_def *cli, char *command, char **argv, return CLI_OK; } + +static int cmd_shutdown(struct cli_def *cli, char *command, char **argv, int argc) +{ + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + + kill(getppid(), SIGQUIT); + return CLI_OK; +} + +static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc) +{ + if (CLI_HELP_REQUESTED) + return CLI_HELP_NO_ARGS; + + kill(getppid(), SIGHUP); + return CLI_OK; +} From 6f39dc956980f55ecd0af16998533a2f88e8e83a Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sun, 14 Jan 2007 04:07:52 +0000 Subject: [PATCH 470/482] remove float --- Docs/vpn/practical-vpns.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Docs/vpn/practical-vpns.xml b/Docs/vpn/practical-vpns.xml index 3cd95b0..ccb92c7 100644 --- a/Docs/vpn/practical-vpns.xml +++ b/Docs/vpn/practical-vpns.xml @@ -109,7 +109,7 @@ shows a Site-To-Site VPN diagram. -
+
Site to Site VPN @@ -130,7 +130,7 @@ shows the tunneling process. -
+
Tunneling Process @@ -389,7 +389,7 @@ gives a general description of how the network will be layed out in real-world scenario. -
+
VPN Deployment @@ -413,7 +413,7 @@ process that the VPN takes and to detail the place that each of that application-in-charge takes place. -
+
VPN Process From 4a10d3188cd1895e571c35375c1722d1f61a198e Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 25 Jan 2007 12:36:48 +0000 Subject: [PATCH 471/482] simplify throttle logic --- ppp.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/ppp.c b/ppp.c index a1ad56a..c76d6da 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.102 2006-08-02 13:35:39 bodea Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.103 2007-01-25 12:36:48 bodea Exp $"; #include #include @@ -1703,17 +1703,13 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) *(uint32_t *) p = htonl(PKTIP); l += 4; - // Are we throttled and a slave? - if (session[s].tbf_in && !config->cluster_iam_master) { - // Pass it to the master for handling. - master_throttle_packet(session[s].tbf_in, p, l); - return; - } - - // Are we throttled and a master?? - if (session[s].tbf_in && config->cluster_iam_master) { - // Actually handle the throttled packets. - tbf_queue_packet(session[s].tbf_in, p, l); + if (session[s].tbf_in) + { + // Are we throttling this session? + if (config->cluster_iam_master) + tbf_queue_packet(session[s].tbf_in, p, l); + else + master_throttle_packet(session[s].tbf_in, p, l); return; } From 46e0772dfa48c9d710c90891dd5295d56bdd1363 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 28 Jun 2007 07:22:50 +0000 Subject: [PATCH 472/482] propagate select error --- fake_epoll.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fake_epoll.h b/fake_epoll.h index b44f846..7983455 100644 --- a/fake_epoll.h +++ b/fake_epoll.h @@ -1,5 +1,5 @@ /* kludge up some limited epoll semantics using select for 2.4 kernels */ -/* $Id: fake_epoll.h,v 1.1 2005-06-04 15:42:35 bodea Exp $ */ +/* $Id: fake_epoll.h,v 1.2 2007-06-28 07:22:50 bodea Exp $ */ #ifndef __FAKE_EPOLL_H__ #define __FAKE_EPOLL_H__ @@ -150,6 +150,9 @@ static int epoll_wait(int epfd __attribute__ ((unused)), tp = 0; n = select(_epoll_fds, &r, &w, 0, tp); + if (n < 0) + return n; + if (n > maxevents) n = maxevents; From 845bb1f376be7666923e4ce0798f3801ea84b910 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Tue, 8 Dec 2009 14:49:28 +0000 Subject: [PATCH 473/482] Apply MLPPP patch from Muhammad Tayseer Alquoatli, very belatedly, with thanks. Changes: - Handle session shutdown gracefully regarding leaving the bundle (bug that is caused when a all session leaves a bundle then another join) - IP assignment is done only for the first session in the bundle (save IP waste for multiple MLPPP sessions) - Route is being added only for the first session in the bundle (less routes on l2tpns system) - Fix route deletion problem for MLPPP sessions (bug that caused when a session leaves a bundle) - Uniformity of sequence number space satisfied (according to RFC1990) - Fix reassembling fragmented packets and handling lost fragments (according to RFC 1990) - FragmentatConnection to l2tpns.cvs.sourceforge.net closed by remote host.n across N session rather than two) - Sequence numbers extraction mask has been corrected (bug in extracting sequence numbers) - some clustering support fixes - Upload/Download statistics has been corrected - add "kill_timedout_sessions" config option --- Changes | 3 + THANKS | 1 + cluster.c | 9 +- constants.c | 5 +- etc/startup-config.default | 3 + l2tpns.c | 382 +++++++++++++++++++------------ l2tpns.h | 85 ++++--- ppp.c | 458 ++++++++++++++++++++++--------------- radius.c | 28 ++- 9 files changed, 592 insertions(+), 382 deletions(-) diff --git a/Changes b/Changes index 83b307a..16a2524 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,6 @@ +Wed Dec 9 2009 Brendan O'Dea 2.2.x +- Apply MLPPP patch from Muhammad Tayseer Alquoatli. + * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. - Add "Practical VPNs" document from Liran Tal as Docs/vpn . diff --git a/THANKS b/THANKS index bc38c6d..5483a89 100644 --- a/THANKS +++ b/THANKS @@ -29,3 +29,4 @@ Patrick Cole Khaled Al Hamwi Graham Maltby Rhys Kidd +Muhammad Tayseer Alquoatli diff --git a/cluster.c b/cluster.c index fb71070..42db014 100644 --- a/cluster.c +++ b/cluster.c @@ -1,6 +1,6 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.54 2006-12-04 20:50:02 bodea Exp $"; +char const *cvs_id_cluster = "$Id: cluster.c,v 1.55 2009-12-08 14:49:28 bodea Exp $"; #include #include @@ -402,7 +402,7 @@ void cluster_send_ping(time_t basetime) x.ver = 1; x.addr = config->bind_address; - x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels; + x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels + config->cluster_undefined_bundles; x.basetime = basetime; add_type(&p, C_PING, basetime, (uint8_t *) &x, sizeof(x)); @@ -940,9 +940,8 @@ void cluster_heartbeat() if (bcount >= config->cluster_highest_bundleid) break; - hb_add_type(&p, C_CTUNNEL, walk_bundle_number); - walk_tunnel_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1); // +1 avoids divide by zero. - + hb_add_type(&p, C_CBUNDLE, walk_bundle_number); + walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1); // +1 avoids divide by zero. ++bcount; } diff --git a/constants.c b/constants.c index 48e7d59..b62b5dd 100644 --- a/constants.c +++ b/constants.c @@ -1,6 +1,6 @@ // L2TPNS: constants -char const *cvs_id_constants = "$Id: constants.c,v 1.7 2005-07-31 10:04:10 bodea Exp $"; +char const *cvs_id_constants = "$Id: constants.c,v 1.8 2009-12-08 14:49:28 bodea Exp $"; #include #include "constants.h" @@ -197,7 +197,8 @@ CONSTANT(radius_state, "RADIUSSTART", // 3 "RADIUSSTOP", // 4 "RADIUSINTERIM", // 5 - "RADIUSWAIT" // 6 + "RADIUSWAIT", // 6 + "RADIUSJUSTAUTH" // 7 ) CONSTANT(radius_code, diff --git a/etc/startup-config.default b/etc/startup-config.default index c448de2..77425c8 100644 --- a/etc/startup-config.default +++ b/etc/startup-config.default @@ -41,6 +41,9 @@ set radius_secret "secret" # Allow multiple logins for the same username #set allow_duplicate_users no +# Kill timedout sessions ? (default yes) +#set kill_timedout_sessions no + # Allow multiple logins for specific username #set guest_account "" diff --git a/l2tpns.c b/l2tpns.c index 41f3dc9..a544134 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.172 2006-12-18 12:05:36 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009-12-08 14:49:28 bodea Exp $"; #include #include @@ -74,6 +74,9 @@ static int tunidx; // ifr_ifindex of tun device static int syslog_log = 0; // are we logging to syslog static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). uint32_t last_id = 0; // Unique ID for radius accounting +// Guest change +char guest_users[10][32]; // Array of guest users +int guest_accounts_num = 0; // Number of guest users // calculated from config->l2tp_mtu uint16_t MRU = 0; // PPP MRU @@ -133,6 +136,7 @@ config_descriptt config_values[] = { CONFIG("radius_bind_min", radius_bind_min, SHORT), CONFIG("radius_bind_max", radius_bind_max, SHORT), CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), + CONFIG("kill_timedout_sessions", kill_timedout_sessions, BOOL), CONFIG("guest_account", guest_user, STRING), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), @@ -1070,45 +1074,63 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e l -= 4; } - // Process this frame - if (*p & 1) - { - proto = *p++; - l--; - } - else - { - proto = ntohs(*(uint16_t *) p); - p += 2; - l -= 2; - } + if (*p & 1) + { + proto = *p++; + l--; + } + else + { + proto = ntohs(*(uint16_t *) p); + p += 2; + l -= 2; + } + if (proto == PPPIP) + { + if (session[s].die) + { + LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + session[s].last_packet = session[s].last_data = time_now; + processipin(s, t, p, l); + } + else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) + { + if (session[s].die) + { + LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } - if (proto == PPPIP) - { - if (session[s].die) - { - LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s); - return; // closing session, PPP not processed - } + session[s].last_packet = session[s].last_data = time_now; + processipv6in(s, t, p, l); + } + else if (proto == PPPIPCP) + { + session[s].last_packet = session[s].last_data = time_now; + processipcp(s, t, p, l); + } + else if (proto == PPPCCP) + { + session[s].last_packet = session[s].last_data = time_now; + processccp(s, t, p, l); + } + else + { + LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); + } +} - session[s].last_packet = session[s].last_data = time_now; - processipin(s, t, p, l); - } - else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) - { - if (session[s].die) - { - LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s); - return; // closing session, PPP not processed - } +static void update_session_out_stat(sessionidt s, sessiont *sp, int len) +{ + increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count + sp->cout_delta += len; + sp->pout++; + sp->last_data = time_now; - session[s].last_packet = session[s].last_data = time_now; - processipv6in(s, t, p, l); - } - else - { - LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); - } + sess_local[s].cout += len; // To send to master.. + sess_local[s].pout++; } // process outgoing (to tunnel) IP @@ -1123,8 +1145,7 @@ static void processipout(uint8_t *buf, int len) uint8_t *data = buf; // Keep a copy of the originals. int size = len; - uint8_t b1[MAXETHER + 20]; - uint8_t b2[MAXETHER + 20]; + uint8_t fragbuf[MAXETHER + 20]; CSTAT(processipout); @@ -1173,9 +1194,15 @@ static void processipout(uint8_t *buf, int len) } return; } + t = session[s].tunnel; + if (len > session[s].mru || (session[s].mrru && len > session[s].mrru)) + { + LOG(3, s, t, "Packet size more than session MRU\n"); + return; + } + sp = &session[s]; - sp->last_data = time_now; // DoS prevention: enforce a maximum number of packets per 0.1s for a session if (config->max_packets > 0) @@ -1246,55 +1273,80 @@ static void processipout(uint8_t *buf, int len) } // Add on L2TP header - { - bundleidt bid = 0; - if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) - { - bid = session[s].bundle; - s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; - LOG(4, s, t, "MPPP: (1)Session number becomes: %u\n", s); - if (len > 256) - { - // Partition the packet to 2 fragments - uint32_t frag1len = len / 2; - uint32_t frag2len = len - frag1len; - uint8_t *p = makeppp(b1, sizeof(b1), buf, frag1len, s, t, PPPIP, 0, bid, MP_BEGIN); - uint8_t *q; + { + bundleidt bid = 0; + if(session[s].bundle != 0 && bundle[session[s].bundle].num_of_links > 1) + { + bid = session[s].bundle; + s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s); + if(len > MINFRAGLEN) + { + // Partition the packet to "bundle[b].num_of_links" fragments + bundlet *b = &bundle[bid]; + uint32_t num_of_links = b->num_of_links; + uint32_t fraglen = len / num_of_links; + fraglen = (fraglen > session[s].mru ? session[s].mru : fraglen); + uint32_t last_fraglen = fraglen + len % num_of_links; + last_fraglen = (last_fraglen > session[s].mru ? len % num_of_links : last_fraglen); + uint32_t remain = len; - if (!p) return; - tunnelsend(b1, frag1len + (p-b1), t); // send it... - s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; - LOG(4, s, t, "MPPP: (2)Session number becomes: %u\n", s); - q = makeppp(b2, sizeof(b2), buf+frag1len, frag2len, s, t, PPPIP, 0, bid, MP_END); - if (!q) return; - tunnelsend(b2, frag2len + (q-b2), t); // send it... - } - else { - // Send it as one frame - uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS); - if (!p) return; - tunnelsend(b1, len + (p-b1), t); // send it... - } - } - else - { - uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, 0, 0); - if (!p) return; - tunnelsend(b1, len + (p-b1), t); // send it... - } - } + // send the first packet + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, fraglen, s, t, PPPIP, 0, bid, MP_BEGIN); + if (!p) return; + tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... + // statistics + update_session_out_stat(s, sp, fraglen); + remain -= fraglen; + while (remain > last_fraglen) + { + s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); + p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), fraglen, s, t, PPPIP, 0, bid, 0); + if (!p) return; + tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, fraglen); + remain -= fraglen; + } + // send the last fragment + s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); + p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), remain, s, t, PPPIP, 0, bid, MP_END); + if (!p) return; + tunnelsend(fragbuf, remain + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, remain); + if (remain != last_fraglen) + LOG(3, s, t, "PROCESSIPOUT ERROR REMAIN != LAST_FRAGLEN, %d != %d\n", remain, last_fraglen); + } + else { + // Send it as one frame + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS); + if (!p) return; + tunnelsend(fragbuf, len + (p-fragbuf), t); // send it... + LOG(4, s, t, "MPPP: packet sent as one frame\n"); + update_session_out_stat(s, sp, len); + } + } + else + { + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0); + if (!p) return; + tunnelsend(fragbuf, len + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, len); + } + } // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count - sp->cout_delta += len; - sp->pout++; udp_tx += len; - - sess_local[s].cout += len; // To send to master.. - sess_local[s].pout++; } // process outgoing (to tunnel) IPv6 @@ -1661,7 +1713,9 @@ void filter_session(sessionidt s, int filter_in, int filter_out) void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause) { int walled_garden = session[s].walled_garden; - + bundleidt b = session[s].bundle; + //delete routes only for last session in bundle (in case of MPPP) + int del_routes = !b || (bundle[b].num_of_links == 1); CSTAT(sessionshutdown); @@ -1710,21 +1764,52 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e (session[s].route[r].ip & session[s].route[r].mask)) routed++; - routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0); + if (del_routes) routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0); session[s].route[r].ip = 0; } if (session[s].ip_pool_index == -1) // static ip { - if (!routed) routeset(s, session[s].ip, 0, 0, 0); + if (!routed && del_routes) routeset(s, session[s].ip, 0, 0, 0); session[s].ip = 0; } else free_ip_address(s); // unroute IPv6, if setup - if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen) + if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen && del_routes) route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0); + + if (b) + { + // This session was part of a bundle + bundle[b].num_of_links--; + LOG(3, s, 0, "MPPP: Dropping member link: %d from bundle %d\n",s,b); + if(bundle[b].num_of_links == 0) + { + bundleclear(b); + LOG(3, s, 0, "MPPP: Kill bundle: %d (No remaing member links)\n",b); + } + else + { + // Adjust the members array to accomodate the new change + uint8_t mem_num = 0; + // It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--") + if(bundle[b].members[bundle[b].num_of_links] != s) + { + uint8_t ml; + for(ml = 0; mlppp_restart_time = 3; config->ppp_max_configure = 10; config->ppp_max_failure = 5; + config->kill_timedout_sessions = 1; strcpy(config->random_device, RANDOMDEVICE); log_stream = stderr; @@ -4737,6 +4789,40 @@ static void update_config() } } + // Guest change + guest_accounts_num = 0; + char *p2 = config->guest_user; + while (p2 && *p2) + { + char *s = strpbrk(p2, " \t,"); + if (s) + { + *s++ = 0; + while (*s == ' ' || *s == '\t') + s++; + + if (!*s) + s = 0; + } + + strcpy(guest_users[guest_accounts_num], p2); + LOG(1, 0, 0, "Guest account[%d]: %s\n", guest_accounts_num, guest_users[guest_accounts_num]); + guest_accounts_num++; + p2 = s; + } + // Rebuild the guest_user array + strcpy(config->guest_user, ""); + int ui = 0; + for (ui=0; uiguest_user, guest_users[ui]); + if (uiguest_user, ","); + } + } + + memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->multi_read_count) config->multi_read_count = 10; if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); @@ -4815,6 +4901,20 @@ int sessionsetup(sessionidt s, tunnelidt t) LOG(3, s, t, "Doing session setup for session\n"); + // Join a bundle if the MRRU option is accepted + if(session[s].mrru > 0 && session[s].bundle == 0) + { + LOG(3, s, t, "This session can be part of multilink bundle\n"); + if (join_bundle(s) > 0) + cluster_send_bundle(session[s].bundle); + else + { + LOG(0, s, t, "MPPP: Mismaching mssf option with other sessions in bundle\n"); + sessionshutdown(s, "Mismaching mssf option.", CDN_NONE, TERM_SERVICE_UNAVAILABLE); + return 0; + } + } + if (!session[s].ip) { assign_ip_address(s); @@ -4832,14 +4932,6 @@ int sessionsetup(sessionidt s, tunnelidt t) // Make sure this is right session[s].tunnel = t; - // Join a bundle if the MRRU option is accepted - if (session[s].mrru > 0 && !session[s].bundle) - { - LOG(3, s, t, "This session can be part of multilink bundle\n"); - if (join_bundle(s)) - cluster_send_bundle(session[s].bundle); - } - // zap old sessions with same IP and/or username // Don't kill gardened sessions - doing so leads to a DoS // from someone who doesn't need to know the password @@ -4850,25 +4942,29 @@ int sessionsetup(sessionidt s, tunnelidt t) { if (i == s) continue; if (!session[s].opened) continue; + // Allow duplicate sessions for multilink ones of the same bundle. + if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle) + continue; if (ip == session[i].ip) { sessionkill(i, "Duplicate IP address"); continue; } - if (config->allow_duplicate_users) - continue; - - if (session[s].walled_garden || session[i].walled_garden) - continue; - - // Allow duplicate sessions for guest account. - if (*config->guest_user && !strcasecmp(user, config->guest_user)) - continue; - - // Allow duplicate sessions for multilink ones of the same bundle. - if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle) - continue; + if (config->allow_duplicate_users) continue; + if (session[s].walled_garden || session[i].walled_garden) continue; + // Guest change + int found = 0; + int gu; + for (gu = 0; gu < guest_accounts_num; gu++) + { + if (!strcasecmp(user, guest_users[gu])) + { + found = 1; + break; + } + } + if (found) continue; // Drop the new session in case of duplicate sessionss, not the old one. if (!strcasecmp(user, session[i].user)) @@ -4876,6 +4972,8 @@ int sessionsetup(sessionidt s, tunnelidt t) } } + // no need to set a route for the same IP address of the bundle + if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1)) { int routed = 0; diff --git a/l2tpns.h b/l2tpns.h index 5614ae9..c68d9c3 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1,5 +1,5 @@ // L2TPNS Global Stuff -// $Id: l2tpns.h,v 1.120 2006-10-23 02:51:53 bodea Exp $ +// $Id: l2tpns.h,v 1.121 2009-12-08 14:49:28 bodea Exp $ #ifndef __L2TPNS_H__ #define __L2TPNS_H__ @@ -19,10 +19,8 @@ // Limits #define MAXTUNNEL 500 // could be up to 65535 #define MAXBUNDLE 300 // could be up to 65535 -#define MAXBUNDLESES 10 // Maximum number of member links in bundle +#define MAXBUNDLESES 12 // Maximum number of member links in bundle #define MAXADDRESS 20 // Maximum length for the Endpoint Discrminiator address -#define MAXFRAGNUM 1500 // Maximum number of Multilink fragment in a bundle -#define MAXFRAGLEN 1000 // Maximum length for Multilink fragment #define MAXSESSION 60000 // could be up to 65535 #define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session. @@ -52,9 +50,15 @@ #define IDLE_TIMEOUT 240 // Time between last packet seen and session shutdown #define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown -#define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header -#define MP_END 0x40 // This value is used when (e)nd bit is set in MP header -#define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header +#define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header +#define MP_END 0x40 // This value is used when (e)nd bit is set in MP header +#define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header + +#define MINFRAGLEN 64 // Minumum fragment length +#define MAXFRAGLEN 750 // Maximum length for Multilink fragment (MTU / 2 sessions) +#define MAXFRAGNUM 128 // Maximum number of Multilink fragment in a bundle (must be in the form of 2^X) + // it's not expected to have a space for more than 10 unassembled packets = 10 * MAXBUNDLESES +#define MAXFRAGNUM_MASK 127 // Must be equal to MAXFRAGNUM-1 // Constants #ifndef ETCDIR @@ -248,8 +252,11 @@ typedef struct { } epdist; typedef struct { - uint16_t length; // Fragment length - uint8_t data[MAXFRAGLEN]; // Fragment data + sessionidt sid; // Fragment originating session + uint8_t flags; // MP frame flags + uint32_t seq; // fragment seq num + uint16_t length; // Fragment length + uint8_t data[MAXFRAGLEN]; // Fragment data } fragmentt; typedef struct @@ -297,10 +304,11 @@ typedef struct char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; - uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit - uint8_t mssf; // Multilink Short Sequence Number Header Format - epdist epdis; // Multilink Endpoint Discriminator - bundleidt bundle; // Multilink Bundle Identifier + clockt timeout; // Session timeout + uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit + uint8_t mssf; // Multilink Short Sequence Number Header Format + epdist epdis; // Multilink Endpoint Discriminator + bundleidt bundle; // Multilink Bundle Identifier in_addr_t snoop_ip; // Interception destination IP uint16_t snoop_port; // Interception destination port uint8_t walled_garden; // is this session gardened? @@ -312,28 +320,31 @@ sessiont; typedef struct { - int state; // current state (bundlestate enum) - uint32_t seq_num_t; // Sequence Number (transmission) - uint32_t seq_num_m; // Last received frame sequence number (bearing B bit) - uint32_t offset; // Offset between sequence number and array index - uint8_t pending_frag; // Indicate that there is pending fragments to reassemble - uint8_t num_of_links; // Number of links joint to this bundle - uint32_t online_time; // The time this bundle is online - clockt last_check; // Last time the timeout is checked - uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit - uint8_t mssf; // Multilink Short Sequence Number Header Format - epdist epdis; // Multilink Endpoint Discriminator - char user[MAXUSER]; // Needed for matching member links - sessionidt current_ses; // Current session to use for sending (used in RR load-balancing) - sessionidt members[MAXBUNDLESES]; // Array for member links sessions + int state; // current state (bundlestate enum) + uint32_t seq_num_t; // Sequence Number (transmission) + uint32_t timeout; // Session-Timeout for bundle + uint32_t max_seq; // Max value of sequence number field + uint8_t num_of_links; // Number of links joint to this bundle + uint32_t online_time; // The time this bundle is online + clockt last_check; // Last time the timeout is checked + uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit + uint8_t mssf; // Multilink Short Sequence Number Header Format + epdist epdis; // Multilink Endpoint Discriminator + char user[MAXUSER]; // Needed for matching member links + sessionidt current_ses; // Current session to use for sending (used in RR load-balancing) + sessionidt members[MAXBUNDLESES]; // Array for member links sessions } bundlet; typedef struct { - fragmentt fragment[MAXFRAGNUM]; - uint8_t reassembled_frame[MAXETHER]; // The reassembled frame - uint16_t re_frame_len; // The reassembled frame length + fragmentt fragment[MAXFRAGNUM]; + uint8_t reassembled_frame[MAXETHER]; // The reassembled frame + uint16_t re_frame_len; // The reassembled frame length + uint16_t re_frame_begin_index, re_frame_end_index; // reassembled frame begin index, end index respectively + uint16_t start_index, end_index; // start and end sequence numbers available on the fragments array respectively + uint32_t M; // Minumum frame sequence number received over all bundle members + uint32_t start_seq; // Last received frame sequence number (bearing B bit) } fragmentationt; @@ -388,6 +399,9 @@ typedef struct // last LCP Echo time_t last_echo; + + // Last Multilink frame sequence number received + uint32_t last_seq; } sessionlocalt; // session flags @@ -497,6 +511,7 @@ enum RADIUSSTOP, // sending stop accounting to RADIUS server RADIUSINTERIM, // sending interim accounting to RADIUS server RADIUSWAIT, // waiting timeout before available, in case delayed replies + RADIUSJUSTAUTH, // sending auth to RADIUS server, just authentication, no ip assigning }; struct Tstats @@ -644,7 +659,7 @@ typedef struct int radius_authprefer; int allow_duplicate_users; // allow multiple logins with the same username - char guest_user[MAXUSER]; // allow multiple logins to guest account username + int kill_timedout_sessions; // kill authenticated sessions with "session_timeout == 0" in_addr_t default_dns1, default_dns2; @@ -691,11 +706,15 @@ typedef struct int cluster_hb_interval; // How often to send a heartbeat. int cluster_hb_timeout; // How many missed heartbeats trigger an election. uint64_t cluster_table_version; // # state changes processed by cluster - int cluster_master_min_adv; // Master advertises routes while the number of up to date - // slaves is less than this value. struct in6_addr ipv6_prefix; // Our IPv6 network pool. + + int cluster_master_min_adv; // Master advertises routes while the number of up to date + // slaves is less than this value. + // Guest change + char guest_user[MAXUSER]; // Guest account username + #ifdef BGP #define BGP_NUM_PEERS 2 uint16_t as_number; diff --git a/ppp.c b/ppp.c index c76d6da..94cc5fa 100644 --- a/ppp.c +++ b/ppp.c @@ -1,6 +1,6 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.103 2007-01-25 12:36:48 bodea Exp $"; +char const *cvs_id_ppp = "$Id: ppp.c,v 1.104 2009-12-08 14:49:28 bodea Exp $"; #include #include @@ -31,6 +31,16 @@ static int epdiscmp(epdist, epdist); static void setepdis(epdist *, epdist); static void ipcp_open(sessionidt s, tunnelidt t); +static int first_session_in_bundle(sessionidt s) +{ + bundleidt i; + for (i = 1; i < MAXBUNDLE; i++) + if (bundle[i].state != BUNDLEFREE) + if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user)) + return 0; + return 1; +} + // Process PAP messages void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { @@ -140,7 +150,10 @@ void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) radius[r].id = p[1]; LOG(3, s, t, "Sending login for %s/%s to RADIUS\n", user, pass); - radiussend(r, RADIUSAUTH); + if ((session[s].mrru) && (!first_session_in_bundle(s))) + radiussend(r, RADIUSJUSTAUTH); + else + radiussend(r, RADIUSAUTH); } } @@ -257,7 +270,10 @@ void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) radius[r].chap = 1; LOG(3, s, t, "CHAP login %s\n", session[s].user); - radiussend(r, RADIUSAUTH); + if ((session[s].mrru) && (!first_session_in_bundle(s))) + radiussend(r, RADIUSJUSTAUTH); + else + radiussend(r, RADIUSAUTH); } static void dumplcp(uint8_t *p, int l) @@ -366,17 +382,7 @@ void lcp_open(sessionidt s, tunnelidt t) } else { - if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) - { - sessionidt first_ses = bundle[session[s].bundle].members[0]; - LOG(3, s, t, "MPPP: Skipping IPCP negotiation for session:%d, first session of bundle is:%d\n", s, first_ses); - session[s].ip = session[first_ses].ip; - session[s].dns1 = session[first_ses].dns1; - session[s].dns2 = session[first_ses].dns2; - session[s].session_timeout = session[first_ses].session_timeout; - ipcp_open(s, t); - } - else + if(session[s].bundle == 0 || bundle[session[s].bundle].num_of_links == 1) { // This-Layer-Up sendipcp(s, t); @@ -389,6 +395,12 @@ void lcp_open(sessionidt s, tunnelidt t) change_state(s, ccp, Stopped); } + else + { + sessionidt first_ses = bundle[session[s].bundle].members[0]; + LOG(3, s, t, "MPPP: Skipping IPCP negotiation for session:%d, first session of bundle is:%d\n",s,first_ses); + ipcp_open(s, t); + } } } @@ -1091,10 +1103,20 @@ int join_bundle(sessionidt s) { if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user)) { + sessionidt first_ses = bundle[i].members[0]; + if (bundle[i].mssf != session[s].mssf) + { + // uniformity of sequence number format must be insured + LOG(3, s, session[s].tunnel, "MPPP: unable to bundle session %d in bundle %d cause of different mssf\n", s, i); + return -1; + } session[s].bundle = i; - bundle[i].mrru = session[s].mrru; - bundle[i].mssf = session[s].mssf; - if (session[s].epdis.length > 0) + session[s].ip = session[first_ses].ip; + session[s].dns1 = session[first_ses].dns1; + session[s].dns2 = session[first_ses].dns2; + session[s].timeout = session[first_ses].timeout; + + if(session[s].epdis.length > 0) setepdis(&bundle[i].epdis, session[s].epdis); strcpy(bundle[i].user, session[s].user); @@ -1112,11 +1134,19 @@ int join_bundle(sessionidt s) session[s].bundle = b; bundle[b].mrru = session[s].mrru; bundle[b].mssf = session[s].mssf; - if (session[s].epdis.length > 0) + // FIXME !!! to enable l2tpns reading mssf frames receiver_max_seq, sender_max_seq must be introduce + // now session[s].mssf flag indecates that the receiver wish to receive frames in mssf, so max_seq (i.e. recv_max_seq) = 1<<24 + /* + if (bundle[b].mssf) + bundle[b].max_seq = 1 << 12; + else */ + bundle[b].max_seq = 1 << 24; + if(session[s].epdis.length > 0) setepdis(&bundle[b].epdis, session[s].epdis); strcpy(bundle[b].user, session[s].user); bundle[b].members[0] = s; + bundle[b].timeout = session[s].timeout; LOG(3, s, session[s].tunnel, "MPPP: Created a new bundle (%d)\n", b); return b; } @@ -1148,23 +1178,24 @@ static void setepdis(epdist *ep1, epdist ep2) static bundleidt new_bundle() { - bundleidt i; - for (i = 1; i < MAXBUNDLE; i++) - { - if (bundle[i].state == BUNDLEFREE) - { - LOG(4, 0, 0, "MPPP: Assigning bundle ID %d\n", i); - bundle[i].num_of_links = 1; - bundle[i].last_check = time_now; // Initialize last_check value - bundle[i].state = BUNDLEOPEN; - bundle[i].current_ses = -1; // This is to enforce the first session 0 to be used at first - if (i > config->cluster_highest_bundleid) - config->cluster_highest_bundleid = i; - return i; - } - } - LOG(0, 0, 0, "MPPP: Can't find a free bundle! There shouldn't be this many in use!\n"); - return 0; + bundleidt i; + for (i = 1; i < MAXBUNDLE; i++) + { + if (bundle[i].state == BUNDLEFREE) + { + LOG(4, 0, 0, "MPPP: Assigning bundle ID %d\n", i); + bundle[i].num_of_links = 1; + bundle[i].last_check = time_now; // Initialize last_check value + bundle[i].state = BUNDLEOPEN; + bundle[i].current_ses = -1; // This is to enforce the first session 0 to be used at first + memset(&frag[i], 0, sizeof(fragmentationt)); + if (i > config->cluster_highest_bundleid) + config->cluster_highest_bundleid = i; + return i; + } + } + LOG(0, 0, 0, "MPPP: Can't find a free bundle! There shouldn't be this many in use!\n"); + return 0; } static void ipcp_open(sessionidt s, tunnelidt t) @@ -1645,6 +1676,39 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) } } +static void update_sessions_in_stat(sessionidt s, uint16_t l) +{ + bundleidt b = session[s].bundle; + if (!b) + { + increment_counter(&session[s].cin, &session[s].cin_wrap, l); + session[s].cin_delta += l; + session[s].pin++; + + sess_local[s].cin += l; + sess_local[s].pin++; + } + else + { + int i = frag[b].re_frame_begin_index; + int end = frag[b].re_frame_end_index; + for (;;) + { + l = frag[b].fragment[i].length; + s = frag[b].fragment[i].sid; + increment_counter(&session[s].cin, &session[s].cin_wrap, l); + session[s].cin_delta += l; + session[s].pin++; + + sess_local[s].cin += l; + sess_local[s].pin++; + if (i == end) + return; + i = (i + 1) & MAXFRAGNUM_MASK; + } + } +} + // process IP packet received // // This MUST be called with at least 4 byte behind 'p'. @@ -1732,12 +1796,7 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); } - increment_counter(&session[s].cin, &session[s].cin_wrap, l); - session[s].cin_delta += l; - session[s].pin++; - - sess_local[s].cin += l; - sess_local[s].pin++; + update_sessions_in_stat(s, l); eth_tx += l; @@ -1748,162 +1807,190 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) // process Multilink PPP packet received void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) { - bundleidt b = session[s].bundle; - uint8_t begin_frame; - uint8_t end_frame; - uint32_t seq_num; - uint32_t offset; + bundleidt b = session[s].bundle; + bundlet * this_bundle = &bundle[b]; + uint32_t frag_offset, M_offset; + uint16_t frag_index, M_index; + fragmentationt *this_fragmentation = &frag[b]; + uint8_t begin_frame = (*p & MP_BEGIN); + uint8_t end_frame = (*p & MP_END); + uint32_t seq_num; + uint8_t flags = *p; + uint16_t begin_index, end_index; - if (!b) + // Perform length checking + if(l > MAXFRAGLEN) + { + LOG(2, s, t, "MPPP: discarding fragment larger than MAXFRAGLEN\n"); + return; + } + + if(!b) + { + LOG(2, s, t, "MPPP: Invalid bundle id: 0\n"); + return; + } + // FIXME !! session[s].mssf means that the receiver wants to receive frames in mssf not means the receiver will send frames in mssf + /* if(session[s].mssf) + { + // Get 12 bit for seq number + seq_num = ntohs((*(uint16_t *) p) & 0xFF0F); + p += 2; + l -= 2; + // After this point the pointer should be advanced 2 bytes + LOG(3, s, t, "MPPP: 12 bits, sequence number: %d\n",seq_num); + } + else */ + { + // Get 24 bit for seq number + seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00); + p += 4; + l -= 4; + // After this point the pointer should be advanced 4 bytes + LOG(4, s, t, "MPPP: 24 bits sequence number:%d\n",seq_num); + } + + // calculate this fragment's offset from the begin seq in the bundle + frag_offset = (seq_num + this_bundle->max_seq - this_fragmentation->start_seq) & (this_bundle->max_seq-1); + + // discard this fragment if frag_offset is bigger that the fragmentation buffer size + if (frag_offset >= MAXFRAGNUM) + { + LOG(3, s, t, "MPPP: Index out of range, received more than MAXFRAGNUM fragment (lost frag) seq:%d, begin_seq:%d, bundle:%d, max:%d\n",seq_num, this_fragmentation->start_seq, b, this_bundle->max_seq); + return; + } + + // update M + sess_local[s].last_seq = seq_num; + if (seq_num < this_fragmentation->M) + this_fragmentation->M = seq_num; + else { - LOG(3, s, t, "MPPP: Invalid bundle id: 0\n"); - return; + uint32_t i, min = sess_local[(this_bundle->members[0])].last_seq;; + for (i = 1; i < this_bundle->num_of_links; i++) + { + uint32_t s_seq = sess_local[(this_bundle->members[i])].last_seq; + if (s_seq < min) + min = s_seq; + } + this_fragmentation->M = min; } - begin_frame = (*p & 0x80); - end_frame = (*p & 0x40); - if (session[s].mssf) + LOG(4, s, t, "MPPP: Setting M to %d\n", this_fragmentation->M); + //calculate M's offset from the begin seq in the bundle + M_offset = (this_fragmentation->M + this_bundle->max_seq - this_fragmentation->start_seq) & (this_bundle->max_seq-1); + + //caculate M's index in the fragment array + M_index = (M_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK; + + //caculate received fragment's index in the fragment array + frag_index = (frag_offset + this_fragmentation->start_index) & MAXFRAGNUM_MASK; + + //frame with a single fragment + if (begin_frame && end_frame) { - // Get 12 bit for seq number - uint16_t short_seq_num = ntohs((*(uint16_t *) p) & 0xFF0F); - uint16_t short_seq_num2 = short_seq_num >> 4; - p += 2; - l -= 2; - seq_num = short_seq_num2; - // After this point the pointer should be advanced 2 bytes - LOG(3, s, t, "MPPP: 12 bits, sequence number: %d, short1: %d, short2: %d\n",seq_num, short_seq_num, short_seq_num2); + // process and reset fragmentation + LOG(4, s, t, "MPPP: Both bits are set (Begin and End).\n"); + this_fragmentation->fragment[frag_index].length = l; + this_fragmentation->fragment[frag_index].sid = s; + this_fragmentation->fragment[frag_index].flags = flags; + this_fragmentation->fragment[frag_index].seq = seq_num; + this_fragmentation->re_frame_begin_index = frag_index; + this_fragmentation->re_frame_end_index = frag_index; + processmpframe(s, t, p, l, 0); + this_fragmentation->fragment[frag_index].length = 0; + this_fragmentation->fragment[frag_index].flags = 0; + end_index = frag_index; } else { - // Get 24 bit for seq number - p++; - seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00); - seq_num = seq_num >> 8; - p += 3; - l -= 4; - // After this point the pointer should be advanced 4 bytes - LOG(4, s, t, "MPPP: 24 bits sequence number:%d\n",seq_num); - } + // insert the frame in it's place + fragmentt *this_frag = &this_fragmentation->fragment[frag_index]; + this_frag->length = l; + this_frag->sid = s; + this_frag->flags = flags; + this_frag->seq = seq_num; + memcpy(this_frag->data, p, l); - if (seq_num - bundle[b].offset < 0) - { - bundle[b].offset = 0; - bundle[b].pending_frag = 0; - } - - offset = bundle[b].offset; - if (begin_frame) - { - // Check for previous non-assembled frames - int error = 0; - if (bundle[b].pending_frag) + // try to assemble the frame that has the received fragment as a member + // get the beginning of this frame + begin_index = end_index = frag_index; + while (this_fragmentation->fragment[begin_index].length) { - uint32_t fn = bundle[b].seq_num_m - offset; - uint16_t cur_len; - bundle[b].pending_frag = 0; - // Check for array indexes - if (fn < 0 || fn > MAXFRAGNUM) - { - LOG(2, s, t, "ERROR: Index out of range fn:%d, bundle:%d\n",fn,b); - return; - } - - if (seq_num-offset < 0 || seq_num-offset > MAXFRAGNUM) - { - LOG(2, s, t, "ERROR: Index out of range fn(last):%d, bundle:%d\n",fn,b); - return; - } - ///////////////////////////////////////////////////// - cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin - for (fn = bundle[b].seq_num_m - offset; fn < seq_num - offset; fn++) - { - if (!frag[b].fragment[fn].length) - { - LOG(4, s, t, "MPPP: Found lost fragment while reassembling frame %d in (%d,%d)\n",fn, bundle[b].seq_num_m-offset, seq_num-offset); - error = 1; - break; - } - - if (cur_len + frag[b].fragment[fn].length > MAXETHER) - { - LOG(2, s, t, "MPPP: ERROR: very long frame after assembling %d\n", frag[b].fragment[fn].length+cur_len); - error = 1; - break; - } - - memcpy(frag[b].reassembled_frame+cur_len, frag[b].fragment[fn].data, frag[b].fragment[fn].length); - cur_len += frag[b].fragment[fn].length; - frag[b].fragment[fn].length = 0; // Indicates that this fragment has been consumed - // This is usefull for compression - memset(frag[b].fragment[fn].data, 0, sizeof(frag[b].fragment[fn].data)); - } - - if (!error) - { - frag[b].re_frame_len = cur_len; - // Process the resassembled frame - LOG(4, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len); - processmpframe(s, t, frag[b].reassembled_frame, frag[b].re_frame_len, 1); - // Set reassembled frame length to zero after processing it - frag[b].re_frame_len = 0; - memset(frag[b].reassembled_frame, 0, sizeof(frag[b].reassembled_frame)); - } + if (this_fragmentation->fragment[begin_index].flags & MP_BEGIN) + break; + begin_index = (begin_index ? (begin_index -1) : (MAXFRAGNUM -1)); } - ////////////////////////////////////////// - bundle[b].seq_num_m = seq_num; - if (end_frame) + + // return if a lost fragment is found + if (!(this_fragmentation->fragment[begin_index].length)) + return; // assembling frame failed + // get the end of his frame + while (this_fragmentation->fragment[end_index].length) { - // Both bits are set - LOG(4, s, t, "MPPP: Both bits are set (Begin and End).\n"); - processmpframe(s, t, p, l, 0); - // The maximum number of fragments is 1500 - if (seq_num - bundle[b].offset >= 1400) - { - bundle[b].offset = seq_num; - LOG(4, s, t, "MPPP: Setting offset to: %d\n",bundle[b].offset); - } + if (this_fragmentation->fragment[end_index].flags & MP_END) + break; + end_index = (end_index +1) & MAXFRAGNUM_MASK; } - else + + // return if a lost fragment is found + if (!(this_fragmentation->fragment[end_index].length)) + return; // assembling frame failed + + // assemble the packet + //assemble frame, process it, reset fragmentation + uint16_t cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin + uint32_t i; + + LOG(4, s, t, "MPPP: processing fragments from %d to %d\n", begin_index, end_index); + // Push to the receive buffer + + for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK) + { + this_frag = &this_fragmentation->fragment[i]; + if(cur_len + this_frag->length > MAXETHER) + { + LOG(2, s, t, "MPPP: discarding reassembled frames larger than MAXETHER\n"); + break; + } + memcpy(this_fragmentation->reassembled_frame+cur_len, this_frag->data, this_frag->length); + LOG(5, s, t, "MPPP: processing frame at %d, with len %d\n", i, this_frag->length); + cur_len += this_frag->length; + if (i == end_index) + { + this_fragmentation->re_frame_len = cur_len; + this_fragmentation->re_frame_begin_index = begin_index; + this_fragmentation->re_frame_end_index = end_index; + // Process the resassembled frame + LOG(5, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len); + processmpframe(s, t, this_fragmentation->reassembled_frame, this_fragmentation->re_frame_len, 1); + break; + } + } + // Set reassembled frame length to zero after processing it + this_fragmentation->re_frame_len = 0; + for (i = begin_index;; i = (i + 1) & MAXFRAGNUM_MASK) { - bundle[b].pending_frag = 1; - // End bit is clear - LOG(4, s, t, "MPPP: Push to receive buffer\n"); - // Push to the receive buffer - // Array indexes checking - if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM) - { - LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(1) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b); - return; - } - // Perform length checking - if (l > MAXFRAGLEN) - { - LOG(2, s, t, "MPPP: ERROR: very long fragment length (1)\n"); - return; - } - frag[b].fragment[seq_num - offset].length = l; - memcpy(frag[b].fragment[seq_num - offset].data, p, l); + this_fragmentation->fragment[i].length = 0; // Indicates that this fragment has been consumed + this_fragmentation->fragment[i].flags = 0; + if (i == end_index) + break; } } - else - { - LOG(4, s, t, "MPPP: Push to receive buffer\n"); - // Push to the receive buffer - // Array indexes checking - if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM) - { - LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(2) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b); - return; - } - // Perform length checking - if (l > MAXFRAGLEN) - { - LOG(2, s, t, "MPPP: ERROR: very long fragment length (2).\n"); - return; - } - frag[b].fragment[seq_num - offset].length = l; - memcpy(frag[b].fragment[seq_num - offset].data, p, l); - } + //discard fragments received before the recently assembled frame + begin_index = this_fragmentation->start_index; + this_fragmentation->start_index = (end_index + 1) & MAXFRAGNUM_MASK; + this_fragmentation->start_seq = (this_fragmentation->fragment[end_index].seq + 1) & (this_bundle->max_seq-1); + //clear length and flags of the discarded fragments + while (begin_index != this_fragmentation->start_index) + { + this_fragmentation->fragment[begin_index].flags = 0; + this_fragmentation->fragment[begin_index].length = 0; + begin_index = (begin_index + 1) & MAXFRAGNUM_MASK; + } + + LOG(4, s, t, "MPPP after assembling: M index is =%d, start index is = %d, start seq=%d\n",M_index, this_fragmentation->start_index, this_fragmentation->start_seq); + return; } // process IPv6 packet received @@ -1989,12 +2076,7 @@ void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port); } - increment_counter(&session[s].cin, &session[s].cin_wrap, l); - session[s].cin_delta += l; - session[s].pin++; - - sess_local[s].cin += l; - sess_local[s].pin++; + update_sessions_in_stat(s, l); eth_tx += l; @@ -2265,7 +2347,7 @@ uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelid { // Set the multilink bits uint16_t bits_send = mp_bits; - *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0xFF0F)|bits_send); + *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0x0FFF)|bits_send); b += 2; } else diff --git a/radius.c b/radius.c index 3da31f8..9c28157 100644 --- a/radius.c +++ b/radius.c @@ -1,6 +1,6 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.55 2006-08-02 14:17:30 bodea Exp $"; +char const *cvs_id_radius = "$Id: radius.c,v 1.56 2009-12-08 14:49:28 bodea Exp $"; #include #include @@ -177,7 +177,7 @@ void radiussend(uint16_t r, uint8_t state) return; } - if (state != RADIUSAUTH && !config->radius_accounting) + if (state != RADIUSAUTH && state != RADIUSJUSTAUTH && !config->radius_accounting) { // Radius accounting is turned off radiusclear(r, s); @@ -197,7 +197,7 @@ void radiussend(uint16_t r, uint8_t state) { if (s) { - if (state == RADIUSAUTH) + if (state == RADIUSAUTH || state == RADIUSJUSTAUTH) sessionshutdown(s, "RADIUS timeout.", CDN_ADMIN_DISC, TERM_REAUTHENTICATION_FAILURE); else { @@ -219,6 +219,7 @@ void radiussend(uint16_t r, uint8_t state) switch (state) { case RADIUSAUTH: + case RADIUSJUSTAUTH: b[0] = AccessRequest; // access request break; case RADIUSSTART: @@ -239,7 +240,7 @@ void radiussend(uint16_t r, uint8_t state) strcpy((char *) p + 2, session[s].user); p += p[1]; } - if (state == RADIUSAUTH) + if (state == RADIUSAUTH || state == RADIUSJUSTAUTH) { if (radius[r].chap) { @@ -380,12 +381,12 @@ void radiussend(uint16_t r, uint8_t state) *p = 6; // Service-Type p[1] = 6; - *(uint32_t *) (p + 2) = htonl(2); // Framed-User + *(uint32_t *) (p + 2) = htonl((state == RADIUSJUSTAUTH ? 8 : 2)); // Authenticate only or Framed-User respectevily p += p[1]; *p = 7; // Framed-Protocol - p[1] = 6; - *(uint32_t *) (p + 2) = htonl(1); // PPP + p[1] = htonl((state == RADIUSJUSTAUTH ? 0 : 6)); + *(uint32_t *) (p + 2) = htonl((state == RADIUSJUSTAUTH ? 0 : 1)); // PPP p += p[1]; if (session[s].ip) @@ -462,7 +463,7 @@ void radiussend(uint16_t r, uint8_t state) // All AVpairs added *(uint16_t *) (b + 2) = htons(p - b); - if (state != RADIUSAUTH) + if (state != RADIUSAUTH && state != RADIUSJUSTAUTH) { // Build auth for accounting packet calc_auth(b, p - b, zero, b + 4); @@ -475,7 +476,7 @@ void radiussend(uint16_t r, uint8_t state) // get radius port uint16_t port = config->radiusport[(radius[r].try - 1) % config->numradiusservers]; // assume RADIUS accounting port is the authentication port +1 - addr.sin_port = htons((state == RADIUSAUTH) ? port : port+1); + addr.sin_port = htons((state == RADIUSAUTH || state == RADIUSJUSTAUTH) ? port : port+1); } LOG_HEX(5, "RADIUS Send", b, (p - b)); @@ -558,7 +559,7 @@ void processrad(uint8_t *buf, int len, char socket_index) LOG(1, s, session[s].tunnel, " Unexpected RADIUS response\n"); return; } - if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSSTART + if (radius[r].state != RADIUSAUTH && radius[r].state != RADIUSJUSTAUTH && radius[r].state != RADIUSSTART && radius[r].state != RADIUSSTOP && radius[r].state != RADIUSINTERIM) { LOG(1, s, session[s].tunnel, " Unexpected RADIUS response\n"); @@ -573,7 +574,7 @@ void processrad(uint8_t *buf, int len, char socket_index) return; // Do nothing. On timeout, it will try the next radius server. } - if ((radius[r].state == RADIUSAUTH && r_code != AccessAccept && r_code != AccessReject) || + if (((radius[r].state == RADIUSAUTH ||radius[r].state == RADIUSJUSTAUTH) && r_code != AccessAccept && r_code != AccessReject) || ((radius[r].state == RADIUSSTART || radius[r].state == RADIUSSTOP || radius[r].state == RADIUSINTERIM) && r_code != AccountingResponse)) { LOG(1, s, session[s].tunnel, " Unexpected RADIUS response %s\n", radius_code(r_code)); @@ -581,7 +582,7 @@ void processrad(uint8_t *buf, int len, char socket_index) // care off finishing the radius session if that's really correct. } - if (radius[r].state == RADIUSAUTH) + if (radius[r].state == RADIUSAUTH || radius[r].state == RADIUSJUSTAUTH) { // run post-auth plugin struct param_post_auth packet = { @@ -783,6 +784,8 @@ void processrad(uint8_t *buf, int len, char socket_index) if (p[1] < 6) continue; session[s].session_timeout = ntohl(*(uint32_t *)(p + 2)); LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", session[s].session_timeout); + if(!session[s].session_timeout && config->kill_timedout_sessions) + sessionshutdown(s, "Session timeout is zero", CDN_ADMIN_DISC, 0); } else if (*p == 28) { @@ -869,6 +872,7 @@ void radiusretry(uint16_t r) sendchap(s, t); break; case RADIUSAUTH: // sending auth to RADIUS server + case RADIUSJUSTAUTH: // sending auth to RADIUS server case RADIUSSTART: // sending start accounting to RADIUS server case RADIUSSTOP: // sending stop accounting to RADIUS server case RADIUSINTERIM: // sending interim accounting to RADIUS server From bab8601f70e39fb021e12857d60216030109c9c3 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sat, 9 Jan 2010 13:33:41 +0000 Subject: [PATCH 474/482] Apply patch from Michael O to avoid sending multiple CDNs. --- l2tpns.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index a544134..a599a50 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009-12-08 14:49:28 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.174 2010-01-09 13:33:41 bodea Exp $"; #include #include @@ -1730,6 +1730,7 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] }; LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason); run_plugins(PLUGIN_KILL_SESSION, &data); + session[s].die = TIME + 150; // Clean up in 15 seconds } if (session[s].ip && !walled_garden && !session[s].die) @@ -1832,9 +1833,6 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e controladd(c, session[s].far, session[s].tunnel); // send the message } - if (!session[s].die) - session[s].die = TIME + 150; // Clean up in 15 seconds - // update filter refcounts if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; @@ -1930,8 +1928,9 @@ void sessionkill(sessionidt s, char *reason) return; } - session[s].die = TIME; - sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. + if (!session[s].die) + sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. + if (sess_local[s].radius) radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed From b6182385093375d90814e937c7954134954049e5 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 11 Jan 2010 12:16:20 +0000 Subject: [PATCH 475/482] Apply patch from Michael O to avoid sending multiple CDNs. --- Changes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 16a2524..b1cd057 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,6 @@ -Wed Dec 9 2009 Brendan O'Dea 2.2.x +* Mon Jan 11 2010 Brendan O'Dea 2.2.x - Apply MLPPP patch from Muhammad Tayseer Alquoatli. +- Apply patch from Michael O to avoid sending multiple CDNs. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. From 97119ee512f1cfa772abbd76fa83271ea26bdb4b Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 25 Mar 2010 05:24:23 +0000 Subject: [PATCH 476/482] Apply patch from Cyril Elkaim to fix an issue with MacOS. --- Changes | 3 ++- l2tpns.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index b1cd057..0740ab8 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,7 @@ -* Mon Jan 11 2010 Brendan O'Dea 2.2.x +* Thu Mar 25 2010 Brendan O'Dea 2.2.x - Apply MLPPP patch from Muhammad Tayseer Alquoatli. - Apply patch from Michael O to avoid sending multiple CDNs. +- Apply patch from Cyril Elkaim to fix an issue with MacOS. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. diff --git a/l2tpns.c b/l2tpns.c index a599a50..84acb68 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.174 2010-01-09 13:33:41 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.175 2010-03-25 05:24:23 bodea Exp $"; #include #include @@ -2686,9 +2686,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) break; case 2: // SCCRP tunnel[t].state = TUNNELOPEN; + tunnel[t].lastrec = time_now; break; case 3: // SCCN tunnel[t].state = TUNNELOPEN; + tunnel[t].lastrec = time_now; controlnull(t); // ack break; case 4: // StopCCN From 352f0ba607fd8ca5433affef1c4b13aa09f4bf6c Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 26 Mar 2010 00:39:24 +0000 Subject: [PATCH 477/482] Apply patch from Cyril Elkaim to fix an issue with MacOS. --- THANKS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/THANKS b/THANKS index 5483a89..ef0ca1f 100644 --- a/THANKS +++ b/THANKS @@ -12,7 +12,7 @@ Yuri Juergen Kammer Simon Talbot Jonathan McDowell -Bjørn Augestad +Bjørn Augestad Roberto Chostakovis Jordan Hrycaj Vladislav Bjelic @@ -30,3 +30,4 @@ Khaled Al Hamwi Graham Maltby Rhys Kidd Muhammad Tayseer Alquoatli +Cyril Elkaim From e934c096dda91697c194e8f6002c52ee252d1956 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 20 Jan 2011 12:48:40 +0000 Subject: [PATCH 478/482] Apply patch from Geoffrey D. Bennett to fix retry of control packets. --- Changes | 3 ++- l2tpns.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Changes b/Changes index 0740ab8..aad8483 100644 --- a/Changes +++ b/Changes @@ -1,7 +1,8 @@ -* Thu Mar 25 2010 Brendan O'Dea 2.2.x +* Thu Jan 20 2011 Brendan O'Dea 2.2.x - Apply MLPPP patch from Muhammad Tayseer Alquoatli. - Apply patch from Michael O to avoid sending multiple CDNs. - Apply patch from Cyril Elkaim to fix an issue with MacOS. +- Apply patch from Geoffrey D. Bennett to fix retry of control packets. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. diff --git a/l2tpns.c b/l2tpns.c index 84acb68..54c2f43 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.175 2010-03-25 05:24:23 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.176 2011-01-20 12:48:40 bodea Exp $"; #include #include @@ -3033,7 +3033,7 @@ static void regular_cleanups(double period) if (tunnel[t].retry <= TIME) { controlt *c = tunnel[t].controls; - uint8_t w = tunnel[t].window; + uint16_t w = tunnel[t].window; tunnel[t].try++; // another try if (tunnel[t].try > 5) tunnelkill(t, "Timeout on control message"); // game over From d5c6f842712c3772a9de56214e312af9174e4207 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Thu, 27 Jan 2011 17:36:01 +1100 Subject: [PATCH 479/482] Import to git from cvs. Remove keywords. --- arp.c | 2 -- autosnoop.c | 2 -- autothrottle.c | 2 -- bgp.c | 2 -- bgp.h | 2 -- cli.c | 67 +------------------------------------------------- cluster.c | 2 -- constants.c | 2 -- control.c | 2 -- garden.c | 2 -- icmp.c | 2 -- l2tpns.c | 2 -- l2tpns.h | 15 ----------- ll.c | 2 -- ppp.c | 2 -- radius.c | 2 -- sessionctl.c | 2 -- setrxspeed.c | 2 -- snoopctl.c | 2 -- stripdomain.c | 2 -- tbf.c | 2 -- throttlectl.c | 2 -- util.c | 2 -- 23 files changed, 1 insertion(+), 123 deletions(-) diff --git a/arp.c b/arp.c index 5fad15d..6d55756 100644 --- a/arp.c +++ b/arp.c @@ -1,7 +1,5 @@ // L2TPNS: arp -char const *cvs_id_arp = "$Id: arp.c,v 1.7 2005-07-31 10:04:09 bodea Exp $"; - #include #include #include diff --git a/autosnoop.c b/autosnoop.c index 570c147..ddc699f 100644 --- a/autosnoop.c +++ b/autosnoop.c @@ -4,8 +4,6 @@ /* set up intercept based on RADIUS reply */ -char const *cvs_id = "$Id: autosnoop.c,v 1.12 2005-10-11 09:04:53 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/autothrottle.c b/autothrottle.c index 7f044f4..73f9a94 100644 --- a/autothrottle.c +++ b/autothrottle.c @@ -13,8 +13,6 @@ * throttle=no */ -char const *cvs_id = "$Id: autothrottle.c,v 1.17 2006-05-18 14:40:31 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/bgp.c b/bgp.c index b5f2bf3..ea4ef45 100644 --- a/bgp.c +++ b/bgp.c @@ -10,8 +10,6 @@ * nor RFC2385 (which requires a kernel patch on 2.4 kernels). */ -char const *cvs_id_bgp = "$Id: bgp.c,v 1.12 2005-09-02 23:39:36 bodea Exp $"; - #include #include #include diff --git a/bgp.h b/bgp.h index 55b0ee3..8540375 100644 --- a/bgp.h +++ b/bgp.h @@ -200,6 +200,4 @@ int bgp_set_poll(void); int bgp_process(uint32_t events[]); char const *bgp_state_str(enum bgp_state state); -extern char const *cvs_id_bgp; - #endif /* __BGP_H__ */ diff --git a/cli.c b/cli.c index 12f4641..1149815 100644 --- a/cli.c +++ b/cli.c @@ -1,9 +1,6 @@ // L2TPNS Command Line Interface // vim: sw=8 ts=8 -char const *cvs_name = "$Name: $"; -char const *cvs_id_cli = "$Id: cli.c,v 1.76 2006-12-18 12:08:28 bodea Exp $"; - #include #include #include @@ -794,72 +791,10 @@ static int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, i static int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc) { - int tag = 0; - int file = 0; - int i = 0; - if (CLI_HELP_REQUESTED) - return cli_arg_help(cli, 1, - "tag", "Include CVS release tag", - "file", "Include file versions", - NULL); - - for (i = 0; i < argc; i++) - if (!strcmp(argv[i], "tag")) - tag++; - else if (!strcmp(argv[i], "file")) - file++; + return CLI_HELP_NO_ARGS; cli_print(cli, "L2TPNS %s", VERSION); - if (tag) - { - char const *p = strchr(cvs_name, ':'); - char const *e; - if (p) - { - p++; - while (isspace(*p)) - p++; - } - - if (!p || *p == '$') - p = "HEAD"; - - e = strpbrk(p, " \t$"); - cli_print(cli, "Tag: %.*s", (int) (e ? e - p + 1 : strlen(p)), p); - } - - if (file) - { - extern linked_list *loaded_plugins; - void *p; - - cli_print(cli, "Files:"); - cli_print(cli, " %s", cvs_id_arp); -#ifdef BGP - cli_print(cli, " %s", cvs_id_bgp); -#endif /* BGP */ - cli_print(cli, " %s", cvs_id_cli); - cli_print(cli, " %s", cvs_id_cluster); - cli_print(cli, " %s", cvs_id_constants); - cli_print(cli, " %s", cvs_id_control); - cli_print(cli, " %s", cvs_id_icmp); - cli_print(cli, " %s", cvs_id_l2tpns); - cli_print(cli, " %s", cvs_id_ll); - cli_print(cli, " %s", cvs_id_ppp); - cli_print(cli, " %s", cvs_id_radius); - cli_print(cli, " %s", cvs_id_tbf); - cli_print(cli, " %s", cvs_id_util); - - ll_reset(loaded_plugins); - while ((p = ll_next(loaded_plugins))) - { - char const **id = dlsym(p, "cvs_id"); - if (id) - cli_print(cli, " %s", *id); - } - } - return CLI_OK; } diff --git a/cluster.c b/cluster.c index 42db014..8f0fa2e 100644 --- a/cluster.c +++ b/cluster.c @@ -1,7 +1,5 @@ // L2TPNS Clustering Stuff -char const *cvs_id_cluster = "$Id: cluster.c,v 1.55 2009-12-08 14:49:28 bodea Exp $"; - #include #include #include diff --git a/constants.c b/constants.c index b62b5dd..2b9b35c 100644 --- a/constants.c +++ b/constants.c @@ -1,7 +1,5 @@ // L2TPNS: constants -char const *cvs_id_constants = "$Id: constants.c,v 1.8 2009-12-08 14:49:28 bodea Exp $"; - #include #include "constants.h" diff --git a/control.c b/control.c index 805a90b..3d38b22 100644 --- a/control.c +++ b/control.c @@ -1,7 +1,5 @@ // L2TPNS: control -char const *cvs_id_control = "$Id: control.c,v 1.5 2005-07-31 10:04:10 bodea Exp $"; - #include #include "l2tpns.h" #include "control.h" diff --git a/garden.c b/garden.c index 30439f1..994ecb5 100644 --- a/garden.c +++ b/garden.c @@ -9,8 +9,6 @@ /* walled garden */ -char const *cvs_id = "$Id: garden.c,v 1.25 2006-02-23 01:07:23 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/icmp.c b/icmp.c index 86c9bdf..6d90467 100644 --- a/icmp.c +++ b/icmp.c @@ -1,7 +1,5 @@ // L2TPNS: icmp -char const *cvs_id_icmp = "$Id: icmp.c,v 1.11 2006-04-27 09:53:49 bodea Exp $"; - #include #include #include diff --git a/l2tpns.c b/l2tpns.c index 54c2f43..3b61951 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,8 +4,6 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.176 2011-01-20 12:48:40 bodea Exp $"; - #include #include #include diff --git a/l2tpns.h b/l2tpns.h index c68d9c3..631f656 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -948,19 +948,4 @@ extern uint16_t MSS; #define CLI_HELP_REQUESTED (argc > 0 && argv[argc-1][strlen(argv[argc-1])-1] == '?') #define CLI_HELP_NO_ARGS (argc > 1 || argv[0][1]) ? CLI_OK : cli_arg_help(cli, 1, NULL) -// CVS identifiers (for "show version file") -extern char const *cvs_id_arp; -extern char const *cvs_id_cli; -extern char const *cvs_id_cluster; -extern char const *cvs_id_constants; -extern char const *cvs_id_control; -extern char const *cvs_id_icmp; -extern char const *cvs_id_l2tpns; -extern char const *cvs_id_ll; -extern char const *cvs_id_md5; -extern char const *cvs_id_ppp; -extern char const *cvs_id_radius; -extern char const *cvs_id_tbf; -extern char const *cvs_id_util; - #endif /* __L2TPNS_H__ */ diff --git a/ll.c b/ll.c index a65b2e4..1b2acad 100644 --- a/ll.c +++ b/ll.c @@ -1,7 +1,5 @@ // L2TPNS Linked List Stuff -char const *cvs_id_ll = "$Id: ll.c,v 1.6 2004-11-18 08:12:55 bodea Exp $"; - #include #include #include diff --git a/ppp.c b/ppp.c index 94cc5fa..bbd2244 100644 --- a/ppp.c +++ b/ppp.c @@ -1,7 +1,5 @@ // L2TPNS PPP Stuff -char const *cvs_id_ppp = "$Id: ppp.c,v 1.104 2009-12-08 14:49:28 bodea Exp $"; - #include #include #include diff --git a/radius.c b/radius.c index 9c28157..7ad2ec3 100644 --- a/radius.c +++ b/radius.c @@ -1,7 +1,5 @@ // L2TPNS Radius Stuff -char const *cvs_id_radius = "$Id: radius.c,v 1.56 2009-12-08 14:49:28 bodea Exp $"; - #include #include #include diff --git a/sessionctl.c b/sessionctl.c index cc7a13d..805b794 100644 --- a/sessionctl.c +++ b/sessionctl.c @@ -5,8 +5,6 @@ /* session control */ -char const *cvs_id = "$Id: sessionctl.c,v 1.5 2006-04-13 11:14:35 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/setrxspeed.c b/setrxspeed.c index 7a5acd9..52fcf4a 100644 --- a/setrxspeed.c +++ b/setrxspeed.c @@ -4,8 +4,6 @@ /* fudge up session rx speed if not set */ -char const *cvs_id = "$Id: setrxspeed.c,v 1.4 2005-10-11 09:04:53 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/snoopctl.c b/snoopctl.c index 943294f..d538a23 100644 --- a/snoopctl.c +++ b/snoopctl.c @@ -5,8 +5,6 @@ /* snoop control */ -char const *cvs_id = "$Id: snoopctl.c,v 1.7 2005-10-11 09:04:53 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/stripdomain.c b/stripdomain.c index c1dcea0..877617e 100644 --- a/stripdomain.c +++ b/stripdomain.c @@ -4,8 +4,6 @@ /* strip domain part of username before sending RADIUS requests */ -char const *cvs_id = "$Id: stripdomain.c,v 1.8 2005-10-11 09:04:53 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/tbf.c b/tbf.c index 9b4993d..c6273ce 100644 --- a/tbf.c +++ b/tbf.c @@ -1,7 +1,5 @@ // L2TPNS: token bucket filters -char const *cvs_id_tbf = "$Id: tbf.c,v 1.13 2005-07-31 10:04:10 bodea Exp $"; - #include #include "l2tpns.h" #include "util.h" diff --git a/throttlectl.c b/throttlectl.c index 46ce824..adcff27 100644 --- a/throttlectl.c +++ b/throttlectl.c @@ -5,8 +5,6 @@ /* throttle control */ -char const *cvs_id = "$Id: throttlectl.c,v 1.9 2005-10-11 09:04:53 bodea Exp $"; - int plugin_api_version = PLUGIN_API_VERSION; static struct pluginfuncs *f = 0; diff --git a/util.c b/util.c index 7237c08..0ab1d92 100644 --- a/util.c +++ b/util.c @@ -1,7 +1,5 @@ /* Misc util functions */ -char const *cvs_id_util = "$Id: util.c,v 1.14 2006-04-05 01:45:57 bodea Exp $"; - #include #include #include From 750e15fc6ca6649b7cdd7150f0702d6f87548867 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Fri, 28 Jan 2011 14:53:26 +1100 Subject: [PATCH 480/482] Bump version for release. --- l2tpns.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l2tpns.h b/l2tpns.h index 631f656..e034dca 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -14,7 +14,7 @@ #include #include -#define VERSION "2.2.0" +#define VERSION "2.2.1" // Limits #define MAXTUNNEL 500 // could be up to 65535 From d2848cebc450b4d1e66a4b7bf290167f2870b618 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Sun, 11 Sep 2011 21:54:45 +1000 Subject: [PATCH 481/482] Apply patch from Geoffrey D. Bennett to fix handle RADIUS Class attribute. Bump heartbeat version to handle Class entry in session (v6). Re-arrange session struct to remove padding. Update cluster code to handle v6 packets. Drop compatability for pre-v5. --- Changes | 6 ++- THANKS | 1 + cluster.c | 133 +++++++++++++++++++++++++++--------------------------- cluster.h | 2 +- l2tpns.h | 27 +++++------ radius.c | 16 +++++++ 6 files changed, 104 insertions(+), 81 deletions(-) diff --git a/Changes b/Changes index aad8483..2149c6d 100644 --- a/Changes +++ b/Changes @@ -1,8 +1,12 @@ -* Thu Jan 20 2011 Brendan O'Dea 2.2.x +* Sun Sep 11 2011 Brendan O'Dea 2.2.x - Apply MLPPP patch from Muhammad Tayseer Alquoatli. - Apply patch from Michael O to avoid sending multiple CDNs. - Apply patch from Cyril Elkaim to fix an issue with MacOS. - Apply patch from Geoffrey D. Bennett to fix retry of control packets. +- Apply patch from Geoffrey D. Bennett to fix handle RADIUS Class attribute. +- Bump heartbeat version to handle Class entry in session (v6). +- Re-arrange session struct to remove padding. +- Update cluster code to handle v6 packets. Drop compatability for pre-v5. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. diff --git a/THANKS b/THANKS index ef0ca1f..3ca2ab5 100644 --- a/THANKS +++ b/THANKS @@ -31,3 +31,4 @@ Graham Maltby Rhys Kidd Muhammad Tayseer Alquoatli Cyril Elkaim +Geoffry D. Bennett diff --git a/cluster.c b/cluster.c index 8f0fa2e..66846d6 100644 --- a/cluster.c +++ b/cluster.c @@ -426,12 +426,6 @@ void master_update_counts(void) if (!config->cluster_master_address) // If we don't have a master, skip it for a while. return; - // C_BYTES format changed in 2.1.0 (cluster version 5) - // during upgrade from previous versions, hang onto our counters - // for a bit until the new master comes up - if (config->cluster_last_hb_ver < 5) - return; - i = MAX_B_RECS * 5; // Examine max 3000 sessions; if (config->cluster_highest_sessionid > i) i = config->cluster_highest_sessionid; @@ -1311,54 +1305,62 @@ static int cluster_recv_tunnel(int more, uint8_t *p) } -// pre v5 heartbeat session structure +// pre v6 heartbeat session structure struct oldsession { sessionidt next; sessionidt far; tunnelidt tunnel; + uint8_t flags; + struct { + uint8_t phase; + uint8_t lcp:4; + uint8_t ipcp:4; + uint8_t ipv6cp:4; + uint8_t ccp:4; + } ppp; + char reserved_1[2]; in_addr_t ip; int ip_pool_index; - unsigned long unique_id; - uint16_t nr; - uint16_t ns; + uint32_t unique_id; + char reserved_2[4]; uint32_t magic; - uint32_t cin, cout; uint32_t pin, pout; - uint32_t total_cin; - uint32_t total_cout; - uint32_t id; + uint32_t cin, cout; + uint32_t cin_wrap, cout_wrap; + uint32_t cin_delta, cout_delta; uint16_t throttle_in; uint16_t throttle_out; + uint8_t filter_in; + uint8_t filter_out; + uint16_t mru; clockt opened; clockt die; + uint32_t session_timeout; + uint32_t idle_timeout; time_t last_packet; + time_t last_data; in_addr_t dns1, dns2; routet route[MAXROUTE]; - uint16_t radius; - uint16_t mru; uint16_t tbf_in; uint16_t tbf_out; - uint8_t l2tp_flags; - uint8_t reserved_old_snoop; - uint8_t walled_garden; - uint8_t flags1; - char random_vector[MAXTEL]; int random_vector_length; - char user[129]; + uint8_t random_vector[MAXTEL]; + char user[MAXUSER]; char called[MAXTEL]; char calling[MAXTEL]; uint32_t tx_connect_speed; uint32_t rx_connect_speed; - uint32_t flags; -#define SF_IPCP_ACKED 1 // Has this session seen an IPCP Ack? -#define SF_LCP_ACKED 2 // LCP negotiated -#define SF_CCP_ACKED 4 // CCP negotiated + clockt timeout; + uint32_t mrru; + uint8_t mssf; + epdist epdis; + bundleidt bundle; in_addr_t snoop_ip; uint16_t snoop_port; - uint16_t sid; - uint8_t filter_in; - uint8_t filter_out; - char reserved[18]; + uint8_t walled_garden; + uint8_t ipv6prefixlen; + struct in6_addr ipv6route; + char reserved_3[11]; }; static uint8_t *convert_session(struct oldsession *old) @@ -1371,17 +1373,24 @@ static uint8_t *convert_session(struct oldsession *old) new.next = old->next; new.far = old->far; new.tunnel = old->tunnel; - new.flags = old->l2tp_flags; + new.flags = old->flags; + new.ppp.phase = old->ppp.phase; + new.ppp.lcp = old->ppp.lcp; + new.ppp.ipcp = old->ppp.ipcp; + new.ppp.ipv6cp = old->ppp.ipv6cp; + new.ppp.ccp = old->ppp.ccp; new.ip = old->ip; new.ip_pool_index = old->ip_pool_index; new.unique_id = old->unique_id; new.magic = old->magic; new.pin = old->pin; new.pout = old->pout; - new.cin = old->total_cin; - new.cout = old->total_cout; - new.cin_delta = old->cin; - new.cout_delta = old->cout; + new.cin = old->cin; + new.cout = old->cout; + new.cin_wrap = old->cin_wrap; + new.cout_wrap = old->cout_wrap; + new.cin_delta = old->cin_delta; + new.cout_delta = old->cout_delta; new.throttle_in = old->throttle_in; new.throttle_out = old->throttle_out; new.filter_in = old->filter_in; @@ -1389,7 +1398,10 @@ static uint8_t *convert_session(struct oldsession *old) new.mru = old->mru; new.opened = old->opened; new.die = old->die; + new.session_timeout = old->session_timeout; + new.idle_timeout = old->idle_timeout; new.last_packet = old->last_packet; + new.last_data = old->last_data; new.dns1 = old->dns1; new.dns2 = old->dns2; new.tbf_in = old->tbf_in; @@ -1397,9 +1409,16 @@ static uint8_t *convert_session(struct oldsession *old) new.random_vector_length = old->random_vector_length; new.tx_connect_speed = old->tx_connect_speed; new.rx_connect_speed = old->rx_connect_speed; + new.timeout = old->timeout; + new.mrru = old->mrru; + new.mssf = old->mssf; + new.epdis = old->epdis; + new.bundle = old->bundle; new.snoop_ip = old->snoop_ip; new.snoop_port = old->snoop_port; new.walled_garden = old->walled_garden; + new.ipv6prefixlen = old->ipv6prefixlen; + new.ipv6route = old->ipv6route; memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector)); memcpy(new.user, old->user, sizeof(new.user)); @@ -1409,30 +1428,13 @@ static uint8_t *convert_session(struct oldsession *old) for (i = 0; i < MAXROUTE; i++) memcpy(&new.route[i], &old->route[i], sizeof(new.route[i])); - if (new.opened) - { - new.ppp.phase = Establish; - if (old->flags & (SF_IPCP_ACKED|SF_LCP_ACKED)) - { - new.ppp.phase = Network; - new.ppp.lcp = Opened; - new.ppp.ipcp = (old->flags & SF_IPCP_ACKED) ? Opened : Starting; - new.ppp.ccp = (old->flags & SF_CCP_ACKED) ? Opened : Stopped; - } - - // no PPPv6 in old session - new.ppp.ipv6cp = Stopped; - } - return (uint8_t *) &new; } // // Process a heartbeat.. // -// v3: added interval, timeout -// v4: added table_version -// v5: added ipv6, re-ordered session structure +// v6: added RADIUS class attribute, re-ordered session structure static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t *p, in_addr_t addr) { heartt *h; @@ -1440,12 +1442,12 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t int i, type; int hb_ver = more; -#if HB_VERSION != 5 +#if HB_VERSION != 6 # error "need to update cluster_process_heartbeat()" #endif - // we handle versions 3 through 5 - if (hb_ver < 3 || hb_ver > HB_VERSION) { + // we handle versions 5 through 6 + if (hb_ver < 5 || hb_ver > HB_VERSION) { LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver); return -1; // Ignore it?? } @@ -1474,18 +1476,17 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t return -1; // Skip it. } - if (hb_ver >= 4) { - if (h->table_version > config->cluster_table_version) { - LOG(0, 0, 0, "They've seen more state changes (%" PRIu64 " vs my %" PRIu64 ") so I'm gone!\n", + if (h->table_version > config->cluster_table_version) { + LOG(0, 0, 0, "They've seen more state changes (%" PRIu64 " vs my %" PRIu64 ") so I'm gone!\n", h->table_version, config->cluster_table_version); - kill(0, SIGTERM); - exit(1); - } - if (h->table_version < config->cluster_table_version) - return -1; + kill(0, SIGTERM); + exit(1); } + if (h->table_version < config->cluster_table_version) + return -1; + if (basetime > h->basetime) { LOG(0, 0, 0, "They're an older master than me so I'm gone!\n"); kill(0, SIGTERM); @@ -1607,7 +1608,7 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t s -= (p - orig_p); // session struct changed with v5 - if (hb_ver < 5) + if (hb_ver < 6) { if (size != sizeof(struct oldsession)) { LOG(0, 0, 0, "DANGER: Received a v%d CSESSION that didn't decompress correctly!\n", hb_ver); @@ -1628,7 +1629,7 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t break; } case C_SESSION: - if (hb_ver < 5) + if (hb_ver < 6) { if (s < sizeof(struct oldsession)) goto shortpacket; diff --git a/cluster.h b/cluster.h index 0849631..02f7965 100644 --- a/cluster.h +++ b/cluster.h @@ -24,7 +24,7 @@ #define C_BUNDLE 17 // Bundle structure. #define C_CBUNDLE 18 // Compressed bundle structure. -#define HB_VERSION 5 // Protocol version number.. +#define HB_VERSION 6 // Protocol version number.. #define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!) #define HB_HISTORY_SIZE 64 // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ) diff --git a/l2tpns.h b/l2tpns.h index e034dca..5b68df7 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -40,6 +40,7 @@ #define MAXTEL 96 // telephone number #define MAXUSER 128 // username #define MAXPASS 128 // password +#define MAXCLASS 128 // radius class attribute size #define MAXPLUGINS 20 // maximum number of plugins to load #define MAXRADSERVER 10 // max radius servers #define MAXROUTE 10 // max static routes per session @@ -272,11 +273,10 @@ typedef struct uint8_t ipv6cp:4; // IPV6CP state uint8_t ccp:4; // CCP state } ppp; - char reserved_1[2]; // unused: padding + uint16_t mru; // maximum receive unit in_addr_t ip; // IP of session set by RADIUS response (host byte order). int ip_pool_index; // index to IP pool uint32_t unique_id; // unique session id - char reserved_2[4]; // unused: was ns/nr uint32_t magic; // ppp magic number uint32_t pin, pout; // packet counts uint32_t cin, cout; // byte counts @@ -286,35 +286,36 @@ typedef struct uint16_t throttle_out; // downstream throttle rate uint8_t filter_in; // input filter index (to ip_filters[N-1]; 0 if none) uint8_t filter_out; // output filter index - uint16_t mru; // maximum receive unit + uint16_t snoop_port; // Interception destination port + in_addr_t snoop_ip; // Interception destination IP clockt opened; // when started clockt die; // being closed, when to finally free uint32_t session_timeout; // Maximum session time in seconds - uint32_t idle_timeout; // Maximum idle time in seconds + uint32_t idle_timeout; // Maximum idle time in seconds time_t last_packet; // Last packet from the user (used for idle timeouts) - time_t last_data; // Last data packet to/from the user (used for idle timeouts) + time_t last_data; // Last data packet to/from the user (used for idle timeouts) in_addr_t dns1, dns2; // DNS servers routet route[MAXROUTE]; // static routes uint16_t tbf_in; // filter bucket for throttling in from the user. uint16_t tbf_out; // filter bucket for throttling out to the user. int random_vector_length; uint8_t random_vector[MAXTEL]; - char user[MAXUSER]; // user (needed in seesion for radius stop messages) + char user[MAXUSER]; // user (needed in session for radius stop messages) char called[MAXTEL]; // called number char calling[MAXTEL]; // calling number uint32_t tx_connect_speed; uint32_t rx_connect_speed; clockt timeout; // Session timeout - uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit - uint8_t mssf; // Multilink Short Sequence Number Header Format - epdist epdis; // Multilink Endpoint Discriminator - bundleidt bundle; // Multilink Bundle Identifier - in_addr_t snoop_ip; // Interception destination IP - uint16_t snoop_port; // Interception destination port + uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit + epdist epdis; // Multilink Endpoint Discriminator + bundleidt bundle; // Multilink Bundle Identifier + uint8_t mssf; // Multilink Short Sequence Number Header Format uint8_t walled_garden; // is this session gardened? + uint8_t classlen; // class (needed for radius accounting messages) + char class[MAXCLASS]; uint8_t ipv6prefixlen; // IPv6 route prefix length struct in6_addr ipv6route; // Static IPv6 route - char reserved_3[11]; // Space to expand structure without changing HB_VERSION + char reserved[12]; // Space to expand structure without changing HB_VERSION } sessiont; diff --git a/radius.c b/radius.c index 7ad2ec3..f9f7263 100644 --- a/radius.c +++ b/radius.c @@ -363,6 +363,13 @@ void radiussend(uint16_t r, uint8_t state) } } + if (session[s].classlen) { + *p = 25; // class + p[1] = session[s].classlen + 2; + memcpy(p + 2, session[s].class, session[s].classlen); + p += p[1]; + } + { struct param_radius_account acct = { &tunnel[session[s].tunnel], &session[s], &p }; run_plugins(PLUGIN_RADIUS_ACCOUNT, &acct); @@ -818,6 +825,15 @@ void processrad(uint8_t *buf, int len, char socket_index) session[s].ipv6prefixlen = prefixlen; } } + else if (*p == 25) + { + // Class + if (p[1] < 3) continue; + session[s].classlen = p[1] - 2; + if (session[s].classlen > MAXCLASS) + session[s].classlen = MAXCLASS; + memcpy(session[s].class, p + 2, session[s].classlen); + } } } else if (r_code == AccessReject) From b939b00afdd2a5bbef84a415243c5b0fc7d50702 Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Mon, 12 Sep 2011 20:46:07 +1000 Subject: [PATCH 482/482] clean up some compiler errors --- Changes | 3 ++- cluster.c | 6 +++--- l2tpns.c | 13 +++++-------- ppp.c | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Changes b/Changes index 2149c6d..decb16e 100644 --- a/Changes +++ b/Changes @@ -3,10 +3,11 @@ - Apply patch from Michael O to avoid sending multiple CDNs. - Apply patch from Cyril Elkaim to fix an issue with MacOS. - Apply patch from Geoffrey D. Bennett to fix retry of control packets. -- Apply patch from Geoffrey D. Bennett to fix handle RADIUS Class attribute. +- Apply patch from Geoffrey D. Bennett to handle RADIUS Class attribute. - Bump heartbeat version to handle Class entry in session (v6). - Re-arrange session struct to remove padding. - Update cluster code to handle v6 packets. Drop compatability for pre-v5. +- Clean up some compiler errors. * Mon Dec 18 2006 Brendan O'Dea 2.2.0 - Only poll clifd if successfully bound. diff --git a/cluster.c b/cluster.c index 66846d6..c6f7eee 100644 --- a/cluster.c +++ b/cluster.c @@ -514,7 +514,7 @@ void cluster_check_slaves(void) // void cluster_check_master(void) { - int i, count, tcount, bcount, high_unique_id = 0; + int i, count, high_unique_id = 0; int last_free = 0; clockt t = TIME; static int probed = 0; @@ -600,7 +600,7 @@ void cluster_check_master(void) // Count the highest used tunnel number as well. // config->cluster_highest_tunnelid = 0; - for (i = 0, tcount = 0; i < MAXTUNNEL; ++i) { + for (i = 0; i < MAXTUNNEL; ++i) { if (tunnel[i].state == TUNNELUNDEF) tunnel[i].state = TUNNELFREE; @@ -613,7 +613,7 @@ void cluster_check_master(void) // Count the highest used bundle number as well. // config->cluster_highest_bundleid = 0; - for (i = 0, bcount = 0; i < MAXBUNDLE; ++i) { + for (i = 0; i < MAXBUNDLE; ++i) { if (bundle[i].state == BUNDLEUNDEF) bundle[i].state = BUNDLEFREE; diff --git a/l2tpns.c b/l2tpns.c index 3b61951..394a323 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -1276,7 +1276,7 @@ static void processipout(uint8_t *buf, int len) if(session[s].bundle != 0 && bundle[session[s].bundle].num_of_links > 1) { bid = session[s].bundle; - s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + s = bundle[bid].members[bundle[bid].current_ses = (bundle[bid].current_ses + 1) % bundle[bid].num_of_links]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s); @@ -1299,8 +1299,8 @@ static void processipout(uint8_t *buf, int len) update_session_out_stat(s, sp, fraglen); remain -= fraglen; while (remain > last_fraglen) - { - s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + { + s = b->members[b->current_ses = (b->current_ses + 1) % num_of_links]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); @@ -1311,7 +1311,7 @@ static void processipout(uint8_t *buf, int len) remain -= fraglen; } // send the last fragment - s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + s = b->members[b->current_ses = (b->current_ses + 1) % num_of_links]; t = session[s].tunnel; sp = &session[s]; LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); @@ -1418,7 +1418,7 @@ static void processipv6out(uint8_t * buf, int len) if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) { bundleidt bid = session[s].bundle; - s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + s = bundle[bid].members[bundle[bid].current_ses = (bundle[bid].current_ses + 1) % bundle[bid].num_of_links]; LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %u\n", s); } t = session[s].tunnel; @@ -1473,7 +1473,6 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) { sessiont *sp; tunnelidt t; - in_addr_t ip; uint8_t b[MAXETHER + 20]; @@ -1487,8 +1486,6 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) buf += 4; len -= 4; - ip = *(in_addr_t *)(buf + 16); - if (!session[s].ip) return; diff --git a/ppp.c b/ppp.c index bbd2244..ed12040 100644 --- a/ppp.c +++ b/ppp.c @@ -2369,7 +2369,7 @@ uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelid if ((b - start) + l > size) { - LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%d)\n", size, (b - start) + l); + LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%ld)\n", size, (b - start) + l); return NULL; }