// 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.125 2005-08-29 11:24:31 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.134 2005-09-16 05:30:30 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog).
uint32_t last_id = 0; // Unique ID for radius accounting
+// calculated from config->l2tp_mtu
+uint16_t MRU = 0; // PPP MRU
+uint16_t MSS = 0; // TCP MSS
+
struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI
struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI
CONFIG("log_file", log_filename, STRING),
CONFIG("pid_file", pid_file, STRING),
CONFIG("random_device", random_device, STRING),
- CONFIG("l2tp_secret", l2tpsecret, STRING),
+ CONFIG("l2tp_secret", l2tp_secret, STRING),
+ CONFIG("l2tp_mtu", l2tp_mtu, INT),
CONFIG("ppp_restart_time", ppp_restart_time, INT),
CONFIG("ppp_max_configure", ppp_max_configure, INT),
CONFIG("ppp_max_failure", ppp_max_failure, INT),
CONFIG("packet_limit", max_packets, INT),
CONFIG("cluster_address", cluster_address, IPv4),
CONFIG("cluster_interface", cluster_interface, STRING),
+ CONFIG("cluster_mcast_ttl", cluster_mcast_ttl, INT),
CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT),
return write(tunfd, data, size);
}
+// adjust tcp mss to avoid fragmentation (called only for tcp packets with syn set)
+void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp)
+{
+ int d = (tcp[12] >> 4) * 4;
+ uint8_t *mss = 0;
+ uint8_t *data;
+
+ if ((tcp[13] & 0x3f) & ~(TCP_FLAG_SYN|TCP_FLAG_ACK)) // only want SYN and SYN,ACK
+ return;
+
+ if (tcp + d > buf + len) // short?
+ return;
+
+ data = tcp + d;
+ tcp += 20;
+
+ while (tcp < data)
+ {
+ if (*tcp == 2 && tcp[1] == 4) // mss option (2), length 4
+ {
+ mss = tcp + 2;
+ if (mss + 2 > data) return; // short?
+ break;
+ }
+
+ if (*tcp == 0) return; // end of options
+ if (*tcp == 1 || !tcp[1]) // no op (one byte), or no length (prevent loop)
+ tcp++;
+ else
+ tcp += tcp[1]; // skip over option
+ }
+
+ if (!mss) return; // not found
+ if (ntohs(*(uint16_t *) mss) <= 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" : "",
+ ntohs(*(uint16_t *) mss), MSS);
+
+ // FIXME
+}
+
// process outgoing (to tunnel) IP
//
static void processipout(uint8_t *buf, int len)
if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
return;
+ // adjust MSS on SYN and SYN,ACK packets with options
+ if ((ntohs(*(uint16_t *) (buf + 6)) & 0x1fff) == 0 && buf[9] == IPPROTO_TCP) // first tcp fragment
+ {
+ int ihl = (buf[0] & 0xf) * 4; // length of IP header
+ if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5))
+ adjust_tcp_mss(s, t, buf, len, buf + ihl);
+ }
+
if (sp->tbf_out)
{
// Are we throttling this session?
void sendipcp(sessionidt s, tunnelidt t)
{
- uint8_t buf[MAXCONTROL];
+ uint8_t buf[MAXETHER];
uint8_t *q;
CSTAT(sendipcp);
session[s].unique_id = last_id;
}
- q = makeppp(buf,sizeof(buf), 0, 0, s, t, PPPIPCP);
+ q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP);
if (!q) return;
*q = ConfigReq;
void sendipv6cp(sessionidt s, tunnelidt t)
{
- uint8_t buf[MAXCONTROL];
+ uint8_t buf[MAXETHER];
uint8_t *q;
CSTAT(sendipv6cp);
LOG(3, s, t, "IPV6CP: send ConfigReq\n");
- q = makeppp(buf,sizeof(buf), 0, 0, s, t, PPPIPV6CP);
+ q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP);
if (!q) return;
*q = ConfigReq;
return;
}
l -= (p - buf);
+
+ // used to time out old tunnels
+ if (t && tunnel[t].state == TUNNELOPEN)
+ tunnel[t].lastrec = time_now;
+
if (*buf & 0x80)
{ // control
uint16_t message = 0xFFFF; // message type
return;
}
- // This is used to time out old tunnels
- tunnel[t].lastrec = time_now;
-
// check sequence of this message
{
int skip = tunnel[t].window; // track how many in-window packets are still in queue
uint16_t orig_len;
// handle hidden AVPs
- if (!*config->l2tpsecret)
+ if (!*config->l2tp_secret)
{
LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n");
fatal = flags;
if (amagic == 0) amagic = time_now;
session[s].magic = amagic; // set magic number
session[s].l2tp_flags = aflags; // set flags received
- session[s].mru = DEFAULT_MRU;
+ session[s].mru = PPPMTU; // default
controlnull(t); // ack
// start LCP
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);
}
else
{ // data
- uint16_t prot;
+ uint16_t proto;
LOG_HEX(5, "Receive Tunnel Data", p, l);
if (l > 2 && p[0] == 0xFF && p[1] == 0x03)
}
if (*p & 1)
{
- prot = *p++;
+ proto = *p++;
l--;
}
else
{
- prot = ntohs(*(uint16_t *) p);
+ proto = ntohs(*(uint16_t *) p);
p += 2;
l -= 2;
}
return;
}
- if (prot == PPPPAP)
+ if (proto == PPPPAP)
{
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; }
processpap(s, t, p, l);
}
- else if (prot == PPPCHAP)
+ else if (proto == PPPCHAP)
{
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; }
processchap(s, t, p, l);
}
- else if (prot == PPPLCP)
+ else if (proto == PPPLCP)
{
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; }
processlcp(s, t, p, l);
}
- else if (prot == PPPIPCP)
+ else if (proto == PPPIPCP)
{
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; }
processipcp(s, t, p, l);
}
- else if (prot == PPPIPV6CP)
+ else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0])
{
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; }
processipv6cp(s, t, p, l);
}
- else if (prot == PPPCCP)
+ else if (proto == PPPCCP)
{
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; }
processccp(s, t, p, l);
}
- else if (prot == PPPIP)
+ else if (proto == PPPIP)
{
if (session[s].die)
{
processipin(s, t, p, l);
}
- else if (prot == PPPIPV6)
+ else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
{
- if (!config->ipv6_prefix.s6_addr[0])
- {
- LOG(1, s, t, "IPv6 not configured; yet received IPv6 packet. Ignoring.\n");
- return;
- }
if (session[s].die)
{
LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s);
processipv6in(s, t, p, l);
}
+ 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);
+ }
else
{
- STAT(tunnel_rx_errors);
- LOG(1, s, t, "Unknown PPP protocol %04X\n", prot);
+ LOG(2, s, t, "Unknown PPP protocol 0x%04X received in LCP %s state\n",
+ proto, ppp_state(session[s].ppp.lcp));
}
}
}
}
}
// Send hello
- if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < TIME + 600)
+ if (tunnel[t].state == TUNNELOPEN && (time_now - tunnel[t].lastrec) > 60)
{
controlt *c = controlnew(6); // sending HELLO
controladd(c, 0, t); // send the message
// No data in ECHO_TIMEOUT seconds, send LCP ECHO
if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT))
{
- uint8_t b[MAXCONTROL] = {0};
+ uint8_t b[MAXETHER];
uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP);
if (!q) continue;
config->debug = optdebug;
config->num_tbfs = MAXTBFS;
config->rl_rate = 28; // 28kbps
+ config->cluster_mcast_ttl = 1;
config->cluster_master_min_adv = 1;
config->ppp_restart_time = 3;
config->ppp_max_configure = 10;
MD5_CTX ctx;
*challenge_response = NULL;
- if (!*config->l2tpsecret)
+ if (!*config->l2tp_secret)
{
LOG(0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n");
return;
MD5_Init(&ctx);
MD5_Update(&ctx, &id, 1);
- MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+ MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
MD5_Update(&ctx, challenge, challenge_length);
MD5_Final(*challenge_response, &ctx);
setbuf(log_stream, NULL);
}
+#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;
+ 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;
+ MSS = MRU - TCP_HDRS;
+
// Update radius
config->numradiusservers = 0;
for (i = 0; i < MAXRADSERVER; i++)
// Compute initial pad
MD5_Init(&ctx);
MD5_Update(&ctx, (unsigned char *) &m, 2);
- MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+ MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
MD5_Update(&ctx, vector, vec_len);
MD5_Final(digest, &ctx);
if (d >= sizeof(digest))
{
MD5_Init(&ctx);
- MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
+ MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
MD5_Update(&ctx, last, sizeof(digest));
MD5_Final(digest, &ctx);