Update changelog.
[l2tpns.git] / l2tplac.c
index 11ac963..fd3a7e0 100644 (file)
--- a/l2tplac.c
+++ b/l2tplac.c
@@ -1,18 +1,25 @@
 /*
+ * Fernando ALVES 2013
  * Add functionality "LAC" to l2tpns.
  * Used to forward a ppp session to another "LNS".
+ * GPL licenced
  */
+
 #include <errno.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
 
 #include "md5.h"
 #include "l2tpns.h"
 #include "util.h"
+#include "cluster.h"
 
 #include "l2tplac.h"
+#include "pppoe.h"
 
 /* sequence diagram: Client <--> LAC <--> LNS1 <--> LNS2
- * 
+ *
  *           LCP Negotiation
  * Client <-------------------> LAC
  *         Challenge (CHAP/PAP)
  * 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
- * TUNNELFREE -> TUNNELOPEN -> TUNNELDIE -> TUNNELFREE
+ * CONFRLNSFREE -> CONFRLNSSET -> CONFRLNSFREE
  */
 enum
 {
        CONFRLNSFREE = 0,       // Not in use
-       CONFRLNSSET             // Config Set
+       CONFRLNSSET,            // Config Set
+       CONFRLNSSETBYRADIUS     // Config Set
 };
 
 // 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];
-       char l2tp_secret[64];           // L2TP shared secret
+       char l2tp_secret[64];   // L2TP shared secret
+       char tunnel_assignment_id[256];
 }
 configrlns;
 
-configrlns *pconfigrlns = NULL;                        // Array of tunnel structures.
+configrlns *pconfigrlns = NULL;
 
 // Init data structures
-void initremotelnsdata()
+void lac_initremotelnsdata()
 {
        confrlnsidt i;
 
@@ -104,135 +120,329 @@ void initremotelnsdata()
 
        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;
+
+       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))
-               {
-                       t = pconfigrlns[i].tid;
+               ptunnelrlns[tag].tunnel_server_endpoint = ntohl(inet_addr(tunnel_server_endpoint));
+       }
+}
 
-                       if ((t != 0) && (tunnel[t].ip != pconfigrlns[i].ip))
-                       {
-                               pconfigrlns[i].tid = t = 0;
-                               LOG(1, 0, t, "Tunnel ID inconsistency\n");
-                       }
+// 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);
+       }
+}
 
-                       if (t == 0)
-                       {
-                               if (main_quit == QUIT_SHUTDOWN) return 0;
+// 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);
+       }
+}
 
-                               // 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].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);
+// Select a tunnel_assignment_id
+int lac_rad_select_assignment_id(sessionidt s, char *assignment_id)
+{
+       int idtag;
+       int nbtagfound = 0;
+       int bufidtag[MAXTAGTUNNEL];
 
-                               random_data(pconfigrlns[i].auth, sizeof(pconfigrlns[i].auth));
+       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)
+               {
+                       bufidtag[nbtagfound] = idtag;
+                       nbtagfound++;
+               }
+       }
 
-                               pconfigrlns[i].tid = t;
+       if (nbtagfound > 0)
+       {
+               // random between 0 and nbtagfound-1
+               idtag = (rand() % nbtagfound);
 
-                               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;
+               if (idtag >= nbtagfound)
+                       idtag = 0; //Sanity checks.
 
-                                       sessionfree = session[new_sess].next;
-                                       memset(&session[new_sess], 0, sizeof(session[new_sess]));
+               strcpy(assignment_id, ptunnelrlns[bufidtag[idtag]].tunnel_assignment_id);
+               return 1;
+       }
 
-                                       if (new_sess > config->cluster_highest_sessionid)
-                                               config->cluster_highest_sessionid = new_sess;
+       // Error no tunnel_assignment_id found
+       return 0;
+}
 
-                                       session[new_sess].opened = time_now;
-                                       session[new_sess].tunnel = t;
-                                       session[new_sess].last_packet = session[s].last_data = time_now;
+// Save the 'radius tag tunnels' context on global configuration
+void lac_save_rad_tag_tunnels(sessionidt s)
+{
+       confrlnsidt idrlns;
+       int idtag;
 
-                                       session[new_sess].ppp.phase = Establish;
-                                       session[new_sess].ppp.lcp = Starting;
+       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 if (ptunnelrlns[idtag].tunnel_server_endpoint == ntohl(config->bind_address))
+                       LOG(0, s, session[s].tunnel, "Error, IP Remote LNS == IP local bind address (%s) !!!\n", fmtaddr(config->bind_address, 0));
+               else
+               {
+                       for (idrlns = 1; idrlns < MAXRLNSTUNNEL; ++idrlns)
+                       {
+                               if (pconfigrlns[idrlns].state == CONFRLNSFREE)
+                               {
+                                       pconfigrlns[idrlns].ip = ptunnelrlns[idtag].tunnel_server_endpoint;
+                                       pconfigrlns[idrlns].port = L2TPPORT; //Default L2TP port
+                                       strcpy(pconfigrlns[idrlns].l2tp_secret, ptunnelrlns[idtag].tunnel_password);
+                                       strcpy(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id);
 
-                                       // Sent ICRQ  Incoming-call-request
-                                       lac_send_ICRQ(t, new_sess);
+                                       config->highest_rlnsid = idrlns;
 
-                                       // Set session to forward to another LNS
-                                       session[s].forwardtosession = new_sess;
-                                       session[new_sess].forwardtosession = s;
+                                       pconfigrlns[idrlns].state = CONFRLNSSETBYRADIUS;
 
-                                       STAT(session_created);
+                                       break;
                                }
-                               else
+                               else if (pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS)
                                {
-                                       lac_tunnelshutdown(t, "Shutting down", 6, 0, 0);
-                                       pconfigrlns[i].tid = 0;
+                                       if ( (pconfigrlns[idrlns].ip == ptunnelrlns[idtag].tunnel_server_endpoint) &&
+                                                (strcmp(pconfigrlns[idrlns].tunnel_assignment_id, ptunnelrlns[idtag].tunnel_assignment_id) == 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;
+                                       }
                                }
                        }
-                       else
+
+                       if (idrlns >= MAXRLNSTUNNEL)
                        {
-                               /** TODO **/
-                               LOG(1, 0, t, "(REMOTE LNS) tunnel is not open\n");
+                               LOG(0, s, session[s].tunnel, "No more Remote LNS Conf Free\n");
+                               return;
                        }
+               }
+       }
+}
 
-                       return 1;
+// 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;
+
+               // 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;
+               tunnel[t].indexudp = config->indexlacudpfd;
+               STAT(tunnel_created);
+
+               random_data(pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
+
+               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)
+               {
 
-       return 0;
-}
+                       /**********************/
+                       /** Open New session **/
+                       /**********************/
+                       sessionidt new_sess = sessionfree;
+
+                       sessionfree = session[new_sess].next;
+                       memset(&session[new_sess], 0, sizeof(session[new_sess]));
+
+                       if (new_sess > config->cluster_highest_sessionid)
+                               config->cluster_highest_sessionid = new_sess;
+
+                       session[new_sess].opened = time_now;
+                       session[new_sess].tunnel = t;
+                       session[new_sess].last_packet = session[s].last_data = time_now;
+
+                       session[new_sess].ppp.phase = Establish;
+                       session[new_sess].ppp.lcp = Starting;
+                       session[s].ppp.phase = Establish;
+
+                       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);
 
-static tunnelidt getidrlns(tunnelidt t)
+                       // 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 1;
+}
+// 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;
 }
 
-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;
 }
 
-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;
 
-       idrlns = getidrlns(t);
+       idrlns = tunnel[t].isremotelns;
 
        MD5_Init(&ctx);
        MD5_Update(&ctx, &id, 1);
@@ -241,8 +451,8 @@ void calc_lac_auth(tunnelidt t, uint8_t id, uint8_t *out)
        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, in_addr_t s_addr, int sin_port, uint16_t indexudpfd)
 {
        uint16_t t = 0, s = 0;
        uint8_t *p = buf + 2; // First word L2TP options
@@ -261,8 +471,33 @@ int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto
                return 0;
        }
 
+       if ((!tunnel[t].isremotelns) && (!tunnel[session[sess].tunnel].isremotelns))
+       {
+               LOG(0, sess, session[sess].tunnel, "Link Tunnel Session (%u/%u) broken\n", s, t);
+               return 0;
+       }
+
+       if (!config->cluster_iam_master)
+       {
+               if ( (proto == PPPIPCP) || (proto == PPPLCP) ||
+                        (proto == PPPPAP) || (proto == PPPCHAP) ||
+                        (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0]) ||
+                        (proto == PPPCCP) )
+               {
+                       session[sess].last_packet = time_now;
+                       master_forward_packet(buf, len, s_addr, sin_port, indexudpfd);
+                       return 1;
+               }
+       }
+
+       if (t == TUNNEL_ID_PPPOE)
+       {
+               pppoe_forwardto_session_pppoe(buf, len, sess, proto);
+               return 1;
+       }
+
        if (*buf & 0x40)
-       {   // length
+       {       // length
                p += 2;
        }
 
@@ -282,6 +517,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;
+               // 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;
@@ -291,7 +540,12 @@ int session_forward_tolns(uint8_t *buf, int len, sessionidt sess, uint16_t proto
        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;
 
@@ -308,9 +562,26 @@ int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *Sec
 
                        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;
                }
@@ -320,3 +591,60 @@ int addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *Sec
 
        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 %3s  %-32s %-32s %11s %7s %10s",
+                               "IP Remote LNS",
+                               "TID",
+                               "l2tp secret",
+                               "assignment Id",
+                               "File/Radius",
+                               "State",
+                               "Count Sess");
+       else
+       {
+               tunnelidt t, tfound = 0;
+               sessionidt s;
+               int countsess = 0;
+               char state[20];
+
+               strcpy(state, "Close");
+               for (t = 0; t <= config->cluster_highest_tunnelid ; ++t)
+               {
+                       if ((tunnel[t].isremotelns == idrlns) &&
+                               (tunnel[t].ip == pconfigrlns[idrlns].ip) &&
+                               (tunnel[t].port == pconfigrlns[idrlns].port) &&
+                               (tunnel[t].state != TUNNELDIE))
+                       {
+                               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++;
+                               tfound = t;
+                               break;
+                       }
+               }
+
+               sprintf(strout, "%15s %3u  %-32s %-32s %11s %7s %10u",
+                               fmtaddr(htonl(pconfigrlns[idrlns].ip), 0),
+                               tfound,
+                               pconfigrlns[idrlns].l2tp_secret,
+                               pconfigrlns[idrlns].tunnel_assignment_id,
+                               (pconfigrlns[idrlns].state == CONFRLNSSET?"File":(pconfigrlns[idrlns].state == CONFRLNSSETBYRADIUS?"Radius":"Free")),
+                               state,
+                               countsess);
+       }
+
+       return 1;
+}