// 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.135 2005/09/16 05:35:31 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.155 2006/01/19 20:55:03 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
static uint32_t ip_pool_size = 1; // Size of the pool of addresses used for dynamic address allocation.
time_t time_now = 0; // Current time in seconds since epoch.
static char time_now_string[64] = {0}; // Current time as a string.
+static int time_changed = 0; // time_now changed
char main_quit = 0; // True if we're in the process of exiting.
+static char main_reload = 0; // Re-load pending
linked_list *loaded_plugins;
linked_list *plugins[MAX_PLUGIN_TYPES];
"plugin_control",
"plugin_radius_response",
"plugin_radius_reset",
+ "plugin_radius_account",
"plugin_become_master",
"plugin_new_session_master",
};
static void free_ip_address(sessionidt s);
static void dump_acct_info(int all);
static void sighup_handler(int sig);
-static void sigalrm_handler(int sig);
static void shutdown_handler(int sig);
static void sigchild_handler(int sig);
static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response);
#define QUIT_SHUTDOWN 2 // SIGQUIT: shutdown sessions/tunnels, reject new connections
// return internal time (10ths since process startup), set f if given
+// as a side-effect sets time_now, and time_changed
static clockt now(double *f)
{
struct timeval t;
gettimeofday(&t, 0);
if (f) *f = t.tv_sec + t.tv_usec / 1000000.0;
+ if (t.tv_sec != time_now)
+ {
+ time_now = t.tv_sec;
+ time_changed++;
+ }
return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1;
}
LOG(0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno));
exit(1);
}
+ /* set MTU to modem MRU */
+ ifr.ifr_mtu = MRU;
+ if (ioctl(ifrfd, SIOCSIFMTU, (void *) &ifr) < 0)
+ {
+ LOG(0, 0, 0, "Error setting tun MTU: %s\n", strerror(errno));
+ exit(1);
+ }
ifr.ifr_flags = IFF_UP;
if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0)
{
if (!t)
{
- static int backtrace_count = 0;
LOG(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)
{
- static int backtrace_count = 0;
LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n");
- log_backtrace(backtrace_count, 5)
STAT(tunnel_tx_errors);
return;
}
{
tunnel[t].last = time_now; // control message sent
tunnel[t].retry = backoff(tunnel[t].try); // when to resend
- if (tunnel[t].try > 1)
+ if (tunnel[t].try)
{
STAT(tunnel_retries);
LOG(3, 0, t, "Control message resend try %d\n", tunnel[t].try);
uint8_t *mss = 0;
uint8_t *opts;
uint8_t *data;
+ uint16_t orig;
+ uint32_t sum;
if ((tcp[13] & 0x3f) & ~(TCP_FLAG_SYN|TCP_FLAG_ACK)) // only want SYN and SYN,ACK
return;
}
if (!mss) return; // not found
- if (ntohs(*(uint16_t *) mss) <= MSS) return; // mss OK
+ orig = ntohs(*(uint16_t *) mss);
+
+ if (orig <= MSS) return; // mss OK
+
+ LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s: adjusted mss from %u to %u\n",
+ fmtaddr(*(in_addr_t *) (buf + 12), 0), ntohs(*(uint16_t *) tcp),
+ fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)),
+ (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS);
- LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s, adjusted mss from %u to %u\n",
- fmtaddr(*(in_addr_t *)(buf + 12), 0), ntohs(*(uint16_t *)tcp),
- fmtaddr(*(in_addr_t *)(buf + 16), 1), ntohs(*(uint16_t *)(tcp + 2)),
- (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "",
- ntohs(*(uint16_t *) mss), MSS);
+ // set mss
+ *(int16_t *) mss = htons(MSS);
- // FIXME
+ // adjust checksum (see rfc1141)
+ sum = orig + (~MSS & 0xffff);
+ sum += ntohs(*(uint16_t *) (tcp + 16));
+ sum = (sum & 0xffff) + (sum >> 16);
+ *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16));
}
// process outgoing (to tunnel) IP
master_throttle_packet(sp->tbf_out, data, size);
return;
}
- else if (sp->walled_garden && !config->cluster_iam_master)
+
+ if (sp->walled_garden && !config->cluster_iam_master)
{
// We are walled-gardening this
master_garden_packet(s, data, size);
my_address; // send my IP
tunnelsend(buf, 10 + (q - buf), t); // send it
+ restart_timer(s, ipcp);
}
void sendipv6cp(sessionidt s, tunnelidt t)
q[13] = 1;
tunnelsend(buf, 14 + (q - buf), t); // send it
+ restart_timer(s, ipv6cp);
}
static void sessionclear(sessionidt s)
if (*p == 5 && p[1] == 6) // Magic-Number
amagic = ntohl(*(uint32_t *) (p + 2));
else if (*p == 7) // Protocol-Field-Compression
- aflags |= SESSIONPFC;
+ aflags |= SESSION_PFC;
else if (*p == 8) // Address-and-Control-Field-Compression
- aflags |= SESSIONACFC;
+ aflags |= SESSION_ACFC;
p += p[1];
}
}
case 12: // ICCN
if (amagic == 0) amagic = time_now;
session[s].magic = amagic; // set magic number
- session[s].l2tp_flags = aflags; // set flags received
- session[s].mru = PPPMTU; // default
+ session[s].flags = aflags; // set flags received
+ session[s].mru = PPPoE_MRU; // default
controlnull(t); // ack
// start LCP
- sess_local[s].lcp.restart = time_now + config->ppp_restart_time;
- sess_local[s].lcp.conf_sent = 1;
- sess_local[s].lcp.nak_sent = 0;
sess_local[s].lcp_authtype = config->radius_authprefer;
sess_local[s].ppp_mru = MRU;
- session[s].ppp.lcp = RequestSent;
sendlcp(s, t);
-
+ change_state(s, lcp, RequestSent);
break;
+
case 14: // CDN
controlnull(t); // ack
sessionshutdown(s, "Closed (Received CDN).", 0, 0);
}
else if (session[s].ppp.lcp == Opened)
{
- uint8_t buf[MAXETHER];
- uint8_t *q;
- int mru = session[s].mru;
- if (mru > sizeof(buf)) mru = sizeof(buf);
-
- l += 6;
- if (l > mru) l = mru;
-
- q = makeppp(buf, sizeof(buf), 0, 0, s, t, proto);
- if (!q) return;
-
- *q = CodeRej;
- *(q + 1) = ++sess_local[s].lcp_ident;
- *(uint16_t *)(q + 2) = l;
- *(uint16_t *)(q + 4) = htons(proto);
- memcpy(q + 6, p, l - 6);
-
- if (proto == PPPIPV6CP)
- LOG(3, s, t, "LCP: send ProtocolRej (IPV6CP: not configured)\n");
- else
- LOG(2, s, t, "LCP: sent ProtocolRej (0x%04X: unsupported)\n", proto);
-
- tunnelsend(buf, l + (q - buf), t);
+ session[s].last_packet = time_now;
+ if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+ protoreject(s, t, p, l, proto);
}
else
{
}
}
// Send hello
- if (tunnel[t].state == TUNNELOPEN && (time_now - tunnel[t].lastrec) > 60)
+ if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60)
{
controlt *c = controlnew(6); // sending HELLO
controladd(c, 0, t); // send the message
if (sess_local[s].lcp.conf_sent < config->ppp_max_configure)
{
LOG(3, s, session[s].tunnel, "No ACK for LCP ConfigReq... resending\n");
- sess_local[s].lcp.restart = time_now + config->ppp_restart_time;
- sess_local[s].lcp.conf_sent++;
sendlcp(s, session[s].tunnel);
change_state(s, lcp, next_state);
}
if (sess_local[s].ipcp.conf_sent < config->ppp_max_configure)
{
LOG(3, s, session[s].tunnel, "No ACK for IPCP ConfigReq... resending\n");
- sess_local[s].ipcp.restart = time_now + config->ppp_restart_time;
- sess_local[s].ipcp.conf_sent++;
sendipcp(s, session[s].tunnel);
change_state(s, ipcp, next_state);
}
if (sess_local[s].ipv6cp.conf_sent < config->ppp_max_configure)
{
LOG(3, s, session[s].tunnel, "No ACK for IPV6CP ConfigReq... resending\n");
- sess_local[s].ipv6cp.restart = time_now + config->ppp_restart_time;
- sess_local[s].ipv6cp.conf_sent++;
sendipv6cp(s, session[s].tunnel);
change_state(s, ipv6cp, next_state);
}
if (sess_local[s].ccp.conf_sent < config->ppp_max_configure)
{
LOG(3, s, session[s].tunnel, "No ACK for CCP ConfigReq... resending\n");
- sess_local[s].ccp.restart = time_now + config->ppp_restart_time;
- sess_local[s].ccp.conf_sent++;
sendccp(s, session[s].tunnel);
change_state(s, ccp, next_state);
}
}
// No data in ECHO_TIMEOUT seconds, send LCP ECHO
- if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT))
+ if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT) &&
+ (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT))
{
uint8_t b[MAXETHER];
*q = EchoReq;
*(uint8_t *)(q + 1) = (time_now % 255); // ID
*(uint16_t *)(q + 2) = htons(8); // Length
- *(uint32_t *)(q + 4) = 0; // Magic Number (not supported)
+ *(uint32_t *)(q + 4) = session[s].ppp.lcp == Opened ? htonl(session[s].magic) : 0; // Magic Number
LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
(int)(time_now - session[s].last_packet));
tunnelsend(b, 24, session[s].tunnel); // send it
+ sess_local[s].last_echo = time_now;
s_actions++;
}
int more = 0;
int n;
+
+ if (main_reload)
+ {
+ main_reload = 0;
+ read_config_file();
+ config->reload_config++;
+ }
+
if (config->reload_config)
{
- // Update the config state based on config settings
+ config->reload_config = 0;
update_config();
}
}
}
+ if (time_changed)
+ {
+ double Mbps = 1024.0 * 1024.0 / 8 * time_changed;
+
+ // Log current traffic stats
+ snprintf(config->bandwidth, sizeof(config->bandwidth),
+ "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u",
+ (udp_rx / Mbps), (eth_tx / Mbps), (eth_rx / Mbps), (udp_tx / Mbps),
+ ((udp_tx + udp_rx + eth_tx + eth_rx) / Mbps),
+ udp_rx_pkt / time_changed, eth_rx_pkt / time_changed);
+
+ udp_tx = udp_rx = 0;
+ udp_rx_pkt = eth_rx_pkt = 0;
+ eth_tx = eth_rx = 0;
+ time_changed = 0;
+
+ if (config->dump_speed)
+ printf("%s\n", config->bandwidth);
+
+ // Update the internal time counter
+ strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
+
+ {
+ // Run timer hooks
+ struct param_timer p = { time_now };
+ run_plugins(PLUGIN_TIMER, &p);
+ }
+ }
+
// Runs on every machine (master and slaves).
if (next_cluster_ping <= TIME)
{
else
{
// It's a single ip address
- add_to_ip_pool(inet_addr(pool), 0);
+ add_to_ip_pool(ntohl(inet_addr(pool)), 0);
}
}
fclose(f);
// Start the timer routine off
time(&time_now);
strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
- signal(SIGALRM, sigalrm_handler);
- siginterrupt(SIGALRM, 0);
initplugins();
initdata(optdebug, optconfig);
init_cli(hostname);
read_config_file();
+ update_config();
init_tbf(config->num_tbfs);
LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
}
- alarm(1);
-
// Drop privileges here
if (config->target_uid > 0 && geteuid() == 0)
setuid(config->target_uid);
static void sighup_handler(int sig)
{
- if (log_stream)
- {
- if (log_stream != stderr)
- fclose(log_stream);
-
- log_stream = NULL;
- }
-
- read_config_file();
-}
-
-static void sigalrm_handler(int sig)
-{
- // Log current traffic stats
-
- snprintf(config->bandwidth, sizeof(config->bandwidth),
- "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u",
- (udp_rx / 1024.0 / 1024.0 * 8),
- (eth_tx / 1024.0 / 1024.0 * 8),
- (eth_rx / 1024.0 / 1024.0 * 8),
- (udp_tx / 1024.0 / 1024.0 * 8),
- ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8),
- udp_rx_pkt, eth_rx_pkt);
-
- udp_tx = udp_rx = 0;
- udp_rx_pkt = eth_rx_pkt = 0;
- eth_tx = eth_rx = 0;
-
- if (config->dump_speed)
- printf("%s\n", config->bandwidth);
-
- // Update the internal time counter
- time(&time_now);
- strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
- alarm(1);
-
- {
- // Run timer hooks
- struct param_timer p = { time_now };
- run_plugins(PLUGIN_TIMER, &p);
- }
-
+ main_reload++;
}
static void shutdown_handler(int sig)
{
- LOG(1, 0, 0, "Shutting down\n");
main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
}
#define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc)
#define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp
- if (config->l2tp_mtu <= 0) config->l2tp_mtu = PPPMTU;
+ if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default
else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU;
else if (config->l2tp_mtu > MAXMTU) config->l2tp_mtu = MAXMTU;
// reset MRU/MSS globals
MRU = config->l2tp_mtu - L2TP_HDRS;
+ if (MRU > PPPoE_MRU)
+ MRU = PPPoE_MRU;
+
MSS = MRU - TCP_HDRS;
// Update radius
LOG(0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno));
}
}
-
- config->reload_config = 0;
}
static void read_config_file()
cli_do_file(f);
LOG(3, 0, 0, "Done reading config file\n");
fclose(f);
- update_config();
}
int sessionsetup(sessionidt s, tunnelidt t)
if (frag_offset)
{
- if (!rule->frag || rule->action == FILTER_ACTION_DENY)
+ // layer 4 deny rules are skipped
+ if (rule->action == FILTER_ACTION_DENY &&
+ (rule->src_ports.op || rule->dst_ports.op || rule->tcp_flag_op))
continue;
}
else