// L2TP Network Server
// Adrian Kennard 2002
-// Copyright (c) 2003, 2004 Optus Internet Engineering
+// Copyright (c) 2003, 2004, 2005 Optus Internet Engineering
// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
// vim: sw=8 ts=8
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.74 2004-12-18 01:20:05 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.77 2005-01-10 07:45:03 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
int snoopfd = -1; // UDP file handle for sending out intercept data
int *radfds = NULL; // RADIUS requests file handles
int ifrfd = -1; // File descriptor for routing, etc
+static int rand_fd = -1; // Random data source
time_t basetime = 0; // base clock
char hostname[1000] = ""; // us.
static uint32_t sessionid = 0; // session id for radius accounting
CONFIG("debug", debug, INT),
CONFIG("log_file", log_filename, STRING),
CONFIG("pid_file", pid_file, STRING),
+ CONFIG("random_device", random_device, STRING),
CONFIG("l2tp_secret", l2tpsecret, STRING),
CONFIG("primary_dns", default_dns1, IPv4),
CONFIG("secondary_dns", default_dns2, IPv4),
CONFIG("secondary_radius_port", radiusport[1], SHORT),
CONFIG("radius_accounting", radius_accounting, BOOL),
CONFIG("radius_secret", radiussecret, STRING),
+ CONFIG("radius_authtypes", radius_authtypes_s, STRING),
CONFIG("bind_address", bind_address, IPv4),
CONFIG("peer_address", peer_address, IPv4),
CONFIG("send_garp", send_garp, BOOL),
CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
CONFIG("lock_pages", lock_pages, BOOL),
CONFIG("icmp_rate", icmp_rate, INT),
+ CONFIG("packet_limit", max_packets, INT),
CONFIG("cluster_address", cluster_address, IPv4),
CONFIG("cluster_interface", cluster_interface, STRING),
CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
void _log(int level, sessionidt s, tunnelidt t, const char *format, ...)
{
static char message[65536] = {0};
- static char message2[65536] = {0};
va_list ap;
#ifdef RINGBUFFER
if (config->debug < level) return;
va_start(ap, format);
+ vsnprintf(message, sizeof(message), format, ap);
+
if (log_stream)
- {
- vsnprintf(message2, 65535, format, ap);
- snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2);
- fprintf(log_stream, "%s", message);
- }
+ fprintf(log_stream, "%s %02d/%02d %s", time_now_string, t, s, message);
else if (syslog_log)
- {
- vsnprintf(message2, 65535, format, ap);
- snprintf(message, 65535, "%02d/%02d %s", t, s, message2);
- syslog(level + 2, message); // We don't need LOG_EMERG or LOG_ALERT
- }
+ syslog(level + 2, "%02d/%02d %s", t, s, message); // We don't need LOG_EMERG or LOG_ALERT
+
va_end(ap);
}
}
}
+// initialise the random generator
+static void initrandom(char *source)
+{
+ static char path[sizeof(config->random_device)] = "*undefined*";
+
+ // reinitialise only if we are forced to do so or if the config has changed
+ if (source && !strncmp(path, source, sizeof(path)))
+ return;
+
+ // close previous source, if any
+ if (rand_fd >= 0) close(rand_fd);
+
+ rand_fd = -1;
+
+ if (source)
+ {
+ // register changes
+ snprintf(path, sizeof(path), "%s", source);
+
+ if (*path == '/')
+ {
+ rand_fd = open(path, O_RDONLY|O_NONBLOCK);
+ if (rand_fd < 0)
+ LOG(0, 0, 0, "Error opening the random device %s: %s\n",
+ path, strerror(errno));
+ }
+ }
+
+ // no source: seed prng
+ {
+ unsigned seed = time_now ^ getpid();
+ LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
+ srand(seed);
+ }
+}
+
+// fill buffer with random data
+void random_data(uint8_t *buf, int len)
+{
+ int n = 0;
+
+ CSTAT(random_data);
+ if (rand_fd >= 0)
+ {
+ n = read(rand_fd, buf, len);
+ if (n >= len) return;
+ if (n < 0)
+ {
+ if (errno != EAGAIN)
+ {
+ LOG(0, 0, 0, "Error reading from random source: %s\n",
+ strerror(errno));
+
+ // fall back to rand()
+ initrandom(0);
+ }
+
+ n = 0;
+ }
+ }
+
+ // append missing data
+ while (n < len)
+ // not using the low order bits from the prng stream
+ buf[n++] = (rand() >> 4) & 0xff;
+}
// Add a route
//
sessionidt sessionbyip(in_addr_t ip)
{
int s = lookup_ipmap(ip);
- CSTAT(call_sessionbyip);
+ CSTAT(sessionbyip);
if (s > 0 && s < MAXSESSION && session[s].tunnel)
return (sessionidt) s;
sessionidt sessionbyuser(char *username)
{
int s;
- CSTAT(call_sessionbyuser);
+ CSTAT(sessionbyuser);
for (s = 1; s < MAXSESSION ; ++s)
{
{
struct sockaddr_in addr;
- CSTAT(call_tunnelsend);
+ CSTAT(tunnelsend);
if (!t)
{
tunnelidt t;
in_addr_t ip;
- char * data = buf; // Keep a copy of the originals.
+ char *data = buf; // Keep a copy of the originals.
int size = len;
uint8_t b[MAXETHER + 20];
- CSTAT(call_processipout);
+ CSTAT(processipout);
if (len < MIN_IP_SIZE)
{
LOG(1, 0, 0, "Short IP, %d bytes\n", len);
- STAT(tunnel_tx_errors);
+ STAT(tun_rx_errors);
return;
}
if (len >= MAXETHER)
{
LOG(1, 0, 0, "Oversize IP packet %d bytes\n", len);
- STAT(tunnel_tx_errors);
+ STAT(tun_rx_errors);
return;
}
t = session[s].tunnel;
sp = &session[s];
+ // DoS prevention: enforce a maximum number of packets per 0.1s for a session
+ if (config->max_packets > 0)
+ {
+ if (sess_count[s].last_packet_out == TIME)
+ {
+ int max = config->max_packets;
+
+ // All packets for throttled sessions are handled by the
+ // master, so further limit by using the throttle rate.
+ // A bit of a kludge, since throttle rate is in kbps,
+ // but should still be generous given our average DSL
+ // packet size is 200 bytes: a limit of 28kbps equates
+ // to around 180 packets per second.
+ if (!config->cluster_iam_master && sp->throttle_out && sp->throttle_out < max)
+ max = sp->throttle_out;
+
+ if (++sess_count[s].packets_out > max)
+ {
+ sess_count[s].packets_dropped++;
+ return;
+ }
+ }
+ else
+ {
+ if (sess_count[s].packets_dropped)
+ {
+ INC_STAT(tun_rx_dropped, sess_count[s].packets_dropped);
+ LOG(2, s, t, "Possible DoS attack on %s (%s); dropped %u packets.\n",
+ fmtaddr(ip, 0), sp->user, sess_count[s].packets_dropped);
+ }
+
+ sess_count[s].last_packet_out = TIME;
+ sess_count[s].packets_out = 1;
+ sess_count[s].packets_dropped = 0;
+ }
+ }
+
// run access-list if any
if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
return;
int walled_garden = session[s].walled_garden;
- CSTAT(call_sessionshutdown);
+ CSTAT(sessionshutdown);
if (!session[s].tunnel)
{
}
else
{
- int n;
- for (n = 0; n < 15; n++)
- radius[r].auth[n] = rand();
+ random_data(radius[r].auth, sizeof(radius[r].auth));
}
}
uint16_t r = session[s].radius;
uint8_t *q;
- CSTAT(call_sendipcp);
+ CSTAT(sendipcp);
if (!r)
r = radiusnew(s);
static void sessionkill(sessionidt s, char *reason)
{
- CSTAT(call_sessionkill);
+ CSTAT(sessionkill);
session[s].die = now();
sessionshutdown(s, reason); // close radius/routes, etc.
sessionidt s;
controlt *c;
- CSTAT(call_tunnelkill);
+ CSTAT(tunnelkill);
tunnel[t].state = TUNNELDIE;
{
sessionidt s;
- CSTAT(call_tunnelshutdown);
+ CSTAT(tunnelshutdown);
if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE)
{
uint8_t *p = buf + 2;
- CSTAT(call_processudp);
+ CSTAT(processudp);
udp_rx += len;
udp_rx_pkt++;
b += 2;
n -= 6;
- LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avpnames[mtype], n);
+ LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avp_name(mtype), n);
switch (mtype)
{
case 0: // message type
message = ntohs(*(uint16_t *) b);
- LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_types[message]);
+ LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_type(message));
mandatorymessage = flags;
break;
case 1: // result code
const char* resdesc = "(unknown)";
if (message == 4)
{ /* StopCCN */
- if (rescode <= MAX_STOPCCN_RESULT_CODE)
- resdesc = stopccn_result_codes[rescode];
+ resdesc = stopccn_result_code(rescode);
}
else if (message == 14)
{ /* CDN */
- if (rescode <= MAX_CDN_RESULT_CODE)
- resdesc = cdn_result_codes[rescode];
+ resdesc = cdn_result_code(rescode);
}
LOG(4, s, t, " Result Code %d: %s\n", rescode, resdesc);
if (n >= 4)
{
uint16_t errcode = ntohs(*(uint16_t *)(b + 2));
- const char* errdesc = "(unknown)";
- if (errcode <= MAX_ERROR_CODE)
- errdesc = error_codes[errcode];
- LOG(4, s, t, " Error Code %d: %s\n", errcode, errdesc);
+ LOG(4, s, t, " Error Code %d: %s\n", errcode, error_code(errcode));
}
if (n > 4)
LOG(4, s, t, " Error String: %.*s\n", n-4, b+4);
}
case 29: // Proxy Authentication Type
{
- uint16_t authtype = ntohs(*(uint16_t *)b);
- LOG(4, s, t, " Proxy Auth Type %d (%s)\n", authtype, authtypes[authtype]);
- requestchap = (authtype == 2);
+ uint16_t atype = ntohs(*(uint16_t *)b);
+ LOG(4, s, t, " Proxy Auth Type %d (%s)\n", atype, auth_type(atype));
+ requestchap = (atype == 2);
break;
}
case 30: // Proxy Authentication Name
LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far);
control16(c, 14, s, 1); // assigned session
controladd(c, t, s); // send the reply
- {
- // Generate a random challenge
- int n;
- for (n = 0; n < 15; n++)
- radius[r].auth[n] = rand();
- }
+
+ // Generate a random challenge
+ random_data(radius[r].auth, sizeof(radius[r].auth));
strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1);
strncpy(session[s].called, called, sizeof(session[s].called) - 1);
strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1);
STAT(tun_rx_packets);
INC_STAT(tun_rx_bytes, len);
- CSTAT(call_processtun);
+ CSTAT(processtun);
eth_rx_pkt++;
eth_rx += len;
config->debug = optdebug;
config->num_tbfs = MAXTBFS;
config->rl_rate = 28; // 28kbps
+ strcpy(config->random_device, RANDOMDEVICE);
if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL)))
{
exit(1);
}
-if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
-{
- LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
- exit(1);
-}
-memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
+ if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
+ {
+ LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
#ifdef RINGBUFFER
if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
char reuse = 0;
- CSTAT(call_assign_ip_address);
+ CSTAT(assign_ip_address);
for (i = 1; i < ip_pool_size; i++)
{
int i = session[s].ip_pool_index;
- CSTAT(call_free_ip_address);
+ CSTAT(free_ip_address);
if (!session[s].ip)
return; // what the?
FILE *f = NULL;
- CSTAT(call_dump_acct_info);
+ CSTAT(dump_acct_info);
if (shut_acct_n)
{
init_tbf(config->num_tbfs);
LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
- LOG(0, 0, 0, "Copyright (c) 2003, 2004 Optus Internet Engineering\n");
+ LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005 Optus Internet Engineering\n");
LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
{
struct rlimit rlim;
static void update_config()
{
int i;
+ char *p;
static int timeout = 0;
static int interval = 0;
fclose(log_stream);
log_stream = NULL;
}
+
if (*config->log_filename)
{
if (strstr(config->log_filename, "syslog:") == config->log_filename)
setbuf(log_stream, NULL);
}
-
// Update radius
config->numradiusservers = 0;
for (i = 0; i < MAXRADSERVER; i++)
config->num_radfds = 2 << RADIUS_SHIFT;
+ // parse radius_authtypes_s
+ config->radius_authtypes = config->radius_authprefer = 0;
+ p = config->radius_authtypes_s;
+ while (*p)
+ {
+ char *s = strpbrk(p, " \t,");
+ int type = 0;
+
+ if (s)
+ {
+ *s++ = 0;
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ if (!*s)
+ s = 0;
+ }
+
+ if (!strncasecmp("chap", p, strlen(p)))
+ type = AUTHCHAP;
+ else if (!strncasecmp("pap", p, strlen(p)))
+ type = AUTHPAP;
+ else
+ LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"", p);
+
+ config->radius_authtypes |= type;
+ if (!config->radius_authprefer)
+ config->radius_authprefer = type;
+ }
+
+ if (!config->radius_authtypes)
+ {
+ LOG(0, 0, 0, "Defaulting to PAP authentication\n");
+ config->radius_authtypes = config->radius_authprefer = AUTHPAP;
+ }
+
+ // normalise radius_authtypes_s
+ if (config->radius_authprefer == AUTHPAP)
+ {
+ strcpy(config->radius_authtypes_s, "pap");
+ if (config->radius_authtypes & AUTHCHAP)
+ strcat(config->radius_authtypes_s, ", chap");
+ }
+ else
+ {
+ strcpy(config->radius_authtypes_s, "chap");
+ if (config->radius_authtypes & AUTHPAP)
+ strcat(config->radius_authtypes_s, ", pap");
+ }
+
+ // re-initialise the random number source
+ initrandom(config->random_device);
+
// Update plugins
for (i = 0; i < MAXPLUGINS; i++)
{
remove_plugin(config->old_plugins[i]);
}
}
+
memcpy(config->old_plugins, config->plugins, sizeof(config->plugins));
if (!config->cleanup_interval) config->cleanup_interval = 10;
if (!config->multi_read_count) config->multi_read_count = 10;
sessionidt i;
int r;
- CSTAT(call_sessionsetup);
+ CSTAT(sessionsetup);
LOG(3, s, t, "Doing session setup for session\n");