// 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.108 2005/06/04 15:42:35 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.113 2005/06/28 14:48:20 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
int udpfd = -1; // UDP file handle
int controlfd = -1; // Control signal handle
int clifd = -1; // Socket listening for CLI connections.
+int daefd = -1; // Socket listening for DAE connections.
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
CONFIG("radius_interim", radius_interim, INT),
CONFIG("radius_secret", radiussecret, STRING),
CONFIG("radius_authtypes", radius_authtypes_s, STRING),
+ CONFIG("radius_dae_port", radius_dae_port, SHORT),
CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL),
CONFIG("bind_address", bind_address, IPv4),
CONFIG("peer_address", peer_address, IPv4),
"plugin_kill_session",
"plugin_control",
"plugin_radius_response",
+ "plugin_radius_reset",
"plugin_become_master",
"plugin_new_session_master",
};
sessionlocalt *sess_local = NULL; // Array of local per-session counters.
radiust *radius = NULL; // Array of radius structures.
ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses.
-ip_filtert *ip_filters = NULL; // Array of named filters.
+ip_filtert *ip_filters = NULL; // Array of named filters.
static controlt *controlfree = 0;
struct Tstats *_statistics = NULL;
#ifdef RINGBUFFER
static void dump_acct_info(int all);
static void sighup_handler(int sig);
static void sigalrm_handler(int sig);
-static void sigterm_handler(int sig);
-static void sigquit_handler(int sig);
+static void shutdown_handler(int sig);
static void sigchild_handler(int sig);
static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response);
static void update_config(void);
// on slaves, alow BGP to withdraw cleanly before exiting
#define QUIT_DELAY 5
+// quit actions (master)
+#define QUIT_FAILOVER 1 // SIGTERM: exit when all control messages have been acked (for cluster failover)
+#define QUIT_SHUTDOWN 2 // SIGQUIT: shutdown sessions/tunnels, reject new connections
+
// return internal time (10ths since process startup), set f if given
static clockt now(double *f)
{
return;
// close previous source, if any
- if (rand_fd >= 0) close(rand_fd);
+ if (rand_fd >= 0)
+ close(rand_fd);
rand_fd = -1;
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
strerror(errno));
// fall back to rand()
- initrandom(0);
+ initrandom(NULL);
}
n = 0;
}
}
-// set up UDP port
+// set up UDP ports
static void initudp(void)
{
int on = 1;
LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno));
exit(1);
}
- snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// Control
memset(&addr, 0, sizeof(addr));
LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno));
exit(1);
}
+
+ // Dynamic Authorization Extensions to RADIUS
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(config->radius_dae_port);
+ daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (bind(daefd, (void *) &addr, sizeof(addr)) < 0)
+ {
+ LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // Intercept
+ snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
//
}
// add/remove filters from session (-1 = no change)
-static void filter_session(sessionidt s, int filter_in, int filter_out)
+void filter_session(sessionidt s, int filter_in, int filter_out)
{
if (!session[s].opened)
return; // No-one home.
// close session
for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
if (session[s].tunnel == t)
- sessionshutdown(s, reason, 3, 0);
+ sessionshutdown(s, reason, 0, 0);
tunnel[t].state = TUNNELDIE;
tunnel[t].die = TIME + 700; // Clean up in 70 seconds
continue;
}
- LOG(4, s, t, "Hidden AVP\n");
-
// Unhide the AVP
unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length);
n = orig_len;
}
- LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avp_name(mtype), n);
+ LOG(4, s, t, " AVP %d (%s) len %d%s%s\n", mtype, avp_name(mtype), n,
+ flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : "");
+
switch (mtype)
{
case 0: // message type
case 36: // Random Vector
LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n");
memset(session[s].random_vector, 0, sizeof(session[s].random_vector));
+ if (n > sizeof(session[s].random_vector))
+ n = sizeof(session[s].random_vector);
memcpy(session[s].random_vector, b, n);
session[s].random_vector_length = n;
break;
switch (message)
{
case 1: // SCCRQ - Start Control Connection Request
+ tunnel[t].state = TUNNELOPENING;
+ if (main_quit != QUIT_SHUTDOWN)
{
controlt *c = controlnew(2); // sending SCCRP
control16(c, 2, version, 1); // protocol version
control16(c, 9, t, 1); // assigned tunnel
controladd(c, t, 0); // send the resply
}
- tunnel[t].state = TUNNELOPENING;
+ else
+ {
+ tunnelshutdown(t, "Shutting down", 6, 0, 0);
+ }
break;
case 2: // SCCRP
tunnel[t].state = TUNNELOPEN;
// TBA
break;
case 10: // ICRQ
- if (sessionfree)
+ if (sessionfree && main_quit != QUIT_SHUTDOWN)
{
uint16_t r;
{
controlt *c = controlnew(14); // CDN
- control16(c, 1, 4, 1); // temporary lack of resources
- controladd(c, session[s].tunnel, asession); // send the message
+ if (main_quit == QUIT_SHUTDOWN)
+ control16(c, 1, 2, 7); // try another
+ else
+ control16(c, 1, 4, 0); // temporary lack of resources
+
+ controladd(c, t, asession); // send the message
}
return;
case 11: // ICRP
continue;
}
- if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
+ if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED)
+ && !(sess_local[s].radius && radius[sess_local[s].radius].state == RADIUSIPCP))
{
// IPCP has not completed yet. Resend
LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
&& !sess_local[s].radius // RADIUS already in progress
&& time_now - sess_local[s].last_interim >= config->radius_interim)
{
- if (!(r = radiusnew(s)))
+ int rad = radiusnew(s);
+ if (!rad)
{
LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n");
STAT(radius_overflow);
LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n",
session[s].user, session[s].unique_id);
- radiussend(r, RADIUSINTERIM);
+ radiussend(rad, RADIUSINTERIM);
sess_local[s].last_interim = time_now;
s_actions++;
}
return 0;
}
+ if (main_quit == QUIT_SHUTDOWN)
+ {
+ static int dropped = 0;
+ if (!dropped)
+ {
+ int i;
+
+ LOG(1, 0, 0, "Dropping sessions and tunnels\n");
+ for (i = 1; i < MAXTUNNEL; i++)
+ if (tunnel[i].ip || tunnel[i].state)
+ tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
+
+ dropped = 1;
+ }
+ }
+
if (start_busy_wait == 0)
start_busy_wait = TIME;
# include "fake_epoll.h"
#endif
-// the base set of fds polled: control, cli, udp, tun, cluster
-#define BASE_FDS 5
+// the base set of fds polled: cli, cluster, tun, udp, control, dae
+#define BASE_FDS 6
// additional polled fds
#ifdef BGP
exit(1);
}
- LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
- udpfd, tunfd, cluster_sockfd, controlfd);
+ LOG(4, 0, 0, "Beginning of main loop. clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d\n",
+ clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd);
/* setup our fds to poll for input */
{
e.events = EPOLLIN;
i = 0;
- d[i].type = FD_TYPE_CONTROL;
- e.data.ptr = &d[i++];
- epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
-
d[i].type = FD_TYPE_CLI;
e.data.ptr = &d[i++];
epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e);
- d[i].type = FD_TYPE_UDP;
+ d[i].type = FD_TYPE_CLUSTER;
e.data.ptr = &d[i++];
- epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
+ epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e);
d[i].type = FD_TYPE_TUN;
e.data.ptr = &d[i++];
epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e);
- d[i].type = FD_TYPE_CLUSTER;
+ d[i].type = FD_TYPE_UDP;
e.data.ptr = &d[i++];
- epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e);
+ epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
+
+ d[i].type = FD_TYPE_CONTROL;
+ e.data.ptr = &d[i++];
+ epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
+
+ d[i].type = FD_TYPE_DAE;
+ e.data.ptr = &d[i++];
+ epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e);
}
#ifdef BGP
continue;
LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
- main_quit++;
- break;
+ break; // exit
}
if (n)
struct event_data *d = events[i].data.ptr;
switch (d->type)
{
- case FD_TYPE_CONTROL: // nsctl commands
- alen = sizeof(addr);
- processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
- n--;
- break;
-
case FD_TYPE_CLI: // CLI connections
{
int cli;
}
// these are handled below, with multiple interleaved reads
- case FD_TYPE_UDP: udp_ready++; break;
- case FD_TYPE_TUN: tun_ready++; break;
case FD_TYPE_CLUSTER: cluster_ready++; break;
+ case FD_TYPE_TUN: tun_ready++; break;
+ case FD_TYPE_UDP: udp_ready++; break;
+
+ case FD_TYPE_CONTROL: // nsctl commands
+ alen = sizeof(addr);
+ processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+ n--;
+ break;
+
+ case FD_TYPE_DAE: // DAE requests
+ alen = sizeof(addr);
+ processdae(buf, recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+ n--;
+ break;
case FD_TYPE_RADIUS: // RADIUS response
s = recv(radfds[d->index], buf, sizeof(buf), 0);
//
// Important!!! We MUST not process any packets past this point!
- LOG(1, 0, 0, "Clean shutdown complete\n");
+ LOG(1, 0, 0, "Shutdown complete\n");
}
static void stripdomain(char *host)
initrad();
initippool();
- signal(SIGHUP, sighup_handler);
- signal(SIGTERM, sigterm_handler);
- signal(SIGINT, sigterm_handler);
- signal(SIGQUIT, sigquit_handler);
+ // seed prng
+ {
+ unsigned seed = time_now ^ getpid();
+ LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
+ srand(seed);
+ }
+
+ signal(SIGHUP, sighup_handler);
signal(SIGCHLD, sigchild_handler);
+ signal(SIGTERM, shutdown_handler);
+ signal(SIGINT, shutdown_handler);
+ signal(SIGQUIT, shutdown_handler);
// Prevent us from getting paged out
if (config->lock_pages)
}
-static void sigterm_handler(int sig)
+static void shutdown_handler(int sig)
{
- LOG(1, 0, 0, "Shutting down cleanly\n");
- main_quit++;
-}
-
-static void sigquit_handler(int sig)
-{
- int i;
-
- LOG(1, 0, 0, "Shutting down without saving sessions\n");
-
- if (config->cluster_iam_master)
- {
- for (i = 1; i < MAXSESSION; i++)
- {
- if (session[i].opened)
- sessionkill(i, "L2TPNS Closing");
- }
- for (i = 1; i < MAXTUNNEL; i++)
- {
- if (tunnel[i].ip || tunnel[i].state)
- tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0);
- }
- }
-
- main_quit++;
+ LOG(1, 0, 0, "Shutting down\n");
+ main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
}
static void sigchild_handler(int sig)
strcat(config->radius_authtypes_s, ", pap");
}
+ if (!config->radius_dae_port)
+ config->radius_dae_port = DAEPORT;
+
// re-initialise the random number source
initrandom(config->random_device);
if (!session[s].ip)
{
LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n");
- sessionshutdown(s, "No IP addresses available.", 2, 7);
+ sessionshutdown(s, "No IP addresses available.", 2, 7); // try another
return 0;
}
LOG(3, s, t, " No IP allocated. Assigned %s from pool\n",
uint8_t digest[16];
uint8_t *last;
size_t d = 0;
+ uint16_t m = htons(type);
// Compute initial pad
MD5Init(&ctx);
- MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1);
- MD5Update(&ctx, (uint8_t) type & 0xff, 1);
+ MD5Update(&ctx, (unsigned char *) &m, 2);
MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
MD5Update(&ctx, vector, vec_len);
MD5Final(digest, &ctx);
}
}
+int find_filter(char const *name, size_t len)
+{
+ int free = -1;
+ int i;
+
+ for (i = 0; i < MAXFILTER; i++)
+ {
+ if (!*ip_filters[i].name)
+ {
+ if (free < 0)
+ free = i;
+
+ continue;
+ }
+
+ if (strlen(ip_filters[i].name) != len)
+ continue;
+
+ if (!strncmp(ip_filters[i].name, name, len))
+ return i;
+ }
+
+ return free;
+}
+
static int ip_filter_port(ip_filter_portt *p, uint16_t port)
{
switch (p->op)