From: David Parrish Date: Tue, 16 Dec 2003 07:07:39 +0000 (+0000) Subject: Initial revision X-Git-Tag: 2.2.1-2fdn3.1~19^2^2~1^2~481 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/c5e4c2cfc092317c1e3ceb0f654f880f6a8573f4?ds=sidebyside Initial revision --- c5e4c2cfc092317c1e3ceb0f654f880f6a8573f4 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);