Initial revision

This commit is contained in:
David Parrish 2003-12-16 07:07:39 +00:00
commit c5e4c2cfc0
42 changed files with 16139 additions and 0 deletions

5
.cvsignore Normal file
View file

@ -0,0 +1,5 @@
*.o
l2tpns
state.dump
*.swp
cluster_master

1
.cvsnotify Normal file
View file

@ -0,0 +1 @@
david.parrish@optusnet.com.au

72
INSTALL Normal file
View file

@ -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

70
Makefile.in Normal file
View file

@ -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)

56
arp.c Normal file
View file

@ -0,0 +1,56 @@
#include <string.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <linux/if_packet.h>
#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));
}

90
bounce.c Normal file
View file

@ -0,0 +1,90 @@
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <getopt.h>
#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);
}

944
cli.c Normal file
View file

@ -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 <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <libcli.h>
#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

85
cluster.c Normal file
View file

@ -0,0 +1,85 @@
// L2TPNS Clustering Stuff
// $Id: cluster.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $ #include <stdio.h> #include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#include <malloc.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#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;
}

18
cluster.h Normal file
View file

@ -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);

480
cluster_master.c Normal file
View file

@ -0,0 +1,480 @@
// L2TPNS Cluster Master
// $Id: cluster_master.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <malloc.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#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 <address>\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;
}

249
cluster_slave.c Normal file
View file

@ -0,0 +1,249 @@
// L2TPNS Cluster Master
// $Id: cluster_slave.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <malloc.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
#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;
}

213
config.h.in Normal file
View file

@ -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 <arpa/inet.h> 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 <fcntl.h> 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 <inttypes.h> 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 <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the <memory.h> 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 <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet/in.h> 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 <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> 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 <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
#undef HAVE_SYS_WAIT_H
/* Define to 1 if you have the <unistd.h> 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 <vfork.h> 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 <sys/time.h> and <time.h>. */
#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 <sys/types.h> does not define. */
#undef pid_t
/* Define to `unsigned' if <sys/types.h> 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

7102
configure vendored Executable file

File diff suppressed because it is too large Load diff

48
configure.in Normal file
View file

@ -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

137
conform.cfg Normal file
View file

@ -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;

136
constants.c Normal file
View file

@ -0,0 +1,136 @@
#include "constants.h"
#include <memory.h>
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",
};

27
constants.h Normal file
View file

@ -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];

70
control.c Normal file
View file

@ -0,0 +1,70 @@
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#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");
}

18
control.h Normal file
View file

@ -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

2
etc/.cvsignore Normal file
View file

@ -0,0 +1,2 @@
ip_pool.txt
l2tpns.cfg

2
etc/ip_pool.default Normal file
View file

@ -0,0 +1,2 @@
10.10.10.0/24
10.13.10.0/24

43
etc/l2tpns.cfg.default Normal file
View file

@ -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

8
etc/l2tpns.logrotate Normal file
View file

@ -0,0 +1,8 @@
/var/log/l2tpns {
daily
missingok
rotate 14
postrotate
/usr/bin/killall -HUP l2tpns
endscript
}

1
etc/users.default Normal file
View file

@ -0,0 +1 @@
# List username:password combinations here for cli users

172
garden.c Normal file
View file

@ -0,0 +1,172 @@
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#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]);
}
}

238
install-sh Executable file
View file

@ -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

2988
l2tpns.c Normal file

File diff suppressed because it is too large Load diff

393
l2tpns.h Normal file
View file

@ -0,0 +1,393 @@
// L2TPNS Global Stuff
// $Id: l2tpns.h,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <netinet/in.h>
#include <stdio.h>
#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);

141
ll.c Normal file
View file

@ -0,0 +1,141 @@
// L2TPNS Linked List Stuff
// $Id: ll.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#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;
}

28
ll.h Normal file
View file

@ -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

39
machines.cfg Normal file
View file

@ -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 } ],
],
};

349
md5.c Normal file
View file

@ -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;
}

72
md5.h Normal file
View file

@ -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 *));

138
nsctl.c Normal file
View file

@ -0,0 +1,138 @@
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#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 <host> <command> [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;
}

116
plugin.h Normal file
View file

@ -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

725
ppp.c Normal file
View file

@ -0,0 +1,725 @@
// L2TPNS PPP Stuff
// $Id: ppp.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#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);
}

614
radius.c Normal file
View file

@ -0,0 +1,614 @@
// L2TPNS Radius Stuff
// $Id: radius.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <netinet/in.h>
#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;
}
}

158
rl.c Normal file
View file

@ -0,0 +1,158 @@
// L2TPNS Rate Limiting Stuff
// $Id: rl.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>
#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));
}

1
stamp-h Normal file
View file

@ -0,0 +1 @@
timestamp

73
throttle.c Normal file
View file

@ -0,0 +1,73 @@
// L2TPNS Throttle Stuff
// $Id: throttle.c,v 1.1 2003-12-16 07:07:39 fred_nerk Exp $
#include <stdio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#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;
}

16
util.c Normal file
View file

@ -0,0 +1,16 @@
/* Misc util functions */
#include "l2tpns.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
char *inet_toa(unsigned long addr)
{
struct in_addr in;
memcpy(&in, &addr, sizeof(unsigned long));
return inet_ntoa(in);
}

1
util.h Normal file
View file

@ -0,0 +1 @@
char *inet_toa(unsigned long addr);