Initial revision
This commit is contained in:
commit
3aa4eda8b1
42 changed files with 16139 additions and 0 deletions
5
.cvsignore
Normal file
5
.cvsignore
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
*.o
|
||||
l2tpns
|
||||
state.dump
|
||||
*.swp
|
||||
cluster_master
|
||||
1
.cvsnotify
Normal file
1
.cvsnotify
Normal file
|
|
@ -0,0 +1 @@
|
|||
david.parrish@optusnet.com.au
|
||||
72
INSTALL
Normal file
72
INSTALL
Normal 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
70
Makefile.in
Normal 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
56
arp.c
Normal 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
90
bounce.c
Normal 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
944
cli.c
Normal 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
85
cluster.c
Normal 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
18
cluster.h
Normal 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
480
cluster_master.c
Normal 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
249
cluster_slave.c
Normal 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
213
config.h.in
Normal 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
|
||||
|
||||
48
configure.in
Normal file
48
configure.in
Normal 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
137
conform.cfg
Normal 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
136
constants.c
Normal 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
27
constants.h
Normal 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
70
control.c
Normal 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
18
control.h
Normal 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
2
etc/.cvsignore
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ip_pool.txt
|
||||
l2tpns.cfg
|
||||
2
etc/ip_pool.default
Normal file
2
etc/ip_pool.default
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
10.10.10.0/24
|
||||
10.13.10.0/24
|
||||
43
etc/l2tpns.cfg.default
Normal file
43
etc/l2tpns.cfg.default
Normal 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
8
etc/l2tpns.logrotate
Normal 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
1
etc/users.default
Normal file
|
|
@ -0,0 +1 @@
|
|||
# List username:password combinations here for cli users
|
||||
172
garden.c
Normal file
172
garden.c
Normal 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
238
install-sh
Executable 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
|
||||
393
l2tpns.h
Normal file
393
l2tpns.h
Normal 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
141
ll.c
Normal 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
28
ll.h
Normal 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
39
machines.cfg
Normal 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
349
md5.c
Normal 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
72
md5.h
Normal 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
138
nsctl.c
Normal 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
116
plugin.h
Normal 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
725
ppp.c
Normal 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
614
radius.c
Normal 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
158
rl.c
Normal 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
1
stamp-h
Normal file
|
|
@ -0,0 +1 @@
|
|||
timestamp
|
||||
73
throttle.c
Normal file
73
throttle.c
Normal 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
16
util.c
Normal 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
1
util.h
Normal file
|
|
@ -0,0 +1 @@
|
|||
char *inet_toa(unsigned long addr);
|
||||
Loading…
Add table
Add a link
Reference in a new issue