- Use multiple radius sockets to allow more concurrent authentication requests

- Add gcc __attribute__ to logging functions
- Fix warnings shown by __attribute__
- Make sure regular cleanup happens regularly under high load
- Add variable cleanup_interval for changing cleanup interval
- Add support for reading more than one packet per fd in each processing loop
- This is configurable with the multi_read_count variable
- Remove segv handler so core dumps can happen
- Use nonblocking sockets
- Increase tun queue length
- Fix minimum length of IP packets
- Remove per-packet plugin hooks (they are slow)
- Don't drop session if no free RADIUS
- Don't expire more than 1000 sessions per cleanup interval
- Remove -a and -c command-line options. They don't work anyway
- Don't require file: in log_filename
This commit is contained in:
fred_nerk 2004-05-24 04:20:28 +00:00
parent fa7f80895b
commit 8512f6d924

300
l2tpns.c
View file

@ -49,7 +49,7 @@ int tapfd = -1; // tap interface file handle
int udpfd = -1; // UDP file handle int udpfd = -1; // UDP file handle
int controlfd = -1; // Control signal handle int controlfd = -1; // Control signal handle
int snoopfd = -1; // UDP file handle for sending out intercept data int snoopfd = -1; // UDP file handle for sending out intercept data
int radfd = -1; // RADIUS requests file handle int *radfds = NULL; // RADIUS requests file handles
int ifrfd = -1; // File descriptor for routing, etc int ifrfd = -1; // File descriptor for routing, etc
time_t basetime = 0; // base clock time_t basetime = 0; // base clock
char hostname[1000] = ""; // us. char hostname[1000] = ""; // us.
@ -62,7 +62,6 @@ FILE *log_stream = NULL;
struct sockaddr_in snoop_addr = {0}; struct sockaddr_in snoop_addr = {0};
extern int cluster_sockfd; extern int cluster_sockfd;
unsigned long last_sid = 0; unsigned long last_sid = 0;
int handle_interface = 0;
int clifd = 0; int clifd = 0;
sessionidt *cli_session_kill = NULL; sessionidt *cli_session_kill = NULL;
tunnelidt *cli_tunnel_kill = NULL; tunnelidt *cli_tunnel_kill = NULL;
@ -99,6 +98,8 @@ struct config_descriptt config_values[] = {
CONFIG("accounting_dir", accounting_dir, STRING), CONFIG("accounting_dir", accounting_dir, STRING),
CONFIG("setuid", target_uid, INT), CONFIG("setuid", target_uid, INT),
CONFIG("dump_speed", dump_speed, BOOL), CONFIG("dump_speed", dump_speed, BOOL),
CONFIG("cleanup_interval", cleanup_interval, INT),
CONFIG("multi_read_count", multi_read_count, INT),
{ NULL, 0, 0, 0 }, { NULL, 0, 0, 0 },
}; };
@ -132,7 +133,6 @@ void sighup_handler(int);
void sigterm_handler(int); void sigterm_handler(int);
void sigquit_handler(int); void sigquit_handler(int);
void sigchild_handler(int); void sigchild_handler(int);
void sigsegv_handler(int);
void read_config_file(); void read_config_file();
void read_state(); void read_state();
void dump_state(); void dump_state();
@ -285,6 +285,10 @@ void inittap(void)
log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno)); log(0, 0, 0, 0, "Can't open %s: %s\n", TAPDEVICE, strerror(errno));
exit(-1); exit(-1);
} }
{
int flags = fcntl(tapfd, F_GETFL, 0);
fcntl(tapfd, F_SETFL, flags | O_NONBLOCK);
}
if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0) if (ioctl(tapfd, TUNSETIFF, (void *) &ifr) < 0)
{ {
log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno)); log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno));
@ -295,7 +299,7 @@ void inittap(void)
ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
sin.sin_addr.s_addr = handle_interface ? config->bind_address : 0x01010101; // 1.1.1.1 sin.sin_addr.s_addr = config->bind_address ? config->bind_address : 0x01010101; // 1.1.1.1
memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0) if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0)
@ -303,6 +307,13 @@ void inittap(void)
perror("set tap addr"); perror("set tap addr");
exit( -1); exit( -1);
} }
/* Bump up the qlen to deal with bursts from the network */
ifr.ifr_qlen = 1000;
if (ioctl(ifrfd, SIOCSIFTXQLEN, (void *) &ifr) < 0)
{
perror("set tap qlen");
exit( -1);
}
ifr.ifr_flags = IFF_UP; ifr.ifr_flags = IFF_UP;
if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0)
{ {
@ -336,6 +347,10 @@ void initudp(void)
addr.sin_addr.s_addr = config->bind_address; addr.sin_addr.s_addr = config->bind_address;
udpfd = socket(AF_INET, SOCK_DGRAM, UDP); udpfd = socket(AF_INET, SOCK_DGRAM, UDP);
setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
{
int flags = fcntl(udpfd, F_GETFL, 0);
fcntl(udpfd, F_SETFL, flags | O_NONBLOCK);
}
if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0)
{ {
perror("udp bind"); perror("udp bind");
@ -361,6 +376,7 @@ sessionidt sessionbyip(ipt ip)
{ {
unsigned char *a = (unsigned char *)&ip; unsigned char *a = (unsigned char *)&ip;
char **d = (char **) ip_hash; char **d = (char **) ip_hash;
sessionidt s;
#ifdef STAT_CALLS #ifdef STAT_CALLS
STAT(call_sessionbyip); STAT(call_sessionbyip);
@ -370,7 +386,10 @@ sessionidt sessionbyip(ipt ip)
if (!(d = (char **) d[(size_t) *a++])) return 0; if (!(d = (char **) d[(size_t) *a++])) return 0;
if (!(d = (char **) d[(size_t) *a++])) return 0; if (!(d = (char **) d[(size_t) *a++])) return 0;
return (ipt) d[(size_t) *a]; s = (ipt) d[(size_t) *a];
if (s && session[s].tunnel)
return s;
return 0;
} }
void cache_sessionid(ipt ip, sessionidt s) void cache_sessionid(ipt ip, sessionidt s)
@ -432,21 +451,23 @@ void send_garp(ipt ip)
s = socket(PF_INET, SOCK_DGRAM, 0); s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) if (s < 0)
{ {
perror("socket"); log(0, 0, 0, 0, "Error creating socket for GARP: %s\n", strerror(errno));
exit(-1); return;
} }
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1); strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1);
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
{ {
perror("get eth0 hwaddr"); log(0, 0, 0, 0, "Error getting eth0 hardware address for GARP: %s\n", strerror(errno));
exit(-1); close(s);
return;
} }
memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char)); memcpy(mac, &ifr.ifr_hwaddr.sa_data, 6*sizeof(char));
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) if (ioctl(s, SIOCGIFINDEX, &ifr) < 0)
{ {
perror("get eth0 ifindex"); log(0, 0, 0, 0, "Error getting eth0 interface index for GARP: %s\n", strerror(errno));
exit(-1); close(s);
return;
} }
close(s); close(s);
sendarp(ifr.ifr_ifindex, mac, ip); sendarp(ifr.ifr_ifindex, mac, ip);
@ -537,7 +558,7 @@ void processarp(u8 * buf, int len)
s = sessionbyip(htonl(ip)); s = sessionbyip(htonl(ip));
if (s) if (s)
{ {
log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255, session[s].tunnel, s); log(3, ip, s, session[s].tunnel, "ARP reply for %u.%u.%u.%u\n", ip >> 24, ip >> 16 & 255, ip >> 8 & 255, ip & 255);
memcpy(buf + 4, buf + 10, 6); // set destination as source memcpy(buf + 4, buf + 10, 6); // set destination as source
*(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address *(u16 *) (buf + 10) = htons(tapmac[0]); // set soucre address
*(u16 *) (buf + 12) = htons(tapmac[1]); *(u16 *) (buf + 12) = htons(tapmac[1]);
@ -565,9 +586,20 @@ void tunnelsend(u8 * buf, u16 l, tunnelidt t)
#ifdef STAT_CALLS #ifdef STAT_CALLS
STAT(call_tunnelsend); STAT(call_tunnelsend);
#endif #endif
if (!t)
{
static int backtrace_count = 0;
log(0, 0, 0, t, "tunnelsend called with 0 as tunnel id\n");
STAT(tunnel_tx_errors);
log_backtrace(backtrace_count, 5)
return;
}
if (!tunnel[t].ip) if (!tunnel[t].ip)
{ {
static int backtrace_count = 0;
log(1, 0, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); log(1, 0, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n");
log_backtrace(backtrace_count, 5)
STAT(tunnel_tx_errors); STAT(tunnel_tx_errors);
return; return;
} }
@ -615,7 +647,7 @@ void processipout(u8 * buf, int len)
#ifdef STAT_CALLS #ifdef STAT_CALLS
STAT(call_processipout); STAT(call_processipout);
#endif #endif
if (len < 38) if (len < MIN_IP_SIZE)
{ {
log(1, 0, 0, 0, "Short IP, %d bytes\n", len); log(1, 0, 0, 0, "Short IP, %d bytes\n", len);
STAT(tunnel_tx_errors); STAT(tunnel_tx_errors);
@ -648,12 +680,6 @@ void processipout(u8 * buf, int len)
log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); log(5, session[s].ip, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
// Plugin hook
{
struct param_packet_rx packet = { &tunnel[t], &session[s], buf, len };
run_plugins(PLUGIN_PACKET_RX, &packet);
}
// Add on L2TP header // Add on L2TP header
{ {
u8 *p = makeppp(b, buf, len, t, s, PPPIP); u8 *p = makeppp(b, buf, len, t, s, PPPIP);
@ -776,7 +802,10 @@ void sessionshutdown(sessionidt s, char *reason)
STAT(call_sessionshutdown); STAT(call_sessionshutdown);
#endif #endif
if (!session[s].tunnel) if (!session[s].tunnel)
{
log(3, session[s].ip, s, session[s].tunnel, "Called sessionshutdown on a session with no tunnel.\n");
return; // not a live session return; // not a live session
}
if (!session[s].die) if (!session[s].die)
log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); log(2, 0, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason);
@ -790,7 +819,7 @@ void sessionshutdown(sessionidt s, char *reason)
// RADIUS Stop message // RADIUS Stop message
if (session[s].opened && !walled_garden && !dead) { if (session[s].opened && !walled_garden && !dead) {
u8 r = session[s].radius; u16 r = session[s].radius;
if (!r) if (!r)
{ {
if (!(r = radiusnew(s))) if (!(r = radiusnew(s)))
@ -811,7 +840,7 @@ void sessionshutdown(sessionidt s, char *reason)
if (session[s].ip) if (session[s].ip)
{ // IP allocated, clear and unroute { // IP allocated, clear and unroute
u8 r; u16 r;
if (session[s].route[0].ip) if (session[s].route[0].ip)
{ {
routeset(session[s].ip, 0, 0, 0); routeset(session[s].ip, 0, 0, 0);
@ -839,7 +868,7 @@ void sessionshutdown(sessionidt s, char *reason)
void sendipcp(tunnelidt t, sessionidt s) void sendipcp(tunnelidt t, sessionidt s)
{ {
u8 buf[MAXCONTROL]; u8 buf[MAXCONTROL];
u8 r = session[s].radius; u16 r = session[s].radius;
u8 *q; u8 *q;
#ifdef STAT_CALLS #ifdef STAT_CALLS
STAT(call_sendipcp); STAT(call_sendipcp);
@ -859,7 +888,7 @@ void sendipcp(tunnelidt t, sessionidt s)
} }
q = makeppp(buf, 0, 0, t, s, PPPIPCP); q = makeppp(buf, 0, 0, t, s, PPPIPCP);
*q = ConfigReq; *q = ConfigReq;
q[1] = r; // ID, dont care, we only send one type of request q[1] = r << RADIUS_SHIFT; // ID, dont care, we only send one type of request
*(u16 *) (q + 2) = htons(10); *(u16 *) (q + 2) = htons(10);
q[4] = 3; q[4] = 3;
q[5] = 6; q[5] = 6;
@ -876,10 +905,10 @@ void sessionkill(sessionidt s, char *reason)
sessionshutdown(s, reason); // close radius/routes, etc. sessionshutdown(s, reason); // close radius/routes, etc.
if (session[s].radius) if (session[s].radius)
radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed
log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason);
memset(&session[s], 0, sizeof(session[s])); memset(&session[s], 0, sizeof(session[s]));
session[s].next = sessionfree; session[s].next = sessionfree;
sessionfree = s; sessionfree = s;
log(2, 0, s, session[s].tunnel, "Kill session %d: %s\n", s, reason);
cluster_send_session(s); cluster_send_session(s);
} }
@ -1143,7 +1172,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
fatal = flags; fatal = flags;
continue; continue;
} }
log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n"); log(4, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP\n");
} }
if (*b & 0x3C) if (*b & 0x3C)
{ {
@ -1294,7 +1323,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
memcpy(tmp, b, (n >= 30) ? 30 : n); memcpy(tmp, b, (n >= 30) ? 30 : n);
session[s].tx_connect_speed = atol(tmp); session[s].tx_connect_speed = atol(tmp);
} }
log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%d>\n", log(4, ntohl(addr->sin_addr.s_addr), s, t, " TX connect speed <%lu>\n",
session[s].tx_connect_speed); session[s].tx_connect_speed);
break; break;
case 38: // rx connect speed case 38: // rx connect speed
@ -1309,7 +1338,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
memcpy(tmp, b, (n >= 30) ? 30 : n); memcpy(tmp, b, (n >= 30) ? 30 : n);
session[s].rx_connect_speed = atol(tmp); session[s].rx_connect_speed = atol(tmp);
} }
log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%d>\n", log(4, ntohl(addr->sin_addr.s_addr), s, t, " RX connect speed <%lu>\n",
session[s].rx_connect_speed); session[s].rx_connect_speed);
break; break;
case 25: // Physical Channel ID case 25: // Physical Channel ID
@ -1337,7 +1366,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
case 31: // Proxy Authentication Challenge case 31: // Proxy Authentication Challenge
{ {
memcpy(radius[session[s].radius].auth, b, 16); memcpy(radius[session[s].radius].auth, b, 16);
log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge (%X)\n", radius[session[s].radius].auth); log(4, ntohl(addr->sin_addr.s_addr), s, t, " Proxy Auth Challenge\n");
break; break;
} }
case 32: // Proxy Authentication ID case 32: // Proxy Authentication ID
@ -1457,7 +1486,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
} }
else else
{ {
u8 r; u16 r;
controlt *c; controlt *c;
s = sessionfree; s = sessionfree;
@ -1468,7 +1497,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
if (!(r = radiusnew(s))) if (!(r = radiusnew(s)))
{ {
log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n"); log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n");
sessionkill(s, "no free RADIUS sesions");
return; return;
} }
@ -1617,42 +1645,45 @@ void processtap(u8 * buf, int len)
} }
if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP if (*(u16 *) (buf + 2) == htons(PKTARP)) // ARP
processarp(buf, len); processarp(buf, len);
else if (*(u16 *) (buf + 2) == htons(PKTIP)) // ARP else if (*(u16 *) (buf + 2) == htons(PKTIP)) // IP
processipout(buf, len); processipout(buf, len);
else
{
log(1, 0, 0, 0, "Unexpected tap packet %04X, %d bytes\n", ntohs(*(u16 *) (buf + 2)), len);
}
} }
// main loop - gets packets on tap or udp and processes them // main loop - gets packets on tap or udp and processes them
void mainloop(void) void mainloop(void)
{ {
fd_set cr; fd_set cr;
int cn; int cn, i;
u8 buf[65536]; u8 buf[65536];
struct timeval to; struct timeval to;
clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc clockt slow = now(); // occasional functions like session/tunnel expiry, tunnel hello, etc
clockt next_acct = slow + ACCT_TIME; clockt next_acct = slow + ACCT_TIME;
clockt next_cluster_ping = slow + 50; clockt next_cluster_ping = slow + 50;
clockt next_clean = time_now + config->cleanup_interval;
to.tv_sec = 1; to.tv_sec = 1;
to.tv_usec = 0; to.tv_usec = 0;
log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, radfd=%d, cluster_sockfd=%d, controlfd=%d\n", log(4, 0, 0, 0, "Beginning of main loop. udpfd=%d, tapfd=%d, cluster_sockfd=%d, controlfd=%d\n",
udpfd, tapfd, radfd, cluster_sockfd, controlfd); udpfd, tapfd, cluster_sockfd, controlfd);
FD_ZERO(&cr); FD_ZERO(&cr);
FD_SET(udpfd, &cr); FD_SET(udpfd, &cr);
FD_SET(tapfd, &cr); FD_SET(tapfd, &cr);
FD_SET(radfd, &cr);
FD_SET(controlfd, &cr); FD_SET(controlfd, &cr);
FD_SET(clifd, &cr); FD_SET(clifd, &cr);
if (cluster_sockfd) FD_SET(cluster_sockfd, &cr); if (cluster_sockfd) FD_SET(cluster_sockfd, &cr);
cn = udpfd; cn = udpfd;
if (cn < radfd) cn = radfd;
if (cn < tapfd) cn = tapfd; if (cn < tapfd) cn = tapfd;
if (cn < controlfd) cn = controlfd; if (cn < controlfd) cn = controlfd;
if (cn < clifd) cn = clifd; if (cn < clifd) cn = clifd;
if (cn < cluster_sockfd) cn = cluster_sockfd; if (cn < cluster_sockfd) cn = cluster_sockfd;
for (i = 0; i < config->num_radfds; i++)
{
if (!radfds[i]) continue;
FD_SET(radfds[i], &cr);
if (radfds[i] > cn)
cn = radfds[i];
}
while (!main_quit) while (!main_quit)
{ {
@ -1680,16 +1711,35 @@ void mainloop(void)
struct sockaddr_in addr; struct sockaddr_in addr;
int alen = sizeof(addr); int alen = sizeof(addr);
if (FD_ISSET(udpfd, &r)) if (FD_ISSET(udpfd, &r))
processudp(buf, recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen), &addr); {
else if (FD_ISSET(tapfd, &r)) int c, n;
processtap(buf, read(tapfd, buf, sizeof(buf))); for (c = 0; c < config->multi_read_count; c++)
else if (FD_ISSET(radfd, &r)) {
processrad(buf, recv(radfd, buf, sizeof(buf), 0)); if ((n = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
else if (FD_ISSET(cluster_sockfd, &r)) processudp(buf, n, &addr);
else
break;
}
}
if (FD_ISSET(tapfd, &r))
{
int c, n;
for (c = 0; c < config->multi_read_count; c++)
{
if ((n = read(tapfd, buf, sizeof(buf))) > 0)
processtap(buf, n);
else
break;
}
}
for (i = 0; i < config->num_radfds; i++)
if (FD_ISSET(radfds[i], &r))
processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
if (FD_ISSET(cluster_sockfd, &r))
processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)); processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen));
else if (FD_ISSET(controlfd, &r)) if (FD_ISSET(controlfd, &r))
processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr); processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr);
else if (FD_ISSET(clifd, &r)) if (FD_ISSET(clifd, &r))
{ {
struct sockaddr_in addr; struct sockaddr_in addr;
int sockfd; int sockfd;
@ -1706,20 +1756,22 @@ void mainloop(void)
close(sockfd); close(sockfd);
} }
} }
else
{
log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n);
continue;
} }
}
else if (n == 0) { // handle timeouts /* Handle timeouts. Make sure that this gets run anyway, even if there was
* something to read, else under load this will never actually run....
*/
if (n == 0 || next_clean <= time_now) {
clockt when = now(); clockt when = now();
clockt best = when + 100; // default timeout clockt best = when + 100; // default timeout
sessionidt s; sessionidt s;
tunnelidt t; tunnelidt t;
u8 r; int count;
u16 r;
log(3, 0, 0, 0, "Begin regular cleanup\n");
for (r = 1; r < MAXRADIUS; r++) for (r = 1; r < MAXRADIUS; r++)
{
if (radius[r].state && radius[r].retry) if (radius[r].state && radius[r].retry)
{ {
if (radius[r].retry <= when) if (radius[r].retry <= when)
@ -1727,6 +1779,9 @@ void mainloop(void)
if (radius[r].retry && radius[r].retry < best) if (radius[r].retry && radius[r].retry < best)
best = radius[r].retry; best = radius[r].retry;
} }
else if (radius[r].state && !radius[r].retry)
radius[r].retry = backoff(radius[r].try+1);
}
for (t = 1; t < MAXTUNNEL; t++) for (t = 1; t < MAXTUNNEL; t++)
{ {
// check for expired tunnels // check for expired tunnels
@ -1770,7 +1825,7 @@ void mainloop(void)
if (cli_session_kill[0]) if (cli_session_kill[0])
{ {
int i; int i;
for (i = 1; i < MAXSESSION && cli_session_kill[i]; i++) for (i = 0; i < MAXSESSION && cli_session_kill[i]; i++)
{ {
log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n"); log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n");
sessionshutdown(cli_session_kill[i], "Requested by administrator"); sessionshutdown(cli_session_kill[i], "Requested by administrator");
@ -1789,12 +1844,14 @@ void mainloop(void)
} }
} }
count = 0;
for (s = 1; s < MAXSESSION; s++) for (s = 1; s < MAXSESSION; s++)
{ {
// check for expired sessions // check for expired sessions
if (session[s].die && session[s].die <= when) if (session[s].die && session[s].die <= when)
{ {
sessionkill(s, "Expired"); sessionkill(s, "Expired");
if (++count >= 1000) break;
continue; continue;
} }
@ -1803,6 +1860,7 @@ void mainloop(void)
{ {
sessionkill(s, "No response to LCP ECHO requests"); sessionkill(s, "No response to LCP ECHO requests");
STAT(session_timeout); STAT(session_timeout);
if (++count >= 1000) break;
continue; continue;
} }
@ -1818,8 +1876,9 @@ void mainloop(void)
*(u32 *)(q + 4) = 0; // Magic Number (not supported) *(u32 *)(q + 4) = 0; // Magic Number (not supported)
log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
time_now - session[s].last_packet); (int)(time_now - session[s].last_packet));
tunnelsend(b, 24, session[s].tunnel); // send it tunnelsend(b, 24, session[s].tunnel); // send it
if (++count >= 1000) break;
continue; continue;
} }
} }
@ -1837,11 +1896,12 @@ void mainloop(void)
cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname)); cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname));
} }
if (best <= when) if (best < when + config->cleanup_interval)
best = when + 1; // should not really happen best = when + config->cleanup_interval; // Throttle to at most once per 10 seconds
to.tv_sec = (best - when) / 10; next_clean = time_now + config->cleanup_interval;
to.tv_usec = 100000 * ((best - when) % 10); to.tv_sec = config->cleanup_interval;
log(5, 0, 0, 0, "Next time check in %d.%d seconds\n", (best - when) / 10, ((best - when) % 10)); to.tv_usec = 0;
log(3, 0, 0, 0, "End regular cleanup, next in %d seconds\n", config->cleanup_interval);
} }
} }
} }
@ -1995,6 +2055,7 @@ int assign_ip_address(sessionidt s)
session[s].ip_pool_index = best; session[s].ip_pool_index = best;
ip_address_pool[best].assigned = 1; ip_address_pool[best].assigned = 1;
ip_address_pool[best].last = time_now; ip_address_pool[best].last = time_now;
ip_address_pool[best].session = s;
if (session[s].walled_garden) if (session[s].walled_garden)
/* Don't track addresses of users in walled garden (note: this /* Don't track addresses of users in walled garden (note: this
means that their address isn't "sticky" even if they get means that their address isn't "sticky" even if they get
@ -2005,7 +2066,7 @@ int assign_ip_address(sessionidt s)
STAT(ip_allocated); STAT(ip_allocated);
log(4, ip_address_pool[best].address, s, session[s].tunnel, log(4, ip_address_pool[best].address, s, session[s].tunnel,
"assign_ip_address(): %s ip address %lu from pool\n", reuse ? "Reusing" : "Allocating", best); "assign_ip_address(): %s ip address %d from pool\n", reuse ? "Reusing" : "Allocating", best);
return 1; return 1;
} }
@ -2025,6 +2086,7 @@ void free_ip_address(sessionidt s)
uncache_sessionid(session[s].ip); uncache_sessionid(session[s].ip);
session[s].ip = 0; session[s].ip = 0;
ip_address_pool[i].assigned = 0; ip_address_pool[i].assigned = 0;
ip_address_pool[i].session = 0;
ip_address_pool[i].last = time_now; ip_address_pool[i].last = time_now;
} }
@ -2077,7 +2139,7 @@ void initippool()
*p++ = 0; *p++ = 0;
if (!*p || !(numbits = atoi(p))) if (!*p || !(numbits = atoi(p)))
{ {
log(0, 0, 0, 0, "Invalid pool range %s/\n", buf, p); log(0, 0, 0, 0, "Invalid pool range %s\n", buf);
continue; continue;
} }
start = end = ntohl(inet_addr(pool)); start = end = ntohl(inet_addr(pool));
@ -2092,7 +2154,7 @@ void initippool()
} }
// Add a static route for this pool // Add a static route for this pool
log(5, 0, 0, 0, "Adding route for address pool %s/%d\n", inet_toa(htonl(start)), 32+mask); log(5, 0, 0, 0, "Adding route for address pool %s/%lu\n", inet_toa(htonl(start)), 32 + mask);
memset(&r, 0, sizeof(r)); memset(&r, 0, sizeof(r));
r.rt_dev = config->tapdevice; r.rt_dev = config->tapdevice;
r.rt_dst.sa_family = AF_INET; r.rt_dst.sa_family = AF_INET;
@ -2102,7 +2164,8 @@ void initippool()
r.rt_flags = (RTF_UP | RTF_STATIC); r.rt_flags = (RTF_UP | RTF_STATIC);
if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0) if (ioctl(ifrfd, SIOCADDRT, (void *) &r) < 0)
{ {
log(0, 0, 0, 0, "Error adding ip address pool route %s/%d: %s\n", inet_toa(start), mask, strerror(errno)); log(0, 0, 0, 0, "Error adding ip address pool route %s/%lu: %s\n",
inet_toa(start), mask, strerror(errno));
} }
} }
else else
@ -2145,7 +2208,7 @@ void dump_acct_info()
for (i = 0; i < MAXSESSION; i++) for (i = 0; i < MAXSESSION; i++)
{ {
if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden) if (!session[i].opened || !session[i].ip || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden)
continue; continue;
if (!f) if (!f)
{ {
@ -2188,20 +2251,9 @@ int main(int argc, char *argv[])
_program_name = strdup(argv[0]); _program_name = strdup(argv[0]);
{
struct rlimit rlim;
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
// Remove the maximum core size
setrlimit(RLIMIT_CORE, &rlim);
// Make core dumps go to /tmp
chdir("/tmp");
}
time(&basetime); // start clock time(&basetime); // start clock
initdata();
// scan args // scan args
while ((o = getopt(argc, argv, "vc:h:a:")) >= 0) while ((o = getopt(argc, argv, "vc:h:a:")) >= 0)
{ {
switch (o) switch (o)
@ -2209,21 +2261,9 @@ int main(int argc, char *argv[])
case 'v': case 'v':
config->debug++; config->debug++;
break; break;
case 'c':
strncpy(config->config_file, optarg, sizeof(config->config_file) - 1);
break;
case 'h': case 'h':
strncpy(hostname, optarg, 999); strncpy(hostname, optarg, 999);
break; break;
case 'a':
myip = inet_addr(optarg);
if (myip == INADDR_NONE) {
log(0, 0, 0, 0, "Invalid ip %s\n", optarg);
exit(-1);
}
config->bind_address = myip;
handle_interface = 1;
break;
case '?': case '?':
default: default:
printf("Args are:\n\t-c <file>\tConfig file\n\t-h <hostname>\tForce hostname\n\t-a <address>\tUse specific address\n\t-v\t\tDebug\n"); printf("Args are:\n\t-c <file>\tConfig file\n\t-h <hostname>\tForce hostname\n\t-a <address>\tUse specific address\n\t-v\t\tDebug\n");
@ -2235,13 +2275,25 @@ int main(int argc, char *argv[])
// Start the timer routine off // Start the timer routine off
time(&time_now); time(&time_now);
strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now)); strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now));
signal(SIGALRM, sigalrm_handler);
siginterrupt(SIGALRM, 0);
initiptables(); initiptables();
initplugins(); initplugins();
initdata();
init_cli(); init_cli();
read_config_file(); read_config_file();
log(1, 0, 0, 0, "L2TPNS Version 1.1.0 - http://l2tpns.sourceforge.net/\n"); log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.6 2004/05/24 04:20:28 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
log(1, 0, 0, 0, "Licensed under the GPL\n"); {
struct rlimit rlim;
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
// Remove the maximum core size
if (setrlimit(RLIMIT_CORE, &rlim) < 0)
log(0, 0, 0, 0, "Can't set ulimit: %s\n", strerror(errno));
// Make core dumps go to /tmp
chdir("/tmp");
}
/* Start up the cluster first, so that we don't have two machines with /* Start up the cluster first, so that we don't have two machines with
* the same IP at once. * the same IP at once.
@ -2257,9 +2309,8 @@ int main(int argc, char *argv[])
initrad(); initrad();
initippool(); initippool();
init_rl(); init_rl();
if (handle_interface) { if (config->bind_address)
send_garp(config->bind_address); send_garp(config->bind_address);
}
// If NOSTATEFILE exists, we will ignore any updates from the cluster master for this execution // If NOSTATEFILE exists, we will ignore any updates from the cluster master for this execution
if (!unlink(NOSTATEFILE)) if (!unlink(NOSTATEFILE))
@ -2267,13 +2318,11 @@ int main(int argc, char *argv[])
read_state(); read_state();
signal(SIGALRM, sigalrm_handler);
signal(SIGHUP, sighup_handler); signal(SIGHUP, sighup_handler);
signal(SIGTERM, sigterm_handler); signal(SIGTERM, sigterm_handler);
signal(SIGINT, sigterm_handler); signal(SIGINT, sigterm_handler);
signal(SIGQUIT, sigquit_handler); signal(SIGQUIT, sigquit_handler);
signal(SIGCHLD, sigchild_handler); signal(SIGCHLD, sigchild_handler);
signal(SIGSEGV, sigsegv_handler);
alarm(1); alarm(1);
@ -2369,14 +2418,6 @@ void sigchild_handler(int signal)
; ;
} }
void sigsegv_handler(int signal)
{
log(0, 0, 0, 0, "----------------------------------------------\n");
log(0, 0, 0, 0, "- SEGFAULT! -\n");
log(0, 0, 0, 0, "----------------------------------------------\n");
_exit(0);
}
void read_state() void read_state()
{ {
struct stat sb; struct stat sb;
@ -2490,7 +2531,6 @@ void read_state()
for (i = 0; i < MAXSESSION; i++) for (i = 0; i < MAXSESSION; i++)
{ {
session[i].tbf = 0; session[i].tbf = 0;
session[i].throttle = 0;
if (session[i].opened) if (session[i].opened)
{ {
log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user); log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user);
@ -2597,9 +2637,18 @@ void update_config()
} }
if (*config->log_filename) if (*config->log_filename)
{ {
if (strstr(config->log_filename, "file:") == config->log_filename) if (strstr(config->log_filename, "syslog:") == config->log_filename)
{ {
if ((log_stream = fopen((char *)(config->log_filename + 5), "a"))) char *p = config->log_filename + 7;
if (*p)
{
openlog("l2tpns", LOG_PID, facility_value(p));
syslog_log = 1;
}
}
else if (strchr(config->log_filename, '/') == config->log_filename)
{
if ((log_stream = fopen((char *)(config->log_filename), "a")))
{ {
fseek(log_stream, 0, SEEK_END); fseek(log_stream, 0, SEEK_END);
setbuf(log_stream, NULL); setbuf(log_stream, NULL);
@ -2610,15 +2659,6 @@ void update_config()
setbuf(log_stream, NULL); setbuf(log_stream, NULL);
} }
} }
else if (strstr(config->log_filename, "syslog:") == config->log_filename)
{
char *p = config->log_filename + 7;
if (*p)
{
openlog("l2tpns", LOG_PID, facility_value(p));
syslog_log = 1;
}
}
} }
else else
{ {
@ -2637,6 +2677,8 @@ void update_config()
log(0, 0, 0, 0, "No RADIUS servers defined!\n"); log(0, 0, 0, 0, "No RADIUS servers defined!\n");
} }
config->num_radfds = 2 << RADIUS_SHIFT;
// Update plugins // Update plugins
for (i = 0; i < MAXPLUGINS; i++) for (i = 0; i < MAXPLUGINS; i++)
{ {
@ -2654,6 +2696,8 @@ void update_config()
} }
} }
memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); 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 = 1;
config->reload_config = 0; config->reload_config = 0;
} }
@ -2672,7 +2716,6 @@ void read_config_file()
log(3, 0, 0, 0, "Done reading config file\n"); log(3, 0, 0, 0, "Done reading config file\n");
fclose(f); fclose(f);
update_config(); update_config();
log_stream = NULL;
} }
int sessionsetup(tunnelidt t, sessionidt s, u8 routes) int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
@ -2693,7 +2736,7 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
// Make sure this is right // Make sure this is right
session[s].tunnel = t; session[s].tunnel = t;
// zap old sessions with same IP and/or username // zap old sessions with same IP and/or username
// Don't kill walled garden sessions - doing so leads to a DoS // Don't kill gardened sessions - doing so leads to a DoS
// from someone who doesn't need to know the password // from someone who doesn't need to know the password
ip = session[s].ip; ip = session[s].ip;
user = session[s].user; user = session[s].user;
@ -2723,7 +2766,7 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
// Force throttling on or off // Force throttling on or off
// This has the advantage of cleaning up after another throttled user who may have left // This has the advantage of cleaning up after another throttled user who may have left
// firewall rules lying around // firewall rules lying around
throttle_session(s, session[s].throttle); session[s].throttle = throttle_session(s, session[s].throttle);
{ {
struct param_new_session data = { &tunnel[t], &session[s] }; struct param_new_session data = { &tunnel[t], &session[s] };
@ -2813,7 +2856,7 @@ void add_plugin(char *plugin_name)
int *v = dlsym(p, "__plugin_api_version"); int *v = dlsym(p, "__plugin_api_version");
if (!v || *v != PLUGIN_API_VERSION) if (!v || *v != PLUGIN_API_VERSION)
{ {
log(1, 0, 0, 0, " Plugin load failed: API version mismatch\n", dlerror()); log(1, 0, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror());
dlclose(p); dlclose(p);
return; return;
} }
@ -2822,14 +2865,14 @@ void add_plugin(char *plugin_name)
initfunc = dlsym(p, "plugin_init"); initfunc = dlsym(p, "plugin_init");
if (!initfunc) if (!initfunc)
{ {
log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist.\n", dlerror()); log(1, 0, 0, 0, " Plugin load failed: function plugin_init() does not exist: %s\n", dlerror());
dlclose(p); dlclose(p);
return; return;
} }
if (!initfunc(&funcs)) if (!initfunc(&funcs))
{ {
log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE.\n", dlerror()); log(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
dlclose(p); dlclose(p);
return; return;
} }
@ -2918,18 +2961,7 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr)
param.response = resp; param.response = resp;
param.response_length = l; param.response_length = l;
if (param.type == PKT_LOAD_PLUGIN && param.data_length)
{
add_plugin(param.data);
}
else if (param.type == PKT_UNLOAD_PLUGIN && param.data_length)
{
remove_plugin(param.data);
}
else
{
run_plugins(PLUGIN_CONTROL, &param); run_plugins(PLUGIN_CONTROL, &param);
}
if (param.send_response) if (param.send_response)
{ {