Following development LAC functionality
authorfendo <fendo@bi12info.com>
Thu, 6 Dec 2012 00:40:12 +0000 (01:40 +0100)
committerfendo <fendo@bi12info.com>
Thu, 6 Dec 2012 00:40:12 +0000 (01:40 +0100)
Changes
Docs/manual.html
cli.c
cluster.c
cluster.h
l2tplac.c
l2tplac.h
l2tpns.c
l2tpns.h
ppp.c
radius.c

diff --git a/Changes b/Changes
index decb16e..6e98ab6 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,10 @@
+* 2012 Fernando Alves <fernando.alves@sameswireless.fr> 2.2.1-2fdn x
+- Fix MLPPP functionality.
+- Fix the inverted "delete/add" of the routes, in cluster mode.
+- Add a echo_timeout configuration option.
+- Add a idle_echo_timeout configuration option.
+- Add LAC functionality, possibility to forward ppp to Remote LNS.
+
 * Sun Sep 11 2011 Brendan O'Dea <bod@optus.net> 2.2.x
 - Apply MLPPP patch from Muhammad Tayseer Alquoatli.
 - Apply patch from Michael O to avoid sending multiple CDNs.
 * Sun Sep 11 2011 Brendan O'Dea <bod@optus.net> 2.2.x
 - Apply MLPPP patch from Muhammad Tayseer Alquoatli.
 - Apply patch from Michael O to avoid sending multiple CDNs.
index e3e19db..f52ad29 100644 (file)
@@ -368,8 +368,22 @@ Drop sessions who have not responded within idle_echo_timeout seconds
 (default: 240 (seconds))
 </LI>
 
 (default: 240 (seconds))
 </LI>
 
+<LI><B>bind_portremotelns</B> (short)<BR>
+Port to bind for the Remote LNS (default: 65432).
+</LI>
+
 </UL>
 
 </UL>
 
+<P>The REMOTES LNS configuration is entered by the command:
+<DL> <DD><B>setforward</B> <I>MASK</I> <I>IP</I> <I>PORT</I> <I>SECRET</I> </DL>
+
+where <I>MASK</I> specifies the mask of users who have forwarded to
+remote LNS (ex: /myISP@company.com).</BR>
+where <I>IP</I> specifies the IP of the  remote LNS (ex: 66.66.66.55).</BR>
+where <I>PORT</I> specifies the L2TP Port of the remote LNS
+(Normally should be 1701) (ex: 1701).</BR>
+where <I>SECRET</I> specifies the secret password the remote LNS (ex: mysecret).</BR>
+
 <P>BGP routing configuration is entered by the command:
 The routing configuration section is entered by the command
 <DL><DD><B>router bgp</B> <I>as</I></DL>
 <P>BGP routing configuration is entered by the command:
 The routing configuration section is entered by the command
 <DL><DD><B>router bgp</B> <I>as</I></DL>
diff --git a/cli.c b/cli.c
index 5b0bcbc..09ea768 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -104,6 +104,7 @@ static int cmd_shutdown(struct cli_def *cli, char *command, char **argv, int arg
 static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc);
 #ifdef LAC
 static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int argc);
 static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc);
 #ifdef LAC
 static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int argc);
+static int cmd_show_rmtlnsconf(struct cli_def *cli, char *command, char **argv, int argc);
 #endif
 
 static int regular_stuff(struct cli_def *cli);
 #endif
 
 static int regular_stuff(struct cli_def *cli);
@@ -155,6 +156,9 @@ void init_cli()
        cli_register_command(cli, c, "pool", cmd_show_pool, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the IP address allocation pool");
        cli_register_command(cli, c, "radius", cmd_show_radius, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show active radius queries");
        cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration");
        cli_register_command(cli, c, "pool", cmd_show_pool, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the IP address allocation pool");
        cli_register_command(cli, c, "radius", cmd_show_radius, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show active radius queries");
        cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration");
+#ifdef LAC
+       cli_register_command(cli, c, "remotelns-conf", cmd_show_rmtlnsconf, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show a list of remote LNS configuration");
+#endif
        cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session");
        cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use");
        cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs");
        cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session");
        cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use");
        cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs");
@@ -538,8 +542,15 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
        }
 
        // Show Summary
        }
 
        // Show Summary
+#ifdef LAC
+       cli_print(cli, "%5s %7s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-18s %s",
+#else
        cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s",
        cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s",
+#endif
                        "SID",
                        "SID",
+#ifdef LAC
+                       "LkToSID",
+#endif
                        "TID",
                        "Username",
                        "IP",
                        "TID",
                        "Username",
                        "IP",
@@ -552,7 +563,11 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        "uploaded",
                        "idle",
                        "Rem.Time",
                        "uploaded",
                        "idle",
                        "Rem.Time",
+#ifdef LAC
+                       "LAC(L)/R LNS(R)",
+#else
                        "LAC",
                        "LAC",
+#endif
                        "CLI");
 
        for (i = 1; i < MAXSESSION; i++)
                        "CLI");
 
        for (i = 1; i < MAXSESSION; i++)
@@ -563,9 +578,15 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        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;
                        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;
-
+#ifdef LAC
+               cli_print(cli, "%5d %7d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-15s %s",
+#else
                cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s",
                cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %-15s %s",
+#endif
                                i,
                                i,
+#ifdef LAC
+                               session[i].forwardtosession,
+#endif
                                session[i].tunnel,
                                session[i].user[0] ? session[i].user : "*",
                                fmtaddr(htonl(session[i].ip), 0),
                                session[i].tunnel,
                                session[i].user[0] ? session[i].user : "*",
                                fmtaddr(htonl(session[i].ip), 0),
@@ -578,6 +599,9 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                                (unsigned long)session[i].cin,
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
                                (unsigned long)session[i].cin,
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
+#ifdef LAC
+                               tunnel[session[i].tunnel].isremotelns?"(R)":"(L)",
+#endif
                                fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1),
                                session[i].calling[0] ? session[i].calling : "*");
        }
                                fmtaddr(htonl(tunnel[ session[i].tunnel ].ip), 1),
                                session[i].calling[0] ? session[i].calling : "*");
        }
@@ -662,12 +686,20 @@ static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int
                if (!show_all && (!tunnel[i].ip || tunnel[i].die)) continue;
 
                for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
                if (!show_all && (!tunnel[i].ip || tunnel[i].die)) continue;
 
                for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
+#ifdef LAC
+               cli_print(cli, "%4d %20s %20s %6s %6d %s",
+#else
                cli_print(cli, "%4d %20s %20s %6s %6d",
                cli_print(cli, "%4d %20s %20s %6s %6d",
+#endif
                                i,
                                *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
                                fmtaddr(htonl(tunnel[i].ip), 0),
                                states[tunnel[i].state],
                                i,
                                *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
                                fmtaddr(htonl(tunnel[i].ip), 0),
                                states[tunnel[i].state],
-                               sessions);
+                               sessions
+#ifdef LAC
+                               ,(tunnel[i].isremotelns?"Tunnel To Remote LNS":"Tunnel To LAC")
+#endif
+                               );
        }
 
        return CLI_OK;
        }
 
        return CLI_OK;
@@ -1277,6 +1309,13 @@ static int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int
                        cli_print(cli, "Dropping session %d", s);
                        cli_session_actions[s].action |= CLI_SESS_KILL;
                }
                        cli_print(cli, "Dropping session %d", s);
                        cli_session_actions[s].action |= CLI_SESS_KILL;
                }
+#ifdef LAC
+               else if (session[s].forwardtosession && session[s].opened && !session[s].die)
+               {
+                       cli_print(cli, "Dropping session %d", s);
+                       cli_session_actions[s].action |= CLI_SESS_KILL;
+               }
+#endif
                else
                {
                        cli_error(cli, "Session %d is not active.", s);
                else
                {
                        cli_error(cli, "Session %d is not active.", s);
@@ -3142,15 +3181,39 @@ static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int a
                return CLI_OK;
        }
 
                return CLI_OK;
        }
 
-       // adremotelns(mask, IP_RemoteLNS, Port_RemoteLNS, SecretRemoteLNS)
-       ret = addremotelns(argv[0], argv[1], argv[2], argv[3]);
+       // lac_addremotelns(mask, IP_RemoteLNS, Port_RemoteLNS, SecretRemoteLNS)
+       ret = lac_addremotelns(argv[0], argv[1], argv[2], argv[3]);
 
        if (ret)
 
        if (ret)
+       {
                cli_print(cli, "setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]);
                cli_print(cli, "setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]);
+               if (ret == 2)
+                       cli_print(cli, "%s Updated, the tunnel must be dropped", argv[0]);
+       }
        else
                cli_error(cli, "ERROR setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]);
 
        return CLI_OK;
 }
 
        else
                cli_error(cli, "ERROR setforward %s %s %s %s", argv[0], argv[1], argv[2], argv[3]);
 
        return CLI_OK;
 }
 
+static int cmd_show_rmtlnsconf(struct cli_def *cli, char *command, char **argv, int argc)
+{
+       confrlnsidt idrlns;
+       char strdisp[1024];
+
+       if (CLI_HELP_REQUESTED)
+       {
+               return cli_arg_help(cli, 0, "remotelns-conf", "Show a list of remote LNS configurations", NULL);
+       }
+
+       for (idrlns = 0; idrlns < MAXRLNSTUNNEL; idrlns++)
+       {
+               if (lac_cli_show_remotelns(idrlns, strdisp) != 0)
+                       cli_print(cli, "%s", strdisp);
+               else
+                       break;
+       }
+
+       return CLI_OK;
+}
 #endif
 #endif
index 67d8d5e..b2a244e 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -1487,6 +1487,7 @@ static uint8_t *convert_session(struct oldsession *old)
 // Process a heartbeat..
 //
 // v6: added RADIUS class attribute, re-ordered session structure
 // Process a heartbeat..
 //
 // v6: added RADIUS class attribute, re-ordered session structure
+// v7: added tunnelt attribute at the end of struct (tunnelt size change)
 static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t *p, in_addr_t addr)
 {
        heartt *h;
 static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t *p, in_addr_t addr)
 {
        heartt *h;
@@ -1494,11 +1495,18 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
        int i, type;
        int hb_ver = more;
 
        int i, type;
        int hb_ver = more;
 
+#ifdef LAC
+#if HB_VERSION != 7
+# error "need to update cluster_process_heartbeat()"
+#endif
+#else
 #if HB_VERSION != 6
 # error "need to update cluster_process_heartbeat()"
 #if HB_VERSION != 6
 # error "need to update cluster_process_heartbeat()"
+#endif
 #endif
 
 #endif
 
-       // we handle versions 5 through 6
+
+       // we handle versions 5 through 7
        if (hb_ver < 5 || hb_ver > HB_VERSION) {
                LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver);
                return -1; // Ignore it??
        if (hb_ver < 5 || hb_ver > HB_VERSION) {
                LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver);
                return -1; // Ignore it??
@@ -1710,7 +1718,13 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
                                size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
                                s -= (p - orig_p);
 
                                size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
                                s -= (p - orig_p);
 
-                               if (size != sizeof(tunnelt) ) { // Ouch! Very very bad!
+#ifdef LAC
+                               if ( ((hb_ver >= HB_VERSION) && (size != sizeof(tunnelt))) ||
+                                        ((hb_ver < HB_VERSION) && (size > sizeof(tunnelt))) )
+#else
+                               if (size != sizeof(tunnelt) )
+#endif
+                               { // Ouch! Very very bad!
                                        LOG(0, 0, 0, "DANGER: Received a CTUNNEL that didn't decompress correctly!\n");
                                                // Now what? Should exit! No-longer up to date!
                                        break;
                                        LOG(0, 0, 0, "DANGER: Received a CTUNNEL that didn't decompress correctly!\n");
                                                // Now what? Should exit! No-longer up to date!
                                        break;
index c1aa574..0581647 100644 (file)
--- a/cluster.h
+++ b/cluster.h
 #define C_CBUNDLE              18      // Compressed bundle structure.
 #define C_MPPP_FORWARD 19      // MPPP Forwarded packet..
 
 #define C_CBUNDLE              18      // Compressed bundle structure.
 #define C_MPPP_FORWARD 19      // MPPP Forwarded packet..
 
+#ifdef LAC
+#define HB_VERSION             7       // Protocol version number..
+#else
 #define HB_VERSION             6       // Protocol version number..
 #define HB_VERSION             6       // Protocol version number..
+#endif
 #define HB_MAX_SEQ             (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
 #define HB_HISTORY_SIZE                64      // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ)
 
 #define HB_MAX_SEQ             (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
 #define HB_HISTORY_SIZE                64      // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ)
 
index 11ac963..7fa8e87 100644 (file)
--- a/l2tplac.c
+++ b/l2tplac.c
@@ -12,7 +12,7 @@
 #include "l2tplac.h"
 
 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
 #include "l2tplac.h"
 
 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
- * 
+ *
  *           LCP Negotiation
  * Client <-------------------> LAC
  *         Challenge (CHAP/PAP)
  *           LCP Negotiation
  * Client <-------------------> LAC
  *         Challenge (CHAP/PAP)
  * Client <------------------------------------------------------------------------> LNS2
  * */
 
  * Client <------------------------------------------------------------------------> LNS2
  * */
 
-// Limits
-#define MAXRLNSTUNNEL  101
+typedef struct
+{
+       uint32_t tunnel_type;
+       uint32_t tunnel_medium_type;
+       in_addr_t tunnel_server_endpoint; /* IP remote LNS */
+       char tunnel_password[64]; /* l2tpsecret remote LNS */
+       char tunnel_assignment_id[256];
+} tunnelrlnst;
 
 
-typedef uint16_t confrlnsidt;
+// Max Radius Tunnels by remote LNS
+#define MAXTAGTUNNEL   0x20
+static tunnelrlnst ptunnelrlns[MAXTAGTUNNEL];
 
 /*
  * Possible configrlns states
 
 /*
  * Possible configrlns states
- * TUNNELFREE -> TUNNELOPEN -> TUNNELDIE -> TUNNELFREE
+ * CONFRLNSFREE -> CONFRLNSSET -> CONFRLNSFREE
  */
 enum
 {
        CONFRLNSFREE = 0,       // Not in use
  */
 enum
 {
        CONFRLNSFREE = 0,       // Not in use
-       CONFRLNSSET             // Config Set
+       CONFRLNSSET,            // Config Set
+       CONFRLNSSETBYRADIUS     // Config Set
 };
 
 // struct remote lns
 typedef struct
 {
 };
 
 // struct remote lns
 typedef struct
 {
-       tunnelidt tid;          // near end tunnel ID
        int state;                      // conf state (tunnelstate enum)
        in_addr_t ip;           // Ip for far end
        uint16_t port;          // port for far end
        hasht auth;                     // request authenticator
        char strmaskuser[MAXUSER];
        int state;                      // conf state (tunnelstate enum)
        in_addr_t ip;           // Ip for far end
        uint16_t port;          // port for far end
        hasht auth;                     // request authenticator
        char strmaskuser[MAXUSER];
-       char l2tp_secret[64];           // L2TP shared secret
+       char l2tp_secret[64];   // L2TP shared secret
+       char tunnel_assignment_id[256];
 }
 configrlns;
 
 }
 configrlns;
 
-configrlns *pconfigrlns = NULL;                        // Array of tunnel structures.
+configrlns *pconfigrlns = NULL;
 
 // Init data structures
 
 // Init data structures
-void initremotelnsdata()
+void lac_initremotelnsdata()
 {
        confrlnsidt i;
 
 {
        confrlnsidt i;
 
@@ -104,135 +113,324 @@ void initremotelnsdata()
 
        memset(pconfigrlns, 0, sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL);
 
 
        memset(pconfigrlns, 0, sizeof(pconfigrlns[0]) * MAXRLNSTUNNEL);
 
-       // Mark all the tunnels as undefined (waiting to be filled in by a download).
+       // Mark all the conf as free.
        for (i = 1; i < MAXRLNSTUNNEL; i++)
                pconfigrlns[i].state = CONFRLNSFREE;    // mark it as not filled in.
 
        config->highest_rlnsid = 0;
        for (i = 1; i < MAXRLNSTUNNEL; i++)
                pconfigrlns[i].state = CONFRLNSFREE;    // mark it as not filled in.
 
        config->highest_rlnsid = 0;
+
+       lac_reset_rad_tag_tunnel_ctxt();
 }
 
 }
 
-// Check if must be forwarded to another LNS
-int forwardtolns(sessionidt s, char * puser)
+// Reset Radius TAG tunnel context
+void lac_reset_rad_tag_tunnel_ctxt()
 {
 {
-       tunnelidt t;
-       confrlnsidt i;
+       memset(ptunnelrlns, 0, sizeof(ptunnelrlns[0]) * MAXTAGTUNNEL);
+}
 
 
-       for (i = 1; i <= config->highest_rlnsid ; ++i)
+// Add tunnel_type radius TAG tunnel to context
+void lac_set_rad_tag_tunnel_type(uint8_t tag, uint32_t tunnel_type)
+{
+       if (tag < MAXTAGTUNNEL)
+               ptunnelrlns[tag].tunnel_type = tunnel_type;
+}
+
+// Add tunnel_medium_type Radius TAG tunnel to context
+void lac_set_rad_tag_tunnel_medium_type(uint8_t tag, uint32_t tunnel_medium_type)
+{
+       if (tag < MAXTAGTUNNEL)
+               ptunnelrlns[tag].tunnel_medium_type = tunnel_medium_type;
+}
+
+// Add tunnel_server_endpoint Radius TAG tunnel to context
+void lac_set_rad_tag_tunnel_serv_endpt(uint8_t tag, char *tunnel_server_endpoint)
+{
+       if (tag < MAXTAGTUNNEL)
        {
        {
-               if ( NULL != strstr(puser, pconfigrlns[i].strmaskuser))
+               ptunnelrlns[tag].tunnel_server_endpoint = ntohl(inet_addr(tunnel_server_endpoint));
+       }
+}
+
+// Add tunnel_password Radius TAG tunnel to context
+void lac_set_rad_tag_tunnel_password(uint8_t tag, char *tunnel_password)
+{
+       if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_password) < 64))
+       {
+               strcpy(ptunnelrlns[tag].tunnel_password, tunnel_password);
+       }
+}
+
+// Add tunnel_assignment_id Radius TAG tunnel to context
+void lac_set_rad_tag_tunnel_assignment_id(uint8_t tag, char *tunnel_assignment_id)
+{
+       if ((tag < MAXTAGTUNNEL) && (strlen(tunnel_assignment_id) < 256))
+       {
+               strcpy(ptunnelrlns[tag].tunnel_assignment_id, tunnel_assignment_id);
+       }
+}
+
+// Select a tunnel_assignment_id
+int lac_rad_select_assignment_id(sessionidt s, char *assignment_id)
+{
+       int idtag;
+       int nbtagfound = 0;
+       int bufidtag[MAXTAGTUNNEL];
+
+       for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag)
+       {
+               if (ptunnelrlns[idtag].tunnel_type == 0)
+                       continue;
+               else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type
+                       LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n");
+               else if (ptunnelrlns[idtag].tunnel_medium_type != 1)
+                       LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n");
+               else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0)
+                       LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n");
+               else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) > 0)
                {
                {
-                       t = pconfigrlns[i].tid;
+                       bufidtag[nbtagfound] = idtag;
+                       nbtagfound++;
+               }
+       }
 
 
-                       if ((t != 0) && (tunnel[t].ip != pconfigrlns[i].ip))
-                       {
-                               pconfigrlns[i].tid = t = 0;
-                               LOG(1, 0, t, "Tunnel ID inconsistency\n");
-                       }
+       if (nbtagfound > 0)
+       {
+               // random between 0 and nbtagfound-1
+               idtag = (nbtagfound*rand()/(RAND_MAX+1.0));
+               if (idtag >= nbtagfound)
+                       idtag = 0; //Sanity checks.
+
+               strcpy(assignment_id, ptunnelrlns[bufidtag[idtag]].tunnel_assignment_id);
+               return 1;
+       }
+
+       // Error no tunnel_assignment_id found
+       return 0;
+}
+
+// Save the 'radius tag tunnels' context on global configuration
+void lac_save_rad_tag_tunnels(sessionidt s)
+{
+       confrlnsidt idrlns;
+       int idtag;
 
 
-                       if (t == 0)
+       for (idtag = 0; idtag < MAXTAGTUNNEL; ++idtag)
+       {
+               if (ptunnelrlns[idtag].tunnel_type == 0)
+                       continue;
+               else if (ptunnelrlns[idtag].tunnel_type != 3) // 3 == L2TP tunnel type
+                       LOG(1, s, session[s].tunnel, "Error, Only L2TP tunnel type supported\n");
+               else if (ptunnelrlns[idtag].tunnel_medium_type != 1)
+                       LOG(1, s, session[s].tunnel, "Error, Only IP tunnel medium type supported\n");
+               else if (ptunnelrlns[idtag].tunnel_server_endpoint == 0)
+                       LOG(1, s, session[s].tunnel, "Error, Bad IP tunnel server endpoint \n");
+               else if (strlen(ptunnelrlns[idtag].tunnel_assignment_id) <= 0)
+                       LOG(1, s, session[s].tunnel, "Error, No tunnel_assignment_id \n");
+               else
+                       for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns)
                        {
                        {
-                               if (main_quit == QUIT_SHUTDOWN) return 0;
+                               if (pconfigrlns[idrlns].state == CONFRLNSFREE)
+                               {
+                                       pconfigrlns[idrlns].ip = ptunnelrlns[idtag].tunnel_server_endpoint;
+                                       pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP poart
+                                       strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password);
+                                       strcpy(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id);
+
+                                       config->highest_rlnsid = idrlns;
 
 
-                               // Start Open Tunnel
-                               if (!(t = lac_new_tunnel()))
+                                       pconfigrlns[idrlns].state = CONFRLNSSETBYRADIUS;
+
+                                       break;
+                               }
+                               else if (pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS)
                                {
                                {
-                                       LOG(1, 0, 0, "No more tunnels\n");
-                                       STAT(tunnel_overflow);
-                                       return 0;
+                                       if ( (pconfigrlns[idrlns].ip == ptunnelrlns[idtag].tunnel_server_endpoint) &&
+                                                (strcmp(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id) == 0) )
+                                       {
+                                               LOG(3, s, session[s].tunnel, "Tunnel IP %s already defined\n", fmtaddr(htonl(pconfigrlns[idrlns].ip), 0));
+                                               // l2tp_secret may be changed
+                                               strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password);
+                                               pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP poart
+
+                                               if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns;
+
+                                               break;
+                                       }
                                }
                                }
-                               lac_tunnelclear(t);
-                               tunnel[t].ip = pconfigrlns[i].ip;
-                               tunnel[t].port = pconfigrlns[i].port;
-                               tunnel[t].window = 4; // default window
-                               STAT(tunnel_created);
-                               LOG(1, 0, t, "New (REMOTE LNS) tunnel to %s:%u ID %u\n", fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t);
+                       }
 
 
-                               random_data(pconfigrlns[i].auth, sizeof(pconfigrlns[i].auth));
+               if (idrlns >= MAXRLNSTUNNEL)
+               {
+                       LOG(0, s, session[s].tunnel, "No more Remote LNS Conf Free\n");
+                       return;
+               }
+       }
+}
 
 
-                               pconfigrlns[i].tid = t;
+// Create Remote LNS a Tunnel or Session
+static int lac_create_tunnelsession(tunnelidt t, sessionidt s, confrlnsidt i_conf, char * puser)
+{
+       if (t == 0)
+       {
+               if (main_quit == QUIT_SHUTDOWN) return 0;
 
 
-                               lac_send_SCCRQ(t, pconfigrlns[i].auth, sizeof(pconfigrlns[i].auth));
-                       }
-                       else if (tunnel[t].state == TUNNELOPEN)
-                       {
-                               if (main_quit != QUIT_SHUTDOWN)
-                               {
-                                       /**********************/
-                                       /** Open New session **/
-                                       /**********************/
-                                       sessionidt new_sess = sessionfree;
+               // Start Open Tunnel
+               if (!(t = lac_new_tunnel()))
+               {
+                       LOG(1, 0, 0, "No more tunnels\n");
+                       STAT(tunnel_overflow);
+                       return 0;
+               }
+               lac_tunnelclear(t);
+               tunnel[t].ip = pconfigrlns[i_conf].ip;
+               tunnel[t].port = pconfigrlns[i_conf].port;
+               tunnel[t].window = 4; // default window
+               tunnel[t].isremotelns = i_conf;
+               STAT(tunnel_created);
 
 
-                                       sessionfree = session[new_sess].next;
-                                       memset(&session[new_sess], 0, sizeof(session[new_sess]));
+               random_data(pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
 
 
-                                       if (new_sess > config->cluster_highest_sessionid)
-                                               config->cluster_highest_sessionid = new_sess;
+               LOG(2, 0, t, "Create New tunnel to REMOTE LNS %s for user %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser);
+               lac_send_SCCRQ(t, pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
+       }
+       else if (tunnel[t].state == TUNNELOPEN)
+       {
+               if (main_quit != QUIT_SHUTDOWN)
+               {
 
 
-                                       session[new_sess].opened = time_now;
-                                       session[new_sess].tunnel = t;
-                                       session[new_sess].last_packet = session[s].last_data = time_now;
+                       /**********************/
+                       /** Open New session **/
+                       /**********************/
+                       sessionidt new_sess = sessionfree;
 
 
-                                       session[new_sess].ppp.phase = Establish;
-                                       session[new_sess].ppp.lcp = Starting;
+                       sessionfree = session[new_sess].next;
+                       memset(&session[new_sess], 0, sizeof(session[new_sess]));
 
 
-                                       // Sent ICRQ  Incoming-call-request
-                                       lac_send_ICRQ(t, new_sess);
+                       if (new_sess > config->cluster_highest_sessionid)
+                               config->cluster_highest_sessionid = new_sess;
 
 
-                                       // Set session to forward to another LNS
-                                       session[s].forwardtosession = new_sess;
-                                       session[new_sess].forwardtosession = s;
+                       session[new_sess].opened = time_now;
+                       session[new_sess].tunnel = t;
+                       session[new_sess].last_packet = session[s].last_data = time_now;
 
 
-                                       STAT(session_created);
-                               }
-                               else
-                               {
-                                       lac_tunnelshutdown(t, "Shutting down", 6, 0, 0);
-                                       pconfigrlns[i].tid = 0;
-                               }
-                       }
-                       else
-                       {
-                               /** TODO **/
-                               LOG(1, 0, t, "(REMOTE LNS) tunnel is not open\n");
-                       }
+                       session[new_sess].ppp.phase = Establish;
+                       session[new_sess].ppp.lcp = Starting;
+                       session[s].ppp.phase = Establish;
 
 
-                       return 1;
+                       LOG(2, 0, t, "Open New session to REMOTE LNS %s for user: %s\n", fmtaddr(htonl(tunnel[t].ip), 0), puser);
+                       // Sent ICRQ  Incoming-call-request
+                       lac_send_ICRQ(t, new_sess);
+
+                       // Set session to forward to another LNS
+                       session[s].forwardtosession = new_sess;
+                       session[new_sess].forwardtosession = s;
+                       strncpy(session[s].user, puser, sizeof(session[s].user) - 1);
+                       strncpy(session[new_sess].user, puser, sizeof(session[new_sess].user) - 1);
+
+                       STAT(session_created);
+               }
+               else
+               {
+                       lac_tunnelshutdown(t, "Shutting down", 6, 0, 0);
                }
        }
                }
        }
+       else
+       {
+               /** TODO **/
+               LOG(1, 0, t, "(REMOTE LNS) tunnel is not open\n");
+       }
 
 
-       return 0;
+       return 1;
 }
 }
-
-static tunnelidt getidrlns(tunnelidt t)
+// Check if session must be forwarded to another LNS
+// return 1 if the session must be forwarded (and Creating a tunnel/session has been started)
+//                     else 0.
+// Note: check from the configuration read on the startup-config (see setforward)
+int lac_conf_forwardtoremotelns(sessionidt s, char * puser)
 {
 {
-       confrlnsidt idrlns;
+       tunnelidt t, j;
+       confrlnsidt i;
 
 
-       for (idrlns = 1; idrlns <= config->highest_rlnsid ; ++idrlns)
+       for (i = 1; i <= config->highest_rlnsid ; ++i)
        {
        {
-               if (pconfigrlns[idrlns].tid == t) return idrlns;
+               if ( (pconfigrlns[i].state == CONFRLNSSET) && (NULL != strstr(puser, pconfigrlns[i].strmaskuser)) )
+               {
+                       t = 0;
+                       for (j = 0; j <= config->cluster_highest_tunnelid ; ++j)
+                       {
+                               if ((tunnel[j].isremotelns) &&
+                                       (tunnel[j].ip == pconfigrlns[i].ip) &&
+                                       (tunnel[j].port == pconfigrlns[i].port) &&
+                                       (tunnel[j].state != TUNNELDIE))
+                               {
+                                       t = j;
+                                       if (tunnel[t].isremotelns != i)
+                                       {
+                                               if ( (tunnel[t].state == TUNNELOPEN) || (tunnel[t].state == TUNNELOPENING) )
+                                               {
+                                                       LOG(1, 0, t, "Tunnel Remote LNS ID inconsistency (IP RLNS:%s)\n",
+                                                               fmtaddr(htonl(pconfigrlns[i].ip), 0));
+
+                                                       tunnel[t].isremotelns = i;
+                                               }
+                                               else t = 0;
+                                       }
+                                       break;
+                               }
+                       }
+
+                       return lac_create_tunnelsession(t, s, i, puser);
+               }
        }
 
        return 0;
 }
 
        }
 
        return 0;
 }
 
-int istunneltolns(tunnelidt t)
+// return 1 if the session must be forwarded (and Creating a tunnel/session has been started)
+//                     else 0.
+// Note: Started from a radius response
+int lac_rad_forwardtoremotelns(sessionidt s, char *assignment_id, char * puser)
 {
 {
-       confrlnsidt idrlns;
+       tunnelidt t, j;
+       confrlnsidt i;
 
 
-       for (idrlns = 1; idrlns <= config->highest_rlnsid ; ++idrlns)
+       for (i = 1; i <= config->highest_rlnsid ; ++i)
        {
        {
-               if (pconfigrlns[idrlns].tid == t) return 1;
+               if ((pconfigrlns[i].state == CONFRLNSSETBYRADIUS) &&
+                       (strcmp(pconfigrlns[i].tunnel_assignment_id, assignment_id) == 0))
+               {
+                       t = 0;
+                       for (j = 1; j <= config->cluster_highest_tunnelid ; ++j)
+                       {
+                               if ((tunnel[j].isremotelns == i) &&
+                                       (tunnel[j].ip == pconfigrlns[i].ip) &&
+                                       (tunnel[j].port == pconfigrlns[i].port) &&
+                                       (tunnel[j].state != TUNNELDIE))
+                               {
+                                       if ( (tunnel[j].state == TUNNELOPEN) ||
+                                            (tunnel[j].state == TUNNELOPENING) )
+                                       {
+                                               t = j;
+                                               LOG(3, 0, t, "Tunnel Remote LNS already open(ing) (RLNS IP:%s)\n", fmtaddr(htonl(pconfigrlns[i].ip), 0));
+                                               break;
+                                       }
+                               }
+                       }
+
+                       return lac_create_tunnelsession(t, s, i, puser);
+               }
        }
 
        return 0;
 }
 
        }
 
        return 0;
 }
 
-void calc_lac_auth(tunnelidt t, uint8_t id, uint8_t *out)
+// Calcul the remote LNS auth
+void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out)
 {
        MD5_CTX ctx;
        confrlnsidt idrlns;
 
 {
        MD5_CTX ctx;
        confrlnsidt idrlns;
 
-       idrlns = getidrlns(t);
+       idrlns = tunnel[t].isremotelns;
 
        MD5_Init(&ctx);
        MD5_Update(&ctx, &id, 1);
 
        MD5_Init(&ctx);
        MD5_Update(&ctx, &id, 1);
@@ -241,8 +439,8 @@ void calc_lac_auth(tunnelidt t, uint8_t id, uint8_t *out)
        MD5_Final(out, &ctx);
 }
 
        MD5_Final(out, &ctx);
 }
 
-// Forward session to external LNS
-int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto)
+// Forward session to LAC or Remote LNS
+int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto)
 {
        uint16_t t = 0, s = 0;
        uint8_t *p = buf + 2; // First word L2TP options
 {
        uint16_t t = 0, s = 0;
        uint8_t *p = buf + 2; // First word L2TP options
@@ -261,8 +459,14 @@ int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto
                return 0;
        }
 
                return 0;
        }
 
+       if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns))
+       {
+               LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u) broken\n", s);
+               return 0;
+       }
+
        if (*buf & 0x40)
        if (*buf & 0x40)
-       {   // length
+       {       // length
                p += 2;
        }
 
                p += 2;
        }
 
@@ -282,6 +486,20 @@ int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto
        if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]))
        {
                session[sess].last_packet = session[sess].last_data = time_now;
        if ((proto == PPPIP) || (proto == PPPMP) ||(proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]))
        {
                session[sess].last_packet = session[sess].last_data = time_now;
+               // Update STAT IN
+               increment_counter(&session[sess].cin, &session[sess].cin_wrap, len);
+               session[sess].cin_delta += len;
+               session[sess].pin++;
+               sess_local[sess].cin += len;
+               sess_local[sess].pin++;
+
+               session[s].last_data = time_now;
+               // Update STAT OUT
+               increment_counter(&session[s].cout, &session[s].cout_wrap, len); // byte count
+               session[s].cout_delta += len;
+               session[s].pout++;
+               sess_local[s].cout += len;
+               sess_local[s].pout++;
        }
        else
                session[sess].last_packet = time_now;
        }
        else
                session[sess].last_packet = time_now;
@@ -291,7 +509,12 @@ int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto
        return 1;
 }
 
        return 1;
 }
 
-int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS)
+// Add new Remote LNS from CLI
+// return:
+//              0 = Error
+//              1 = New Remote LNS conf ADD
+//              2 = Remote LNS Conf Updated
+int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS)
 {
        confrlnsidt idrlns;
 
 {
        confrlnsidt idrlns;
 
@@ -308,9 +531,26 @@ int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *Sec
 
                        pconfigrlns[idrlns].state = CONFRLNSSET;
 
 
                        pconfigrlns[idrlns].state = CONFRLNSSET;
 
-                       LOG(1, 0, 0, "New Remote LNS conf (count %u) mask:%s IP:%s Port:%u l2tpsecret:*****\n", idrlns,
-                               pconfigrlns[idrlns].strmaskuser, fmtaddr(htonl(pconfigrlns[idrlns].ip), 0),
-                               pconfigrlns[idrlns].port);
+                       return 1;
+               }
+               else if ((pconfigrlns[idrlns].state == CONFRLNSSET) && (strcmp(pconfigrlns[idrlns].strmaskuser, mask) == 0))
+               {
+                       if ( (pconfigrlns[idrlns].ip != ntohl(inet_addr(IP_RemoteLNS))) ||
+                                (pconfigrlns[idrlns].port != atoi(Port_RemoteLNS)) ||
+                                (strcmp(pconfigrlns[idrlns].l2tp_secret, SecretRemoteLNS) != 0) )
+                       {
+                               memset(&pconfigrlns[idrlns], 0, sizeof(pconfigrlns[idrlns]));
+                               snprintf((char *) pconfigrlns[idrlns].strmaskuser, sizeof(pconfigrlns[idrlns].strmaskuser), "%s", mask);
+                               pconfigrlns[idrlns].ip = ntohl(inet_addr(IP_RemoteLNS));
+                               pconfigrlns[idrlns].port = atoi(Port_RemoteLNS);
+                               snprintf((char *) pconfigrlns[idrlns].l2tp_secret, sizeof(pconfigrlns[idrlns].l2tp_secret), "%s", SecretRemoteLNS);
+
+                               if (config->highest_rlnsid < idrlns) config->highest_rlnsid = idrlns;
+
+                               pconfigrlns[idrlns].state = CONFRLNSSET;
+                               // Conf Updated, the tunnel must be dropped
+                               return 2;
+                       }
 
                        return 1;
                }
 
                        return 1;
                }
@@ -320,3 +560,61 @@ int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *Sec
 
        return 0;
 }
 
        return 0;
 }
+
+// Cli Show remote LNS defined
+int lac_cli_show_remotelns(confrlnsidt idrlns, char *strout)
+{
+       if (idrlns > config->highest_rlnsid)
+               return 0;
+
+       if (idrlns == 0)
+               // Show Summary
+               sprintf(strout, "%15s %-32s %-32s %11s %7s %10s",
+                               "IP Remote LNS",
+                               "l2tp secret",
+                               "assignment Id",
+                               "File/Radius",
+                               "State",
+                               "Count Sess");
+       else
+       {
+               tunnelidt t;
+               sessionidt s;
+               int countsess = 0;
+               char state[20];
+
+               strcpy(state, "Close");
+               for (t = 0; t <= config->cluster_highest_tunnelid ; ++t)
+               {
+                       if ((tunnel[t].isremotelns) &&
+                               (tunnel[t].ip == pconfigrlns[idrlns].ip) &&
+                               (tunnel[t].port == pconfigrlns[idrlns].port) &&
+                               (tunnel[t].state != TUNNELDIE))
+                       {
+                               if (tunnel[t].isremotelns)
+                               {
+                                       if (tunnel[t].state == TUNNELOPENING)
+                                               strcpy(state, "Opening");
+                                       else if (tunnel[t].state == TUNNELOPEN)
+                                               strcpy(state, "Open");
+
+                                       for (s = 1; s <= config->cluster_highest_sessionid ; ++s)
+                                               if (session[s].tunnel == t)
+                                                       countsess++;
+
+                                       break;
+                               }
+                       }
+               }
+
+               sprintf(strout, "%15s %-32s %-32s %11s %7s %10u",
+                               fmtaddr(htonl(pconfigrlns[idrlns].ip), 0),
+                               pconfigrlns[idrlns].l2tp_secret,
+                               pconfigrlns[idrlns].tunnel_assignment_id,
+                               (pconfigrlns[idrlns].state == CONFRLNSSET?"File":(pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS?"Radius":"Free")),
+                               state,
+                               countsess);
+       }
+
+       return 1;
+}
index ba81a8e..3b3f583 100644 (file)
--- a/l2tplac.h
+++ b/l2tplac.h
@@ -4,11 +4,30 @@
 #ifndef __L2TPLAC_H__
 #define __L2TPLAC_H__
 
 #ifndef __L2TPLAC_H__
 #define __L2TPLAC_H__
 
+#define L2TPLACPORT    65432   // L2TP port for Remote LNS
+// Limits
+#define MAXRLNSTUNNEL  201
+
+typedef uint16_t confrlnsidt;
+
 // l2tplac.c
 // l2tplac.c
-void initremotelnsdata();
-int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto);
-int forwardtolns(sessionidt s, char * puser);
-void calc_lac_auth(tunnelidt t, uint8_t id, uint8_t *out);
-int istunneltolns(tunnelidt t);
-int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS);
+void lac_initremotelnsdata();
+int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto);
+int lac_conf_forwardtoremotelns(sessionidt s, char * puser);
+void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out);
+int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS);
+
+/* Function for Tunnels creating from radius reponses */
+void lac_reset_rad_tag_tunnel_ctxt();
+void lac_set_rad_tag_tunnel_type(uint8_t tag, uint32_t tunnel_type);
+void lac_set_rad_tag_tunnel_medium_type(uint8_t tag, uint32_t tunnel_medium_type);
+void lac_set_rad_tag_tunnel_serv_endpt(uint8_t tag, char *tunnel_server_endpoint);
+void lac_set_rad_tag_tunnel_password(uint8_t tag, char *tunnel_password);
+void lac_set_rad_tag_tunnel_assignment_id(uint8_t tag, char *tunnel_assignment_id);
+void lac_save_rad_tag_tunnels(sessionidt s);
+int lac_rad_select_assignment_id(sessionidt s, char *assignment_id);
+
+int lac_rad_forwardtoremotelns(sessionidt s, char *assignment_id, char * puser);
+
+int lac_cli_show_remotelns(confrlnsidt idrlns, char *strout);
 #endif /* __L2TPLAC_H__ */
 #endif /* __L2TPLAC_H__ */
index d4bb9ca..694726d 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -67,6 +67,9 @@ configt *config = NULL;               // all configuration
 int nlfd = -1;                 // netlink socket
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int nlfd = -1;                 // netlink socket
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
+#ifdef LAC
+int udplacfd = -1;             // UDP LAC file handle
+#endif
 int controlfd = -1;            // Control signal handle
 int clifd = -1;                        // Socket listening for CLI connections.
 int daefd = -1;                        // Socket listening for DAE connections.
 int controlfd = -1;            // Control signal handle
 int clifd = -1;                        // Socket listening for CLI connections.
 int daefd = -1;                        // Socket listening for DAE connections.
@@ -175,6 +178,10 @@ config_descriptt config_values[] = {
 #endif
        CONFIG("echo_timeout", echo_timeout, INT),
        CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
 #endif
        CONFIG("echo_timeout", echo_timeout, INT),
        CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
+#ifdef LAC
+       CONFIG("disable_lac_func", disable_lac_func, BOOL),
+       CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
+#endif
        { NULL, 0, 0, 0 },
 };
 
        { NULL, 0, 0, 0 },
 };
 
@@ -866,6 +873,24 @@ static void initudp(void)
                exit(1);
        }
 
                exit(1);
        }
 
+#ifdef LAC
+       // Tunnel to Remote LNS
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(config->bind_portremotelns);
+       udplacfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       setsockopt(udplacfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       {
+               int flags = fcntl(udplacfd, F_GETFL, 0);
+               fcntl(udplacfd, F_SETFL, flags | O_NONBLOCK);
+       }
+       if (bind(udplacfd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+       {
+               LOG(0, 0, 0, "Error in UDP REMOTE LNS bind: %s\n", strerror(errno));
+               exit(1);
+       }
+#endif
+
        // Intercept
        snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 }
        // Intercept
        snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 }
@@ -1186,8 +1211,11 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
                        LOG(3, 0, t, "Control message resend try %d\n", tunnel[t].try);
                }
        }
                        LOG(3, 0, t, "Control message resend try %d\n", tunnel[t].try);
                }
        }
-
+#ifdef LAC
+       if (sendto((tunnel[t].isremotelns?udplacfd:udpfd), buf, l, 0, (void *) &addr, sizeof(addr)) < 0)
+#else
        if (sendto(udpfd, buf, l, 0, (void *) &addr, sizeof(addr)) < 0)
        if (sendto(udpfd, buf, l, 0, (void *) &addr, sizeof(addr)) < 0)
+#endif
        {
                LOG(0, ntohs((*(uint16_t *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n",
                                strerror(errno), udpfd, buf, l, inet_ntoa(addr.sin_addr));
        {
                LOG(0, ntohs((*(uint16_t *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n",
                                strerror(errno), udpfd, buf, l, inet_ntoa(addr.sin_addr));
@@ -2213,6 +2241,18 @@ void sessionkill(sessionidt s, char *reason)
        if (sess_local[s].radius)
                radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
        if (sess_local[s].radius)
                radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed
 
+#ifdef LAC
+       if (session[s].forwardtosession)
+       {
+               sessionidt sess = session[s].forwardtosession;
+               if (session[sess].forwardtosession == s)
+               {
+                       // Shutdown the linked session also.
+                       sessionshutdown(sess, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);
+               }
+       }
+#endif
+
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
        sessionclear(s);
        cluster_send_session(s);
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
        sessionclear(s);
        cluster_send_session(s);
@@ -2724,11 +2764,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        break;
                                case 13:    // Response
 #ifdef LAC
                                        break;
                                case 13:    // Response
 #ifdef LAC
-                                       if (istunneltolns(t))
+                                       if (tunnel[t].isremotelns)
                                        {
                                                chapresponse = calloc(17, 1);
                                                memcpy(chapresponse, b, (n < 17) ? n : 16);
                                        {
                                                chapresponse = calloc(17, 1);
                                                memcpy(chapresponse, b, (n < 17) ? n : 16);
-                                               LOG(1, s, t, "received challenge response from (REMOTE LNS)\n");
+                                               LOG(3, s, t, "received challenge response from REMOTE LNS\n");
                                        }
                                        else
 #endif /* LAC */
                                        }
                                        else
 #endif /* LAC */
@@ -2957,8 +2997,10 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                {
                                case 1:       // SCCRQ - Start Control Connection Request
                                        tunnel[t].state = TUNNELOPENING;
                                {
                                case 1:       // SCCRQ - Start Control Connection Request
                                        tunnel[t].state = TUNNELOPENING;
+                                       LOG(3, s, t, "Received SCCRQ\n");
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
+                                               LOG(3, s, t, "sending SCCRP\n");
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
                                                control32(c, 3, 3, 1); // framing
                                                controlt *c = controlnew(2); // sending SCCRP
                                                control16(c, 2, version, 1); // protocol version
                                                control32(c, 3, 3, 1); // framing
@@ -2976,17 +3018,18 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
 #ifdef LAC
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
 #ifdef LAC
-                                       LOG(1, s, t, "Recieved SCCRP (REMOTE LNS)\n");
+                                       LOG(3, s, t, "Received SCCRP\n");
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
-                                               if (istunneltolns(t) && chapresponse)
+                                               if (tunnel[t].isremotelns && chapresponse)
                                                {
                                                        hasht hash;
 
                                                {
                                                        hasht hash;
 
-                                                       calc_lac_auth(t, 2, hash); // id = 2 (SCCRP)
+                                                       lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP)
                                                        // check authenticator
                                                        if (memcmp(hash, chapresponse, 16) == 0)
                                                        {
                                                        // check authenticator
                                                        if (memcmp(hash, chapresponse, 16) == 0)
                                                        {
+                                                               LOG(3, s, t, "sending SCCCN to REMOTE LNS\n");
                                                                controlt *c = controlnew(3); // sending SCCCN
                                                                controls(c, 7, hostname, 1); // host name
                                                                controls(c, 8, Vendor_name, 1); // Vendor name
                                                                controlt *c = controlnew(3); // sending SCCCN
                                                                controls(c, 7, hostname, 1); // host name
                                                                controls(c, 8, Vendor_name, 1); // Vendor name
@@ -2994,12 +3037,10 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                                                control32(c, 3, 3, 1); // framing Capabilities
                                                                control16(c, 9, t, 1); // assigned tunnel
                                                                controladd(c, 0, t); // send
                                                                control32(c, 3, 3, 1); // framing Capabilities
                                                                control16(c, 9, t, 1); // assigned tunnel
                                                                controladd(c, 0, t); // send
-
-                                                               LOG(1, s, t, "sending SCCCN (REMOTE LNS)\n");
                                                        }
                                                        else
                                                        {
                                                        }
                                                        else
                                                        {
-                                                               tunnelshutdown(t, "(REMOTE LNS) Bad chap response", 4, 0, 0);
+                                                               tunnelshutdown(t, "Bad chap response from REMOTE LNS", 4, 0, 0);
                                                        }
                                                }
                                        }
                                                        }
                                                }
                                        }
@@ -3010,11 +3051,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 #endif /* LAC */
                                        break;
                                case 3:       // SCCN
 #endif /* LAC */
                                        break;
                                case 3:       // SCCN
+                                       LOG(3, s, t, "Received SCCN\n");
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
+                                       LOG(3, s, t, "Received StopCCN\n");
                                        controlnull(t); // ack
                                        tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly
                                        break;
                                        controlnull(t); // ack
                                        tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly
                                        break;
@@ -3023,18 +3066,24 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        break;
                                case 7:       // OCRQ
                                        // TBA
                                        break;
                                case 7:       // OCRQ
                                        // TBA
+                                       LOG(3, s, t, "Received OCRQ\n");
                                        break;
                                case 8:       // OCRO
                                        // TBA
                                        break;
                                case 8:       // OCRO
                                        // TBA
+                                       LOG(3, s, t, "Received OCRO\n");
                                        break;
                                case 9:       // OCCN
                                        // TBA
                                        break;
                                case 9:       // OCCN
                                        // TBA
+                                       LOG(3, s, t, "Received OCCN\n");
                                        break;
                                case 10:      // ICRQ
                                        break;
                                case 10:      // ICRQ
+                                       LOG(3, s, t, "Received ICRQ\n");
                                        if (sessionfree && main_quit != QUIT_SHUTDOWN)
                                        {
                                                controlt *c = controlnew(11); // ICRP
 
                                        if (sessionfree && main_quit != QUIT_SHUTDOWN)
                                        {
                                                controlt *c = controlnew(11); // ICRP
 
+                                               LOG(3, s, t, "Sending ICRP\n");
+
                                                s = sessionfree;
                                                sessionfree = session[s].next;
                                                memset(&session[s], 0, sizeof(session[s]));
                                                s = sessionfree;
                                                sessionfree = session[s].next;
                                                memset(&session[s], 0, sizeof(session[s]));
@@ -3062,6 +3111,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                                        {
                                                controlt *c = controlnew(14); // CDN
 
                                        {
                                                controlt *c = controlnew(14); // CDN
+                                               LOG(3, s, t, "Sending CDN\n");
                                                if (!sessionfree)
                                                {
                                                        STAT(session_overflow);
                                                if (!sessionfree)
                                                {
                                                        STAT(session_overflow);
@@ -3076,7 +3126,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        return;
                                case 11:      // ICRP
 #ifdef LAC
                                        return;
                                case 11:      // ICRP
 #ifdef LAC
-                               LOG(1, s, t, "Recieved ICRP (REMOTE LNS)\n");
+                               LOG(3, s, t, "Received ICRP\n");
                                if (session[s].forwardtosession)
                                {
                                        controlt *c = controlnew(12); // ICCN
                                if (session[s].forwardtosession)
                                {
                                        controlt *c = controlnew(12); // ICCN
@@ -3089,11 +3139,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        control32(c, 19, 1, 1); // Framing Type
                                        control32(c, 24, 10000000, 1); // Tx Connect Speed
                                        controladd(c, asession, t); // send the message
                                        control32(c, 19, 1, 1); // Framing Type
                                        control32(c, 24, 10000000, 1); // Tx Connect Speed
                                        controladd(c, asession, t); // send the message
-                                       LOG(1, s, t, "Sending ICCN (REMOTE LNS)\n");
+                                       LOG(3, s, t, "Sending ICCN\n");
                                }
 #endif /* LAC */
                                        break;
                                case 12:      // ICCN
                                }
 #endif /* LAC */
                                        break;
                                case 12:      // ICCN
+                                       LOG(3, s, t, "Received ICCN\n");
                                        if (amagic == 0) amagic = time_now;
                                        session[s].magic = amagic; // set magic number
                                        session[s].flags = aflags; // set flags received
                                        if (amagic == 0) amagic = time_now;
                                        session[s].magic = amagic; // set magic number
                                        session[s].flags = aflags; // set flags received
@@ -3113,10 +3164,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        break;
 
                                case 14:      // CDN
                                        break;
 
                                case 14:      // CDN
+                                       LOG(3, s, t, "Received CDN\n");
                                        controlnull(t); // ack
                                        controlnull(t); // ack
-#ifdef LAC
-
-#endif /* LAC */
                                        sessionshutdown(s, disc_reason, CDN_NONE, disc_cause);
                                        break;
                                case 0xFFFF:
                                        sessionshutdown(s, disc_reason, CDN_NONE, disc_cause);
                                        break;
                                case 0xFFFF:
@@ -3169,9 +3218,9 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 #ifdef LAC
                if (session[s].forwardtosession)
                {
 #ifdef LAC
                if (session[s].forwardtosession)
                {
-                       LOG(4, s, t, "Forwarding data session to %u (REMOTE LNS)\n", session[s].forwardtosession);
-                       // Forward to Remote LNS
-                       session_forward_tolns(buf, len, s, proto);
+                       LOG(5, s, t, "Forwarding data session to session %u\n", session[s].forwardtosession);
+                       // Forward to LAC or Remote LNS session
+                       lac_session_forward(buf, len, s, proto);
                        return;
                }
 #endif /* LAC */
                        return;
                }
 #endif /* LAC */
@@ -3843,8 +3892,13 @@ static int still_busy(void)
 # include "fake_epoll.h"
 #endif
 
 # include "fake_epoll.h"
 #endif
 
+#ifdef LAC
+// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink, udplac
+#define BASE_FDS       8
+#else
 // the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink
 #define BASE_FDS       7
 // the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink
 #define BASE_FDS       7
+#endif
 
 // additional polled fds
 #ifdef BGP
 
 // additional polled fds
 #ifdef BGP
@@ -3870,8 +3924,13 @@ static void mainloop(void)
                exit(1);
        }
 
                exit(1);
        }
 
+#ifdef LAC
+       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d , udplacfd=%d\n",
+               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, udplacfd);
+#else
        LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d\n",
                clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd);
        LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d\n",
                clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd);
+#endif
 
        /* setup our fds to poll for input */
        {
 
        /* setup our fds to poll for input */
        {
@@ -3911,6 +3970,12 @@ static void mainloop(void)
                d[i].type = FD_TYPE_NETLINK;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, nlfd, &e);
                d[i].type = FD_TYPE_NETLINK;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, nlfd, &e);
+
+#ifdef LAC
+               d[i].type = FD_TYPE_UDPLAC;
+               e.data.ptr = &d[i++];
+               epoll_ctl(epollfd, EPOLL_CTL_ADD, udplacfd, &e);
+#endif
        }
 
 #ifdef BGP
        }
 
 #ifdef BGP
@@ -3973,6 +4038,10 @@ static void mainloop(void)
                        socklen_t alen;
                        int c, s;
                        int udp_ready = 0;
                        socklen_t alen;
                        int c, s;
                        int udp_ready = 0;
+#ifdef LAC
+                       int udplac_ready = 0;
+                       int udplac_pkts = 0;
+#endif
                        int tun_ready = 0;
                        int cluster_ready = 0;
                        int udp_pkts = 0;
                        int tun_ready = 0;
                        int cluster_ready = 0;
                        int udp_pkts = 0;
@@ -4010,7 +4079,9 @@ static void mainloop(void)
                                case FD_TYPE_CLUSTER:   cluster_ready++; break;
                                case FD_TYPE_TUN:       tun_ready++; break;
                                case FD_TYPE_UDP:       udp_ready++; break;
                                case FD_TYPE_CLUSTER:   cluster_ready++; break;
                                case FD_TYPE_TUN:       tun_ready++; break;
                                case FD_TYPE_UDP:       udp_ready++; break;
-
+#ifdef LAC
+                               case FD_TYPE_UDPLAC:    udplac_ready++; break;
+#endif
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
                                        s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
                                        s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
@@ -4100,7 +4171,25 @@ static void mainloop(void)
                                                n--;
                                        }
                                }
                                                n--;
                                        }
                                }
+#ifdef LAC
+                               // L2TP REMOTE LNS
+                               if (udplac_ready)
+                               {
+                                       alen = sizeof(addr);
+                                       if ((s = recvfrom(udplacfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+                                       {
+                                               if (!config->disable_lac_func)
+                                                       processudp(buf, s, &addr);
 
 
+                                               udplac_pkts++;
+                                       }
+                                       else
+                                       {
+                                               udplac_ready = 0;
+                                               n--;
+                                       }
+                               }
+#endif
                                // incoming IP
                                if (tun_ready)
                                {
                                // incoming IP
                                if (tun_ready)
                                {
@@ -4138,9 +4227,13 @@ static void mainloop(void)
 
                        if (c >= config->multi_read_count)
                        {
 
                        if (c >= config->multi_read_count)
                        {
+#ifdef LAC
+                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster %d rmlns packets\n",
+                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts, udplac_pkts);
+#else
                                LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
                                        config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
                                LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
                                        config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
-
+#endif
                                STAT(multi_read_exceeded);
                                more++;
                        }
                                STAT(multi_read_exceeded);
                                more++;
                        }
@@ -4476,7 +4569,7 @@ static void initdata(int optdebug, char *optconfig)
 #endif /* BGP */
 
 #ifdef LAC
 #endif /* BGP */
 
 #ifdef LAC
-       initremotelnsdata();
+       lac_initremotelnsdata();
 #endif
 }
 
 #endif
 }
 
@@ -4764,7 +4857,11 @@ void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, ui
 
 static int dump_session(FILE **f, sessiont *s)
 {
 
 static int dump_session(FILE **f, sessiont *s)
 {
+#ifdef LAC
+       if (!s->opened || (!s->ip && !s->forwardtosession) || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
+#else
        if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
        if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
+#endif
                return 1;
 
        if (!*f)
                return 1;
 
        if (!*f)
@@ -5171,6 +5268,11 @@ static void update_config()
        if (!config->radius_dae_port)
                config->radius_dae_port = DAEPORT;
 
        if (!config->radius_dae_port)
                config->radius_dae_port = DAEPORT;
 
+#ifdef LAC
+       if(!config->bind_portremotelns)
+               config->bind_portremotelns = L2TPLACPORT;
+#endif
+
        // re-initialise the random number source
        initrandom(config->random_device);
 
        // re-initialise the random number source
        initrandom(config->random_device);
 
@@ -6251,7 +6353,7 @@ void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len)
        control32(c, 3, 3, 1); // framing Capabilities
        control16(c, 9, t, 1); // assigned tunnel
        controlb(c, 11, (uint8_t *) auth, auth_len, 1);  // CHAP Challenge
        control32(c, 3, 3, 1); // framing Capabilities
        control16(c, 9, t, 1); // assigned tunnel
        controlb(c, 11, (uint8_t *) auth, auth_len, 1);  // CHAP Challenge
-       LOG(1, 0, t, "Sent SCCRQ tunnel (REMOTE LNS)\n");
+       LOG(3, 0, t, "Sent SCCRQ to REMOTE LNS\n");
        controladd(c, 0, t); // send
 }
 
        controladd(c, 0, t); // send
 }
 
@@ -6263,7 +6365,7 @@ void lac_send_ICRQ(tunnelidt t, sessionidt s)
        control16(c, 14, s, 1); // assigned sesion
        call_serial_number++;
        control32(c, 15, call_serial_number, 1);  // call serial number
        control16(c, 14, s, 1); // assigned sesion
        call_serial_number++;
        control32(c, 15, call_serial_number, 1);  // call serial number
-       LOG(1, s, t, "Sent ICRQ (REMOTE LNS) (tunnel far ID %u)\n", tunnel[t].far);
+       LOG(3, s, t, "Sent ICRQ to REMOTE LNS (far ID %u)\n", tunnel[t].far);
        controladd(c, 0, t); // send
 }
 
        controladd(c, 0, t); // send
 }
 
index e83234d..33b4cfc 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -426,7 +426,7 @@ typedef struct
 #define SESSION_ACFC   (1 << 1)        // use Address-and-Control-Field-Compression
 #define SESSION_STARTED        (1 << 2)        // RADIUS Start record sent
 
 #define SESSION_ACFC   (1 << 1)        // use Address-and-Control-Field-Compression
 #define SESSION_STARTED        (1 << 2)        // RADIUS Start record sent
 
-// 168 bytes per tunnel
+// 328 bytes per tunnel
 typedef struct
 {
        tunnelidt far;          // far end tunnel ID
 typedef struct
 {
        tunnelidt far;          // far end tunnel ID
@@ -446,6 +446,12 @@ typedef struct
        uint16_t controlc;      // outstaind messages in queue
        controlt *controls;     // oldest message
        controlt *controle;     // newest message
        uint16_t controlc;      // outstaind messages in queue
        controlt *controls;     // oldest message
        controlt *controle;     // newest message
+#ifdef LAC
+       uint16_t isremotelns;   // != 0 if the tunnel is to remote LNS (== index on the conf remote lns)
+       char reserved[14];              // Space to expand structure without changing HB_VERSION
+#else
+       char reserved[16];              // Space to expand structure without changing HB_VERSION
+#endif
 }
 tunnelt;
 
 }
 tunnelt;
 
@@ -756,7 +762,9 @@ typedef struct
        int idle_echo_timeout; // Time between last packet seen and
                                                   // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds
 #ifdef LAC
        int idle_echo_timeout; // Time between last packet seen and
                                                   // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds
 #ifdef LAC
+       int disable_lac_func;
        int highest_rlnsid;
        int highest_rlnsid;
+       uint16_t bind_portremotelns;
 #endif
 } configt;
 
 #endif
 } configt;
 
@@ -891,7 +899,9 @@ void radiusretry(uint16_t r);
 uint16_t radiusnew(sessionidt s);
 void radiusclear(uint16_t r, sessionidt s);
 void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
 uint16_t radiusnew(sessionidt s);
 void radiusclear(uint16_t r, sessionidt s);
 void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
-
+#ifdef LAC
+int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen, const char *radiussecret, const uint8_t * auth);
+#endif
 
 // l2tpns.c
 clockt backoff(uint8_t try);
 
 // l2tpns.c
 clockt backoff(uint8_t try);
@@ -985,6 +995,9 @@ struct event_data {
                FD_TYPE_RADIUS,
                FD_TYPE_BGP,
                FD_TYPE_NETLINK,
                FD_TYPE_RADIUS,
                FD_TYPE_BGP,
                FD_TYPE_NETLINK,
+#ifdef LAC
+               FD_TYPE_UDPLAC,
+#endif
        } type;
        int index; // for RADIUS, BGP
 };
        } type;
        int index; // for RADIUS, BGP
 };
diff --git a/ppp.c b/ppp.c
index 518f6ec..fb74ba9 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,7 +1,5 @@
 // L2TPNS PPP Stuff
 
 // L2TPNS PPP Stuff
 
-//#define LAC
-
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -107,9 +105,9 @@ void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
        }
 
 #ifdef LAC
        }
 
 #ifdef LAC
-       if (forwardtolns(s, user))
+       if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, user))
        {
        {
-               LOG(3, s, t, "Forwarding login for %s to other LNS\n", user);
+               // Creating a tunnel/session has been started
                return;
        }
 #endif
                return;
        }
 #endif
@@ -266,12 +264,11 @@ void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                memcpy(packet.username, p, l);
 
 #ifdef LAC
                memcpy(packet.username, p, l);
 
 #ifdef LAC
-               if (forwardtolns(s, packet.username))
+               if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, packet.username))
                {
                {
-                       LOG(3, s, t, "Forwarding login for %s to other LNS\n", packet.username);
-
                        free(packet.username);
                        free(packet.password);
                        free(packet.username);
                        free(packet.password);
+                       // Creating a tunnel/session has been started
                        return;
                }
 #endif
                        return;
                }
 #endif
index 02b29b7..5c2ab46 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -11,6 +11,7 @@
 #include <ctype.h>
 #include <netinet/in.h>
 #include <errno.h>
 #include <ctype.h>
 #include <netinet/in.h>
 #include <errno.h>
+
 #include "md5.h"
 #include "constants.h"
 #include "l2tpns.h"
 #include "md5.h"
 #include "constants.h"
 #include "l2tpns.h"
 #include "util.h"
 #include "cluster.h"
 
 #include "util.h"
 #include "cluster.h"
 
+#ifdef LAC
+#include "l2tplac.h"
+#endif
+
 extern radiust *radius;
 extern sessiont *session;
 extern tunnelt *tunnel;
 extern radiust *radius;
 extern sessiont *session;
 extern tunnelt *tunnel;
@@ -230,6 +235,7 @@ void radiussend(uint16_t r, uint8_t state)
        }
        b[1] = r >> RADIUS_SHIFT;       // identifier
        memcpy(b + 4, radius[r].auth, 16);
        }
        b[1] = r >> RADIUS_SHIFT;       // identifier
        memcpy(b + 4, radius[r].auth, 16);
+
        p = b + 20;
        if (s)
        {
        p = b + 20;
        if (s)
        {
@@ -530,6 +536,9 @@ void processrad(uint8_t *buf, int len, char socket_index)
        uint8_t routes = 0;
        int r_code;
        int r_id;
        uint8_t routes = 0;
        int r_code;
        int r_id;
+#ifdef LAC
+       int OpentunnelReq = 0;
+#endif
 
        CSTAT(processrad);
 
 
        CSTAT(processrad);
 
@@ -629,6 +638,11 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                // Extract IP, routes, etc
                                uint8_t *p = buf + 20;
                                uint8_t *e = buf + len;
                                // Extract IP, routes, etc
                                uint8_t *p = buf + 20;
                                uint8_t *e = buf + len;
+#ifdef LAC
+                               uint8_t tag;
+                               uint8_t strtemp[256];
+                               lac_reset_rad_tag_tunnel_ctxt();
+#endif
                                for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1])
                                {
                                        if (*p == 26 && p[1] >= 7)
                                for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1])
                                {
                                        if (*p == 26 && p[1] >= 7)
@@ -823,6 +837,96 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                        session[s].classlen = MAXCLASS;
                                                memcpy(session[s].class, p + 2, session[s].classlen);
                                        }
                                                        session[s].classlen = MAXCLASS;
                                                memcpy(session[s].class, p + 2, session[s].classlen);
                                        }
+#ifdef LAC
+                                       else if (*p == 64)
+                                       {
+                                               // Tunnel-Type
+                                               if (p[1] != 6) continue;
+                                               tag = p[2];
+                                               LOG(3, s, session[s].tunnel, "   Radius reply Tunnel-Type:%d %d\n",
+                                                       tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF);
+                                               // Fill context
+                                               lac_set_rad_tag_tunnel_type(tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF);
+                                               /* Request open tunnel to remote LNS*/
+                                               OpentunnelReq = 1;
+                                       }
+                                       else if (*p == 65)
+                                       {
+                                               // Tunnel-Medium-Type
+                                               if (p[1] < 6) continue;
+                                               tag = p[2];
+                                               LOG(3, s, session[s].tunnel, "   Radius reply Tunnel-Medium-Type:%d %d\n",
+                                                       tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF);
+                                               // Fill context
+                                               lac_set_rad_tag_tunnel_medium_type(tag, ntohl(*(uint32_t *)(p + 2)) & 0xFFFFFF);
+                                       }
+                                       else if (*p == 67)
+                                       {
+                                               // Tunnel-Server-Endpoint
+                                               if (p[1] < 3) continue;
+                                               tag = p[2];
+                                               //If the Tag field is greater than 0x1F,
+                                               // it SHOULD be interpreted as the first byte of the following String field.
+                                               memset(strtemp, 0, 256);
+                                               if (tag > 0x1F)
+                                               {
+                                                       tag = 0;
+                                                       memcpy(strtemp, (p + 2), p[1]-2);
+                                               }
+                                               else
+                                                       memcpy(strtemp, (p + 3), p[1]-3);
+
+                                               LOG(3, s, session[s].tunnel, "   Radius reply Tunnel-Server-Endpoint:%d %s\n", tag, strtemp);
+                                               // Fill context
+                                               lac_set_rad_tag_tunnel_serv_endpt(tag, (char *) strtemp);
+                                       }
+                                       else if (*p == 69)
+                                       {
+                                               // Tunnel-Password
+                                               size_t lentemp;
+
+                                               if (p[1] < 5) continue;
+                                               tag = p[2];
+
+                                               memset(strtemp, 0, 256);
+                                               lentemp = p[1]-3;
+                                               memcpy(strtemp, (p + 3), lentemp);
+                                               if (!rad_tunnel_pwdecode(strtemp, &lentemp, config->radiussecret, radius[r].auth))
+                                               {
+                                                       LOG_HEX(3, "Error Decode Tunnel-Password, Dump Radius reponse:", p, p[1]);
+                                                       continue;
+                                               }
+
+                                               LOG(3, s, session[s].tunnel, "   Radius reply Tunnel-Password:%d %s\n", tag, strtemp);
+                                               if (strlen((char *) strtemp) > 63)
+                                               {
+                                                       LOG(1, s, session[s].tunnel, "tunnel password is too long (>63)\n");
+                                                       continue;
+                                               }
+                                               // Fill context
+                                               lac_set_rad_tag_tunnel_password(tag, (char *) strtemp);
+                                       }
+                                       else if (*p == 82)
+                                       {
+                                               // Tunnel-Assignment-Id
+                                               if (p[1] < 3) continue;
+                                               tag = p[2];
+                                               //If the Tag field is greater than 0x1F,
+                                               // it SHOULD be interpreted as the first byte of the following String field.
+                                               memset(strtemp, 0, 256);
+                                               if (tag > 0x1F)
+                                               {
+                                                       tag = 0;
+                                                       memcpy(strtemp, (p + 2), p[1]-2);
+                                               }
+                                               else
+                                                       memcpy(strtemp, (p + 3), p[1]-3);
+
+                                               LOG(3, s, session[s].tunnel, "   Radius reply Tunnel-Assignment-Id:%d %s\n", tag, strtemp);
+                                               // Fill context
+                                               lac_set_rad_tag_tunnel_assignment_id(tag, (char *) strtemp);
+                                       }
+#endif
                                }
                        }
                        else if (r_code == AccessReject)
                                }
                        }
                        else if (r_code == AccessReject)
@@ -832,6 +936,30 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                break;
                        }
 
                                break;
                        }
 
+#ifdef LAC
+                       if ((!config->disable_lac_func) && OpentunnelReq)
+                       {
+                               char assignment_id[256];
+                               // Save radius tag context to conf
+                               lac_save_rad_tag_tunnels(s);
+
+                               memset(assignment_id, 0, 256);
+                               if (!lac_rad_select_assignment_id(s, assignment_id))
+                                       break; // Error no assignment_id
+
+                               if (lac_rad_forwardtoremotelns(s, assignment_id, session[s].user))
+                               {
+                                       int ro;
+                                       // Sanity check, no local IP to session forwarded
+                                       session[s].ip = 0;
+                                       for (ro = 0; r < MAXROUTE && session[s].route[ro].ip; r++)
+                                       {
+                                               session[s].route[ro].ip = 0;
+                                       }
+                                       break;
+                               }
+                       }
+#endif
                        if (!session[s].dns1 && config->default_dns1)
                        {
                                session[s].dns1 = ntohl(config->default_dns1);
                        if (!session[s].dns1 && config->default_dns1)
                        {
                                session[s].dns1 = ntohl(config->default_dns1);
@@ -1174,3 +1302,96 @@ void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struc
        if (sendtofrom(daefd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) addr, alen, local) < 0)
                LOG(0, 0, 0, "Error sending DAE response packet: %s\n", strerror(errno));
 }
        if (sendtofrom(daefd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) addr, alen, local) < 0)
                LOG(0, 0, 0, "Error sending DAE response packet: %s\n", strerror(errno));
 }
+
+#ifdef LAC
+// Decrypte the encrypted Tunnel Password.
+// Defined in RFC-2868.
+// the pl2tpsecret buffer must set to 256 characters.
+// return 0 on decoding error else length of decoded l2tpsecret
+int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen,
+                                               const char *radiussecret, const uint8_t * auth)
+{
+       MD5_CTX ctx, oldctx;
+       hasht hash;
+       int secretlen;
+       unsigned i, n, len, decodedlen;
+
+/* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 6 7
+* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |     Salt      |     Salt      |   String ..........
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/
+
+       len = *pl2tpsecretlen;
+
+       if (len < 2)
+       {
+               LOG(1, 0, 0, "tunnel password is too short, We need at least a salt\n");
+               return 0;
+       }
+
+       if (len <= 3)
+       {
+               pl2tpsecret[0] = 0;
+               *pl2tpsecretlen = 0;
+               LOG(1, 0, 0, "tunnel passwd is empty !!!\n");
+               return 0;
+       }
+
+       len -= 2;       /* discount the salt */
+
+       //Use the secret to setup the decryption
+       secretlen = strlen(radiussecret);
+
+       MD5_Init(&ctx);
+       MD5_Update(&ctx, (void *) radiussecret, secretlen);
+       oldctx = ctx;   /* save intermediate work */
+
+       // Set up the initial key:
+       //      b(1) = MD5(radiussecret + auth + salt)
+       MD5_Update(&ctx, (void *) auth, 16);
+       MD5_Update(&ctx, pl2tpsecret, 2);
+
+       decodedlen = 0;
+       for (n = 0; n < len; n += 16)
+       {
+               int base = 0;
+
+               if (n == 0)
+               {
+                       MD5_Final(hash, &ctx);
+
+                       ctx = oldctx;
+
+                        // the first octet, it's the 'data_len'
+                        // Check is correct
+                       decodedlen = pl2tpsecret[2] ^ hash[0];
+                       if (decodedlen >= len)
+                       {
+                               LOG(1, 0, 0, "tunnel password is too long !!!\n");
+                               return 0;
+                       }
+
+                       MD5_Update(&ctx, pl2tpsecret + 2, 16);
+                       base = 1;
+               } else
+               {
+                       MD5_Final(hash, &ctx);
+
+                       ctx = oldctx;
+                       MD5_Update(&ctx, pl2tpsecret + n + 2, 16);
+               }
+
+               for (i = base; i < 16; i++)
+               {
+                       pl2tpsecret[n + i - 1] = pl2tpsecret[n + i + 2] ^ hash[i];
+               }
+       }
+
+       if (decodedlen > 239) decodedlen = 239;
+
+       *pl2tpsecretlen = decodedlen;
+       pl2tpsecret[decodedlen] = 0;
+
+       return decodedlen;
+};
+#endif /* LAC */