Add DHCPv6 functionality.
[l2tpns.git] / l2tpns.c
index 0da9a24..07a45f7 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -10,6 +10,7 @@
 #include <fcntl.h>
 #include <linux/if_tun.h>
 #define SYSLOG_NAMES
 #include <fcntl.h>
 #include <linux/if_tun.h>
 #define SYSLOG_NAMES
+#include <stdio.h>
 #include <syslog.h>
 #include <malloc.h>
 #include <net/route.h>
 #include <syslog.h>
 #include <malloc.h>
 #include <net/route.h>
@@ -19,7 +20,6 @@
 #include <netinet/ip6.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <netinet/ip6.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <sys/ioctl.h>
 #include <string.h>
 #include <ctype.h>
 #include <sys/ioctl.h>
@@ -40,6 +40,7 @@
 #include <linux/rtnetlink.h>
 
 #include "md5.h"
 #include <linux/rtnetlink.h>
 
 #include "md5.h"
+#include "dhcp6.h"
 #include "l2tpns.h"
 #include "cluster.h"
 #include "plugin.h"
 #include "l2tpns.h"
 #include "cluster.h"
 #include "plugin.h"
 #include "bgp.h"
 #endif
 
 #include "bgp.h"
 #endif
 
-#ifdef LAC
 #include "l2tplac.h"
 #include "l2tplac.h"
-#endif
 #include "pppoe.h"
 #include "pppoe.h"
+#include "dhcp6.h"
 
 
-#ifdef LAC
 char * Vendor_name = "Linux L2TPNS";
 uint32_t call_serial_number = 0;
 char * Vendor_name = "Linux L2TPNS";
 uint32_t call_serial_number = 0;
-#endif
 
 // Globals
 configt *config = NULL;                // all configuration
 int nlfd = -1;                 // netlink socket
 int tunfd = -1;                        // tun interface file handle. (network device)
 
 // Globals
 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
-#ifdef LAC
+int udpfd[MAX_UDPFD + 1] = INIT_TABUDPFD;              // array UDP file handle + 1 for lac udp
 int udplacfd = -1;             // UDP LAC file handle
 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.
@@ -158,6 +154,7 @@ config_descriptt config_values[] = {
        CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
        CONFIG("throttle_buckets", num_tbfs, INT),
        CONFIG("accounting_dir", accounting_dir, STRING),
        CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
        CONFIG("throttle_buckets", num_tbfs, INT),
        CONFIG("accounting_dir", accounting_dir, STRING),
+       CONFIG("account_all_origin", account_all_origin, BOOL),
        CONFIG("dump_speed", dump_speed, BOOL),
        CONFIG("multi_read_count", multi_read_count, INT),
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
        CONFIG("dump_speed", dump_speed, BOOL),
        CONFIG("multi_read_count", multi_read_count, INT),
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
@@ -181,16 +178,26 @@ config_descriptt config_values[] = {
        CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
        CONFIG("iftun_address", iftun_address, IPv4),
        CONFIG("tundevicename", tundevicename, STRING),
        CONFIG("idle_echo_timeout", idle_echo_timeout, INT),
        CONFIG("iftun_address", iftun_address, IPv4),
        CONFIG("tundevicename", tundevicename, STRING),
-#ifdef LAC
        CONFIG("disable_lac_func", disable_lac_func, BOOL),
        CONFIG("auth_tunnel_change_addr_src", auth_tunnel_change_addr_src, BOOL),
        CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4),
        CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
        CONFIG("disable_lac_func", disable_lac_func, BOOL),
        CONFIG("auth_tunnel_change_addr_src", auth_tunnel_change_addr_src, BOOL),
        CONFIG("bind_address_remotelns", bind_address_remotelns, IPv4),
        CONFIG("bind_portremotelns", bind_portremotelns, SHORT),
-#endif
        CONFIG("pppoe_if_to_bind", pppoe_if_to_bind, STRING),
        CONFIG("pppoe_service_name", pppoe_service_name, STRING),
        CONFIG("pppoe_ac_name", pppoe_ac_name, STRING),
        CONFIG("pppoe_if_to_bind", pppoe_if_to_bind, STRING),
        CONFIG("pppoe_service_name", pppoe_service_name, STRING),
        CONFIG("pppoe_ac_name", pppoe_ac_name, STRING),
-       { NULL, 0, 0, 0 },
+       CONFIG("disable_sending_hello", disable_sending_hello, BOOL),
+       CONFIG("disable_no_spoof", disable_no_spoof, BOOL),
+       CONFIG("bind_multi_address", bind_multi_address, STRING),
+       CONFIG("pppoe_only_equal_svc_name", pppoe_only_equal_svc_name, BOOL),
+       CONFIG("multi_hostname", multi_hostname, STRING),
+       CONFIG("no_throttle_local_IP", no_throttle_local_IP, BOOL),
+       CONFIG("dhcp6_preferred_lifetime", dhcp6_preferred_lifetime, INT),
+       CONFIG("dhcp6_valid_lifetime", dhcp6_valid_lifetime, INT),
+       CONFIG("dhcp6_server_duid", dhcp6_server_duid, INT),
+       CONFIG("primary_ipv6_dns", default_ipv6_dns1, IPv6),
+       CONFIG("secondary_ipv6_dns", default_ipv6_dns2, IPv6),
+       CONFIG("default_ipv6_domain_list", default_ipv6_domain_list, STRING),
+       { NULL, 0, 0, 0 }
 };
 
 static char *plugin_functions[] = {
 };
 
 static char *plugin_functions[] = {
@@ -698,7 +705,7 @@ static void inittun(void)
        }
 
    if (*config->tundevicename)
        }
 
    if (*config->tundevicename)
-         strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ);
+               strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ);
 
        if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0)
        {
 
        if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0)
        {
@@ -760,14 +767,30 @@ static void inittun(void)
                req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
                req.ifmsg.ifaddr.ifa_index = tunidx;
 
                req.ifmsg.ifaddr.ifa_scope = RT_SCOPE_UNIVERSE;
                req.ifmsg.ifaddr.ifa_index = tunidx;
 
-               if (config->iftun_address)
-                       ip = config->iftun_address;
+               if (config->nbmultiaddress > 1)
+               {
+                       int i;
+                       for (i = 0; i < config->nbmultiaddress ; i++)
+                       {
+                               ip = config->iftun_n_address[i];
+                               netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip));
+                               if (netlink_send(&req.nh) < 0)
+                                       goto senderror;
+                       }
+               }
                else
                else
-                       ip = 0x01010101; // 1.1.1.1
-               netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip));
+               {
+                       if (config->iftun_address)
+                               ip = config->iftun_address;
+                       else
+                               ip = 0x01010101; // 1.1.1.1
+                       netlink_addattr(&req.nh, IFA_LOCAL, &ip, sizeof(ip));
+
+                       if (netlink_send(&req.nh) < 0)
+                               goto senderror;
+               }
+
 
 
-               if (netlink_send(&req.nh) < 0)
-                       goto senderror;
 
                // Only setup IPv6 on the tun device if we have a configured prefix
                if (config->ipv6_prefix.s6_addr[0]) {
 
                // Only setup IPv6 on the tun device if we have a configured prefix
                if (config->ipv6_prefix.s6_addr[0]) {
@@ -837,28 +860,35 @@ senderror:
        exit(1);
 }
 
        exit(1);
 }
 
-// set up UDP ports
-static void initudp(void)
+// set up LAC UDP ports
+static void initlacudp(void)
 {
        int on = 1;
        struct sockaddr_in addr;
 
 {
        int on = 1;
        struct sockaddr_in addr;
 
-       // Tunnel
+       // Tunnel to Remote LNS
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
-       addr.sin_port = htons(L2TPPORT);
-       addr.sin_addr.s_addr = config->bind_address;
-       udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       addr.sin_port = htons(config->bind_portremotelns);
+       addr.sin_addr.s_addr = config->bind_address_remotelns;
+       udplacfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       setsockopt(udplacfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        {
        {
-               int flags = fcntl(udpfd, F_GETFL, 0);
-               fcntl(udpfd, F_SETFL, flags | O_NONBLOCK);
+               int flags = fcntl(udplacfd, F_GETFL, 0);
+               fcntl(udplacfd, F_SETFL, flags | O_NONBLOCK);
        }
        }
-       if (bind(udpfd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+       if (bind(udplacfd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
        {
-               LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno));
+               LOG(0, 0, 0, "Error in UDP REMOTE LNS bind: %s\n", strerror(errno));
                exit(1);
        }
                exit(1);
        }
+}
+
+// set up control ports
+static void initcontrol(void)
+{
+       int on = 1;
+       struct sockaddr_in addr;
 
        // Control
        memset(&addr, 0, sizeof(addr));
 
        // Control
        memset(&addr, 0, sizeof(addr));
@@ -872,6 +902,13 @@ static void initudp(void)
                LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno));
                exit(1);
        }
                LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno));
                exit(1);
        }
+}
+
+// set up Dynamic Authorization Extensions to RADIUS port
+static void initdae(void)
+{
+       int on = 1;
+       struct sockaddr_in addr;
 
        // Dynamic Authorization Extensions to RADIUS
        memset(&addr, 0, sizeof(addr));
 
        // Dynamic Authorization Extensions to RADIUS
        memset(&addr, 0, sizeof(addr));
@@ -885,28 +922,30 @@ static void initudp(void)
                LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno));
                exit(1);
        }
                LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno));
                exit(1);
        }
+}
 
 
-#ifdef LAC
-       // Tunnel to Remote LNS
+// set up UDP ports
+static void initudp(int * pudpfd, in_addr_t ip_bind)
+{
+       int on = 1;
+       struct sockaddr_in addr;
+
+       // Tunnel
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
-       addr.sin_port = htons(config->bind_portremotelns);
-       addr.sin_addr.s_addr = config->bind_address_remotelns;
-       udplacfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       setsockopt(udplacfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+       addr.sin_port = htons(L2TPPORT);
+       addr.sin_addr.s_addr = ip_bind;
+       (*pudpfd) = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       setsockopt((*pudpfd), SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        {
        {
-               int flags = fcntl(udplacfd, F_GETFL, 0);
-               fcntl(udplacfd, F_SETFL, flags | O_NONBLOCK);
+               int flags = fcntl((*pudpfd), F_GETFL, 0);
+               fcntl((*pudpfd), F_SETFL, flags | O_NONBLOCK);
        }
        }
-       if (bind(udplacfd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+       if (bind((*pudpfd), (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
        {
-               LOG(0, 0, 0, "Error in UDP REMOTE LNS bind: %s\n", strerror(errno));
+               LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno));
                exit(1);
        }
                exit(1);
        }
-#endif
-
-       // Intercept
-       snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 }
 
 //
 }
 
 //
@@ -983,8 +1022,10 @@ sessionidt sessionbyipv6(struct in6_addr ip)
                 ip.s6_addr[1] == 0x80 &&
                 ip.s6_addr16[1] == 0 &&
                 ip.s6_addr16[2] == 0 &&
                 ip.s6_addr[1] == 0x80 &&
                 ip.s6_addr16[1] == 0 &&
                 ip.s6_addr16[2] == 0 &&
-                ip.s6_addr16[3] == 0)) {
-               s = lookup_ipmap(*(in_addr_t *) &ip.s6_addr[8]);
+                ip.s6_addr16[3] == 0))
+       {
+               in_addr_t *pipv4 = (in_addr_t *) &ip.s6_addr[8];
+               s = lookup_ipmap(*pipv4);
        } else {
                s = lookup_ipv6map(ip);
        }
        } else {
                s = lookup_ipv6map(ip);
        }
@@ -1070,7 +1111,7 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s)
 //
 // CLI list to dump current ipcache.
 //
 //
 // CLI list to dump current ipcache.
 //
-int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc)
+int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int argc)
 {
        union iphash *d = ip_hash, *e, *f, *g;
        int i, j, k, l;
 {
        union iphash *d = ip_hash, *e, *f, *g;
        int i, j, k, l;
@@ -1231,14 +1272,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)
-#endif
+
+       if (sendto(udpfd[tunnel[t].indexudp], buf, l, 0, (void *) &addr, sizeof(addr)) < 0)
        {
                LOG(0, ntohs((*(uint16_t *) (buf + 6))), t, "Error sending data out tunnel: %s (udpfd=%d, buf=%p, len=%d, dest=%s)\n",
        {
                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));
+                               strerror(errno), udpfd[tunnel[t].indexudp], buf, l, inet_ntoa(addr.sin_addr));
                STAT(tunnel_tx_errors);
                return;
        }
                STAT(tunnel_tx_errors);
                return;
        }
@@ -1388,7 +1426,7 @@ void processipout(uint8_t *buf, int len)
        sessionidt s;
        sessiont *sp;
        tunnelidt t;
        sessionidt s;
        sessiont *sp;
        tunnelidt t;
-       in_addr_t ip;
+       in_addr_t ip, ip_src;
 
        uint8_t *data = buf;    // Keep a copy of the originals.
        int size = len;
 
        uint8_t *data = buf;    // Keep a copy of the originals.
        int size = len;
@@ -1421,6 +1459,7 @@ void processipout(uint8_t *buf, int len)
                return;
        }
 
                return;
        }
 
+       ip_src = *(uint32_t *)(buf + 12);
        ip = *(uint32_t *)(buf + 16);
        if (!(s = sessionbyip(ip)))
        {
        ip = *(uint32_t *)(buf + 16);
        if (!(s = sessionbyip(ip)))
        {
@@ -1505,12 +1544,15 @@ void processipout(uint8_t *buf, int len)
 
        if (sp->tbf_out)
        {
 
        if (sp->tbf_out)
        {
-               // Are we throttling this session?
-               if (config->cluster_iam_master)
-                       tbf_queue_packet(sp->tbf_out, data, size);
-               else
-                       master_throttle_packet(sp->tbf_out, data, size);
-               return;
+               if (!config->no_throttle_local_IP || !sessionbyip(ip_src))
+               {
+                       // Are we throttling this session?
+                       if (config->cluster_iam_master)
+                               tbf_queue_packet(sp->tbf_out, data, size);
+                       else
+                               master_throttle_packet(sp->tbf_out, data, size);
+                       return;
+               }
        }
 
        if (sp->walled_garden && !config->cluster_iam_master)
        }
 
        if (sp->walled_garden && !config->cluster_iam_master)
@@ -1624,17 +1666,15 @@ void processipout(uint8_t *buf, int len)
                else
                {
                        // Send it as one frame (NO MPPP Frame)
                else
                {
                        // Send it as one frame (NO MPPP Frame)
-                       uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
-                       if (!p) return;
-                       tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+                       uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0);
+                       tunnelsend(p, len + (buf-p), t); // send it...
                        update_session_out_stat(s, sp, len);
                }
        }
        else
        {
                        update_session_out_stat(s, sp, len);
                }
        }
        else
        {
-               uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0);
-               if (!p) return;
-               tunnelsend(fragbuf, len + (p-fragbuf), t); // send it...
+               uint8_t *p = opt_makeppp(buf, len, s, t, PPPIP, 0, 0, 0);
+               tunnelsend(p, len + (buf-p), t); // send it...
                update_session_out_stat(s, sp, len);
        }
 
                update_session_out_stat(s, sp, len);
        }
 
@@ -1774,6 +1814,8 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
 {
        sessiont *sp;
        tunnelidt t;
 {
        sessiont *sp;
        tunnelidt t;
+       uint8_t *p;
+       uint8_t *data = buf;    // Keep a copy of the originals.
 
        uint8_t b[MAXETHER + 20];
 
 
        uint8_t b[MAXETHER + 20];
 
@@ -1796,11 +1838,14 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
        LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
 
        // Add on L2TP header
        LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
 
        // Add on L2TP header
-       {
-               uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0);
-               if (!p) return;
-               tunnelsend(b, len + (p-b), t); // send it...
-       }
+       if (*(uint16_t *) (data + 2) == htons(PKTIPV6))
+               p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); // IPV6
+       else
+               p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0); // IPV4
+
+       if (!p) return;
+
+       tunnelsend(b, len + (p-b), t); // send it...
 
        // Snooping this session.
        if (sp->snoop_ip && sp->snoop_port)
 
        // Snooping this session.
        if (sp->snoop_ip && sp->snoop_port)
@@ -1819,10 +1864,11 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
 static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
 {
        uint16_t l = (m ? 0x8008 : 0x0008);
 static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
 {
        uint16_t l = (m ? 0x8008 : 0x0008);
-       c->buf16[c->length/2 + 0] = htons(l);
-       c->buf16[c->length/2 + 1] = htons(0);
-       c->buf16[c->length/2 + 2] = htons(avp);
-       c->buf16[c->length/2 + 3] = htons(val);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       pint16[3] = htons(val);
        c->length += 8;
 }
 
        c->length += 8;
 }
 
@@ -1830,10 +1876,12 @@ static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
 static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
 {
        uint16_t l = (m ? 0x800A : 0x000A);
 static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
 {
        uint16_t l = (m ? 0x800A : 0x000A);
-       c->buf16[c->length/2 + 0] = htons(l);
-       c->buf16[c->length/2 + 1] = htons(0);
-       c->buf16[c->length/2 + 2] = htons(avp);
-       *(uint32_t *) &c->buf[c->length + 6] = htonl(val);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       uint32_t *pint32 = (uint32_t *) (c->buf + c->length + 6);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       pint32[0] = htonl(val);
        c->length += 10;
 }
 
        c->length += 10;
 }
 
@@ -1841,10 +1889,11 @@ static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
 static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
 static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
-       c->buf16[c->length/2 + 0] = htons(l);
-       c->buf16[c->length/2 + 1] = htons(0);
-       c->buf16[c->length/2 + 2] = htons(avp);
-       memcpy(&c->buf[c->length + 6], val, strlen(val));
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       memcpy(c->buf + c->length + 6, val, strlen(val));
        c->length += 6 + strlen(val);
 }
 
        c->length += 6 + strlen(val);
 }
 
@@ -1852,10 +1901,11 @@ static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
 static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + len + 6);
 static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m)
 {
        uint16_t l = ((m ? 0x8000 : 0) + len + 6);
-       c->buf16[c->length/2 + 0] = htons(l);
-       c->buf16[c->length/2 + 1] = htons(0);
-       c->buf16[c->length/2 + 2] = htons(avp);
-       memcpy(&c->buf[c->length + 6], val, len);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       memcpy(c->buf + c->length + 6, val, len);
        c->length += 6 + len;
 }
 
        c->length += 6 + len;
 }
 
@@ -1872,7 +1922,8 @@ static controlt *controlnew(uint16_t mtype)
        }
        assert(c);
        c->next = 0;
        }
        assert(c);
        c->next = 0;
-       c->buf16[0] = htons(0xC802); // flags/ver
+       c->buf[0] = 0xC8; // flags
+       c->buf[1] = 0x02; // ver
        c->length = 12;
        control16(c, 0, mtype, 1);
        return c;
        c->length = 12;
        control16(c, 0, mtype, 1);
        return c;
@@ -1898,10 +1949,11 @@ static void controlnull(tunnelidt t)
 // add a control message to a tunnel, and send if within window
 static void controladd(controlt *c, sessionidt far, tunnelidt t)
 {
 // add a control message to a tunnel, and send if within window
 static void controladd(controlt *c, sessionidt far, tunnelidt t)
 {
-       c->buf16[1] = htons(c->length); // length
-       c->buf16[2] = htons(tunnel[t].far); // tunnel
-       c->buf16[3] = htons(far); // session
-       c->buf16[4] = htons(tunnel[t].ns); // sequence
+       uint16_t *pint16 = (uint16_t *) (c->buf + 2);
+       pint16[0] = htons(c->length); // length
+       pint16[1] = htons(tunnel[t].far); // tunnel
+       pint16[2] = htons(far); // session
+       pint16[3] = htons(tunnel[t].ns); // sequence
        tunnel[t].ns++;              // advance sequence
        // link in message in to queue
        if (tunnel[t].controlc)
        tunnel[t].ns++;              // advance sequence
        // link in message in to queue
        if (tunnel[t].controlc)
@@ -2026,7 +2078,6 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
                LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason);
                run_plugins(PLUGIN_KILL_SESSION, &data);
                struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] };
                LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason);
                run_plugins(PLUGIN_KILL_SESSION, &data);
-               session[s].die = TIME + 150; // Clean up in 15 seconds
        }
 
        if (session[s].ip && !walled_garden && !session[s].die)
        }
 
        if (session[s].ip && !walled_garden && !session[s].die)
@@ -2051,6 +2102,9 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                        memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s]));
        }
 
                        memcpy(&shut_acct[shut_acct_n++], &session[s], sizeof(session[s]));
        }
 
+       if (!session[s].die)
+               session[s].die = TIME + 150; // Clean up in 15 seconds
+
        if (session[s].ip)
        {                          // IP allocated, clear and unroute
                int r;
        if (session[s].ip)
        {                          // IP allocated, clear and unroute
                int r;
@@ -2074,9 +2128,14 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                        free_ip_address(s);
 
                // unroute IPv6, if setup
                        free_ip_address(s);
 
                // unroute IPv6, if setup
-               if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen && del_routes)
+               if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen && del_routes)
                        route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
                        route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
-               
+
+               if (session[s].ipv6address.s6_addr[0] && del_routes)
+               {
+                       route6set(s, session[s].ipv6address, 128, 0);
+               }
+
                if (b)
                {
                        // This session was part of a bundle
                if (b)
                {
                        // This session was part of a bundle
@@ -2132,6 +2191,11 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                                                // IPV6 route
                                                if (session[new_s].ipv6prefixlen)
                                                        cache_ipv6map(session[new_s].ipv6route, session[new_s].ipv6prefixlen, new_s);
                                                // IPV6 route
                                                if (session[new_s].ipv6prefixlen)
                                                        cache_ipv6map(session[new_s].ipv6route, session[new_s].ipv6prefixlen, new_s);
+
+                                               if (session[new_s].ipv6address.s6_addr[0])
+                                               {
+                                                       cache_ipv6map(session[new_s].ipv6address, 128, new_s);
+                                               }
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -2205,7 +2269,7 @@ void sendipcp(sessionidt s, tunnelidt t)
        q[4] = 3;                               // ip address option
        q[5] = 6;                               // option length
        *(in_addr_t *) (q + 6) = config->peer_address ? config->peer_address :
        q[4] = 3;                               // ip address option
        q[5] = 6;                               // option length
        *(in_addr_t *) (q + 6) = config->peer_address ? config->peer_address :
-                                config->iftun_address ? config->iftun_address :
+                                config->iftun_n_address[tunnel[t].indexudp] ? config->iftun_n_address[tunnel[t].indexudp] :
                                 my_address; // send my IP
 
        tunnelsend(buf, 10 + (q - buf), t); // send it
                                 my_address; // send my IP
 
        tunnelsend(buf, 10 + (q - buf), t); // send it
@@ -2269,7 +2333,6 @@ 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[s].forwardtosession)
        {
                sessionidt sess = session[s].forwardtosession;
@@ -2279,7 +2342,6 @@ void sessionkill(sessionidt s, char *reason)
                        sessionshutdown(sess, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET);
                }
        }
                        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);
 
        LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason);
        sessionclear(s);
@@ -2385,9 +2447,10 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha
 }
 
 // read and process packet on tunnel (UDP)
 }
 
 // read and process packet on tunnel (UDP)
-void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
+void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexudpfd)
 {
 {
-       uint8_t *chapresponse = NULL;
+       uint8_t *sendchalresponse = NULL;
+       uint8_t *recvchalresponse = NULL;
        uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0;
        uint8_t *p = buf + 2;
 
        uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0;
        uint8_t *p = buf + 2;
 
@@ -2476,7 +2539,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                if (!config->cluster_iam_master)
                {
 
                if (!config->cluster_iam_master)
                {
-                       master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+                       master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                        return;
                }
 
                        return;
                }
 
@@ -2526,6 +2589,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        tunnel[t].ip = ntohl(*(in_addr_t *) & addr->sin_addr);
                        tunnel[t].port = ntohs(addr->sin_port);
                        tunnel[t].window = 4; // default window
                        tunnel[t].ip = ntohl(*(in_addr_t *) & addr->sin_addr);
                        tunnel[t].port = ntohs(addr->sin_port);
                        tunnel[t].window = 4; // default window
+                       tunnel[t].indexudp = indexudpfd;
                        STAT(tunnel_created);
                        LOG(1, 0, t, "   New tunnel from %s:%u ID %u\n",
                                fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t);
                        STAT(tunnel_created);
                        LOG(1, 0, t, "   New tunnel from %s:%u ID %u\n",
                                fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t);
@@ -2790,22 +2854,23 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                                tunnel[t].window = 1; // window of 0 is silly
                                        LOG(4, s, t, "   rx window = %u\n", tunnel[t].window);
                                        break;
                                                tunnel[t].window = 1; // window of 0 is silly
                                        LOG(4, s, t, "   rx window = %u\n", tunnel[t].window);
                                        break;
-                               case 11:        // Challenge
+                               case 11:        // Request Challenge
                                        {
                                                LOG(4, s, t, "   LAC requested CHAP authentication for tunnel\n");
                                        {
                                                LOG(4, s, t, "   LAC requested CHAP authentication for tunnel\n");
-                                               build_chap_response(b, 2, n, &chapresponse);
+                                               if (message == 1)
+                                                       build_chap_response(b, 2, n, &sendchalresponse);
+                                               else if (message == 2)
+                                                       build_chap_response(b, 3, n, &sendchalresponse);
                                        }
                                        break;
                                        }
                                        break;
-                               case 13:    // Response
-#ifdef LAC
+                               case 13:    // receive challenge Response
                                        if (tunnel[t].isremotelns)
                                        {
                                        if (tunnel[t].isremotelns)
                                        {
-                                               chapresponse = calloc(17, 1);
-                                               memcpy(chapresponse, b, (n < 17) ? n : 16);
+                                               recvchalresponse = calloc(17, 1);
+                                               memcpy(recvchalresponse, b, (n < 17) ? n : 16);
                                                LOG(3, s, t, "received challenge response from REMOTE LNS\n");
                                        }
                                        else
                                                LOG(3, s, t, "received challenge response from REMOTE LNS\n");
                                        }
                                        else
-#endif /* LAC */
                                        // Why did they send a response? We never challenge.
                                        LOG(2, s, t, "   received unexpected challenge response\n");
                                        break;
                                        // Why did they send a response? We never challenge.
                                        LOG(2, s, t, "   received unexpected challenge response\n");
                                        break;
@@ -3038,8 +3103,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                                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
-                                               controls(c, 7, hostname, 1); // host name
-                                               if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response
+                                               controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
+                                               if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Send Challenge response
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, 0, t); // send the resply
                                        }
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, 0, t); // send the resply
                                        }
@@ -3051,24 +3116,24 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
                                case 2:       // SCCRP
                                        tunnel[t].state = TUNNELOPEN;
                                        tunnel[t].lastrec = time_now;
-#ifdef LAC
                                        LOG(3, s, t, "Received SCCRP\n");
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
                                        LOG(3, s, t, "Received SCCRP\n");
                                        if (main_quit != QUIT_SHUTDOWN)
                                        {
-                                               if (tunnel[t].isremotelns && chapresponse)
+                                               if (tunnel[t].isremotelns && recvchalresponse)
                                                {
                                                        hasht hash;
 
                                                        lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP)
                                                        // check authenticator
                                                {
                                                        hasht hash;
 
                                                        lac_calc_rlns_auth(t, 2, hash); // id = 2 (SCCRP)
                                                        // check authenticator
-                                                       if (memcmp(hash, chapresponse, 16) == 0)
+                                                       if (memcmp(hash, recvchalresponse, 16) == 0)
                                                        {
                                                                LOG(3, s, t, "sending SCCCN to REMOTE LNS\n");
                                                                controlt *c = controlnew(3); // sending SCCCN
                                                        {
                                                                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, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
                                                                controls(c, 8, Vendor_name, 1); // Vendor name
                                                                control16(c, 2, version, 1); // protocol version
                                                                control32(c, 3, 3, 1); // framing Capabilities
                                                                controls(c, 8, Vendor_name, 1); // Vendor name
                                                                control16(c, 2, version, 1); // protocol version
                                                                control32(c, 3, 3, 1); // framing Capabilities
+                                                               if (sendchalresponse) controlb(c, 13, sendchalresponse, 16, 1); // Challenge response
                                                                control16(c, 9, t, 1); // assigned tunnel
                                                                controladd(c, 0, t); // send
                                                        }
                                                                control16(c, 9, t, 1); // assigned tunnel
                                                                controladd(c, 0, t); // send
                                                        }
@@ -3082,7 +3147,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        {
                                                tunnelshutdown(t, "Shutting down", 6, 0, 0);
                                        }
                                        {
                                                tunnelshutdown(t, "Shutting down", 6, 0, 0);
                                        }
-#endif /* LAC */
                                        break;
                                case 3:       // SCCN
                                        LOG(3, s, t, "Received SCCN\n");
                                        break;
                                case 3:       // SCCN
                                        LOG(3, s, t, "Received SCCN\n");
@@ -3160,7 +3224,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        }
                                        return;
                                case 11:      // ICRP
                                        }
                                        return;
                                case 11:      // ICRP
-#ifdef LAC
                                LOG(3, s, t, "Received ICRP\n");
                                if (session[s].forwardtosession)
                                {
                                LOG(3, s, t, "Received ICRP\n");
                                if (session[s].forwardtosession)
                                {
@@ -3176,7 +3239,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        controladd(c, asession, t); // send the message
                                        LOG(3, s, t, "Sending ICCN\n");
                                }
                                        controladd(c, asession, t); // send the message
                                        LOG(3, s, t, "Sending ICCN\n");
                                }
-#endif /* LAC */
                                        break;
                                case 12:      // ICCN
                                        LOG(3, s, t, "Received ICCN\n");
                                        break;
                                case 12:      // ICCN
                                        LOG(3, s, t, "Received ICCN\n");
@@ -3214,7 +3276,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                                LOG(1, s, t, "Unknown message type %u\n", message);
                                        break;
                                }
                                                LOG(1, s, t, "Unknown message type %u\n", message);
                                        break;
                                }
-                       if (chapresponse) free(chapresponse);
+                       if (sendchalresponse) free(sendchalresponse);
+                       if (recvchalresponse) free(recvchalresponse);
                        cluster_send_tunnel(t);
                }
                else
                        cluster_send_tunnel(t);
                }
                else
@@ -3250,12 +3313,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        l -= 2;
                }
 
                        l -= 2;
                }
 
-#ifdef LAC
                if (session[s].forwardtosession)
                {
                        LOG(5, s, t, "Forwarding data session to session %u\n", session[s].forwardtosession);
                        // Forward to LAC/BAS or Remote LNS session
                if (session[s].forwardtosession)
                {
                        LOG(5, s, t, "Forwarding data session to session %u\n", session[s].forwardtosession);
                        // Forward to LAC/BAS or Remote LNS session
-                       lac_session_forward(buf, len, s, proto, addr->sin_addr.s_addr, addr->sin_port);
+                       lac_session_forward(buf, len, s, proto, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                        return;
                }
                else if (config->auth_tunnel_change_addr_src)
                        return;
                }
                else if (config->auth_tunnel_change_addr_src)
@@ -3270,14 +3332,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                tunnel[t].ip = ntohl(addr->sin_addr.s_addr);
                        }
                }
                                tunnel[t].ip = ntohl(addr->sin_addr.s_addr);
                        }
                }
-#endif /* LAC */
 
                if (s && !session[s].opened)    // Is something wrong??
                {
                        if (!config->cluster_iam_master)
                        {
                                // Pass it off to the master to deal with..
 
                if (s && !session[s].opened)    // Is something wrong??
                {
                        if (!config->cluster_iam_master)
                        {
                                // Pass it off to the master to deal with..
-                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                                return;
                        }
 
                                return;
                        }
 
@@ -3289,37 +3350,37 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                if (proto == PPPPAP)
                {
                        session[s].last_packet = time_now;
                if (proto == PPPPAP)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processpap(s, t, p, l);
                }
                else if (proto == PPPCHAP)
                {
                        session[s].last_packet = time_now;
                        processpap(s, t, p, l);
                }
                else if (proto == PPPCHAP)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processchap(s, t, p, l);
                }
                else if (proto == PPPLCP)
                {
                        session[s].last_packet = time_now;
                        processchap(s, t, p, l);
                }
                else if (proto == PPPLCP)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processlcp(s, t, p, l);
                }
                else if (proto == PPPIPCP)
                {
                        session[s].last_packet = time_now;
                        processlcp(s, t, p, l);
                }
                else if (proto == PPPIPCP)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processipcp(s, t, p, l);
                }
                else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0])
                {
                        session[s].last_packet = time_now;
                        processipcp(s, t, p, l);
                }
                else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[0])
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processipv6cp(s, t, p, l);
                }
                else if (proto == PPPCCP)
                {
                        session[s].last_packet = time_now;
                        processipv6cp(s, t, p, l);
                }
                else if (proto == PPPCCP)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        processccp(s, t, p, l);
                }
                else if (proto == PPPIP)
                        processccp(s, t, p, l);
                }
                else if (proto == PPPIP)
@@ -3333,7 +3394,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        session[s].last_packet = session[s].last_data = time_now;
                        if (session[s].walled_garden && !config->cluster_iam_master)
                        {
                        session[s].last_packet = session[s].last_data = time_now;
                        if (session[s].walled_garden && !config->cluster_iam_master)
                        {
-                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                                return;
                        }
 
                                return;
                        }
 
@@ -3351,7 +3412,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        if (!config->cluster_iam_master)
                        {
                                // The fragments reconstruction is managed by the Master.
                        if (!config->cluster_iam_master)
                        {
                                // The fragments reconstruction is managed by the Master.
-                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                                return;
                        }
 
                                return;
                        }
 
@@ -3368,16 +3429,30 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        session[s].last_packet = session[s].last_data = time_now;
                        if (session[s].walled_garden && !config->cluster_iam_master)
                        {
                        session[s].last_packet = session[s].last_data = time_now;
                        if (session[s].walled_garden && !config->cluster_iam_master)
                        {
-                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
+                               master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
                                return;
                        }
 
                                return;
                        }
 
+                       if (!config->cluster_iam_master)
+                       {
+                               // Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547)
+                               if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
+                                               *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
+                                               *(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 &&
+                                               *(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23)
+                               {
+                                       // DHCPV6 must be managed by the Master.
+                                       master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
+                                       return;
+                               }
+                       }
+
                        processipv6in(s, t, p, l);
                }
                else if (session[s].ppp.lcp == Opened)
                {
                        session[s].last_packet = time_now;
                        processipv6in(s, t, p, l);
                }
                else if (session[s].ppp.lcp == Opened)
                {
                        session[s].last_packet = time_now;
-                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; }
+                       if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd); return; }
                        protoreject(s, t, p, l, proto);
                }
                else
                        protoreject(s, t, p, l, proto);
                }
                else
@@ -3500,10 +3575,13 @@ static void regular_cleanups(double period)
                // Send hello
                if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60)
                {
                // Send hello
                if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60)
                {
-                       controlt *c = controlnew(6); // sending HELLO
-                       controladd(c, 0, t); // send the message
-                       LOG(3, 0, t, "Sending HELLO message\n");
-                       t_actions++;
+                       if (!config->disable_sending_hello)
+                       {
+                               controlt *c = controlnew(6); // sending HELLO
+                               controladd(c, 0, t); // send the message
+                               LOG(3, 0, t, "Sending HELLO message\n");
+                               t_actions++;
+                       }
                }
 
                // Check for tunnel changes requested from the CLI
                }
 
                // Check for tunnel changes requested from the CLI
@@ -3943,13 +4021,8 @@ 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, pppoedisc, pppoesess
-#define BASE_FDS       10
-#else
-// the base set of fds polled: cli, cluster, tun, udp, control, dae, netlink, pppoedisc, pppoesess
-#define BASE_FDS       9
-#endif
+// the base set of fds polled: cli, cluster, tun, udp (MAX_UDPFD), control, dae, netlink, udplac, pppoedisc, pppoesess
+#define BASE_FDS       (9 + MAX_UDPFD)
 
 // additional polled fds
 #ifdef BGP
 
 // additional polled fds
 #ifdef BGP
@@ -3961,11 +4034,11 @@ static int still_busy(void)
 // main loop - gets packets on tun or udp and processes them
 static void mainloop(void)
 {
 // main loop - gets packets on tun or udp and processes them
 static void mainloop(void)
 {
-       int i;
+       int i, j;
        uint8_t buf[65536];
        uint8_t buf[65536];
-       uint8_t *p = buf + 24; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
+       uint8_t *p = buf + 32; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
                                                // and the forwarded pppoe session
                                                // and the forwarded pppoe session
-       int size_bufp = sizeof(buf) - 24;
+       int size_bufp = sizeof(buf) - 32;
        clockt next_cluster_ping = 0;   // send initial ping immediately
        struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS];
        int maxevent = sizeof(events)/sizeof(*events);
        clockt next_cluster_ping = 0;   // send initial ping immediately
        struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS];
        int maxevent = sizeof(events)/sizeof(*events);
@@ -3976,13 +4049,8 @@ 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, pppoefd=%d, pppoesessfd=%d\n",
        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, pppoefd=%d, pppoesessfd=%d\n",
-               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, udplacfd, pppoediscfd, pppoesessfd);
-#else
-       LOG(4, 0, 0, "Beginning of main loop.  clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d, nlfd=%d, pppoefd=%d, pppoesessfd=%d\n",
-               clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd, nlfd, pppoediscfd, pppoesessfd);
-#endif
+               clifd, cluster_sockfd, tunfd, udpfd[0], controlfd, daefd, nlfd, udplacfd, pppoediscfd, pppoesessfd);
 
        /* setup our fds to poll for input */
        {
 
        /* setup our fds to poll for input */
        {
@@ -4007,10 +4075,6 @@ static void mainloop(void)
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e);
 
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e);
 
-               d[i].type = FD_TYPE_UDP;
-               e.data.ptr = &d[i++];
-               epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e);
-
                d[i].type = FD_TYPE_CONTROL;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
                d[i].type = FD_TYPE_CONTROL;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e);
@@ -4023,12 +4087,6 @@ static void mainloop(void)
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, nlfd, &e);
 
                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
-
                d[i].type = FD_TYPE_PPPOEDISC;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoediscfd, &e);
                d[i].type = FD_TYPE_PPPOEDISC;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoediscfd, &e);
@@ -4036,6 +4094,14 @@ static void mainloop(void)
                d[i].type = FD_TYPE_PPPOESESS;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e);
                d[i].type = FD_TYPE_PPPOESESS;
                e.data.ptr = &d[i++];
                epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e);
+
+               for (j = 0; j < config->nbudpfd; j++)
+               {
+                       d[i].type = FD_TYPE_UDP;
+                       d[i].index = j;
+                       e.data.ptr = &d[i++];
+                       epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd[j], &e);
+               }
        }
 
 #ifdef BGP
        }
 
 #ifdef BGP
@@ -4097,16 +4163,12 @@ static void mainloop(void)
                        struct in_addr local;
                        socklen_t alen;
                        int c, s;
                        struct in_addr local;
                        socklen_t alen;
                        int c, s;
-                       int udp_ready = 0;
-#ifdef LAC
-                       int udplac_ready = 0;
-                       int udplac_pkts = 0;
-#endif
+                       int udp_ready[MAX_UDPFD + 1] = INIT_TABUDPVAR;
                        int pppoesess_ready = 0;
                        int pppoesess_pkts = 0;
                        int tun_ready = 0;
                        int cluster_ready = 0;
                        int pppoesess_ready = 0;
                        int pppoesess_pkts = 0;
                        int tun_ready = 0;
                        int cluster_ready = 0;
-                       int udp_pkts = 0;
+                       int udp_pkts[MAX_UDPFD + 1] = INIT_TABUDPVAR;
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
 #ifdef BGP
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
 #ifdef BGP
@@ -4140,10 +4202,7 @@ static void mainloop(void)
                                // these are handled below, with multiple interleaved reads
                                case FD_TYPE_CLUSTER:   cluster_ready++; break;
                                case FD_TYPE_TUN:       tun_ready++; break;
                                // these are handled below, with multiple interleaved reads
                                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_UDP:       udp_ready[d->index]++; break;
                                case FD_TYPE_PPPOESESS: pppoesess_ready++; break;
 
                                case FD_TYPE_PPPOEDISC: // pppoe discovery
                                case FD_TYPE_PPPOESESS: pppoesess_ready++; break;
 
                                case FD_TYPE_PPPOEDISC: // pppoe discovery
@@ -4154,26 +4213,26 @@ static void mainloop(void)
 
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
 
                                case FD_TYPE_CONTROL: // nsctl commands
                                        alen = sizeof(addr);
-                                       s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
-                                       if (s > 0) processcontrol(buf, s, &addr, alen, &local);
+                                       s = recvfromto(controlfd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processcontrol(p, s, &addr, alen, &local);
                                        n--;
                                        break;
 
                                case FD_TYPE_DAE: // DAE requests
                                        alen = sizeof(addr);
                                        n--;
                                        break;
 
                                case FD_TYPE_DAE: // DAE requests
                                        alen = sizeof(addr);
-                                       s = recvfromto(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
-                                       if (s > 0) processdae(buf, s, &addr, alen, &local);
+                                       s = recvfromto(daefd, p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local);
+                                       if (s > 0) processdae(p, s, &addr, alen, &local);
                                        n--;
                                        break;
 
                                case FD_TYPE_RADIUS: // RADIUS response
                                        alen = sizeof(addr);
                                        n--;
                                        break;
 
                                case FD_TYPE_RADIUS: // RADIUS response
                                        alen = sizeof(addr);
-                                       s = recvfrom(radfds[d->index], buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen);
+                                       s = recvfrom(radfds[d->index], p, size_bufp, MSG_WAITALL, (struct sockaddr *) &addr, &alen);
                                        if (s >= 0 && config->cluster_iam_master)
                                        {
                                                if (addr.sin_addr.s_addr == config->radiusserver[0] ||
                                                    addr.sin_addr.s_addr == config->radiusserver[1])
                                        if (s >= 0 && config->cluster_iam_master)
                                        {
                                                if (addr.sin_addr.s_addr == config->radiusserver[0] ||
                                                    addr.sin_addr.s_addr == config->radiusserver[1])
-                                                       processrad(buf, s, d->index);
+                                                       processrad(p, s, d->index);
                                                else
                                                        LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n",
                                                                fmtaddr(addr.sin_addr.s_addr, 0));
                                                else
                                                        LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n",
                                                                fmtaddr(addr.sin_addr.s_addr, 0));
@@ -4184,15 +4243,15 @@ static void mainloop(void)
 
 #ifdef BGP
                                case FD_TYPE_BGP:
 
 #ifdef BGP
                                case FD_TYPE_BGP:
-                                       bgp_events[d->index] = events[i].events;
-                                       n--;
+                                       bgp_events[d->index] = events[i].events;
+                                       n--;
                                        break;
 #endif /* BGP */
 
                                case FD_TYPE_NETLINK:
                                {
                                        break;
 #endif /* BGP */
 
                                case FD_TYPE_NETLINK:
                                {
-                                       struct nlmsghdr *nh = (struct nlmsghdr *)buf;
-                                       s = netlink_recv(buf, sizeof(buf));
+                                       struct nlmsghdr *nh = (struct nlmsghdr *)p;
+                                       s = netlink_recv(p, size_bufp);
                                        if (nh->nlmsg_type == NLMSG_ERROR)
                                        {
                                                struct nlmsgerr *errmsg = NLMSG_DATA(nh);
                                        if (nh->nlmsg_type == NLMSG_ERROR)
                                        {
                                                struct nlmsgerr *errmsg = NLMSG_DATA(nh);
@@ -4204,7 +4263,6 @@ static void mainloop(void)
                                                                exit(1);
                                                        }
                                                        else
                                                                exit(1);
                                                        }
                                                        else
-
                                                                LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error));
                                                }
                                                // else it's a ack
                                                                LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error));
                                                }
                                                // else it's a ack
@@ -4216,7 +4274,7 @@ static void mainloop(void)
                                }
 
                                default:
                                }
 
                                default:
-                                       LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
+                                       LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
                                }
                        }
 
                                }
                        }
 
@@ -4226,40 +4284,25 @@ static void mainloop(void)
 
                        for (c = 0; n && c < config->multi_read_count; c++)
                        {
 
                        for (c = 0; n && c < config->multi_read_count; c++)
                        {
-                               // L2TP
-                               if (udp_ready)
+                               for (j = 0; j < config->nbudpfd; j++)
                                {
                                {
-                                       alen = sizeof(addr);
-                                       if ((s = recvfrom(udpfd, p, size_bufp, 0, (void *) &addr, &alen)) > 0)
+                                       // L2TP and L2TP REMOTE LNS
+                                       if (udp_ready[j])
                                        {
                                        {
-                                               processudp(p, s, &addr);
-                                               udp_pkts++;
-                                       }
-                                       else
-                                       {
-                                               udp_ready = 0;
-                                               n--;
+                                               alen = sizeof(addr);
+                                               if ((s = recvfrom(udpfd[j], p, size_bufp, 0, (void *) &addr, &alen)) > 0)
+                                               {
+                                                       processudp(p, s, &addr, j);
+                                                       udp_pkts[j]++;
+                                               }
+                                               else
+                                               {
+                                                       udp_ready[j] = 0;
+                                                       n--;
+                                               }
                                        }
                                }
                                        }
                                }
-#ifdef LAC
-                               // L2TP REMOTE LNS
-                               if (udplac_ready)
-                               {
-                                       alen = sizeof(addr);
-                                       if ((s = recvfrom(udplacfd, p, size_bufp, 0, (void *) &addr, &alen)) > 0)
-                                       {
-                                               if (!config->disable_lac_func)
-                                                       processudp(p, s, &addr);
 
 
-                                               udplac_pkts++;
-                                       }
-                                       else
-                                       {
-                                               udplac_ready = 0;
-                                               n--;
-                                       }
-                               }
-#endif
                                // incoming IP
                                if (tun_ready)
                                {
                                // incoming IP
                                if (tun_ready)
                                {
@@ -4307,18 +4350,13 @@ static void mainloop(void)
                                }
                        }
 
                                }
                        }
 
-                       if (udp_pkts > 1 || tun_pkts > 1 || cluster_pkts > 1)
+                       if (udp_pkts[0] > 1 || tun_pkts > 1 || cluster_pkts > 1)
                                STAT(multi_read_used);
 
                        if (c >= config->multi_read_count)
                        {
                                STAT(multi_read_used);
 
                        if (c >= config->multi_read_count)
                        {
-#ifdef LAC
-                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster and %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);
-#endif
+                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster and %d pppoe packets\n",
+                                       config->multi_read_count, udp_pkts[0], tun_pkts, cluster_pkts, pppoesess_pkts);
                                STAT(multi_read_exceeded);
                                more++;
                        }
                                STAT(multi_read_exceeded);
                                more++;
                        }
@@ -4653,9 +4691,7 @@ static void initdata(int optdebug, char *optconfig)
        }
 #endif /* BGP */
 
        }
 #endif /* BGP */
 
-#ifdef LAC
        lac_initremotelnsdata();
        lac_initremotelnsdata();
-#endif
 }
 
 static int assign_ip_address(sessionidt s)
 }
 
 static int assign_ip_address(sessionidt s)
@@ -4942,11 +4978,7 @@ 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)
        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)
-#endif
                return 1;
 
        if (!*f)
                return 1;
 
        if (!*f)
@@ -4965,6 +4997,21 @@ static int dump_session(FILE **f, sessiont *s)
                }
 
                LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
                }
 
                LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
+               if(config->account_all_origin)
+               {
+               fprintf(*f, "# dslwatch.pl dump file V1.01\n"
+                       "# host: %s\n"
+                       "# endpoint: %s\n"
+                       "# time: %ld\n"
+                       "# uptime: %ld\n"
+                       "# format: username ip qos uptxoctets downrxoctets origin(L=LAC, R=Remote LNS, P=PPPOE)\n",
+                       hostname,
+                       fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0),
+                       now,
+                       now - basetime);
+               }
+               else
+               {
                fprintf(*f, "# dslwatch.pl dump file V1.01\n"
                        "# host: %s\n"
                        "# endpoint: %s\n"
                fprintf(*f, "# dslwatch.pl dump file V1.01\n"
                        "# host: %s\n"
                        "# endpoint: %s\n"
@@ -4972,18 +5019,32 @@ static int dump_session(FILE **f, sessiont *s)
                        "# uptime: %ld\n"
                        "# format: username ip qos uptxoctets downrxoctets\n",
                        hostname,
                        "# uptime: %ld\n"
                        "# format: username ip qos uptxoctets downrxoctets\n",
                        hostname,
-                       fmtaddr(config->iftun_address ? config->iftun_address : my_address, 0),
+                       fmtaddr(config->iftun_n_address[tunnel[s->tunnel].indexudp] ? config->iftun_n_address[tunnel[s->tunnel].indexudp] : my_address, 0),
                        now,
                        now - basetime);
                        now,
                        now - basetime);
+               }
        }
 
        LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user);
        }
 
        LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user);
+       if(config->account_all_origin)
+       {
+       fprintf(*f, "%s %s %d %u %u %s\n",
+               s->user,                                                // username
+               fmtaddr(htonl(s->ip), 0),                               // ip
+               (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
+               (uint32_t) s->cin_delta,                                // uptxoctets
+               (uint32_t) s->cout_delta,                               // downrxoctets
+               (s->tunnel == TUNNEL_ID_PPPOE)?"P":(tunnel[s->tunnel].isremotelns?"R":"L"));    // Origin
+       }
+       else if (!tunnel[s->tunnel].isremotelns && (s->tunnel != TUNNEL_ID_PPPOE))
+       {
        fprintf(*f, "%s %s %d %u %u\n",
                s->user,                                                // username
                fmtaddr(htonl(s->ip), 0),                               // ip
                (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
                (uint32_t) s->cin_delta,                                // uptxoctets
                (uint32_t) s->cout_delta);                              // downrxoctets
        fprintf(*f, "%s %s %d %u %u\n",
                s->user,                                                // username
                fmtaddr(htonl(s->ip), 0),                               // ip
                (s->throttle_in || s->throttle_out) ? 2 : 1,            // qos
                (uint32_t) s->cin_delta,                                // uptxoctets
                (uint32_t) s->cout_delta);                              // downrxoctets
+       }
 
        s->cin_delta = s->cout_delta = 0;
 
 
        s->cin_delta = s->cout_delta = 0;
 
@@ -5031,9 +5092,9 @@ int main(int argc, char *argv[])
                case 'd':
                        if (fork()) exit(0);
                        setsid();
                case 'd':
                        if (fork()) exit(0);
                        setsid();
-                       freopen("/dev/null", "r", stdin);
-                       freopen("/dev/null", "w", stdout);
-                       freopen("/dev/null", "w", stderr);
+                       if(!freopen("/dev/null", "r", stdin)) LOG(0, 0, 0, "Error freopen stdin: %s\n", strerror(errno));
+                       if(!freopen("/dev/null", "w", stdout)) LOG(0, 0, 0, "Error freopen stdout: %s\n", strerror(errno));
+                       if(!freopen("/dev/null", "w", stderr)) LOG(0, 0, 0, "Error freopen stderr: %s\n", strerror(errno));
                        break;
                case 'v':
                        optdebug++;
                        break;
                case 'v':
                        optdebug++;
@@ -5073,6 +5134,7 @@ int main(int argc, char *argv[])
        init_tbf(config->num_tbfs);
 
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
        init_tbf(config->num_tbfs);
 
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
+       LOG(0, 0, 0, "Copyright (c) 2012, 2013, 2014 ISP FDN & SAMESWIRELESS\n");
        LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n");
        LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
        {
        LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n");
        LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
        {
@@ -5084,7 +5146,7 @@ int main(int argc, char *argv[])
                        LOG(0, 0, 0, "Can't set ulimit: %s\n", strerror(errno));
 
                // Make core dumps go to /tmp
                        LOG(0, 0, 0, "Can't set ulimit: %s\n", strerror(errno));
 
                // Make core dumps go to /tmp
-               chdir("/tmp");
+               if(chdir("/tmp")) LOG(0, 0, 0, "Error chdir /tmp: %s\n", strerror(errno));
        }
 
        if (config->scheduler_fifo)
        }
 
        if (config->scheduler_fifo)
@@ -5126,9 +5188,29 @@ int main(int argc, char *argv[])
                init_pppoe();
                LOG(1, 0, 0, "Set up on pppoe interface %s\n", config->pppoe_if_to_bind);
        }
                init_pppoe();
                LOG(1, 0, 0, "Set up on pppoe interface %s\n", config->pppoe_if_to_bind);
        }
-       initudp();
+
+       if (!config->nbmultiaddress)
+       {
+               config->bind_n_address[0] = config->bind_address;
+               config->nbmultiaddress++;
+       }
+       config->nbudpfd = config->nbmultiaddress;
+       for (i = 0; i < config->nbudpfd; i++)
+               initudp(&udpfd[i], config->bind_n_address[i]);
+       initlacudp();
+       config->indexlacudpfd = config->nbudpfd;
+       udpfd[config->indexlacudpfd] = udplacfd;
+       config->nbudpfd++;
+
+       initcontrol();
+       initdae();
+
+       // Intercept
+       snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
        initrad();
        initippool();
        initrad();
        initippool();
+       dhcpv6_init();
 
        // seed prng
        {
 
        // seed prng
        {
@@ -5358,14 +5440,100 @@ 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;
        if(!config->bind_address_remotelns)
                config->bind_address_remotelns = INADDR_ANY;
        if(!config->bind_portremotelns)
                config->bind_portremotelns = L2TPLACPORT;
        if(!config->bind_address_remotelns)
                config->bind_address_remotelns = INADDR_ANY;
-#endif
+
+       if (*config->bind_multi_address)
+       {
+               char *sip = config->bind_multi_address;
+               char *n = sip;
+               char *e = config->bind_multi_address + strlen(config->bind_multi_address);
+               config->nbmultiaddress = 0;
+
+               while (*sip && (sip < e))
+               {
+                       in_addr_t ip = 0;
+                       uint8_t u = 0;
+
+                       while (n < e && (*n == ',' || *n == ' ')) n++;
+
+                       while (n < e && (isdigit(*n) || *n == '.'))
+                       {
+                                if (*n == '.')
+                                {
+                                        ip = (ip << 8) + u;
+                                        u = 0;
+                                }
+                                else
+                                       u = u * 10 + *n - '0';
+                                n++;
+                       }
+                       ip = (ip << 8) + u;
+                       n++;
+
+                       if (ip)
+                       {
+                               config->bind_n_address[config->nbmultiaddress] = htonl(ip);
+                               config->iftun_n_address[config->nbmultiaddress] = htonl(ip);
+                               config->nbmultiaddress++;
+                               LOG(1, 0, 0, "Bind address %s\n", fmtaddr(htonl(ip), 0));
+
+                               if (config->nbmultiaddress >= MAX_BINDADDR) break;
+                       }
+
+                       sip = n;
+               }
+
+               if (config->nbmultiaddress >= 1)
+               {
+                       config->bind_address = config->bind_n_address[0];
+                       config->iftun_address = config->bind_address;
+               }
+       }
+
        if(!config->iftun_address)
        if(!config->iftun_address)
+       {
                config->iftun_address = config->bind_address;
                config->iftun_address = config->bind_address;
+               config->iftun_n_address[0] = config->iftun_address;
+       }
+
+       if (*config->multi_hostname)
+       {
+               char *shost = config->multi_hostname;
+               char *n = shost;
+               char *e = config->multi_hostname + strlen(config->multi_hostname);
+               config->nbmultihostname = 0;
+
+               while (*shost && (shost < e))
+               {
+                       while ((n < e) && (*n == ' ' || *n == ',' || *n == '\t')) n++;
+
+                       i = 0;
+                       while (n < e && (*n != ',') && (*n != '\t'))
+                       {
+                               config->multi_n_hostname[config->nbmultihostname][i] = *n;
+                               n++;i++;
+                       }
+
+                       if (i > 0)
+                       {
+                               config->multi_n_hostname[config->nbmultihostname][i] = 0;
+                               LOG(1, 0, 0, "Bind Hostname %s\n", config->multi_n_hostname[config->nbmultihostname]);
+                               config->nbmultihostname++;
+                               if (config->nbmultihostname >= MAX_NBHOSTNAME) break;
+                       }
+
+                       shost = n;
+               }
+
+               if (config->nbmultihostname >= 1)
+               {
+                       strcpy(hostname, config->multi_n_hostname[0]);
+                       strcpy(config->hostname, hostname);
+               }
+       }
 
        if (!*config->pppoe_ac_name)
                strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1);
 
        if (!*config->pppoe_ac_name)
                strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1);
@@ -5548,8 +5716,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
 
                        if (ip == session[i].ip)
                        {
 
                        if (ip == session[i].ip)
                        {
-                               sessionkill(i, "Duplicate IP address");
-                               cluster_listinvert_session(s, i);
+                               sessionshutdown(i, "Duplicate IP address", CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
                                continue;
                        }
 
                                continue;
                        }
 
@@ -5570,7 +5737,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
 
                        // Drop the new session in case of duplicate sessionss, not the old one.
                        if (!strcasecmp(user, session[i].user))
 
                        // Drop the new session in case of duplicate sessionss, not the old one.
                        if (!strcasecmp(user, session[i].user))
-                               sessionkill(i, "Duplicate session for users");
+                               sessionshutdown(i, "Duplicate session for users", CDN_ADMIN_DISC, TERM_ADMIN_RESET);  // close radius/routes, etc.
                }
        }
 
                }
        }
 
@@ -5686,6 +5853,15 @@ int load_session(sessionidt s, sessiont *new)
                                uncache_ipmap(session[s].ip);
                }
 
                                uncache_ipmap(session[s].ip);
                }
 
+               // remove old IPV6 routes...
+               if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen)
+                       route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
+
+               if (session[s].ipv6address.s6_addr[0])
+               {
+                       route6set(s, session[s].ipv6address, 128, 0);
+               }
+
                routed = 0;
 
                // add new routes...
                routed = 0;
 
                // add new routes...
@@ -5713,7 +5889,12 @@ int load_session(sessionidt s, sessiont *new)
 
        // check v6 routing
        if (new->ipv6prefixlen && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
 
        // check v6 routing
        if (new->ipv6prefixlen && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
-                   route6set(s, new->ipv6route, new->ipv6prefixlen, 1);
+               route6set(s, new->ipv6route, new->ipv6prefixlen, 1);
+
+       if (new->ipv6address.s6_addr[0] && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
+       {
+               route6set(s, new->ipv6address, 128, 1);
+       }
 
        // check filters
        if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0]))
 
        // check filters
        if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0]))
@@ -6160,7 +6341,7 @@ void become_master(void)
        }
 }
 
        }
 }
 
-int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc)
+int cmd_show_hist_idle(struct cli_def *cli, const char *command, char **argv, int argc)
 {
        int s, i;
        int count = 0;
 {
        int s, i;
        int count = 0;
@@ -6198,7 +6379,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc
        return CLI_OK;
 }
 
        return CLI_OK;
 }
 
-int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc)
+int cmd_show_hist_open(struct cli_def *cli, const char *command, char **argv, int argc)
 {
        int s, i;
        int count = 0;
 {
        int s, i;
        int count = 0;
@@ -6424,8 +6605,6 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter)
        return 0;
 }
 
        return 0;
 }
 
-#ifdef LAC
-
 tunnelidt lac_new_tunnel()
 {
        return new_tunnel();
 tunnelidt lac_new_tunnel()
 {
        return new_tunnel();
@@ -6444,7 +6623,7 @@ void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len)
 
        // Sent SCCRQ - Start Control Connection Request
        controlt *c = controlnew(1); // sending SCCRQ
 
        // Sent SCCRQ - Start Control Connection Request
        controlt *c = controlnew(1); // sending SCCRQ
-       controls(c, 7, hostname, 1); // host name
+       controls(c, 7, config->multi_n_hostname[tunnel[t].indexudp][0]?config->multi_n_hostname[tunnel[t].indexudp]:hostname, 1); // host name
        controls(c, 8, Vendor_name, 1); // Vendor name
        control16(c, 2, version, 1); // protocol version
        control32(c, 3, 3, 1); // framing Capabilities
        controls(c, 8, Vendor_name, 1); // Vendor name
        control16(c, 2, version, 1); // protocol version
        control32(c, 3, 3, 1); // framing Capabilities
@@ -6471,4 +6650,3 @@ void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *
        tunnelshutdown(t, reason, result, error, msg);
 }
 
        tunnelshutdown(t, reason, result, error, msg);
 }
 
-#endif