merge from multibind
authorfendo <fendo@bi12info.com>
Wed, 6 Mar 2013 23:45:05 +0000 (00:45 +0100)
committerfendo <fendo@bi12info.com>
Wed, 6 Mar 2013 23:45:05 +0000 (00:45 +0100)
1  2 
Makefile
cli.c
cluster.c
l2tplac.c
l2tpns.c
l2tpns.h
ppp.c
pppoe.c
radius.c
util.c

diff --combined Makefile
+++ b/Makefile
@@@ -26,7 -26,7 +26,7 @@@ INSTALL = install -c -D -o root -g roo
  l2tpns.LIBS = -lm -lcli -ldl
  
  OBJS = arp.o cli.o cluster.o constants.o control.o icmp.o l2tpns.o \
-     ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o grpsess.o
 -    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o
++    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o grpsess.o
  
  PROGRAMS = l2tpns nsctl
  PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
@@@ -43,9 -43,6 +43,6 @@@ endi
  DEFINES += -DBGP
  OBJS += bgp.o
  
- DEFINES += -DLAC
- OBJS += l2tplac.o
  all: programs plugins
  programs: $(PROGRAMS)
  plugins: $(PLUGINS)
@@@ -129,9 -126,8 +126,9 @@@ radius.o: radius.c md5.h constants.h l2
  tbf.o: tbf.c l2tpns.h util.h tbf.h
  util.o: util.c l2tpns.h bgp.h
  pppoe.o: pppoe.c l2tpns.h cluster.h constants.h md5.h util.h
+ l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h
 +grpsess.o: grpsess.c l2tpns.h util.h bgp.h
  bgp.o: bgp.c l2tpns.h bgp.h util.h
- l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h
  autosnoop.so: autosnoop.c l2tpns.h plugin.h
  autothrottle.so: autothrottle.c l2tpns.h plugin.h
  garden.so: garden.c l2tpns.h plugin.h control.h
diff --combined cli.c
--- 1/cli.c
--- 2/cli.c
+++ b/cli.c
@@@ -21,7 -21,6 +21,7 @@@
  #include <dlfcn.h>
  #include <netdb.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #include "constants.h"
@@@ -32,9 -31,7 +32,7 @@@
  #ifdef BGP
  #include "bgp.h"
  #endif
- #ifdef LAC
  #include "l2tplac.h"
- #endif
  
  extern tunnelt *tunnel;
  extern bundlet *bundle;
@@@ -78,7 -75,6 +76,7 @@@ static char *debug_levels[] = 
  #endif
  
  static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc);
 +static int cmd_show_group(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc);
@@@ -104,10 -100,8 +102,8 @@@ static int cmd_remove_plugin(struct cli
  static int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_shutdown(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc);
- #ifdef LAC
  static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int argc);
  static int cmd_show_rmtlnsconf(struct cli_def *cli, char *command, char **argv, int argc);
- #endif
  
  static int regular_stuff(struct cli_def *cli);
  
@@@ -158,11 -152,8 +154,9 @@@ void init_cli(
        cli_register_command(cli, c, "pool", cmd_show_pool, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show the IP address allocation pool");
        cli_register_command(cli, c, "radius", cmd_show_radius, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show active radius queries");
        cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration");
- #ifdef LAC
        cli_register_command(cli, c, "remotelns-conf", cmd_show_rmtlnsconf, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show a list of remote LNS configuration");
- #endif
        cli_register_command(cli, c, "session", cmd_show_session, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of sessions or details for a single session");
 +      cli_register_command(cli, c, "group", cmd_show_group, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of groups or details for a single group");
        cli_register_command(cli, c, "tbf", cmd_show_tbf, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all token bucket filters in use");
        cli_register_command(cli, c, "throttle", cmd_show_throttle, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "List all throttled sessions and associated TBFs");
        cli_register_command(cli, c, "tunnels", cmd_show_tunnels, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show a list of tunnels or details for a single tunnel");
  
        cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set a configuration variable");
  
- #ifdef LAC
        cli_register_command(cli, NULL, "setforward", cmd_setforward, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set the Remote LNS Forward");
- #endif
  
        c = cli_register_command(cli, NULL, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL);
        cli_register_command(cli, c, "access-list", cmd_ip_access_list, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Add named access-list");
@@@ -545,15 -534,9 +537,9 @@@ static int cmd_show_session(struct cli_
        }
  
        // Show Summary
- #ifdef LAC
        cli_print(cli, "%5s %7s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-18s %s",
- #else
-       cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %10s %-15s %s",
- #endif
                        "SID",
- #ifdef LAC
                        "LkToSID",
- #endif
                        "TID",
                        "Username",
                        "IP",
                        "uploaded",
                        "idle",
                        "Rem.Time",
- #ifdef LAC
                        "LAC(L)/RLNS(R)/PPPOE(P)",
- #else
-                       "LAC(L)/PPPOE(P)",
- #endif
                        "CLI");
  
        for (i = 1; i < MAXSESSION; i++)
                        rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0;
                else
                        rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
- #ifdef LAC
                cli_print(cli, "%5d %7d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-20s %s",
- #else
-               cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %10lu %3s%-20s %s",
- #endif
                                i,
- #ifdef LAC
                                session[i].forwardtosession,
- #endif
                                session[i].tunnel,
                                session[i].user[0] ? session[i].user : "*",
                                fmtaddr(htonl(session[i].ip), 0),
                                (unsigned long)session[i].cin,
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
- #ifdef LAC
                                (session[i].tunnel == TUNNEL_ID_PPPOE)?"(P)":(tunnel[session[i].tunnel].isremotelns?"(R)":"(L)"),
- #else
-                               (session[i].tunnel == TUNNEL_ID_PPPOE)?"(P)":"(L)",
- #endif
                                (session[i].tunnel == TUNNEL_ID_PPPOE)?fmtMacAddr(session[i].src_hwaddr):fmtaddr(htonl(tunnel[session[i].tunnel].ip), 1),
                                session[i].calling[0] ? session[i].calling : "*");
        }
@@@ -697,11 -666,7 +669,7 @@@ static int cmd_show_tunnels(struct cli_
                                fmtaddr(htonl(tunnel[i].ip), 0),
                                states[tunnel[i].state],
                                sessions
- #ifdef LAC
                                ,(i == TUNNEL_ID_PPPOE)?"Tunnel pppoe":(tunnel[i].isremotelns?"Tunnel To Remote LNS":"Tunnel To LAC")
- #else
-                               ,(i == TUNNEL_ID_PPPOE)?"Tunnel pppoe":"Tunnel To LAC"
- #endif
                                );
        }
  
@@@ -1312,13 -1277,11 +1280,11 @@@ static int cmd_drop_session(struct cli_
                        cli_print(cli, "Dropping session %d", s);
                        cli_session_actions[s].action |= CLI_SESS_KILL;
                }
- #ifdef LAC
                else if (session[s].forwardtosession && session[s].opened && !session[s].die)
                {
                        cli_print(cli, "Dropping session %d", s);
                        cli_session_actions[s].action |= CLI_SESS_KILL;
                }
- #endif
                else
                {
                        cli_error(cli, "Session %d is not active.", s);
@@@ -3147,79 -3110,6 +3113,77 @@@ static int cmd_reload(struct cli_def *c
        return CLI_OK;
  }
  
- #ifdef LAC
 +static int cmd_show_group(struct cli_def *cli, char *command, char **argv, int argc)
 +{
 +      int i;
 +      groupidt g;
 +
 +      if (CLI_HELP_REQUESTED)
 +              return cli_arg_help(cli, 1,
 +                      "<1-%d>", MAXGROUPE-1, "Show specific group by id",
 +                      NULL);
 +
 +      time(&time_now);
 +      if (argc > 0)
 +      {
 +              // Show individual group
 +              for (i = 0; i < argc; i++)
 +              {
 +                      sessionidt s;
 +
 +                      g = atoi(argv[i]);
 +                      if (g <= 0 || g >= MAXGROUPE)
 +                      {
 +                              cli_print(cli, "Invalid group id \"%s\"", argv[i]);
 +                              continue;
 +                      }
 +
 +                      cli_print(cli, "\r\nGroup %d:", g);
 +                      cli_print(cli, "\tNb Session:\t\t%d", grpsession[g].nbsession);
 +                      cli_print(cli, "\tNb Routes:\t\t%d", grpsession[g].nbroutesgrp);
 +                      cli_print(cli, "\ttime_changed:\t\t%d\n", grpsession[g].time_changed);
 +
 +                      for (i = 0; i < grpsession[g].nbsession; i++)
 +                      {
 +                              if ((s = grpsession[g].sesslist[i].sid))
 +                              {
 +                                      cli_print(cli, "\tSession:\t%d\tTx Rate:%d Kbps\t\t\tweight:\t%d",
 +                                                              s,
 +                                                              grpsession[g].sesslist[i].tx_rate/(1024/8),
 +                                                              grpsession[g].sesslist[i].weight);
 +                              }
 +                      }
 +
 +                      for (i = 0; i < grpsession[g].nbroutesgrp; i++)
 +                      {
 +                              if (grpsession[g].route[i].ip != 0)
 +                              {
 +                                      cli_print(cli, "\tRoute:\t%s/%d", fmtaddr(htonl(grpsession[g].route[i].ip), 0), grpsession[g].route[i].prefixlen);
 +                              }
 +                      }
 +              }
 +              return CLI_OK;
 +      }
 +
 +      // Show Summary
 +      cli_print(cli, "%5s %7s %9s %12s",
 +                      "GID",
 +                      "Nb Sess",
 +                      "Nb Routes",
 +                      "Time changed");
 +
 +      for (g = gnextgrpid; g != 0; g = grpsession[g].prev)
 +      {
 +              cli_print(cli, "%5d %7d %9d %12d",
 +                                      g,
 +                                      grpsession[g].nbsession,
 +                                      grpsession[g].nbroutesgrp,
 +                                      grpsession[g].time_changed);
 +      }
 +
 +      return CLI_OK;
 +}
 +
  static int cmd_setforward(struct cli_def *cli, char *command, char **argv, int argc)
  {
        int ret;
@@@ -3290,4 -3180,3 +3254,3 @@@ static int cmd_show_rmtlnsconf(struct c
  
        return CLI_OK;
  }
- #endif
diff --combined cluster.c
+++ b/cluster.c
@@@ -16,7 -16,6 +16,7 @@@
  #include <malloc.h>
  #include <errno.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #include "cluster.h"
@@@ -306,10 -305,11 +306,11 @@@ static int _forward_packet(uint8_t *dat
  // The master just processes the payload as if it had
  // received it off the tun device.
  //(note: THIS ROUTINE WRITES TO pack[-6]).
- int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port)
+ int master_forward_packet(uint8_t *data, int size, in_addr_t addr, uint16_t port, uint16_t indexudp)
  {
        uint8_t *p = data - (3 * sizeof(uint32_t));
        uint8_t *psave = p;
+       uint32_t indexandport = port | ((indexudp << 16) & 0xFFFF0000);
  
        if (!config->cluster_master_address) // No election has been held yet. Just skip it.
                return -1;
        LOG(4, 0, 0, "Forwarding packet from %s to master (size %d)\n", fmtaddr(addr, 0), size);
  
        STAT(c_forwarded);
-       add_type(&p, C_FORWARD, addr, (uint8_t *) &port, sizeof(port)); // ick. should be uint16_t
+       add_type(&p, C_FORWARD, addr, (uint8_t *) &indexandport, sizeof(indexandport));
  
        return peer_send_data(config->cluster_master_address, psave, size + (3 * sizeof(uint32_t)));
  }
@@@ -710,7 -710,6 +711,7 @@@ void cluster_check_master(void
                increment_counter(&session[i].cout, &session[i].cout_wrap, sess_local[i].cout);
                session[i].cin_delta += sess_local[i].cin;
                session[i].cout_delta += sess_local[i].cout;
 +              session[i].coutgrp_delta += sess_local[i].cout;
  
                session[i].pin += sess_local[i].pin;
                session[i].pout += sess_local[i].pout;
@@@ -1265,7 -1264,6 +1266,7 @@@ static int cluster_handle_bytes(uint8_
  
                session[b->sid].cin_delta += b->cin;
                session[b->sid].cout_delta += b->cout;
 +              session[b->sid].coutgrp_delta += b->cout;
  
                if (b->cin)
                        session[b->sid].last_packet = session[b->sid].last_data = time_now;
@@@ -1506,17 -1504,10 +1507,10 @@@ static int cluster_process_heartbeat(ui
        int i, type;
        int hb_ver = more;
  
- #ifdef LAC
  #if HB_VERSION != 7
  # error "need to update cluster_process_heartbeat()"
- #endif
- #else
- #if HB_VERSION != 6
- # error "need to update cluster_process_heartbeat()"
- #endif
  #endif
  
        // we handle versions 5 through 7
        if (hb_ver < 5 || hb_ver > HB_VERSION) {
                LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver);
                                size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
                                s -= (p - orig_p);
  
- #ifdef LAC
                                if ( ((hb_ver >= HB_VERSION) && (size != sizeof(tunnelt))) ||
                                         ((hb_ver < HB_VERSION) && (size > sizeof(tunnelt))) )
- #else
-                               if (size != sizeof(tunnelt) )
- #endif
                                { // Ouch! Very very bad!
                                        LOG(0, 0, 0, "DANGER: Received a CTUNNEL that didn't decompress correctly!\n");
                                                // Now what? Should exit! No-longer up to date!
@@@ -1857,9 -1844,11 +1847,11 @@@ int processcluster(uint8_t *data, int s
                else
                {
                        struct sockaddr_in a;
+                       uint16_t indexudp;
                        a.sin_addr.s_addr = more;
  
-                       a.sin_port = *(int *) p;
+                       a.sin_port = (*(int *) p) & 0xFFFF;
+                       indexudp = ((*(int *) p) >> 16) & 0xFFFF;
                        s -= sizeof(int);
                        p += sizeof(int);
  
                                processdae(p, s, &a, sizeof(a), &local);
                        }
                        else
-                               processudp(p, s, &a);
+                               processudp(p, s, &a, indexudp);
  
                        return 0;
                }
diff --combined l2tplac.c
+++ b/l2tplac.c
@@@ -7,8 -7,6 +7,8 @@@
  
  #include <errno.h>
  #include <string.h>
 +#include <sys/socket.h>
 +#include <linux/rtnetlink.h>
  
  #include "md5.h"
  #include "l2tpns.h"
@@@ -297,6 -295,7 +297,7 @@@ static int lac_create_tunnelsession(tun
                tunnel[t].port = pconfigrlns[i_conf].port;
                tunnel[t].window = 4; // default window
                tunnel[t].isremotelns = i_conf;
+               tunnel[t].indexudp = config->indexlacudpfd;
                STAT(tunnel_created);
  
                random_data(pconfigrlns[i_conf].auth, sizeof(pconfigrlns[i_conf].auth));
@@@ -451,7 -450,7 +452,7 @@@ void lac_calc_rlns_auth(tunnelidt t, ui
  }
  
  // Forward session to LAC or Remote LNS
- int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto, in_addr_t s_addr, int sin_port)
+ int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto, in_addr_t s_addr, int sin_port, uint16_t indexudpfd)
  {
        uint16_t t = 0, s = 0;
        uint8_t *p = buf + 2; // First word L2TP options
                         (proto == PPPCCP) )
                {
                        session[sess].last_packet = time_now;
-                       master_forward_packet(buf, len, s_addr, sin_port);
+                       master_forward_packet(buf, len, s_addr, sin_port, indexudpfd);
                        return 1;
                }
        }
                // Update STAT OUT
                increment_counter(&session[s].cout, &session[s].cout_wrap, len); // byte count
                session[s].cout_delta += len;
 +              session[s].coutgrp_delta += len;
                session[s].pout++;
                sess_local[s].cout += len;
                sess_local[s].pout++;
diff --combined l2tpns.c
+++ b/l2tpns.c
  #include "bgp.h"
  #endif
  
- #ifdef LAC
  #include "l2tplac.h"
- #endif
  #include "pppoe.h"
  
- #ifdef LAC
  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)
- 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
- #endif
  int controlfd = -1;           // Control signal handle
  int clifd = -1;                       // Socket listening for CLI connections.
  int daefd = -1;                       // Socket listening for DAE connections.
@@@ -81,7 -75,7 +75,7 @@@ int cluster_sockfd = -1;      // Intra-clust
  int epollfd = -1;             // event polling
  time_t basetime = 0;          // base clock
  char hostname[MAXHOSTNAME] = "";      // us.
 -static int tunidx;            // ifr_ifindex of tun device
 +int tunidx;                           // ifr_ifindex of tun device
  int nlseqnum = 0;             // netlink sequence number
  int min_initok_nlseqnum = 0;  // minimun seq number for messages after init is ok
  static int syslog_log = 0;    // are we logging to syslog
@@@ -98,7 -92,10 +92,7 @@@ uint16_t MSS = 0;            // TCP MS
  struct cli_session_actions *cli_session_actions = NULL;       // Pending session changes requested by CLI
  struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI
  
 -union iphash {
 -      sessionidt sess;
 -      union iphash *idx;
 -} ip_hash[256];                       // Mapping from IP address to session structures.
 +union iphash ip_hash[256];    // Mapping from IP address to session structures.
  
  struct ipv6radix {
        sessionidt sess;
@@@ -178,18 -175,16 +172,17 @@@ config_descriptt config_values[] = 
        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),
- #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("disable_sending_hello", disable_sending_hello, BOOL),
        CONFIG("disable_no_spoof", disable_no_spoof, BOOL),
+       CONFIG("bind_multi_address", bind_multi_address, STRING),
 +      CONFIG("grp_txrate_average_time", grp_txrate_average_time, INT),
        { NULL, 0, 0, 0 }
  };
  
@@@ -218,7 -213,6 +211,7 @@@ tunnelt *tunnel = NULL;                    // Array of t
  bundlet *bundle = NULL;                       // Array of bundle structures.
  fragmentationt *frag = NULL;          // Array of fragmentation structures.
  sessiont *session = NULL;             // Array of session structures.
 +groupsesst *grpsession = NULL;                // Array of groupsesst structures.
  sessionlocalt *sess_local = NULL;     // Array of local per-session counters.
  radiust *radius = NULL;                       // Array of radius structures.
  ippoolt *ip_address_pool = NULL;      // Array of dynamic IP addresses.
@@@ -229,6 -223,9 +222,6 @@@ struct Tstats *_statistics = NULL
  struct Tringbuffer *ringbuffer = NULL;
  #endif
  
 -static ssize_t netlink_send(struct nlmsghdr *nh);
 -static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen);
 -static void cache_ipmap(in_addr_t ip, sessionidt s);
  static void uncache_ipmap(in_addr_t ip);
  static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s);
  static void free_ip_address(sessionidt s);
@@@ -257,9 -254,8 +250,9 @@@ static clockt now(double *f
        if (f) *f = t.tv_sec + t.tv_usec / 1000000.0;
        if (t.tv_sec != time_now)
        {
 -          time_now = t.tv_sec;
 -          time_changed++;
 +              time_now = t.tv_sec;
 +              time_changed++;
 +              grp_time_changed();
        }
  
        // Time in milliseconds
@@@ -618,7 -614,7 +611,7 @@@ static void initnetlink(void
        }
  }
  
 -static ssize_t netlink_send(struct nlmsghdr *nh)
 +ssize_t netlink_send(struct nlmsghdr *nh)
  {
        struct sockaddr_nl nladdr;
        struct iovec iov;
@@@ -654,7 -650,7 +647,7 @@@ static ssize_t netlink_recv(void *buf, 
  }
  
  /* adapted from iproute2 */
 -static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
 +void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
  {
        int len = RTA_LENGTH(alen);
        struct rtattr *rta;
@@@ -697,7 -693,7 +690,7 @@@ static void inittun(void
        }
  
     if (*config->tundevicename)
-          strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ);
+               strncpy(ifr.ifr_name, config->tundevicename, IFNAMSIZ);
  
        if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0)
        {
                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
-                       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]) {
@@@ -836,28 -848,35 +845,35 @@@ senderror
        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;
  
-       // Tunnel
+       // Tunnel to Remote LNS
        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);
        }
+ }
+ // set up control ports
+ static void initcontrol(void)
+ {
+       int on = 1;
+       struct sockaddr_in addr;
  
        // Control
        memset(&addr, 0, sizeof(addr));
                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));
                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;
-       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);
        }
- #endif
-       // Intercept
-       snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  }
  
  //
@@@ -1000,7 -1028,7 +1025,7 @@@ sessionidt sessionbyipv6(struct in6_add
  //
  // (It's actually cached in network order)
  //
 -static void cache_ipmap(in_addr_t ip, sessionidt s)
 +void cache_ipmap(in_addr_t ip, sessionidt s)
  {
        in_addr_t nip = htonl(ip);      // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
        uint8_t *a = (uint8_t *) &nip;
@@@ -1230,14 -1258,11 +1255,11 @@@ void tunnelsend(uint8_t * buf, uint16_
                        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",
-                               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;
        }
@@@ -1372,7 -1397,6 +1394,7 @@@ static void update_session_out_stat(ses
  {
        increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
        sp->cout_delta += len;
 +      sp->coutgrp_delta += len;
        sp->pout++;
        sp->last_data = time_now;
  
  void processipout(uint8_t *buf, int len)
  {
        sessionidt s;
 +      groupidt g;
        sessiont *sp;
        tunnelidt t;
        in_addr_t ip;
        }
  
        ip = *(uint32_t *)(buf + 16);
 -      if (!(s = sessionbyip(ip)))
 +      if ((g = grp_groupbyip(ip)))
 +      {
 +              s = grp_getnextsession(g, ip);
 +              if (!s)
 +              {
 +                      // Is this a packet for a session that doesn't exist?
 +                      static int rate = 0;    // Number of ICMP packets we've sent this second.
 +                      static int last = 0;    // Last time we reset the ICMP packet counter 'rate'.
 +
 +                      if (last != time_now)
 +                      {
 +                              last = time_now;
 +                              rate = 0;
 +                      }
 +
 +                      if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second.
 +                      {
 +                              LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0));
 +                              host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4),
 +                                      config->bind_address ? config->bind_address : my_address, buf, len);
 +                      }
 +                      return;
 +              }
 +      }
 +      else if (!(s = sessionbyip(ip)))
        {
                // Is this a packet for a session that doesn't exist?
                static int rate = 0;    // Number of ICMP packets we've sent this second.
@@@ -1782,7 -1781,6 +1804,7 @@@ static void processipv6out(uint8_t * bu
  
        increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
        sp->cout_delta += len;
 +      sp->coutgrp_delta += len;
        sp->pout++;
        udp_tx += len;
  
@@@ -1832,7 -1830,6 +1854,7 @@@ static void send_ipout(sessionidt s, ui
  
        increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count
        sp->cout_delta += len;
 +      sp->coutgrp_delta += len;
        sp->pout++;
        udp_tx += len;
  
@@@ -2080,7 -2077,7 +2102,7 @@@ void sessionshutdown(sessionidt s, cha
                session[s].die = TIME + 150; // Clean up in 15 seconds
  
        if (session[s].ip)
 -      {                          // IP allocated, clear and unroute
 +      {       // IP allocated, clear and unroute
                int r;
                int routed = 0;
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
@@@ -2233,7 -2230,7 +2255,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 :
-                                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
@@@ -2280,8 -2277,6 +2302,8 @@@ static void sessionclear(sessionidt s
  // kill a session now
  void sessionkill(sessionidt s, char *reason)
  {
 +      groupidt g;
 +
        CSTAT(sessionkill);
  
        if (!session[s].opened) // not alive
        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;
                        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);
 +
 +      if ((g = grp_groupbysession(s)))
 +      {
 +              grp_removesession(g, s);
 +      }
 +
        sessionclear(s);
        cluster_send_session(s);
  }
@@@ -2421,7 -2408,7 +2441,7 @@@ static void tunnelshutdown(tunnelidt t
  }
  
  // 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;
        uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0;
  
                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;
                }
  
                        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);
                                        }
                                        break;
                                case 13:    // Response
- #ifdef LAC
                                        if (tunnel[t].isremotelns)
                                        {
                                                chapresponse = calloc(17, 1);
                                                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;
                                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)
                                        {
                                        {
                                                tunnelshutdown(t, "Shutting down", 6, 0, 0);
                                        }
- #endif /* LAC */
                                        break;
                                case 3:       // SCCN
                                        LOG(3, s, t, "Received SCCN\n");
                                        }
                                        return;
                                case 11:      // ICRP
- #ifdef LAC
                                LOG(3, s, t, "Received ICRP\n");
                                if (session[s].forwardtosession)
                                {
                                        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");
  
                                        // Set multilink options before sending initial LCP packet
                                        sess_local[s].mp_mrru = 1614;
-                                       sess_local[s].mp_epdis = ntohl(config->iftun_address ? config->iftun_address : my_address);
+                                       sess_local[s].mp_epdis = ntohl(config->iftun_n_address[tunnel[t].indexudp] ? config->iftun_n_address[tunnel[t].indexudp] : my_address);
  
                                        sendlcp(s, t);
                                        change_state(s, lcp, RequestSent);
                        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
-                       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)
                                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..
-                               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;
                        }
  
                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;
-                       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;
-                       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;
-                       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;
-                       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;
-                       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)
                        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;
                        }
  
                        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;
                        }
  
                        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;
                        }
  
                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
@@@ -3716,7 -3696,7 +3729,7 @@@ static void regular_cleanups(double per
  
                // No data in ECHO_TIMEOUT seconds, send LCP ECHO
                if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= config->echo_timeout) &&
 -                      (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT))
 +                      (time_now - sess_local[s].last_echo >= config->echo_timeout))
                {
                        uint8_t b[MAXETHER];
  
@@@ -3982,13 -3962,8 +3995,8 @@@ static int still_busy(void
  # 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
  // 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 *p = buf + 32; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
                                                // and the forwarded pppoe session
                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",
-               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 */
        {
                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);
                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_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
                        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 udp_pkts = 0;
+                       int udp_pkts[MAX_UDPFD + 1] = INIT_TABUDPVAR;
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
  #ifdef BGP
                                // 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
  
  #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 */
  
                                                                exit(1);
                                                        }
                                                        else
                                                                LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error));
                                                }
                                                // else it's a ack
                                }
  
                                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);
                                }
                        }
  
  
                        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)
-                                       {
-                                               processudp(p, s, &addr);
-                                               udp_pkts++;
-                                       }
-                                       else
+                                       // L2TP and L2TP REMOTE LNS
+                                       if (udp_ready[j])
                                        {
-                                               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)
                                {
                                }
                        }
  
-                       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)
                        {
- #ifdef LAC
-                               LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun %d cluster %d rmlns and %d pppoe packets\n",
-                                       config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts, udplac_pkts, pppoesess_pkts);
- #else
                                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, tun_pkts, cluster_pkts, pppoesess_pkts);
- #endif
+                                       config->multi_read_count, udp_pkts[0], tun_pkts, cluster_pkts, pppoesess_pkts);
                                STAT(multi_read_exceeded);
                                more++;
                        }
@@@ -4692,11 -4632,7 +4665,9 @@@ static void initdata(int optdebug, cha
        }
  #endif /* BGP */
  
- #ifdef LAC
        lac_initremotelnsdata();
- #endif
 +
 +      grp_initdata();
  }
  
  static int assign_ip_address(sessionidt s)
@@@ -4983,11 -4919,7 +4954,7 @@@ void snoop_send_packet(uint8_t *packet
  
  static int dump_session(FILE **f, sessiont *s)
  {
- #ifdef LAC
        if (!s->opened || (!s->ip && !s->forwardtosession) || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
- #else
-       if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
- #endif
                return 1;
  
        if (!*f)
                        "# 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);
        }
@@@ -5167,7 -5099,26 +5134,26 @@@ int main(int argc, char *argv[]
                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();
  
@@@ -5399,14 -5350,62 +5385,62 @@@ static void update_config(
        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;
- #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));
+                       }
+                       sip = n;
+               }
+               if (config->nbmultiaddress >= 1)
+               {
+                       config->bind_address = config->bind_n_address[0];
+                       config->iftun_address = config->bind_address;
+               }
+       }
        if(!config->iftun_address)
+       {
                config->iftun_address = config->bind_address;
+               config->iftun_n_address[0] = config->iftun_address;
+       }
  
        if (!*config->pppoe_ac_name)
                strncpy(config->pppoe_ac_name, DEFAULT_PPPOE_AC_NAME, sizeof(config->pppoe_ac_name) - 1);
@@@ -5618,7 -5617,6 +5652,7 @@@ int sessionsetup(sessionidt s, tunnelid
        if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1))
        {
                int routed = 0;
 +              groupidt g;
  
                // Add the route for this session.
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
                }
                else
                        cache_ipmap(session[s].ip, s);
 +
 +              if ((g = grp_groupbysession(s)))
 +              {
 +                      grp_setgrouproute(g, 1);
 +              }
        }
  
        sess_local[s].lcp_authtype = 0; // RADIUS authentication complete
@@@ -6470,8 -6463,6 +6504,6 @@@ int ip_filter(uint8_t *buf, int len, ui
        return 0;
  }
  
- #ifdef LAC
  tunnelidt lac_new_tunnel()
  {
        return new_tunnel();
@@@ -6517,4 -6508,3 +6549,3 @@@ void lac_tunnelshutdown(tunnelidt t, ch
        tunnelshutdown(t, reason, result, error, msg);
  }
  
- #endif
diff --combined l2tpns.h
+++ b/l2tpns.h
  #define MAXADDRESS    20              // Maximum length for the Endpoint Discrminiator address
  #define MAXSESSION    60000           // could be up to 65535
  #define MAXTBFS               6000            // Maximum token bucket filters. Might need up to 2 * session.
 +#define MAXSESSINGRP  12              // Maximum number of member links in grouped session
 +#define MAXGRPINSESS  12              // Maximum number of member links in session group
 +#define MAXGROUPE             300     // could be up to 65535, Maximum number of grouped session
 +#define MAXROUTEINGRP 15              // max static routes per group
  
  // Tunnel Id reserved for pppoe
  #define TUNNEL_ID_PPPOE       1
                                        // it's not expected to have a space for more than 10 unassembled packets = 10 * MAXBUNDLESES
  #define       MAXFRAGNUM_MASK (MAXFRAGNUM - 1)                // Must be equal to MAXFRAGNUM-1
  
+ // Multi bind address constants
+ #define MAX_UDPFD 4
+ #define MAX_BINDADDR MAX_UDPFD
+ // 4 + 1 for the udplac
+ #define INIT_TABUDPFD {-1, -1, -1, -1, -1}
+ #define INIT_TABUDPVAR {0, 0, 0, 0, 0}
  // Constants
  #ifndef ETCDIR
  #define ETCDIR                "/etc/l2tpns"
@@@ -211,7 -214,6 +218,7 @@@ enum 
  typedef uint16_t sessionidt;
  typedef uint16_t bundleidt;
  typedef uint16_t tunnelidt;
 +typedef uint16_t groupidt;
  typedef uint32_t clockt;
  typedef uint8_t hasht[16];
  
@@@ -327,45 -329,12 +334,39 @@@ typedef struc
        char class[MAXCLASS];
        uint8_t ipv6prefixlen;          // IPv6 route prefix length
        struct in6_addr ipv6route;      // Static IPv6 route
- #ifdef LAC
        sessionidt forwardtosession;    // LNS id_session to forward
        uint8_t src_hwaddr[ETH_ALEN];   // MAC addr source (for pppoe sessions 6 bytes)
 -      char reserved[4];               // Space to expand structure without changing HB_VERSION
 +      uint32_t coutgrp_delta;
- #else
-       uint8_t src_hwaddr[ETH_ALEN];   // MAC addr source (for pppoe sessions 6 bytes)
-       uint32_t coutgrp_delta;
-       char reserved[2];               // Space to expand structure without changing HB_VERSION
- #endif
  }
  sessiont;
  
 +typedef struct
 +{
 +      uint32_t tx_rate;
 +      sessionidt sid;
 +      uint8_t weight;
 +}
 +groupsessionidt;
 +
 +typedef struct
 +{
 +      uint32_t time_changed;
 +      groupidt prev;
 +      sessionidt smax;
 +      sessionidt smin;
 +      groupsessionidt sesslist[MAXSESSINGRP];
 +      routet route[MAXROUTEINGRP];            // static routes
 +      uint8_t nbroutesgrp;
 +      uint8_t nbsession;
 +}
 +groupsesst;
 +
 +union iphash
 +{
 +      sessionidt sess;
 +      union iphash *idx;
 +};                    // Mapping from IP address to session structures.
 +
  typedef struct
  {
          int state;                              // current state (bundlestate enum)
@@@ -482,12 -451,9 +483,9 @@@ typedef struc
        uint16_t controlc;      // outstaind messages in queue
        controlt *controls;     // oldest message
        controlt *controle;     // newest message
- #ifdef LAC
        uint16_t isremotelns;   // != 0 if the tunnel is to remote LNS (== index on the conf remote lns)
-       char reserved[14];              // Space to expand structure without changing HB_VERSION
- #else
-       char reserved[16];              // Space to expand structure without changing HB_VERSION
- #endif
+       uint16_t indexudp;      // Index UDP file handle (in udpfd[])
+       char reserved[12];              // Space to expand structure without changing HB_VERSION
  }
  tunnelt;
  
@@@ -798,20 -764,23 +796,24 @@@ typedef struc
        int idle_echo_timeout; // Time between last packet seen and
                                                   // Drop sessions who have not responded within IDLE_ECHO_TIMEOUT seconds
        in_addr_t iftun_address;
- #ifdef LAC
        int disable_lac_func;
        int auth_tunnel_change_addr_src;
        int highest_rlnsid;
        uint16_t bind_portremotelns;
        in_addr_t bind_address_remotelns;
- #endif
        char pppoe_if_to_bind[IFNAMSIZ];        // Name pppoe interface to bind
        char pppoe_service_name[64];    // pppoe service name
        char pppoe_ac_name[64];
        uint8_t pppoe_hwaddr[ETH_ALEN]; // MAC addr of interface pppoe to bind
        int disable_sending_hello; // Disable l2tp sending HELLO message for Apple compatibility.
        int disable_no_spoof; // Disable no spoof (permit load balancing client --> internet)
+       int nbudpfd; // number UDP file handle
+       int nbmultiaddress; // number multi address to bind
+       int indexlacudpfd;      // Index UDP LAC file handle (in udpfd[])
+       in_addr_t bind_n_address[MAX_BINDADDR];
+       in_addr_t iftun_n_address[MAX_BINDADDR];
+       char bind_multi_address[256];
 +      int grp_txrate_average_time; // caculation txrate average time (default 10s)
  } configt;
  
  enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 };
@@@ -947,9 -916,7 +949,7 @@@ void radiusretry(uint16_t r)
  uint16_t radiusnew(sessionidt s);
  void radiusclear(uint16_t r, sessionidt s);
  void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local);
- #ifdef LAC
  int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen, const char *radiussecret, const uint8_t * auth);
- #endif
  
  // l2tpns.c
  clockt backoff(uint8_t try);
@@@ -969,7 -936,7 +969,7 @@@ int tun_write(uint8_t *data, int size)
  void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp);
  void sendipcp(sessionidt s, tunnelidt t);
  void sendipv6cp(sessionidt s, tunnelidt t);
- 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);
  void processipout(uint8_t *buf, int len);
  void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, uint16_t port);
  int find_filter(char const *name, size_t len);
@@@ -977,28 -944,12 +977,26 @@@ int ip_filter(uint8_t *buf, int len, ui
  int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc);
  int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc);
  int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc);
- #ifdef LAC
 +void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen);
 +ssize_t netlink_send(struct nlmsghdr *nh);
 +void cache_ipmap(in_addr_t ip, sessionidt s);
 +
  tunnelidt lac_new_tunnel();
  void lac_tunnelclear(tunnelidt t);
  void lac_send_SCCRQ(tunnelidt t, uint8_t * auth, unsigned int auth_len);
  void lac_send_ICRQ(tunnelidt t, sessionidt s);
  void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg);
- #endif
  
 +// grpsess.c
 +sessionidt grp_getnextsession(groupidt g, in_addr_t ip);
 +void grp_initdata(void);
 +void grp_processvendorspecific(sessionidt s, uint8_t *pvs);
 +groupidt grp_groupbysession(sessionidt s);
 +groupidt grp_groupbyip(in_addr_t ip);
 +void grp_setgrouproute(groupidt g, int add);
 +void grp_time_changed(void);
 +void grp_removesession(groupidt g, sessionidt s);
 +
  #undef LOG
  #undef LOG_HEX
  #define LOG(D, s, t, f, ...)  ({ if (D <= config->debug) _log(D, s, t, f, ## __VA_ARGS__); })
@@@ -1033,8 -984,6 +1031,8 @@@ extern bundlet *bundle
  extern sessiont *session;
  extern sessionlocalt *sess_local;
  extern ippoolt *ip_address_pool;
 +extern groupsesst *grpsession;
 +groupidt gnextgrpid;
  #define sessionfree (session[0].next)
  
  
@@@ -1047,8 -996,6 +1045,8 @@@ extern struct Tstats *_statistics
  extern in_addr_t my_address;
  extern int clifd;
  extern int epollfd;
 +extern int tunidx;            // ifr_ifindex of tun device
 +extern union iphash ip_hash[256];
  
  struct event_data {
        enum {
                FD_TYPE_RADIUS,
                FD_TYPE_BGP,
                FD_TYPE_NETLINK,
- #ifdef LAC
-               FD_TYPE_UDPLAC,
- #endif
                FD_TYPE_PPPOEDISC,
                FD_TYPE_PPPOESESS
        } type;
-       int index; // for RADIUS, BGP
+       int index; // for RADIUS, BGP, UDP
  };
  
  #define TIME (config->current_time)
diff --combined ppp.c
--- 1/ppp.c
--- 2/ppp.c
+++ b/ppp.c
@@@ -5,9 -5,6 +5,9 @@@
  #include <unistd.h>
  #include <errno.h>
  #include <stdlib.h>
 +#include <sys/socket.h>
 +#include <linux/rtnetlink.h>
 +
  #include "l2tpns.h"
  #include "constants.h"
  #include "plugin.h"
@@@ -15,9 -12,7 +15,7 @@@
  #include "tbf.h"
  #include "cluster.h"
  
- #ifdef LAC
  #include "l2tplac.h"
- #endif
  #include "pppoe.h"
  
  extern tunnelt *tunnel;
@@@ -108,13 -103,11 +106,11 @@@ void processpap(sessionidt s, tunnelid
                LOG(3, s, t, "PAP login %s/%s\n", user, pass);
        }
  
- #ifdef LAC
        if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, user))
        {
                // Creating a tunnel/session has been started
                return;
        }
- #endif
  
        if (session[s].ip || !(r = radiusnew(s)))
        {
@@@ -267,7 -260,6 +263,6 @@@ void processchap(sessionidt s, tunnelid
                packet.username = calloc(l + 1, 1);
                memcpy(packet.username, p, l);
  
- #ifdef LAC
                if ((!config->disable_lac_func) && lac_conf_forwardtoremotelns(s, packet.username))
                {
                        free(packet.username);
                        // Creating a tunnel/session has been started
                        return;
                }
- #endif
  
                run_plugins(PLUGIN_PRE_AUTH, &packet);
                if (!packet.continue_auth)
diff --combined pppoe.c
+++ b/pppoe.c
@@@ -21,7 -21,6 +21,7 @@@
  #include <netpacket/packet.h>
  #include <arpa/inet.h>
  #include <linux/if_pppox.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #include "cluster.h"
@@@ -903,7 -902,6 +903,6 @@@ void process_pppoe_disc(uint8_t *pack, 
        }
  }
  
- #ifdef LAC
  // Forward from pppoe to l2tp remote LNS
  static void pppoe_forwardto_session_rmlns(uint8_t *pack, int size, sessionidt sess, uint16_t proto)
  {
                // Update STAT OUT
                increment_counter(&session[s].cout, &session[s].cout_wrap, ll2tp); // byte count
                session[s].cout_delta += ll2tp;
 +              session[s].coutgrp_delta += ll2tp;
                session[s].pout++;
                sess_local[s].cout += ll2tp;
                sess_local[s].pout++;
@@@ -1029,7 -1026,6 +1028,7 @@@ void pppoe_forwardto_session_pppoe(uint
                // Update STAT OUT
                increment_counter(&session[s].cout, &session[s].cout_wrap, lpppoe); // byte count
                session[s].cout_delta += lpppoe;
 +              session[s].coutgrp_delta += lpppoe;
                session[s].pout++;
                sess_local[s].cout += lpppoe;
                sess_local[s].pout++;
  
        tunnelsend(p, lpppoe, t); // send it....
  }
- #endif
  
  void process_pppoe_sess(uint8_t *pack, int size)
  {
                lppp -= 2;
        }
  
- #ifdef LAC
        if (session[sid].forwardtosession)
        {       // Must be forwaded to a remote lns tunnel l2tp
                pppoe_forwardto_session_rmlns(pack, size, sid, proto);
                return;
        }
- #endif
  
        if (proto == PPPPAP)
        {
diff --combined radius.c
+++ b/radius.c
@@@ -11,7 -11,6 +11,7 @@@
  #include <ctype.h>
  #include <netinet/in.h>
  #include <errno.h>
 +#include <linux/rtnetlink.h>
  
  #include "md5.h"
  #include "constants.h"
@@@ -20,9 -19,7 +20,7 @@@
  #include "util.h"
  #include "cluster.h"
  
- #ifdef LAC
  #include "l2tplac.h"
- #endif
  #include "pppoe.h"
  
  extern radiust *radius;
@@@ -538,9 -535,7 +536,7 @@@ void processrad(uint8_t *buf, int len, 
        uint8_t routes = 0;
        int r_code;
        int r_id;
- #ifdef LAC
        int OpentunnelReq = 0;
- #endif
  
        CSTAT(processrad);
  
                                // Extract IP, routes, etc
                                uint8_t *p = buf + 20;
                                uint8_t *e = buf + len;
- #ifdef LAC
                                uint8_t tag;
                                uint8_t strtemp[256];
                                lac_reset_rad_tag_tunnel_ctxt();
- #endif
                                for (; p + 2 <= e && p[1] && p + p[1] <= e; p += p[1])
                                {
                                        if (*p == 26 && p[1] >= 7)
                                                else if (vendor == 529 && attrib >= 135 && attrib <= 136) // Ascend
                                                {
                                                        // handle old-format ascend DNS attributes below
 -                                                      p += 6;
 +                                                      p += 6;
 +                                              }
 +                                              else if (vendor == 64520) // Sames
 +                                              {
 +                                                      //Sames vendor-specific 64520
 +                                                      uint8_t *pvs = p + 6; // pvs set to begin to attribute
 +                                                      LOG(3, s, session[s].tunnel, "   Sames vendor-specific: %d, Attrib: %d, lenght: %d\n", vendor, attrib, attrib_length);
 +                                                      grp_processvendorspecific(s, pvs);
 +                                                      continue;
                                                }
                                                else
                                                {
 -                                                      LOG(3, s, session[s].tunnel, "      Unknown vendor-specific\n");
 +                                                      LOG(3, s, session[s].tunnel, "   Unknown vendor-specific: %d, Attrib: %d\n", vendor, attrib);
                                                        continue;
                                                }
                                        }
                                                        session[s].classlen = MAXCLASS;
                                                memcpy(session[s].class, p + 2, session[s].classlen);
                                        }
- #ifdef LAC
                                        else if (*p == 64)
                                        {
                                                // Tunnel-Type
                                                // Fill context
                                                lac_set_rad_tag_tunnel_assignment_id(tag, (char *) strtemp);
                                        }
- #endif
                                }
                        }
                        else if (r_code == AccessReject)
                                break;
                        }
  
- #ifdef LAC
                        if ((!config->disable_lac_func) && OpentunnelReq)
                        {
                                char assignment_id[256];
                                LOG(3, s, session[s].tunnel, "   PAP User %s authentication %s.\n", session[s].user,
                                                (r_code == AccessAccept) ? "allowed" : "denied");
                        }
- #endif
  
                        if (!session[s].dns1 && config->default_dns1)
                        {
@@@ -1350,7 -1332,6 +1341,6 @@@ void processdae(uint8_t *buf, int len, 
                LOG(0, 0, 0, "Error sending DAE response packet: %s\n", strerror(errno));
  }
  
- #ifdef LAC
  // Decrypte the encrypted Tunnel Password.
  // Defined in RFC-2868.
  // the pl2tpsecret buffer must set to 256 characters.
@@@ -1441,4 -1422,3 +1431,3 @@@ int rad_tunnel_pwdecode(uint8_t *pl2tps
  
        return decodedlen;
  };
- #endif /* LAC */
diff --combined util.c
--- 1/util.c
--- 2/util.c
+++ b/util.c
@@@ -8,7 -8,6 +8,7 @@@
  #include <arpa/inet.h>
  #include <string.h>
  #include <sys/mman.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #ifdef BGP
@@@ -52,100 -51,109 +52,109 @@@ void *shared_malloc(unsigned int size
  }
  
  extern int forked;
- extern int cluster_sockfd, tunfd, udpfd, controlfd, daefd, snoopfd, ifrfd, ifr6fd, rand_fd;
+ extern int cluster_sockfd, tunfd, controlfd, daefd, snoopfd, ifrfd, ifr6fd, rand_fd;
+ extern int pppoediscfd, pppoesessfd;
  extern int *radfds;
+ extern int udpfd[MAX_UDPFD + 1];
  
  pid_t fork_and_close()
  {
-     pid_t pid = fork();
-     int i;
-     if (pid)
-       return pid;
-     forked++;
-     if (config->scheduler_fifo)
-     {
-       struct sched_param params = {0};
-       params.sched_priority = 0;
-       if (sched_setscheduler(0, SCHED_OTHER, &params))
+       pid_t pid = fork();
+       int i;
+       if (pid)
+               return pid;
+       forked++;
+       if (config->scheduler_fifo)
+       {
+               struct sched_param params = {0};
+               params.sched_priority = 0;
+               if (sched_setscheduler(0, SCHED_OTHER, &params))
+               {
+                       LOG(0, 0, 0, "Error setting scheduler to OTHER after fork: %s\n", strerror(errno));
+                       LOG(0, 0, 0, "This is probably really really bad.\n");
+               }
+       }
+       signal(SIGPIPE, SIG_DFL);
+       signal(SIGCHLD, SIG_DFL);
+       signal(SIGHUP,  SIG_DFL);
+       signal(SIGUSR1, SIG_DFL);
+       signal(SIGQUIT, SIG_DFL);
+       signal(SIGKILL, SIG_DFL);
+       signal(SIGTERM, SIG_DFL);
+       // Close sockets
+       if (clifd != -1)        close(clifd);
+       if (cluster_sockfd != -1)       close(cluster_sockfd);
+       if (tunfd != -1)        close(tunfd);
+       for (i = 0; i < config->nbudpfd; i++)
        {
-           LOG(0, 0, 0, "Error setting scheduler to OTHER after fork: %s\n", strerror(errno));
-           LOG(0, 0, 0, "This is probably really really bad.\n");
+               if (udpfd[i] != -1)     close(udpfd[i]);
        }
-     }
-     signal(SIGPIPE, SIG_DFL);
-     signal(SIGCHLD, SIG_DFL);
-     signal(SIGHUP,  SIG_DFL);
-     signal(SIGUSR1, SIG_DFL);
-     signal(SIGQUIT, SIG_DFL);
-     signal(SIGKILL, SIG_DFL);
-     signal(SIGTERM, SIG_DFL);
-     // Close sockets
-     if (clifd != -1)          close(clifd);
-     if (cluster_sockfd != -1) close(cluster_sockfd);
-     if (tunfd != -1)          close(tunfd);
-     if (udpfd != -1)          close(udpfd);
-     if (controlfd != -1)      close(controlfd);
-     if (daefd != -1)          close(daefd);
-     if (snoopfd != -1)        close(snoopfd);
-     if (rand_fd != -1)        close(rand_fd);
-     if (epollfd != -1)        close(epollfd);
-     for (i = 0; radfds && i < RADIUS_FDS; i++)
-       close(radfds[i]);
+       if (pppoediscfd != -1)  close(pppoediscfd);
+       if (pppoediscfd != -1)  close(pppoediscfd);
+       if (controlfd != -1)    close(controlfd);
+       if (daefd != -1)        close(daefd);
+       if (snoopfd != -1)      close(snoopfd);
+       if (rand_fd != -1)      close(rand_fd);
+       if (epollfd != -1)      close(epollfd);
+       for (i = 0; radfds && i < RADIUS_FDS; i++)
+               close(radfds[i]);
  
  #ifdef BGP
-     for (i = 0; i < BGP_NUM_PEERS; i++)
-       if (bgp_peers[i].sock != -1)
-           close(bgp_peers[i].sock);
+       for (i = 0; i < BGP_NUM_PEERS; i++)
+               if (bgp_peers[i].sock != -1)
+                       close(bgp_peers[i].sock);
  #endif /* BGP */
  
      return pid;
  }
  
  ssize_t recvfromto(int s, void *buf, size_t len, int flags,
-     struct sockaddr *from, socklen_t *fromlen, struct in_addr *toaddr)
+       struct sockaddr *from, socklen_t *fromlen, struct in_addr *toaddr)
  {
-     ssize_t r;
-     struct msghdr msg;
-     struct cmsghdr *cmsg;
-     struct iovec vec;
-     char cbuf[128];
+       ssize_t r;
+       struct msghdr msg;
+       struct cmsghdr *cmsg;
+       struct iovec vec;
+       char cbuf[128];
  
-     memset(&msg, 0, sizeof(msg));
-     msg.msg_name = from;
-     msg.msg_namelen = *fromlen;
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_name = from;
+       msg.msg_namelen = *fromlen;
  
-     vec.iov_base = buf;
-     vec.iov_len = len;
-     msg.msg_iov = &vec;
-     msg.msg_iovlen = 1;
-     msg.msg_flags = 0;
+       vec.iov_base = buf;
+       vec.iov_len = len;
+       msg.msg_iov = &vec;
+       msg.msg_iovlen = 1;
+       msg.msg_flags = 0;
  
-     msg.msg_control = cbuf;
-     msg.msg_controllen = sizeof(cbuf);
+       msg.msg_control = cbuf;
+       msg.msg_controllen = sizeof(cbuf);
  
-     if ((r = recvmsg(s, &msg, flags)) < 0)
-       return r;
+       if ((r = recvmsg(s, &msg, flags)) < 0)
+               return r;
  
-     if (fromlen)
-       *fromlen = msg.msg_namelen;
+       if (fromlen)
+               *fromlen = msg.msg_namelen;
  
-     memset(toaddr, 0, sizeof(*toaddr));
-     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
-     {
-       if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO)
+       memset(toaddr, 0, sizeof(*toaddr));
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
        {
-           struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
-           memcpy(toaddr, &i->ipi_addr, sizeof(*toaddr));
-           break;
+               if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO)
+               {
+                       struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
+                       memcpy(toaddr, &i->ipi_addr, sizeof(*toaddr));
+                       break;
+               }
        }
-     }
  
-     return r;
+       return r;
  }
  
  ssize_t sendtofrom(int s, void const *buf, size_t len, int flags,