// 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.176 2011/01/20 12:48:40 bodea Exp $";
-
#include <arpa/inet.h>
#include <assert.h>
#include <errno.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.
+uint64_t time_now_ms = 0; // Current time in milliseconds 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.
CONFIG("hostname", hostname, STRING),
CONFIG("nexthop_address", nexthop_address, IPv4),
CONFIG("nexthop6_address", nexthop6_address, IPv6),
+ CONFIG("echo_timeout", echo_timeout, INT),
+ CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
{ NULL, 0, 0, 0 },
};
time_now = t.tv_sec;
time_changed++;
}
+
+ // Time in milliseconds
+ time_now_ms = (t.tv_sec * 1000) + (t.tv_usec/1000);
+
return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1;
}
req.rt.rtm_family = AF_INET;
req.rt.rtm_dst_len = prefixlen;
req.rt.rtm_table = RT_TABLE_MAIN;
- req.rt.rtm_protocol = RTPROT_BOOT; // XXX
+ req.rt.rtm_protocol = 42;
req.rt.rtm_scope = RT_SCOPE_LINK;
req.rt.rtm_type = RTN_UNICAST;
netlink_addattr(&req.nh, RTA_GATEWAY, &n_ip, sizeof(n_ip));
}
- LOG(1, s, 0, "Route %s %s/%d%s%s\n", add ? "add" : "del",
+ LOG(1, s, session[s].tunnel, "Route %s %s/%d%s%s\n", add ? "add" : "del",
fmtaddr(htonl(ip), 0), prefixlen,
gw ? " via" : "", gw ? fmtaddr(htonl(gw), 2) : "");
req.rt.rtm_family = AF_INET6;
req.rt.rtm_dst_len = prefixlen;
req.rt.rtm_table = RT_TABLE_MAIN;
- req.rt.rtm_protocol = RTPROT_BOOT; // XXX
+ req.rt.rtm_protocol = 42;
req.rt.rtm_scope = RT_SCOPE_LINK;
req.rt.rtm_type = RTN_UNICAST;
metric = 1;
netlink_addattr(&req.nh, RTA_METRICS, &metric, sizeof(metric));
- LOG(1, 0, 0, "Route %s %s/%d\n",
+ LOG(1, s, session[s].tunnel, "Route %s %s/%d\n",
add ? "add" : "del",
inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN),
prefixlen);
// process outgoing (to tunnel) IP
//
-static void processipout(uint8_t *buf, int len)
+// (i.e. this routine writes to data[-8]).
+void processipout(uint8_t *buf, int len)
{
sessionidt s;
sessiont *sp;
return;
}
- // Add on L2TP header
- {
- bundleidt bid = 0;
- if(session[s].bundle != 0 && bundle[session[s].bundle].num_of_links > 1)
- {
- bid = session[s].bundle;
- s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
- t = session[s].tunnel;
- sp = &session[s];
- LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s);
- if(len > MINFRAGLEN)
- {
- // Partition the packet to "bundle[b].num_of_links" fragments
- bundlet *b = &bundle[bid];
- uint32_t num_of_links = b->num_of_links;
- uint32_t fraglen = len / num_of_links;
- fraglen = (fraglen > session[s].mru ? session[s].mru : fraglen);
- uint32_t last_fraglen = fraglen + len % num_of_links;
- last_fraglen = (last_fraglen > session[s].mru ? len % num_of_links : last_fraglen);
+ if(session[s].bundle != 0 && bundle[session[s].bundle].num_of_links > 1)
+ {
+
+ if (!config->cluster_iam_master)
+ {
+ // The MPPP packets must be managed by the Master.
+ master_forward_mppp_packet(s, data, size);
+ return;
+ }
+
+ // Add on L2TP header
+ sessionidt members[MAXBUNDLESES];
+ bundleidt bid = session[s].bundle;
+ bundlet *b = &bundle[bid];
+ uint32_t num_of_links, nb_opened;
+ int i;
+
+ num_of_links = b->num_of_links;
+ nb_opened = 0;
+ for (i = 0;i < num_of_links;i++)
+ {
+ s = b->members[i];
+ if (session[s].ppp.lcp == Opened)
+ {
+ members[nb_opened] = s;
+ nb_opened++;
+ }
+ }
+
+ if (nb_opened < 1)
+ {
+ LOG(3, s, t, "MPPP: PROCESSIPOUT ERROR, no session opened in bundle:%d\n", bid);
+ return;
+ }
+
+ num_of_links = nb_opened;
+ b->current_ses = (b->current_ses + 1) % num_of_links;
+ s = members[b->current_ses];
+ t = session[s].tunnel;
+ sp = &session[s];
+ LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s);
+
+ if (num_of_links > 1)
+ {
+ if(len > MINFRAGLEN)
+ {
+ //for rotate traffic among the member links
+ uint32_t divisor = num_of_links;
+ if (divisor > 2)
+ divisor = divisor/2 + (divisor & 1);
+
+ // Partition the packet to "num_of_links" fragments
+ uint32_t fraglen = len / divisor;
+ uint32_t last_fraglen = fraglen + len % divisor;
uint32_t remain = len;
// send the first packet
- uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, fraglen, s, t, PPPIP, 0, bid, MP_BEGIN);
- if (!p) return;
- tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it...
+ uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, fraglen, s, t, PPPIP, 0, bid, MP_BEGIN);
+ if (!p) return;
+ tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it...
+
// statistics
update_session_out_stat(s, sp, fraglen);
+
remain -= fraglen;
while (remain > last_fraglen)
- {
- s = b->members[b->current_ses = ++b->current_ses % num_of_links];
+ {
+ b->current_ses = (b->current_ses + 1) % num_of_links;
+ s = members[b->current_ses];
t = session[s].tunnel;
sp = &session[s];
- LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
- p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), fraglen, s, t, PPPIP, 0, bid, 0);
- if (!p) return;
- tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it...
+ LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
+ p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), fraglen, s, t, PPPIP, 0, bid, 0);
+ if (!p) return;
+ tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it...
update_session_out_stat(s, sp, fraglen);
remain -= fraglen;
}
// send the last fragment
- s = b->members[b->current_ses = ++b->current_ses % num_of_links];
+ b->current_ses = (b->current_ses + 1) % num_of_links;
+ s = members[b->current_ses];
t = session[s].tunnel;
sp = &session[s];
- LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
- p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), remain, s, t, PPPIP, 0, bid, MP_END);
- if (!p) return;
- tunnelsend(fragbuf, remain + (p-fragbuf), t); // send it...
+ LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
+ p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), remain, s, t, PPPIP, 0, bid, MP_END);
+ if (!p) return;
+ tunnelsend(fragbuf, remain + (p-fragbuf), t); // send it...
update_session_out_stat(s, sp, remain);
if (remain != last_fraglen)
LOG(3, s, t, "PROCESSIPOUT ERROR REMAIN != LAST_FRAGLEN, %d != %d\n", remain, last_fraglen);
- }
- else {
- // Send it as one frame
- uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS);
- if (!p) return;
- tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+ }
+ else
+ {
+ // Send it as one frame
+ uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS);
+ if (!p) return;
+ tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
LOG(4, s, t, "MPPP: packet sent as one frame\n");
update_session_out_stat(s, sp, len);
- }
- }
- else
- {
- uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
- if (!p) return;
- tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+ }
+ }
+ else
+ {
+ // Send it as one frame (NO MPPP Frame)
+ uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
+ if (!p) return;
+ tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
update_session_out_stat(s, sp, len);
- }
- }
+ }
+ }
+ else
+ {
+ uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
+ if (!p) return;
+ tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+ update_session_out_stat(s, sp, len);
+ }
// Snooping this session, send it to intercept box
if (sp->snoop_ip && sp->snoop_port)
if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
{
bundleidt bid = session[s].bundle;
- s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
+ bundlet *b = &bundle[bid];
+
+ b->current_ses = (b->current_ses + 1) % b->num_of_links;
+ s = b->members[b->current_ses];
LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %u\n", s);
}
t = session[s].tunnel;
{
sessiont *sp;
tunnelidt t;
- in_addr_t ip;
uint8_t b[MAXETHER + 20];
buf += 4;
len -= 4;
- ip = *(in_addr_t *)(buf + 16);
-
if (!session[s].ip)
return;
static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
{
uint16_t l = (m ? 0x8008 : 0x0008);
- *(uint16_t *) (c->buf + c->length + 0) = htons(l);
- *(uint16_t *) (c->buf + c->length + 2) = htons(0);
- *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
- *(uint16_t *) (c->buf + c->length + 6) = htons(val);
+ c->buf16[c->length/2 + 0] = htons(l);
+ c->buf16[c->length/2 + 1] = htons(0);
+ c->buf16[c->length/2 + 2] = htons(avp);
+ c->buf16[c->length/2 + 3] = htons(val);
c->length += 8;
}
static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
{
uint16_t l = (m ? 0x800A : 0x000A);
- *(uint16_t *) (c->buf + c->length + 0) = htons(l);
- *(uint16_t *) (c->buf + c->length + 2) = htons(0);
- *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
- *(uint32_t *) (c->buf + c->length + 6) = htonl(val);
+ c->buf16[c->length/2 + 0] = htons(l);
+ c->buf16[c->length/2 + 1] = htons(0);
+ c->buf16[c->length/2 + 2] = htons(avp);
+ *(uint32_t *) &c->buf[c->length + 6] = htonl(val);
c->length += 10;
}
static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
{
uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
- *(uint16_t *) (c->buf + c->length + 0) = htons(l);
- *(uint16_t *) (c->buf + c->length + 2) = htons(0);
- *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
- memcpy(c->buf + c->length + 6, val, strlen(val));
+ c->buf16[c->length/2 + 0] = htons(l);
+ c->buf16[c->length/2 + 1] = htons(0);
+ c->buf16[c->length/2 + 2] = htons(avp);
+ memcpy(&c->buf[c->length + 6], val, strlen(val));
c->length += 6 + strlen(val);
}
static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m)
{
uint16_t l = ((m ? 0x8000 : 0) + len + 6);
- *(uint16_t *) (c->buf + c->length + 0) = htons(l);
- *(uint16_t *) (c->buf + c->length + 2) = htons(0);
- *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
- memcpy(c->buf + c->length + 6, val, len);
+ c->buf16[c->length/2 + 0] = htons(l);
+ c->buf16[c->length/2 + 1] = htons(0);
+ c->buf16[c->length/2 + 2] = htons(avp);
+ memcpy(&c->buf[c->length + 6], val, len);
c->length += 6 + len;
}
}
assert(c);
c->next = 0;
- *(uint16_t *) (c->buf + 0) = htons(0xC802); // flags/ver
+ c->buf16[0] = htons(0xC802); // flags/ver
c->length = 12;
control16(c, 0, mtype, 1);
return c;
// (ZLB send).
static void controlnull(tunnelidt t)
{
- uint8_t buf[12];
+ uint16_t buf[6];
if (tunnel[t].controlc) // Messages queued; They will carry the ack.
return;
- *(uint16_t *) (buf + 0) = htons(0xC802); // flags/ver
- *(uint16_t *) (buf + 2) = htons(12); // length
- *(uint16_t *) (buf + 4) = htons(tunnel[t].far); // tunnel
- *(uint16_t *) (buf + 6) = htons(0); // session
- *(uint16_t *) (buf + 8) = htons(tunnel[t].ns); // sequence
- *(uint16_t *) (buf + 10) = htons(tunnel[t].nr); // sequence
- tunnelsend(buf, 12, t);
+ buf[0] = htons(0xC802); // flags/ver
+ buf[1] = htons(12); // length
+ buf[2] = htons(tunnel[t].far); // tunnel
+ buf[3] = htons(0); // session
+ buf[4] = htons(tunnel[t].ns); // sequence
+ buf[5] = htons(tunnel[t].nr); // sequence
+ tunnelsend((uint8_t *)buf, 12, t);
}
// add a control message to a tunnel, and send if within window
static void controladd(controlt *c, sessionidt far, tunnelidt t)
{
- *(uint16_t *) (c->buf + 2) = htons(c->length); // length
- *(uint16_t *) (c->buf + 4) = htons(tunnel[t].far); // tunnel
- *(uint16_t *) (c->buf + 6) = htons(far); // session
- *(uint16_t *) (c->buf + 8) = htons(tunnel[t].ns); // sequence
+ c->buf16[1] = htons(c->length); // length
+ c->buf16[2] = htons(tunnel[t].far); // tunnel
+ c->buf16[3] = htons(far); // session
+ c->buf16[4] = htons(tunnel[t].ns); // sequence
tunnel[t].ns++; // advance sequence
// link in message in to queue
if (tunnel[t].controlc)
if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen && del_routes)
route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
- if (b)
+ if (b)
{
- // This session was part of a bundle
- bundle[b].num_of_links--;
- LOG(3, s, 0, "MPPP: Dropping member link: %d from bundle %d\n",s,b);
- if(bundle[b].num_of_links == 0)
+ // This session was part of a bundle
+ bundle[b].num_of_links--;
+ LOG(3, s, session[s].tunnel, "MPPP: Dropping member link: %d from bundle %d\n",s,b);
+ if(bundle[b].num_of_links == 0)
{
- bundleclear(b);
- LOG(3, s, 0, "MPPP: Kill bundle: %d (No remaing member links)\n",b);
- }
- else
+ bundleclear(b);
+ LOG(3, s, session[s].tunnel, "MPPP: Kill bundle: %d (No remaing member links)\n",b);
+ }
+ else
{
- // Adjust the members array to accomodate the new change
- uint8_t mem_num = 0;
- // It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--")
- if(bundle[b].members[bundle[b].num_of_links] != s)
+ // Adjust the members array to accomodate the new change
+ uint8_t mem_num = 0;
+ // It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--")
+ if(bundle[b].members[bundle[b].num_of_links] != s)
{
- uint8_t ml;
- for(ml = 0; ml<bundle[b].num_of_links; ml++)
- if(bundle[b].members[ml] == s)
- {
- mem_num = ml;
- break;
- }
- bundle[b].members[mem_num] = bundle[b].members[bundle[b].num_of_links];
- LOG(3, s, 0, "MPPP: Adjusted member links array\n");
- }
- }
- cluster_send_bundle(b);
- }
+ uint8_t ml;
+ for(ml = 0; ml<bundle[b].num_of_links; ml++)
+ if(bundle[b].members[ml] == s)
+ {
+ mem_num = ml;
+ break;
+ }
+ bundle[b].members[mem_num] = bundle[b].members[bundle[b].num_of_links];
+ LOG(3, s, session[s].tunnel, "MPPP: Adjusted member links array\n");
+
+ // If the killed session is the first of the bundle,
+ // the new first session must be stored in the cache_ipmap
+ // else the function sessionbyip return 0 and the sending not work any more (processipout).
+ if (mem_num == 0)
+ {
+ sessionidt new_s = bundle[b].members[0];
+
+ routed = 0;
+ // Add the route for this session.
+ for (r = 0; r < MAXROUTE && session[new_s].route[r].ip; r++)
+ {
+ int i, prefixlen;
+ in_addr_t ip;
+
+ prefixlen = session[new_s].route[r].prefixlen;
+ ip = session[new_s].route[r].ip;
+
+ if (!prefixlen) prefixlen = 32;
+ ip &= 0xffffffff << (32 - prefixlen); // Force the ip to be the first one in the route.
+
+ for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i)
+ cache_ipmap(i, new_s);
+ }
+ cache_ipmap(session[new_s].ip, new_s);
+
+ // IPV6 route
+ if (session[new_s].ipv6prefixlen)
+ cache_ipv6map(session[new_s].ipv6route, session[new_s].ipv6prefixlen, new_s);
+ }
+ }
+ }
+
+ cluster_send_bundle(b);
+ }
}
if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled.
controlt *c = controlnew(14); // sending CDN
if (cdn_error)
{
- uint8_t buf[4];
- *(uint16_t *) buf = htons(cdn_result);
- *(uint16_t *) (buf+2) = htons(cdn_error);
- controlb(c, 1, buf, 4, 1);
+ uint16_t buf[2];
+ buf[0] = htons(cdn_result);
+ buf[1] = htons(cdn_error);
+ controlb(c, 1, (uint8_t *)buf, 4, 1);
}
else
control16(c, 1, cdn_result, 1);
controlt *c = controlnew(4); // sending StopCCN
if (error)
{
- uint8_t buf[64];
+ uint16_t buf[32];
int l = 4;
- *(uint16_t *) buf = htons(result);
- *(uint16_t *) (buf+2) = htons(error);
+ buf[0] = htons(result);
+ buf[1] = htons(error);
if (msg)
{
int m = strlen(msg);
if (m + 4 > sizeof(buf))
m = sizeof(buf) - 4;
- memcpy(buf+4, msg, m);
+ memcpy(buf+2, msg, m);
l += m;
}
- controlb(c, 1, buf, l, 1);
+ controlb(c, 1, (uint8_t *)buf, l, 1);
}
else
control16(c, 1, result, 1);
case 0: // message type
message = ntohs(*(uint16_t *) b);
mandatory = flags & 0x80;
- LOG(4, s, t, " Message type = %u (%s)\n", *b, l2tp_code(message));
+ LOG(4, s, t, " Message type = %u (%s)\n", message, l2tp_code(message));
break;
case 1: // result code
{
}
session[s].last_packet = session[s].last_data = time_now;
- if (session[s].walled_garden && !config->cluster_iam_master)
+ if (!config->cluster_iam_master)
{
+ // The fragments reconstruction is managed by the Master.
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
return;
}
}
// read and process packet on tun
+// (i.e. this routine writes to buf[-8]).
static void processtun(uint8_t * buf, int len)
{
LOG_HEX(5, "Receive TUN Data", buf, len);
}
}
- // Drop sessions who have not responded within IDLE_TIMEOUT seconds
- if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
+ // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds
+ if (session[s].last_packet && (time_now - session[s].last_packet >= config->idle_echo_timeout))
{
sessionshutdown(s, "No response to LCP ECHO requests.", CDN_ADMIN_DISC, TERM_LOST_SERVICE);
STAT(session_timeout);
}
// 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 >= config->echo_timeout) &&
(time_now - sess_local[s].last_echo >= ECHO_TIMEOUT))
{
uint8_t b[MAXETHER];
{
int i;
uint8_t buf[65536];
+ uint8_t *p = buf + 8; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
+ int size_bufp = sizeof(buf) - 8;
clockt next_cluster_ping = 0; // send initial ping immediately
struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS];
int maxevent = sizeof(events)/sizeof(*events);
// incoming IP
if (tun_ready)
{
- if ((s = read(tunfd, buf, sizeof(buf))) > 0)
+ if ((s = read(tunfd, p, size_bufp)) > 0)
{
- processtun(buf, s);
+ processtun(p, s);
tun_pkts++;
}
else
config->ppp_max_failure = 5;
config->kill_timedout_sessions = 1;
strcpy(config->random_device, RANDOMDEVICE);
+ // Set default value echo_timeout and idle_echo_timeout
+ config->echo_timeout = ECHO_TIMEOUT;
+ config->idle_echo_timeout = IDLE_ECHO_TIMEOUT;
log_stream = stderr;
if (!*hostname)
{
- if (!*config->hostname)
- {
- // Grab my hostname unless it's been specified
- gethostname(hostname, sizeof(hostname));
- stripdomain(hostname);
- }
- else
- strcpy(hostname, config->hostname);
+ // Grab my hostname unless it's been specified
+ gethostname(hostname, sizeof(hostname));
+ stripdomain(hostname);
}
_statistics->start_time = _statistics->last_reset = time(NULL);
initplugins();
initdata(optdebug, optconfig);
- init_cli(hostname);
+ init_cli();
read_config_file();
+ /* set hostname /after/ having read the config file */
+ if (*config->hostname)
+ strcpy(hostname, config->hostname);
+ cli_init_complete(hostname);
update_config();
init_tbf(config->num_tbfs);