-* Tue Apr 25 2006 Brendan O'Dea <bod@optus.net> 2.1.19
+* Thu Apr 27 2006 Brendan O'Dea <bod@optus.net> 2.2.0
- Only poll clifd if successfully bound.
- Add "Practical VPNs" document from Liran Tal as Docs/vpn .
+- Add Multilink support from Khaled Al Hamwi.
* Tue Apr 18 2006 Brendan O'Dea <bod@optus.net> 2.1.18
- Don't shutdown on TerminateReq, wait for CDN.
.de Id
.ds Dt \\$4 \\$5
..
-.Id $Id: startup-config.5,v 1.15 2005/09/16 05:04:31 bodea Exp $
+.Id $Id: startup-config.5,v 1.16 2006/04/27 09:53:50 bodea Exp $
.TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
.SH NAME
startup\-config \- configuration file for l2tpns
any prior session with the same username will be dropped when a new
session is established.
.TP
+.B guest_account
+Allow multiple logins matching this specific username.
+.TP
.B bind_address
When the tun interface is created, it is assigned the address
specified here. If no address is given, 1.1.1.1 is used. Packets
Paul Martin <pm@zetnet.net>
Jonathan Yarden <jyarden@bluegrass.net>
Patrick Cole <z@amused.net>
+Khaled Al Hamwi <kh.alhamwi@gmail.com>
// vim: sw=8 ts=8
char const *cvs_name = "$Name: $";
-char const *cvs_id_cli = "$Id: cli.c,v 1.71 2005/12/06 09:43:42 bodea Exp $";
+char const *cvs_id_cli = "$Id: cli.c,v 1.72 2006/04/27 09:53:49 bodea Exp $";
#include <stdio.h>
#include <stddef.h>
#endif
extern tunnelt *tunnel;
+extern bundlet *bundle;
extern sessiont *session;
extern radiust *radius;
extern ippoolt *ip_address_pool;
cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0);
cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0);
cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin);
+ if (session[s].timeout)
+ {
+ cli_print(cli, "\tRemaing time:\t%u",
+ (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
+ ? (unsigned) (session[s].timeout - bundle[session[s].bundle].online_time)
+ : (unsigned) (session[s].timeout - (time_now - session[s].opened)));
+ }
+
cli_print(cli, "\tPkts In/Out:\t%u/%u", session[s].pout, session[s].pin);
cli_print(cli, "\tMRU:\t\t%d", session[s].mru);
cli_print(cli, "\tRx Speed:\t%u", session[s].rx_connect_speed);
}
// Show Summary
- cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %-15s %s",
+ cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s",
"SID",
"TID",
"Username",
"downloaded",
"uploaded",
"idle",
+ "Rem.Time",
"LAC",
"CLI");
for (i = 1; i < MAXSESSION; i++)
{
+ uint32_t rem_time;
if (!session[i].opened) continue;
- cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %-15s %s",
+ if (session[i].bundle && bundle[session[i].bundle].num_of_links > 1)
+ rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0;
+ else
+ rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
+
+ cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s",
i,
session[i].tunnel,
session[i].user[0] ? session[i].user : "*",
(unsigned long)session[i].cout,
(unsigned long)session[i].cin,
abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
+ (unsigned long)(rem_time),
fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1),
session[i].calling[0] ? session[i].calling : "*");
}
// L2TPNS Clustering Stuff
-char const *cvs_id_cluster = "$Id: cluster.c,v 1.50 2006/04/05 02:13:48 bodea Exp $";
+char const *cvs_id_cluster = "$Id: cluster.c,v 1.51 2006/04/27 09:53:49 bodea Exp $";
#include <stdio.h>
#include <stdlib.h>
in_addr_t my_address = 0; // The network address of my ethernet port.
static int walk_session_number = 0; // The next session to send when doing the slow table walk.
+static int walk_bundle_number = 0; // The next bundle to send when doing the slow table walk.
static int walk_tunnel_number = 0; // The next tunnel to send when doing the slow table walk.
int forked = 0; // Sanity check: CLI must not diddle with heartbeat table
int opt;
config->cluster_undefined_sessions = MAXSESSION-1;
+ config->cluster_undefined_bundles = MAXBUNDLE-1;
config->cluster_undefined_tunnels = MAXTUNNEL-1;
if (!config->cluster_address)
if (config->cluster_iam_uptodate)
return;
- if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels)
+ if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles)
return;
config->cluster_iam_uptodate = 1;
//
void cluster_check_master(void)
{
- int i, count, tcount, high_unique_id = 0;
+ int i, count, tcount, bcount, high_unique_id = 0;
int last_free = 0;
clockt t = TIME;
static int probed = 0;
config->cluster_highest_tunnelid = i;
}
+ //
+ // Go through and mark all the bundles as defined.
+ // Count the highest used bundle number as well.
+ //
+ config->cluster_highest_bundleid = 0;
+ for (i = 0, bcount = 0; i < MAXBUNDLE; ++i) {
+ if (bundle[i].state == BUNDLEUNDEF)
+ bundle[i].state = BUNDLEFREE;
+
+ if (bundle[i].state != BUNDLEFREE && i > config->cluster_highest_bundleid)
+ config->cluster_highest_bundleid = i;
+ }
+
//
// Go through and mark all the sessions as being defined.
// reset the idle timeouts.
rebuild_address_pool();
// If we're not the very first master, this is a big issue!
- if(count>0)
+ if (count > 0)
LOG(0, 0, 0, "Warning: Fixed %d uninitialized sessions in becoming master!\n", count);
config->cluster_undefined_sessions = 0;
+ config->cluster_undefined_bundles = 0;
config->cluster_undefined_tunnels = 0;
config->cluster_iam_uptodate = 1; // assume all peers are up-to-date
// we fix it up here, and we ensure that the 'first free session'
// pointer is valid.
//
-static void cluster_check_sessions(int highsession, int freesession_ptr, int hightunnel)
+static void cluster_check_sessions(int highsession, int freesession_ptr, int highbundle, int hightunnel)
{
int i;
if (config->cluster_iam_uptodate)
return;
- if (highsession > config->cluster_undefined_sessions && hightunnel > config->cluster_undefined_tunnels)
+ if (highsession > config->cluster_undefined_sessions && highbundle > config->cluster_undefined_bundles && hightunnel > config->cluster_undefined_tunnels)
return;
// Clear out defined sessions, counting the number of
++config->cluster_undefined_sessions;
}
+ // Clear out defined bundles, counting the number of
+ // undefs remaining.
+ config->cluster_undefined_bundles = 0;
+ for (i = 1 ; i < MAXBUNDLE; ++i) {
+ if (i > highbundle) {
+ if (bundle[i].state == BUNDLEUNDEF) bundle[i].state = BUNDLEFREE; // Defined.
+ continue;
+ }
+
+ if (bundle[i].state == BUNDLEUNDEF)
+ ++config->cluster_undefined_bundles;
+ }
+
// Clear out defined tunnels, counting the number of
// undefs remaining.
config->cluster_undefined_tunnels = 0;
}
- if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels) {
- LOG(2, 0, 0, "Cleared undefined sessions/tunnels. %d sess (high %d), %d tunn (high %d)\n",
- config->cluster_undefined_sessions, highsession, config->cluster_undefined_tunnels, hightunnel);
+ if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles) {
+ LOG(2, 0, 0, "Cleared undefined sessions/bundles/tunnels. %d sess (high %d), %d bund (high %d), %d tunn (high %d)\n",
+ config->cluster_undefined_sessions, highsession, config->cluster_undefined_bundles, highbundle, config->cluster_undefined_tunnels, hightunnel);
return;
}
add_type(p, C_SESSION, id, (uint8_t *) &session[id], sizeof(sessiont));
break;
+ case C_CBUNDLE: { // Compressed C_BUNDLE
+ uint8_t c[sizeof(bundlet) * 2]; // Bigger than worst case.
+ uint8_t *d = (uint8_t *) &bundle[id];
+ uint8_t *orig = d;
+ int size;
+
+ size = rle_compress( &d, sizeof(bundlet), c, sizeof(c) );
+
+ // Did we compress the full structure, and is the size actually
+ // reduced??
+ if ( (d - orig) == sizeof(bundlet) && size < sizeof(bundlet) ) {
+ add_type(p, C_CBUNDLE, id, c, size);
+ break;
+ }
+ // Failed to compress : Fall through.
+ }
+
+ case C_BUNDLE:
+ add_type(p, C_BUNDLE, id, (uint8_t *) &bundle[id], sizeof(bundlet));
+ break;
+
case C_CTUNNEL: { // Compressed C_TUNNEL
uint8_t c[sizeof(tunnelt) * 2]; // Bigger than worst case.
uint8_t *d = (uint8_t *) &tunnel[id];
//
void cluster_heartbeat()
{
- int i, count = 0, tcount = 0;
+ int i, count = 0, tcount = 0, bcount = 0;
uint8_t buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ];
heartt h;
uint8_t *p = buff;
h.highsession = config->cluster_highest_sessionid;
h.freesession = sessionfree;
h.hightunnel = config->cluster_highest_tunnelid;
+ h.highbundle = config->cluster_highest_bundleid;
h.size_sess = sizeof(sessiont); // Just in case.
+ h.size_bund = sizeof(bundlet);
h.size_tunn = sizeof(tunnelt);
h.interval = config->cluster_hb_interval;
h.timeout = config->cluster_hb_timeout;
++tcount;
}
+ //
+ // Fill out the packet with bundles from the bundle table...
+ while ( (p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE) ) {
+
+ if (!walk_bundle_number) // bundle #0 isn't valid.
+ ++walk_bundle_number;
+
+ if (bcount >= config->cluster_highest_bundleid)
+ break;
+
+ hb_add_type(&p, C_CTUNNEL, walk_bundle_number);
+ walk_tunnel_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1); // +1 avoids divide by zero.
+
+ ++bcount;
+ }
+
//
// Did we do something wrong?
if (p > (buff + sizeof(buff))) { // Did we somehow manage to overun the buffer?
}
LOG(3, 0, 0, "Sending v%d heartbeat #%d, change #%" PRIu64 " with %d changes "
- "(%d x-sess, %d x-tunnels, %d highsess, %d hightun, size %d)\n",
+ "(%d x-sess, %d x-bundles, %d x-tunnels, %d highsess, %d highbund, %d hightun, size %d)\n",
HB_VERSION, h.seq, h.table_version, config->cluster_num_changes,
- count, tcount, config->cluster_highest_sessionid,
+ count, bcount, tcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
config->cluster_highest_tunnelid, (int) (p - buff));
config->cluster_num_changes = 0;
return type_changed(C_CSESSION, sid);
}
+// A particular bundle has been changed!
+int cluster_send_bundle(int bid)
+{
+ if (!config->cluster_iam_master) {
+ LOG(0, 0, bid, "I'm not a master, but I just tried to change a bundle!\n");
+ return -1;
+ }
+
+ return type_changed(C_CBUNDLE, bid);
+}
+
// A particular tunnel has been changed!
int cluster_send_tunnel(int tid)
{
return 0;
}
+static int cluster_recv_bundle(int more, uint8_t *p)
+{
+ if (more >= MAXBUNDLE) {
+ LOG(0, 0, 0, "DANGER: Received a bundle id > MAXBUNDLE!\n");
+ return -1;
+ }
+
+ if (bundle[more].state == BUNDLEUNDEF) {
+ if (config->cluster_iam_uptodate) { // Sanity.
+ LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined bundle!\n");
+ } else {
+ --config->cluster_undefined_bundles;
+ }
+ }
+
+ memcpy(&bundle[more], p, sizeof(bundle[more]) );
+
+ LOG(5, 0, more, "Received bundle update\n");
+
+ if (!config->cluster_iam_uptodate)
+ cluster_uptodate(); // Check to see if we're up to date.
+
+ return 0;
+}
+
static int cluster_recv_tunnel(int more, uint8_t *p)
{
if (more >= MAXTUNNEL) {
// Check that we don't have too many undefined sessions, and
// that the free session pointer is correct.
- cluster_check_sessions(h->highsession, h->freesession, h->hightunnel);
+ cluster_check_sessions(h->highsession, h->freesession, h->highbundle, h->hightunnel);
if (h->interval != config->cluster_hb_interval)
{
p += sizeof(tunnel[more]);
s -= sizeof(tunnel[more]);
break;
+
+ case C_CBUNDLE: { // Compressed bundle structure.
+ uint8_t c[ sizeof(bundlet) + 2];
+ int size;
+ uint8_t *orig_p = p;
+
+ size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
+ s -= (p - orig_p);
+
+ if (size != sizeof(bundlet) ) { // Ouch! Very very bad!
+ LOG(0, 0, 0, "DANGER: Received a CBUNDLE that didn't decompress correctly!\n");
+ // Now what? Should exit! No-longer up to date!
+ break;
+ }
+
+ cluster_recv_bundle(more, c);
+ break;
+
+ }
+ case C_BUNDLE:
+ if ( s < sizeof(bundle[more]))
+ goto shortpacket;
+
+ cluster_recv_bundle(more, p);
+
+ p += sizeof(bundle[more]);
+ s -= sizeof(bundle[more]);
+ break;
default:
LOG(0, 0, 0, "DANGER: I received a heartbeat element where I didn't understand the type! (%d)\n", type);
return -1; // can't process any more of the packet!!
cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version);
cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number);
cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid);
+ cli_print(cli, "%d bundles undefined of %d", config->cluster_undefined_bundles, config->cluster_highest_bundleid);
cli_print(cli, "%d tunnels undefined of %d", config->cluster_undefined_tunnels, config->cluster_highest_tunnelid);
} else {
cli_print(cli, "Table version # : %" PRIu64, config->cluster_table_version);
cli_print(cli, "Next heartbeat # : %d", config->cluster_seq_number);
cli_print(cli, "Highest session : %d", config->cluster_highest_sessionid);
+ cli_print(cli, "Highest bundle : %d", config->cluster_highest_bundleid);
cli_print(cli, "Highest tunnel : %d", config->cluster_highest_tunnelid);
cli_print(cli, "%d changes queued for sending", config->cluster_num_changes);
}
// L2TPNS Clustering Stuff
-// $Id: cluster.h,v 1.14 2005/07/31 10:04:10 bodea Exp $
+// $Id: cluster.h,v 1.15 2006/04/27 09:53:49 bodea Exp $
#ifndef __CLUSTER_H__
#define __CLUSTER_H__
#define C_GARDEN 14 // Gardened packet
#define C_MASTER 15 // Tell a slave the address of the master.
#define C_FORWARD_DAE 16 // A DAE packet for the master to handle
+#define C_BUNDLE 17 // Bundle structure.
+#define C_CBUNDLE 18 // Compressed bundle structure.
#define HB_VERSION 5 // Protocol version number..
#define HB_MAX_SEQ (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
uint32_t highsession; // Id of the highest in-use session.
uint32_t freesession; // Id of the first free session.
+ uint32_t highbundle; // Id of the highest used bundle.
uint32_t hightunnel; // Id of the highest used tunnel.
uint32_t size_sess; // Size of the session structure.
+ uint32_t size_bund; // size of the bundle structure.
uint32_t size_tunn; // size of the tunnel structure.
uint32_t interval; // ping/heartbeat interval
uint32_t timeout; // heartbeat timeout
int cluster_init(void);
int processcluster(uint8_t *buf, int size, in_addr_t addr);
int cluster_send_session(int sid);
+int cluster_send_bundle(int bid);
int cluster_send_tunnel(int tid);
int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port);
int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port);
# Allow multiple logins for the same username
#set allow_duplicate_users no
+# Allow multiple logins for specific username
+#set guest_account ""
+
# Write usage accounting files into specified directory
set accounting_dir "/var/run/l2tpns/acct"
# Minimum number of slaves before master withdraws routes
#set cluster_master_min_adv 1
+# IPv6 address prefix
+#set ipv6_prefix ::
+
# Drop/kill sessions
#load plugin "sessionctl"
// L2TPNS: icmp
-char const *cvs_id_icmp = "$Id: icmp.c,v 1.10 2005/08/10 11:25:56 bodea Exp $";
+char const *cvs_id_icmp = "$Id: icmp.c,v 1.11 2006/04/27 09:53:49 bodea Exp $";
#include <arpa/inet.h>
#include <netdb.h>
LOG(3, s, t, "Sending IPv6 RA\n");
memset(b, 0, sizeof(b));
- o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6);
+ o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0);
if (!o)
{
// 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.162 2006/04/23 23:18:31 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.163 2006/04/27 09:53:49 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
CONFIG("radius_authtypes", radius_authtypes_s, STRING),
CONFIG("radius_dae_port", radius_dae_port, SHORT),
CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL),
+ CONFIG("guest_account", guest_user, STRING),
CONFIG("bind_address", bind_address, IPv4),
CONFIG("peer_address", peer_address, IPv4),
CONFIG("send_garp", send_garp, BOOL),
static sessionidt shut_acct_n = 0;
tunnelt *tunnel = NULL; // Array of tunnel structures.
+bundlet *bundle = NULL; // Array of bundle structures.
+fragmentationt *frag = NULL; // Array of fragmentation structures.
sessiont *session = NULL; // Array of session structures.
sessionlocalt *sess_local = NULL; // Array of local per-session counters.
radiust *radius = NULL; // Array of radius structures.
static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
static tunnelidt new_tunnel(void);
static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len);
+static void bundleclear(bundleidt b);
// on slaves, alow BGP to withdraw cleanly before exiting
#define QUIT_DELAY 5
*(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16));
}
+void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra)
+{
+ uint16_t proto;
+ if (extra) {
+ // Skip the four extra bytes
+ p += 4;
+ l -= 4;
+ }
+
+ // Process this frame
+ if (*p & 1)
+ {
+ proto = *p++;
+ l--;
+ }
+ else
+ {
+ proto = ntohs(*(uint16_t *) p);
+ p += 2;
+ l -= 2;
+ }
+ if (proto == PPPIP)
+ {
+ if (session[s].die)
+ {
+ LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s);
+ return; // closing session, PPP not processed
+ }
+ session[s].last_packet = time_now;
+ processipin(s, t, p, l);
+ }
+ else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
+ {
+ if (session[s].die)
+ {
+ LOG(4, s, t, "MPPP: Session %d is closing. Don't process PPP packets\n", s);
+ return; // closing session, PPP not processed
+ }
+
+ session[s].last_packet = time_now;
+ processipv6in(s, t, p, l);
+ }
+ else
+ {
+ LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto);
+ }
+}
+
// process outgoing (to tunnel) IP
//
static void processipout(uint8_t *buf, int len)
uint8_t *data = buf; // Keep a copy of the originals.
int size = len;
- uint8_t b[MAXETHER + 20];
+ uint8_t b1[MAXETHER + 20];
+ uint8_t b2[MAXETHER + 20];
CSTAT(processipout);
return;
}
- LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
-
// Add on L2TP header
{
- uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP);
- if (!p) return;
- tunnelsend(b, len + (p-b), t); // send it...
+ bundleidt bid = 0;
+ if (session[s].bundle && 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];
+ LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s);
+ if (len > 256)
+ {
+ // Partition the packet to 2 fragments
+ uint32_t frag1len = len / 2;
+ uint32_t frag2len = len - frag1len;
+ uint8_t *p = makeppp(b1, sizeof(b1), buf, frag1len, s, t, PPPIP, 0, bid, MP_BEGIN);
+ uint8_t *q;
+
+ if (!p) return;
+ tunnelsend(b1, frag1len + (p-b1), t); // send it...
+ s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links];
+ LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s);
+ q = makeppp(b2, sizeof(b2), buf+frag1len, frag2len, s, t, PPPIP, 0, bid, MP_END);
+ if (!q) return;
+ tunnelsend(b2, frag2len + (q-b2), t); // send it...
+ }
+ else {
+ // Send it as one frame
+ uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS);
+ if (!p) return;
+ tunnelsend(b1, len + (p-b1), t); // send it...
+ }
+ }
+ else
+ {
+ uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, 0, 0);
+ if (!p) return;
+ tunnelsend(b1, len + (p-b1), t); // send it...
+ }
}
// Snooping this session, send it to intercept box
}
return;
}
+ 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];
+ LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %d\n", s);
+ }
t = session[s].tunnel;
sp = &session[s];
// Add on L2TP header
{
- uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6);
+ uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0);
if (!p) return;
tunnelsend(b, len + (p-b), t); // send it...
}
// Add on L2TP header
{
- uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP);
+ uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0);
if (!p) return;
tunnelsend(b, len + (p-b), t); // send it...
}
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, 0, 0, 0);
if (!q) return;
*q = ConfigReq;
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, 0, 0, 0);
if (!q) return;
*q = ConfigReq;
// kill a session now
void sessionkill(sessionidt s, char *reason)
{
+ bundleidt b;
CSTAT(sessionkill);
radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
+ if ((b = session[s].bundle))
+ {
+ // 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)
+ {
+ bundleclear(b);
+ LOG(3, s, 0, "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)
+ {
+ 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);
+ }
sessionclear(s);
cluster_send_session(s);
}
tunnel[t].state = TUNNELFREE;
}
+static void bundleclear(bundleidt b)
+{
+ if (!b) return;
+ memset(&bundle[b], 0, sizeof(bundle[b]));
+ bundle[b].state = BUNDLEFREE;
+}
+
// kill a tunnel now
static void tunnelkill(tunnelidt t, char *reason)
{
// start LCP
sess_local[s].lcp_authtype = config->radius_authprefer;
sess_local[s].ppp_mru = MRU;
+
+ // Set multilink options before sending initial LCP packet
+ sess_local[s].mp_mrru = 1614;
+ sess_local[s].mp_epdis = config->bind_address ? config->bind_address : my_address;
+
sendlcp(s, t);
change_state(s, lcp, RequestSent);
break;
processipin(s, t, p, l);
}
+ else if (proto == PPPMP)
+ {
+ if (session[s].die)
+ {
+ LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s);
+ return; // closing session, PPP not processed
+ }
+
+ session[s].last_packet = time_now;
+ if (session[s].walled_garden && !config->cluster_iam_master)
+ {
+ master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+ return;
+ }
+
+ processmpin(s, t, p, l);
+ }
else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
{
if (session[s].die)
continue;
}
+ // check for timed out sessions
+ if (session[s].timeout)
+ {
+ bundleidt bid = session[s].bundle;
+ if (bid)
+ {
+ clockt curr_time = time_now;
+ if (curr_time - bundle[bid].last_check >= 1)
+ {
+ bundle[bid].online_time += (curr_time-bundle[bid].last_check)*bundle[bid].num_of_links;
+ bundle[bid].last_check = curr_time;
+ if (bundle[bid].online_time >= session[s].timeout)
+ {
+ int ses;
+ for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--)
+ {
+ sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
+ s_actions++;
+ continue;
+ }
+ }
+ }
+ }
+ else if (session[s].timeout <= time_now - session[s].opened)
+ {
+ sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
+ s_actions++;
+ continue;
+ }
+ }
+
// PPP timeouts
if (sess_local[s].lcp.restart <= time_now)
{
{
uint8_t b[MAXETHER];
- uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP);
+ uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP, 1, 0, 0);
if (!q) continue;
*q = EchoReq;
LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno));
exit(1);
}
+ if (!(bundle = shared_malloc(sizeof(bundlet) * MAXBUNDLE)))
+ {
+ LOG(0, 0, 0, "Error doing malloc for bundles: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (!(frag = shared_malloc(sizeof(fragmentationt) * MAXBUNDLE)))
+ {
+ LOG(0, 0, 0, "Error doing malloc for fragmentations: %s\n", strerror(errno));
+ exit(1);
+ }
if (!(session = shared_malloc(sizeof(sessiont) * MAXSESSION)))
{
LOG(0, 0, 0, "Error doing malloc for sessions: %s\n", strerror(errno));
memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION);
memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL);
+ memset(bundle, 0, sizeof(bundlet) * MAXBUNDLE);
memset(session, 0, sizeof(sessiont) * MAXSESSION);
memset(radius, 0, sizeof(radiust) * MAXRADIUS);
memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL);
for (i = 1; i < MAXTUNNEL; i++)
tunnel[i].state = TUNNELUNDEF; // mark it as not filled in.
+ for (i = 1; i < MAXBUNDLE; i++) {
+ bundle[i].state = BUNDLEUNDEF;
+ }
+
if (!*hostname)
{
// Grab my hostname unless it's been specified
// Make sure this is right
session[s].tunnel = t;
+ // Join a bundle if the MRRU option is accepted
+ if (session[s].mrru > 0 && !session[s].bundle)
+ {
+ LOG(3, s, t, "This session can be part of multilink bundle\n");
+ if (join_bundle(s))
+ cluster_send_bundle(session[s].bundle);
+ }
+
// zap old sessions with same IP and/or username
// Don't kill gardened sessions - doing so leads to a DoS
// from someone who doesn't need to know the password
continue;
}
- if (config->allow_duplicate_users) continue;
- if (session[s].walled_garden || session[i].walled_garden) continue;
+ if (config->allow_duplicate_users)
+ continue;
+
+ if (session[s].walled_garden || session[i].walled_garden)
+ continue;
+
+ // Allow duplicate sessions for guest account.
+ if (*config->guest_user && !strcasecmp(user, config->guest_user))
+ continue;
+
+ // Allow duplicate sessions for multilink ones of the same bundle.
+ if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle)
+ continue;
+
+ // Drop the new session in case of duplicate sessionss, not the old one.
if (!strcasecmp(user, session[i].user))
sessionkill(i, "Duplicate session for users");
}
// L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.114 2006/04/23 23:18:32 bodea Exp $
+// $Id: l2tpns.h,v 1.115 2006/04/27 09:53:50 bodea Exp $
#ifndef __L2TPNS_H__
#define __L2TPNS_H__
#include <sys/types.h>
#include <libcli.h>
-#define VERSION "2.1.19"
+#define VERSION "2.2.0"
// Limits
#define MAXTUNNEL 500 // could be up to 65535
+#define MAXBUNDLE 300 // could be up to 65535
+#define MAXBUNDLESES 10 // Maximum number of member links in bundle
+#define MAXADDRESS 20 // Maximum length for the Endpoint Discrminiator address
+#define MAXFRAGNUM 1500 // Maximum number of Multilink fragment in a bundle
+#define MAXFRAGLEN 1000 // Maximum length for Multilink fragment
#define MAXSESSION 60000 // could be up to 65535
#define MAXTBFS 6000 // Maximum token bucket filters. Might need up to 2 * session.
#define MAXIPPOOL 131072 // max number of ip addresses in pool
#define RINGBUFFER_SIZE 10000 // Number of ringbuffer entries to allocate
#define MAX_LOG_LENGTH 512 // Maximum size of log message
-#define ECHO_TIMEOUT 60 // Time between last packet sent and LCP ECHO generation
+#define ECHO_TIMEOUT 10 // Time between last packet sent and LCP ECHO generation
#define IDLE_TIMEOUT 240 // Time between last packet sent and LCP ECHO generation
#define BUSY_WAIT_TIME 3000 // 5 minutes in 1/10th seconds to wait for radius to cleanup on shutdown
+#define MP_BEGIN 0x80 // This value is used when (b)egin bit is set in MP header
+#define MP_END 0x40 // This value is used when (e)nd bit is set in MP header
+#define MP_BOTH_BITS 0xC0 // This value is used when both bits (begin and end) are set in MP header
+
+#define DEFAULT_EPDIS_ADDRESS "L2TPNetServer" // Company name may goes here!
+
// Constants
#ifndef ETCDIR
#define ETCDIR "/etc/l2tpns"
// Types
typedef uint16_t sessionidt;
+typedef uint16_t bundleidt;
typedef uint16_t tunnelidt;
typedef uint32_t clockt;
typedef uint8_t hasht[16];
}
controlt;
+typedef struct {
+ uint8_t length; // Endpoint Discriminator length
+ uint8_t addr_class; // Endpoint Discriminator class
+ uint8_t address[MAXADDRESS]; // Endpoint Discriminator address
+} epdist;
+
+typedef struct {
+ uint16_t length; // Fragment length
+ uint8_t data[MAXFRAGLEN]; // Fragment data
+} fragmentt;
+
typedef struct
{
sessionidt next; // next session in linked list
char calling[MAXTEL]; // calling number
uint32_t tx_connect_speed;
uint32_t rx_connect_speed;
+ clockt timeout; // Session timeout
+ uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit
+ uint8_t mssf; // Multilink Short Sequence Number Header Format
+ epdist epdis; // Multilink Endpoint Discriminator
+ bundleidt bundle; // Multilink Bundle Identifier
in_addr_t snoop_ip; // Interception destination IP
uint16_t snoop_port; // Interception destination port
uint8_t walled_garden; // is this session gardened?
}
sessiont;
+typedef struct
+{
+ int state; // current state (bundlestate enum)
+ uint32_t seq_num_t; // Sequence Number (transmission)
+ uint32_t seq_num_m; // Last received frame sequence number (bearing B bit)
+ uint32_t offset; // Offset between sequence number and array index
+ uint8_t pending_frag; // Indicate that there is pending fragments to reassemble
+ uint8_t num_of_links; // Number of links joint to this bundle
+ uint32_t online_time; // The time this bundle is online
+ clockt last_check; // Last time the timeout is checked
+ uint32_t mrru; // Multilink Max-Receive-Reconstructed-Unit
+ uint8_t mssf; // Multilink Short Sequence Number Header Format
+ epdist epdis; // Multilink Endpoint Discriminator
+ char user[MAXUSER]; // Needed for matching member links
+ sessionidt current_ses; // Current session to use for sending (used in RR load-balancing)
+ sessionidt members[MAXBUNDLESES]; // Array for member links sessions
+}
+bundlet;
+
+typedef struct
+{
+ fragmentt fragment[MAXFRAGNUM];
+ uint8_t reassembled_frame[MAXETHER]; // The reassembled frame
+ uint16_t re_frame_len; // The reassembled frame length
+}
+fragmentationt;
+
#define AUTHPAP 1 // allow PAP
#define AUTHCHAP 2 // allow CHAP
// our MRU
uint16_t ppp_mru;
+ // our MRRU
+ uint16_t mp_mrru;
+
+ // our mssf
+ uint16_t mp_mssf;
+
+ // our Endpoint Discriminator
+ in_addr_t mp_epdis;
+
// DoS prevention
clockt last_packet_out;
uint32_t packets_out;
TUNNELUNDEF, // Undefined
};
+enum
+{
+ BUNDLEFREE, // Not in use
+ BUNDLEOPEN, // Active bundle
+ BUNDLEUNDEF, // Undefined
+};
+
+enum
+{
+ NULLCLASS = 0, //End Point Discriminator classes
+ LOCALADDR,
+ IPADDR,
+ IEEEMACADDR,
+ PPPMAGIC,
+ PSNDN,
+};
+
enum
{
RADIUSNULL, // Not in use
int radius_authprefer;
int allow_duplicate_users; // allow multiple logins with the same username
+ char guest_user[MAXUSER]; // allow multiple logins to guest account username
in_addr_t default_dns1, default_dns2;
int lock_pages; // Lock pages into memory.
int icmp_rate; // Max number of ICMP unreachable per second to send
int max_packets; // DoS prevention: per session limit of packets/0.1s
+ char epdis_addr[20]; // MP Endpoint Discriminator address
in_addr_t cluster_address; // Multicast address of cluster.
// Send to this address to have everyone hear.
int cluster_seq_number; // Sequence number of the next heartbeat we'll send out
// (or the seq number we're next expecting if we're a slave).
int cluster_undefined_sessions; // How many sessions we're yet to receive from the master.
+ int cluster_undefined_bundles; // How many bundles we're yet to receive from the master.
int cluster_undefined_tunnels; // How many tunnels we're yet to receive from the master.
int cluster_highest_sessionid;
+ int cluster_highest_bundleid;
int cluster_highest_tunnelid;
clockt cluster_last_hb; // Last time we saw a heartbeat from the master.
int cluster_last_hb_ver; // Heartbeat version last seen from master
int cluster_hb_interval; // How often to send a heartbeat.
int cluster_hb_timeout; // How many missed heartbeats trigger an election.
uint64_t cluster_table_version; // # state changes processed by cluster
-
- struct in6_addr ipv6_prefix; // Our IPv6 network pool.
-
-
int cluster_master_min_adv; // Master advertises routes while the number of up to date
// slaves is less than this value.
+ struct in6_addr ipv6_prefix; // Our IPv6 network pool.
+
#ifdef BGP
#define BGP_NUM_PEERS 2
uint16_t as_number;
void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
+void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
+void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra);
void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
void sendchap(sessionidt s, tunnelidt t);
-uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype);
+uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits);
void sendlcp(sessionidt s, tunnelidt t);
void send_ipin(sessionidt s, uint8_t *buf, int len);
void sendccp(sessionidt s, tunnelidt t);
void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t proto);
+int join_bundle(sessionidt s);
// radius.c
extern tunnelt *tunnel;
+extern bundlet *bundle;
extern sessiont *session;
extern sessionlocalt *sess_local;
extern ippoolt *ip_address_pool;
Summary: A high-speed clustered L2TP LNS
Name: l2tpns
-Version: 2.1.19
+Version: 2.2.0
Release: 1
License: GPL
Group: System Environment/Daemons
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog
-* Tue Apr 25 2006 Brendan O'Dea <bod@optus.net> 2.1.19-1
-- 2.1.19 release, see /usr/share/doc/l2tpns-2.1.19/Changes
+* Thu Apr 27 2006 Brendan O'Dea <bod@optus.net> 2.2.0-1
+- 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes
// L2TPNS PPP Stuff
-char const *cvs_id_ppp = "$Id: ppp.c,v 1.99 2006/04/18 06:00:08 bodea Exp $";
+char const *cvs_id_ppp = "$Id: ppp.c,v 1.100 2006/04/27 09:53:50 bodea Exp $";
#include <stdio.h>
#include <string.h>
#include "cluster.h"
extern tunnelt *tunnel;
+extern bundlet *bundle;
+extern fragmentationt *frag;
extern sessiont *session;
extern radiust *radius;
extern int tunfd;
extern configt *config;
static int add_lcp_auth(uint8_t *b, int size, int authtype);
+static bundleidt new_bundle(void);
+static int epdiscmp(epdist, epdist);
+static void setepdis(epdist *, epdist);
+static void ipcp_open(sessionidt s, tunnelidt t);
// Process PAP messages
void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
// respond now, either no RADIUS available or already authenticated
uint8_t b[MAXETHER];
uint8_t id = p[1];
- uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP);
+ uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0);
if (!p) return;
if (session[s].ip)
}
else
{
- // This-Layer-Up
- sendipcp(s, t);
- change_state(s, ipcp, RequestSent);
- // move to passive state for IPv6 (if configured), CCP
- if (config->ipv6_prefix.s6_addr[0])
- change_state(s, ipv6cp, Stopped);
+ if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1)
+ {
+ sessionidt first_ses = bundle[session[s].bundle].members[0];
+ LOG(3, s, t, "MPPP: Skipping IPCP negotiation for session:%d, first session of bundle is:%d\n", s, first_ses);
+ session[s].ip = session[first_ses].ip;
+ session[s].dns1 = session[first_ses].dns1;
+ session[s].dns2 = session[first_ses].dns2;
+ session[s].timeout = session[first_ses].timeout;
+ ipcp_open(s, t);
+ }
else
- change_state(s, ipv6cp, Closed);
+ {
+ // This-Layer-Up
+ sendipcp(s, t);
+ change_state(s, ipcp, RequestSent);
+ // move to passive state for IPv6 (if configured), CCP
+ if (config->ipv6_prefix.s6_addr[0])
+ change_state(s, ipv6cp, Stopped);
+ else
+ change_state(s, ipv6cp, Closed);
- change_state(s, ccp, Stopped);
+ change_state(s, ccp, Stopped);
+ }
}
}
{
if (!*response || **response != ConfigRej)
{
- queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype);
+ queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0);
if (!queued)
return 0;
if (*nak_sent >= config->ppp_max_failure)
return ppp_conf_rej(s, buf, blen, mtype, response, 0, packet, option);
- queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype);
+ queued = *response = makeppp(buf, blen, packet, 2, s, session[s].tunnel, mtype, 0, 0, 0);
if (!queued)
return 0;
l += 4;
if (l > mru) l = mru;
- q = makeppp(buf, size, 0, 0, s, t, proto);
+ q = makeppp(buf, size, 0, 0, s, t, proto, 0, 0, 0);
if (!q) return;
*q = CodeRej;
uint8_t *response = 0;
static uint8_t asyncmap[4] = { 0, 0, 0, 0 }; // all zero
static uint8_t authproto[5];
+ int changed = 0;
while (x > 2)
{
if (mru >= MINMTU)
{
session[s].mru = mru;
- cluster_send_session(s);
+ changed++;
break;
}
case 8: // Address-And-Control-Field-Compression
break;
+ case 17: // Multilink Max-Receive-Reconstructed-Unit
+ {
+ uint16_t mrru = ntohs(*(uint16_t *)(o + 2));
+ session[s].mrru = mrru;
+ changed++;
+ LOG(3, s, t, " Received PPP LCP option MRRU: %d\n",mrru);
+ }
+ break;
+
+ case 18: // Multilink Short Sequence Number Header Format
+ {
+ session[s].mssf = 1;
+ changed++;
+ LOG(3, s, t, " Received PPP LCP option MSSN format\n");
+ }
+ break;
+
+ case 19: // Multilink Endpoint Discriminator
+ {
+ uint8_t epdis_class = o[2];
+ int addr;
+
+ session[s].epdis.addr_class = epdis_class;
+ session[s].epdis.length = length - 3;
+ if (session[s].epdis.length > 20)
+ {
+ LOG(1, s, t, "Error: received EndDis Address Length more than 20: %d\n", session[s].epdis.length);
+ session[s].epdis.length = 20;
+ }
+
+ for (addr = 0; addr < session[s].epdis.length; addr++)
+ session[s].epdis.address[addr] = o[3+addr];
+
+ changed++;
+
+ switch (epdis_class)
+ {
+ case LOCALADDR:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis Local Address Class: %d\n",epdis_class);
+ break;
+ case IPADDR:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis IP Address Class: %d\n",epdis_class);
+ break;
+ case IEEEMACADDR:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis IEEE MAC Address Class: %d\n",epdis_class);
+ break;
+ case PPPMAGIC:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis PPP Magic No Class: %d\n",epdis_class);
+ break;
+ case PSNDN:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis PSND No Class: %d\n",epdis_class);
+ break;
+ default:
+ LOG(3, s, t, " Received PPP LCP option Multilink EndDis NULL Class %d\n",epdis_class);
+ }
+ }
+ break;
+
default: // Reject any unknown options
LOG(3, s, t, " Rejecting unknown PPP LCP option %d\n", type);
q = ppp_conf_rej(s, b, sizeof(b), PPPLCP, &response, q, p, o);
o += length;
}
+ if (changed)
+ cluster_send_session(s);
+
if (response)
{
l = q - response; // LCP packet length
else
{
// Send packet back as ConfigAck
- response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ response = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
switch (session[s].ppp.lcp)
{
case Closed:
- response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP);
+ response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
cluster_send_session(s);
break;
+ case 17: // Multilink Max-Receive-Reconstructed-Unit
+ {
+ if (*p == ConfigNak)
+ {
+ sess_local[s].mp_mrru = ntohs(*(uint16_t *)(o + 2));
+ LOG(3, s, t, " Remote requested MRRU of %u\n", sess_local[s].mp_mrru);
+ }
+ else
+ {
+ sess_local[s].mp_mrru = 0;
+ LOG(3, s, t, " Remote rejected MRRU negotiation\n");
+ }
+ }
+ break;
+
+ case 18: // Multilink Short Sequence Number Header Format
+ {
+ if (*p == ConfigNak)
+ {
+ sess_local[s].mp_mssf = 0;
+ LOG(3, s, t, " Remote requested Naked mssf\n");
+ }
+ else
+ {
+ sess_local[s].mp_mssf = 0;
+ LOG(3, s, t, " Remote rejected mssf\n");
+ }
+ }
+ break;
+
+ case 19: // Multilink Endpoint Discriminator
+ {
+ if (*p == ConfigNak)
+ {
+ LOG(2, s, t, " Remote should not configNak Endpoint Dis!\n");
+ }
+ else
+ {
+ sess_local[s].mp_epdis = 0;
+ LOG(3, s, t, " Remote rejected Endpoint Discriminator\n");
+ }
+ }
+ break;
+
default:
LOG(2, s, t, "LCP: remote sent %s for type %u?\n", ppp_code(*p), type);
sessionshutdown(s, "Unable to negotiate LCP.", CDN_ADMIN_DISC, TERM_USER_ERROR);
case Closed:
case Stopped:
{
- uint8_t *response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP);
+ uint8_t *response = makeppp(b, sizeof(b), p, 2, s, t, PPPLCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
}
*p = TerminateAck; // send ack
- q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "LCP: send %s\n", ppp_code(*q));
{
*p = EchoReply; // reply
*(uint32_t *) (p + 4) = htonl(session[s].magic); // our magic number
- q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
LOG(4, s, t, "LCP: send %s\n", ppp_code(*q));
}
}
+int join_bundle(sessionidt s)
+{
+ // Search for a bundle to join
+ bundleidt i;
+ bundleidt b;
+ for (i = 1; i < MAXBUNDLE; i++)
+ {
+ if (bundle[i].state != BUNDLEFREE)
+ {
+ if (epdiscmp(session[s].epdis,bundle[i].epdis) && !strcmp(session[s].user, bundle[i].user))
+ {
+ session[s].bundle = i;
+ bundle[i].mrru = session[s].mrru;
+ bundle[i].mssf = session[s].mssf;
+ if (session[s].epdis.length > 0)
+ setepdis(&bundle[i].epdis, session[s].epdis);
+
+ strcpy(bundle[i].user, session[s].user);
+ bundle[i].members[bundle[i].num_of_links] = s;
+ bundle[i].num_of_links++;
+ LOG(3, s, session[s].tunnel, "MPPP: Bundling additional line in bundle (%d), lines:%d\n",i,bundle[i].num_of_links);
+ return i;
+ }
+ }
+ }
+
+ // No previously created bundle was found for this session, so create a new one
+ if (!(b = new_bundle())) return 0;
+
+ session[s].bundle = b;
+ bundle[b].mrru = session[s].mrru;
+ bundle[b].mssf = session[s].mssf;
+ if (session[s].epdis.length > 0)
+ setepdis(&bundle[b].epdis, session[s].epdis);
+
+ strcpy(bundle[b].user, session[s].user);
+ bundle[b].members[0] = s;
+ LOG(3, s, session[s].tunnel, "MPPP: Created a new bundle (%d)\n", b);
+ return b;
+}
+
+static int epdiscmp(epdist ep1, epdist ep2)
+{
+ int ad;
+ if (ep1.length != ep2.length)
+ return 0;
+
+ if (ep1.addr_class != ep2.addr_class)
+ return 0;
+
+ for (ad = 0; ad < ep1.length; ad++)
+ if (ep1.address[ad] != ep2.address[ad])
+ return 0;
+
+ return 1;
+}
+
+static void setepdis(epdist *ep1, epdist ep2)
+{
+ int ad;
+ ep1->length = ep2.length;
+ ep1->addr_class = ep2.addr_class;
+ for (ad = 0; ad < ep2.length; ad++)
+ ep1->address[ad] = ep2.address[ad];
+}
+
+static bundleidt new_bundle()
+{
+ bundleidt i;
+ for (i = 1; i < MAXBUNDLE; i++)
+ {
+ if (bundle[i].state == BUNDLEFREE)
+ {
+ LOG(4, 0, 0, "MPPP: Assigning bundle ID %d\n", i);
+ bundle[i].num_of_links = 1;
+ bundle[i].last_check = time_now; // Initialize last_check value
+ bundle[i].state = BUNDLEOPEN;
+ bundle[i].current_ses = -1; // This is to enforce the first session 0 to be used at first
+ if (i > config->cluster_highest_bundleid)
+ config->cluster_highest_bundleid = i;
+ return i;
+ }
+ }
+ LOG(0, 0, 0, "MPPP: Can't find a free bundle! There shouldn't be this many in use!\n");
+ return 0;
+}
+
static void ipcp_open(sessionidt s, tunnelidt t)
{
LOG(3, s, t, "IPCP: Opened, session is now active\n");
else if (gotip)
{
// Send packet back as ConfigAck
- response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP);
+ response = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
switch (session[s].ppp.ipcp)
{
case Closed:
- response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPCP);
+ response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPCP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
}
*p = TerminateAck; // send ack
- q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPIPCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "IPCP: send %s\n", ppp_code(*q));
else if (gotip)
{
// Send packet back as ConfigAck
- response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP);
+ response = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0);
if (!response) return;
*response = ConfigAck;
}
switch (session[s].ppp.ipv6cp)
{
case Closed:
- response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPV6CP);
+ response = makeppp(b, sizeof(b), p, 2, s, t, PPPIPV6CP, 0, 0, 0);
if (!response) return;
*response = TerminateAck;
*((uint16_t *) (response + 2)) = htons(l = 4);
}
*p = TerminateAck; // send ack
- q = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPIPV6CP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "IPV6CP: send %s\n", ppp_code(*q));
if (session[s].ppp.phase != Network || session[s].ppp.ipcp != Opened)
return;
- // no spoof (do sessionbyip to handled statically routed subnets)
- if (ip != session[s].ip && sessionbyip(htonl(ip)) != s)
+ if (!session[s].bundle || bundle[session[s].bundle].num_of_links < 2) // FIXME:
{
- LOG(5, s, t, "Dropping packet with spoofed IP %s\n", fmtaddr(htonl(ip), 0));
- return;
+ // no spoof (do sessionbyip to handled statically routed subnets)
+ if (ip != session[s].ip && sessionbyip(htonl(ip)) != s)
+ {
+ LOG(4, s, t, "Dropping packet with spoofed IP %s\n", fmtaddr(htonl(ip), 0));
+ return;
+ }
}
// run access-list if any
INC_STAT(tun_tx_bytes, l);
}
+// process Multilink PPP packet received
+void processmpin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
+{
+ bundleidt b = session[s].bundle;
+ uint8_t begin_frame;
+ uint8_t end_frame;
+ uint32_t seq_num;
+ uint32_t offset;
+
+ if (!b)
+ {
+ LOG(3, s, t, "MPPP: Invalid bundle id: 0\n");
+ return;
+ }
+
+ begin_frame = (*p & 0x80);
+ end_frame = (*p & 0x40);
+ if (session[s].mssf)
+ {
+ // Get 12 bit for seq number
+ uint16_t short_seq_num = ntohs((*(uint16_t *) p) & 0xFF0F);
+ uint16_t short_seq_num2 = short_seq_num >> 4;
+ p += 2;
+ l -= 2;
+ seq_num = short_seq_num2;
+ // After this point the pointer should be advanced 2 bytes
+ LOG(3, s, t, "MPPP: 12 bits, sequence number: %d, short1: %d, short2: %d\n",seq_num, short_seq_num, short_seq_num2);
+ }
+ else
+ {
+ // Get 24 bit for seq number
+ p++;
+ seq_num = ntohl((*(uint32_t *) p) & 0xFFFFFF00);
+ seq_num = seq_num >> 8;
+ p += 3;
+ l -= 4;
+ // After this point the pointer should be advanced 4 bytes
+ LOG(4, s, t, "MPPP: 24 bits sequence number:%d\n",seq_num);
+ }
+
+ if (seq_num - bundle[b].offset < 0)
+ {
+ bundle[b].offset = 0;
+ bundle[b].pending_frag = 0;
+ }
+
+ offset = bundle[b].offset;
+ if (begin_frame)
+ {
+ // Check for previous non-assembled frames
+ int error = 0;
+ if (bundle[b].pending_frag)
+ {
+ uint32_t fn = bundle[b].seq_num_m - offset;
+ uint16_t cur_len;
+ bundle[b].pending_frag = 0;
+ // Check for array indexes
+ if (fn < 0 || fn > MAXFRAGNUM)
+ {
+ LOG(2, s, t, "ERROR: Index out of range fn:%d, bundle:%d\n",fn,b);
+ return;
+ }
+
+ if (seq_num-offset < 0 || seq_num-offset > MAXFRAGNUM)
+ {
+ LOG(2, s, t, "ERROR: Index out of range fn(last):%d, bundle:%d\n",fn,b);
+ return;
+ }
+ /////////////////////////////////////////////////////
+ cur_len = 4; // This is set to 4 to leave 4 bytes for function processipin
+ for (fn = bundle[b].seq_num_m - offset; fn < seq_num - offset; fn++)
+ {
+ if (!frag[b].fragment[fn].length)
+ {
+ LOG(4, s, t, "MPPP: Found lost fragment while reassembling frame %d in (%d,%d)\n",fn, bundle[b].seq_num_m-offset, seq_num-offset);
+ error = 1;
+ break;
+ }
+
+ if (cur_len + frag[b].fragment[fn].length > MAXETHER)
+ {
+ LOG(2, s, t, "MPPP: ERROR: very long frame after assembling %d\n", frag[b].fragment[fn].length+cur_len);
+ error = 1;
+ break;
+ }
+
+ memcpy(frag[b].reassembled_frame+cur_len, frag[b].fragment[fn].data, frag[b].fragment[fn].length);
+ cur_len += frag[b].fragment[fn].length;
+ frag[b].fragment[fn].length = 0; // Indicates that this fragment has been consumed
+ // This is usefull for compression
+ memset(frag[b].fragment[fn].data, 0, sizeof(frag[b].fragment[fn].data));
+ }
+
+ if (!error)
+ {
+ frag[b].re_frame_len = cur_len;
+ // Process the resassembled frame
+ LOG(4, s, t, "MPPP: Process the reassembled frame, len=%d\n",cur_len);
+ processmpframe(s, t, frag[b].reassembled_frame, frag[b].re_frame_len, 1);
+ // Set reassembled frame length to zero after processing it
+ frag[b].re_frame_len = 0;
+ memset(frag[b].reassembled_frame, 0, sizeof(frag[b].reassembled_frame));
+ }
+ }
+ //////////////////////////////////////////
+ bundle[b].seq_num_m = seq_num;
+ if (end_frame)
+ {
+ // Both bits are set
+ LOG(4, s, t, "MPPP: Both bits are set (Begin and End).\n");
+ processmpframe(s, t, p, l, 0);
+ // The maximum number of fragments is 1500
+ if (seq_num - bundle[b].offset >= 1400)
+ {
+ bundle[b].offset = seq_num;
+ LOG(4, s, t, "MPPP: Setting offset to: %d\n",bundle[b].offset);
+ }
+ }
+ else
+ {
+ bundle[b].pending_frag = 1;
+ // End bit is clear
+ LOG(4, s, t, "MPPP: Push to receive buffer\n");
+ // Push to the receive buffer
+ // Array indexes checking
+ if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM)
+ {
+ LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(1) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b);
+ return;
+ }
+ // Perform length checking
+ if (l > MAXFRAGLEN)
+ {
+ LOG(2, s, t, "MPPP: ERROR: very long fragment length (1)\n");
+ return;
+ }
+ frag[b].fragment[seq_num - offset].length = l;
+ memcpy(frag[b].fragment[seq_num - offset].data, p, l);
+ }
+ }
+ else
+ {
+ LOG(4, s, t, "MPPP: Push to receive buffer\n");
+ // Push to the receive buffer
+ // Array indexes checking
+ if (seq_num-offset < 0 || seq_num-offset >= MAXFRAGNUM)
+ {
+ LOG(2, s, t, "ERROR: Index out of range, push to receive buffer(2) seq:%d, offset:%d, bundle:%d\n",seq_num,offset,b);
+ return;
+ }
+ // Perform length checking
+ if (l > MAXFRAGLEN)
+ {
+ LOG(2, s, t, "MPPP: ERROR: very long fragment length (2).\n");
+ return;
+ }
+ frag[b].fragment[seq_num - offset].length = l;
+ memcpy(frag[b].fragment[seq_num - offset].data, p, l);
+ }
+}
+
// process IPv6 packet received
//
// This MUST be called with at least 4 byte behind 'p'.
STAT(tunnel_rx_errors);
}
- LOG(3, s, t, "CCP: recv %s\n", ppp_code(*p));
+ LOG(4, s, t, "CCP: recv %s\n", ppp_code(*p));
if (*p == ConfigAck)
{
switch (session[s].ppp.ccp)
else // compression requested--reject
*p = ConfigRej;
- q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
switch (session[s].ppp.ccp)
{
case Closed:
- q = makeppp(b, sizeof(b), p, 2, s, t, PPPCCP);
+ q = makeppp(b, sizeof(b), p, 2, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
*q = TerminateAck;
*((uint16_t *) (q + 2)) = htons(l = 4);
return;
}
- LOG(3, s, t, "CCP: send %s\n", ppp_code(*q));
+ LOG(4, s, t, "CCP: send %s\n", ppp_code(*q));
tunnelsend(b, l + (q - b), t);
}
else if (*p == TerminateReq)
{
*p = TerminateAck;
- q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP);
+ q = makeppp(b, sizeof(b), p, l, s, t, PPPCCP, 0, 0, 0);
if (!q) return;
LOG(3, s, t, "CCP: send %s\n", ppp_code(*q));
tunnelsend(b, l + (q - b), t);
STAT(tunnel_tx_errors);
return ;
}
- q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP);
+ q = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
if (!q) return;
*q = 1; // challenge
// fill in a L2TP message with a PPP frame,
// returns start of PPP frame
-uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype)
+uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, sessionidt s, tunnelidt t, uint16_t mtype, uint8_t prio, bundleidt bid, uint8_t mp_bits)
{
- if (size < 12) // Need more space than this!!
+ uint16_t hdr = 0x0002; // L2TP with no options
+ uint16_t type = mtype;
+ uint8_t *start = b;
+
+ if (size < 16) // Need more space than this!!
{
LOG(0, s, t, "makeppp buffer too small for L2TP header (size=%d)\n", size);
return NULL;
}
- *(uint16_t *) (b + 0) = htons(0x0002); // L2TP with no options
+ if (prio) hdr |= 0x0100; // set priority bit
+
+ *(uint16_t *) (b + 0) = htons(hdr);
*(uint16_t *) (b + 2) = htons(tunnel[t].far); // tunnel
*(uint16_t *) (b + 4) = htons(session[s].far); // session
b += 6;
- if (mtype == PPPLCP || !(session[s].flags & SESSION_ACFC))
+
+ // Check whether this session is part of multilink
+ if (bid)
+ {
+ if (bundle[bid].num_of_links > 1)
+ type = PPPMP; // Change PPP message type to the PPPMP
+ else
+ bid = 0;
+ }
+
+ if (type == PPPLCP || !(session[s].flags & SESSION_ACFC))
{
*(uint16_t *) b = htons(0xFF03); // HDLC header
b += 2;
}
- if (mtype < 0x100 && session[s].flags & SESSION_PFC)
- *b++ = mtype;
+
+ if (type < 0x100 && session[s].flags & SESSION_PFC)
+ {
+ *b++ = type;
+ }
else
{
- *(uint16_t *) b = htons(mtype);
+ *(uint16_t *) b = htons(type);
b += 2;
}
- if (l + 12 > size)
+ if (bid)
{
- LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%d)\n", size, l + 12);
+ // Set the sequence number and (B)egin (E)nd flags
+ if (session[s].mssf)
+ {
+ // Set the multilink bits
+ uint16_t bits_send = mp_bits;
+ *(uint16_t *) b = htons((bundle[bid].seq_num_t & 0xFF0F)|bits_send);
+ b += 2;
+ }
+ else
+ {
+ *(uint32_t *) b = htonl(bundle[bid].seq_num_t);
+ // Set the multilink bits
+ *b = mp_bits;
+ b += 4;
+ }
+
+ bundle[bid].seq_num_t++;
+
+ // Add the message type if this fragment has the begin bit set
+ if (mp_bits & MP_BEGIN)
+ {
+ //*b++ = mtype; // The next two lines are instead of this
+ *(uint16_t *) b = htons(mtype); // Message type
+ b += 2;
+ }
+ }
+
+ if ((b - start) + l > size)
+ {
+ LOG(2, s, t, "makeppp would overflow buffer (size=%d, header+payload=%d)\n", size, (b - start) + l);
return NULL;
}
+ // Copy the payload
if (p && l)
memcpy(b, p, l);
uint8_t b[500], *q, *l;
int authtype = sess_local[s].lcp_authtype;
- if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPLCP)))
+ if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPLCP, 0, 0, 0)))
return;
- LOG(3, s, t, "LCP: send ConfigReq%s%s%s\n",
+ LOG(3, s, t, "LCP: send ConfigReq%s%s%s including MP options\n",
authtype ? " (" : "",
authtype ? (authtype == AUTHCHAP ? "CHAP" : "PAP") : "",
authtype ? ")" : "");
l += 4;
}
+ if (sess_local[s].mp_mrru)
+ {
+ *l++ = 17; *l++ = 4; // Multilink Max-Receive-Reconstructed-Unit (length 4)
+ *(uint16_t *) l = htons(sess_local[s].mp_mrru); l += 2;
+ }
+
+ if (sess_local[s].mp_epdis)
+ {
+ *l++ = 19; *l++ = 7; // Multilink Endpoint Discriminator (length 7)
+ *l++ = IPADDR; // Endpoint Discriminator class
+ *(uint32_t *) l = htonl(sess_local[s].mp_epdis);
+ l += 4;
+ }
+
*(uint16_t *)(q + 2) = htons(l - q); // Length
LOG_HEX(5, "PPPLCP", q, l - q);
{
uint8_t b[500], *q;
- if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPCCP)))
+ if (!(q = makeppp(b, sizeof(b), NULL, 0, s, t, PPPCCP, 0, 0, 0)))
return;
LOG(3, s, t, "CCP: send ConfigReq (no compression)\n");
l += 6;
if (l > mru) l = mru;
- q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP);
+ q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP, 0, 0, 0);
if (!q) return;
*q = ProtocolRej;
// L2TPNS Radius Stuff
-char const *cvs_id_radius = "$Id: radius.c,v 1.49 2006/04/13 11:14:35 bodea Exp $";
+char const *cvs_id_radius = "$Id: radius.c,v 1.50 2006/04/27 09:53:50 bodea Exp $";
#include <time.h>
#include <stdio.h>
if (radius[r].chap)
{
// CHAP
- uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP);
+ uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPCHAP, 0, 0, 0);
if (!p) return; // Abort!
*p = (r_code == AccessAccept) ? 3 : 4; // ack/nak
else
{
// PAP
- uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP);
+ uint8_t *p = makeppp(b, sizeof(b), 0, 0, s, t, PPPPAP, 0, 0, 0);
if (!p) return; // Abort!
// ack/nak
LOG(3, s, session[s].tunnel, " Radius reply contains primary DNS address %s\n",
fmtaddr(htonl(session[s].dns1), 0));
}
+ else if (*p == 27)
+ {
+ // Session timeout
+ if (p[1] < 6) {
+ LOG(2, s, session[s].tunnel, "Error: Received Session timeout with length %d < 6\n", p[1]);
+ continue;
+ }
+
+ session[s].timeout = ntohl(*(uint32_t *) (p + 2));
+ LOG(3, s, session[s].tunnel, " Radius reply contains Session timeout %d\n", session[s].timeout);
+ if (!session[s].timeout)
+ sessionshutdown(s, "Session timeout is zero", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
+ }
else if (*p == 136)
{
// DNS address