add Multilink support from Khaled Al Hamwi
authorBrendan O'Dea <bod@optus.net>
Thu, 27 Apr 2006 09:53:49 +0000 (09:53 +0000)
committerBrendan O'Dea <bod@optus.net>
Thu, 27 Apr 2006 09:53:49 +0000 (09:53 +0000)
13 files changed:
Changes
Docs/startup-config.5
THANKS
cli.c
cluster.c
cluster.h
etc/startup-config.default
icmp.c
l2tpns.c
l2tpns.h
l2tpns.spec
ppp.c
radius.c

diff --git a/Changes b/Changes
index ce33fe5..3c7c124 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,7 @@
-* 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.
index 97ebab9..d0036e8 100644 (file)
@@ -2,7 +2,7 @@
 .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
@@ -123,6 +123,9 @@ Allow multiple logins with the same username.  If false (the default),
 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
diff --git a/THANKS b/THANKS
index 1016be5..046a513 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -26,3 +26,4 @@ Jon Morby                  <jon@fido.net>
 Paul Martin                <pm@zetnet.net>
 Jonathan Yarden            <jyarden@bluegrass.net>
 Patrick Cole               <z@amused.net>
+Khaled Al Hamwi            <kh.alhamwi@gmail.com>
diff --git a/cli.c b/cli.c
index 24634f2..f007ac3 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -2,7 +2,7 @@
 // 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>
@@ -36,6 +36,7 @@ char const *cvs_id_cli = "$Id: cli.c,v 1.71 2005-12-06 09:43:42 bodea Exp $";
 #endif
 
 extern tunnelt *tunnel;
+extern bundlet *bundle;
 extern sessiont *session;
 extern radiust *radius;
 extern ippoolt *ip_address_pool;
@@ -426,6 +427,14 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        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);
@@ -492,7 +501,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
        }
 
        // 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",
@@ -505,13 +514,20 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        "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 : "*",
@@ -524,6 +540,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                                (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 : "*");
        }
index cc1e875..6e1bf57 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -1,6 +1,6 @@
 // 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>
@@ -42,6 +42,7 @@ extern int cluster_sockfd;            // The filedescriptor for the cluster communications
 
 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
 
@@ -85,6 +86,7 @@ int cluster_init()
        int opt;
 
        config->cluster_undefined_sessions = MAXSESSION-1;
+       config->cluster_undefined_bundles = MAXBUNDLE-1;
        config->cluster_undefined_tunnels = MAXTUNNEL-1;
 
        if (!config->cluster_address)
@@ -227,7 +229,7 @@ static void cluster_uptodate(void)
        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;
@@ -520,7 +522,7 @@ void cluster_check_slaves(void)
 //
 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;
@@ -614,6 +616,19 @@ void cluster_check_master(void)
                        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.
@@ -675,10 +690,11 @@ void cluster_check_master(void)
        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
 
@@ -696,7 +712,7 @@ void cluster_check_master(void)
 // 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;
 
@@ -705,7 +721,7 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
        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
@@ -721,6 +737,19 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
                        ++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;
@@ -735,9 +764,9 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
        }
 
 
-       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;
        }
 
@@ -770,6 +799,27 @@ static int hb_add_type(uint8_t **p, int type, int id)
                        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];
@@ -802,7 +852,7 @@ static int hb_add_type(uint8_t **p, int type, int 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;
@@ -823,7 +873,9 @@ void cluster_heartbeat()
        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;
@@ -878,6 +930,22 @@ void cluster_heartbeat()
                ++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?
@@ -887,9 +955,9 @@ void cluster_heartbeat()
        }
 
        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;
@@ -938,6 +1006,17 @@ int cluster_send_session(int sid)
        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)
 {
@@ -1175,6 +1254,31 @@ static int cluster_recv_session(int more, uint8_t *p)
        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) {
@@ -1461,7 +1565,7 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
 
                        // 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)
        {
@@ -1569,6 +1673,34 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
                                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!!
@@ -1754,11 +1886,13 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc)
                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);
        }
index 4fe7ad0..a568029 100644 (file)
--- a/cluster.h
+++ b/cluster.h
@@ -1,5 +1,5 @@
 // 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__
@@ -21,6 +21,8 @@
 #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!)
@@ -43,9 +45,11 @@ typedef struct {
 
        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
@@ -74,6 +78,7 @@ typedef struct {
 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);
index 0c5051c..4d77482 100644 (file)
@@ -41,6 +41,9 @@ set radius_secret "secret"
 # 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"
 
@@ -96,6 +99,9 @@ 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"
 
diff --git a/icmp.c b/icmp.c
index 56b83b4..86c9bdf 100644 (file)
--- a/icmp.c
+++ b/icmp.c
@@ -1,6 +1,6 @@
 // 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>
@@ -114,7 +114,7 @@ void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip)
        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)
        {
index b4828c3..b373955 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -4,7 +4,7 @@
 // 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>
@@ -127,6 +127,7 @@ config_descriptt config_values[] = {
        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),
@@ -174,6 +175,8 @@ static sessiont shut_acct[8192];
 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.
@@ -203,6 +206,7 @@ static void plugins_done(void);
 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
@@ -1051,6 +1055,54 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t
        *(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)
@@ -1063,7 +1115,8 @@ 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);
 
@@ -1183,13 +1236,43 @@ static void processipout(uint8_t *buf, int len)
                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
@@ -1273,6 +1356,12 @@ static void processipv6out(uint8_t * buf, int len)
                }
                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];
 
@@ -1298,7 +1387,7 @@ static void processipv6out(uint8_t * buf, int len)
 
        // 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...
        }
@@ -1350,7 +1439,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
 
        // 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...
        }
@@ -1679,7 +1768,7 @@ void sendipcp(sessionidt s, tunnelidt t)
                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;
@@ -1703,7 +1792,7 @@ void sendipv6cp(sessionidt s, tunnelidt t)
        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;
@@ -1735,6 +1824,7 @@ static void sessionclear(sessionidt s)
 // kill a session now
 void sessionkill(sessionidt s, char *reason)
 {
+       bundleidt b;
 
        CSTAT(sessionkill);
 
@@ -1753,6 +1843,38 @@ void sessionkill(sessionidt s, char *reason)
                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);
 }
@@ -1764,6 +1886,13 @@ static void tunnelclear(tunnelidt t)
        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)
 {
@@ -2556,6 +2685,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        // 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;
@@ -2679,6 +2813,23 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                        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)
@@ -2873,6 +3024,37 @@ static void regular_cleanups(double period)
                        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)
                {
@@ -2999,7 +3181,7 @@ static void regular_cleanups(double period)
                {
                        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;
@@ -3714,6 +3896,16 @@ static void initdata(int optdebug, char *optconfig)
                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));
@@ -3760,6 +3952,7 @@ static void initdata(int optdebug, char *optconfig)
        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);
@@ -3777,6 +3970,10 @@ static void initdata(int optdebug, char *optconfig)
        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
@@ -4601,6 +4798,14 @@ int sessionsetup(sessionidt s, tunnelidt t)
        // 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
@@ -4617,8 +4822,21 @@ int sessionsetup(sessionidt s, tunnelidt t)
                                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");
                }
index 8997f78..0701e0c 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,5 +1,5 @@
 // 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"
@@ -186,6 +197,7 @@ enum {
 
 // Types
 typedef uint16_t sessionidt;
+typedef uint16_t bundleidt;
 typedef uint16_t tunnelidt;
 typedef uint32_t clockt;
 typedef uint8_t hasht[16];
@@ -231,6 +243,17 @@ typedef struct controls            // control message
 }
 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
@@ -273,6 +296,11 @@ typedef struct
        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?
@@ -282,6 +310,33 @@ typedef struct
 }
 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
 
@@ -311,6 +366,15 @@ typedef struct
        // 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;
@@ -407,6 +471,23 @@ enum
        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
@@ -561,6 +642,7 @@ typedef struct
        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;
 
@@ -583,6 +665,7 @@ typedef struct
        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.
@@ -594,8 +677,10 @@ typedef struct
        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
@@ -605,13 +690,11 @@ typedef struct
        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;
@@ -727,14 +810,17 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l);
 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
@@ -802,6 +888,7 @@ void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, uint
 
 
 extern tunnelt *tunnel;
+extern bundlet *bundle;
 extern sessiont *session;
 extern sessionlocalt *sess_local;
 extern ippoolt *ip_address_pool;
index da52c9a..4ed564c 100644 (file)
@@ -1,6 +1,6 @@
 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
@@ -43,5 +43,5 @@ rm -rf %{buildroot}
 %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
diff --git a/ppp.c b/ppp.c
index 49f0858..c369f37 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,6 +1,6 @@
 // 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>
@@ -15,6 +15,8 @@ char const *cvs_id_ppp = "$Id: ppp.c,v 1.99 2006-04-18 06:00:08 bodea Exp $";
 #include "cluster.h"
 
 extern tunnelt *tunnel;
+extern bundlet *bundle;
+extern fragmentationt *frag;
 extern sessiont *session;
 extern radiust *radius;
 extern int tunfd;
@@ -24,6 +26,10 @@ extern time_t time_now;
 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)
@@ -90,7 +96,7 @@ 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)
@@ -352,16 +358,29 @@ void lcp_open(sessionidt s, tunnelidt t)
        }
        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);
+               }
        }
 }
 
@@ -379,7 +398,7 @@ static uint8_t *ppp_conf_rej(sessionidt s, uint8_t *buf, size_t blen, uint16_t m
 {
        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;
 
@@ -423,7 +442,7 @@ static uint8_t *ppp_conf_nak(sessionidt s, uint8_t *buf, size_t blen, uint16_t m
                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;
 
@@ -455,7 +474,7 @@ static void ppp_code_rej(sessionidt s, tunnelidt t, uint16_t proto,
        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;
@@ -567,6 +586,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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)
                {
@@ -582,7 +602,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                                                if (mru >= MINMTU)
                                                {
                                                        session[s].mru = mru;
-                                                       cluster_send_session(s);
+                                                       changed++;
                                                        break;
                                                }
 
@@ -655,6 +675,64 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                                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);
@@ -663,6 +741,9 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                        o += length;
                }
 
+               if (changed)
+                       cluster_send_session(s);
+
                if (response)
                {
                        l = q - response; // LCP packet length
@@ -671,7 +752,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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;
                }
@@ -679,7 +760,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -812,6 +893,50 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                                        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);
@@ -835,7 +960,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -893,7 +1018,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                }
 
                *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));
@@ -929,7 +1054,7 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        {
                *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));
@@ -947,6 +1072,93 @@ void processlcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        }
 }
 
+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");
@@ -1105,7 +1317,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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;
                }
@@ -1119,7 +1331,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -1192,7 +1404,7 @@ void processipcp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                }
 
                *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));
@@ -1326,7 +1538,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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;
                }
@@ -1340,7 +1552,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -1413,7 +1625,7 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                }
 
                *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));
@@ -1456,11 +1668,14 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        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
@@ -1526,6 +1741,167 @@ void processipin(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        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'.
@@ -1685,7 +2061,7 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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)
@@ -1718,13 +2094,13 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -1770,13 +2146,13 @@ void processccp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                        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);
@@ -1820,7 +2196,7 @@ void sendchap(sessionidt s, tunnelidt 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
@@ -1834,37 +2210,86 @@ void sendchap(sessionidt s, tunnelidt t)
 
 // 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);
 
@@ -1903,10 +2328,10 @@ void sendlcp(sessionidt s, tunnelidt t)
        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 ? ")" : "");
@@ -1933,6 +2358,20 @@ void sendlcp(sessionidt s, tunnelidt t)
                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);
@@ -1947,7 +2386,7 @@ void sendccp(sessionidt s, tunnelidt t)
 {
        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");
@@ -1974,7 +2413,7 @@ void protoreject(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint16_t pro
        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;
index b886b32..bfeda40 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -1,6 +1,6 @@
 // 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>
@@ -537,7 +537,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
                        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
@@ -551,7 +551,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
                        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
@@ -593,6 +593,19 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                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