Merge branch 'master' into samesversion
authorfendo <fendo@bi12info.com>
Thu, 28 Mar 2013 09:29:32 +0000 (10:29 +0100)
committerfendo <fendo@bi12info.com>
Thu, 28 Mar 2013 09:29:32 +0000 (10:29 +0100)
30 files changed:
Docs/manual.html
Makefile
arp.c
autosnoop.c
autothrottle.c
bgp.c
cli.c
cluster.c
cluster.h
control.c
debian/changelog
dictionary.sames [new file with mode: 0644]
garden.c
grpsess.c [new file with mode: 0644]
icmp.c
l2tplac.c
l2tplac.h
l2tpns.c
l2tpns.h
nsctl.c
ppp.c
pppoe.c
radius.c
sessionctl.c
setrxspeed.c
snoopctl.c
stripdomain.c
tbf.c
throttlectl.c
util.c

index 12180ea..384fe9a 100644 (file)
@@ -265,6 +265,24 @@ from the address of "bind_address" (For use in cases of specific configuration).
 If no address is given to iftun_address and bind_address, 1.1.1.1 is used.
 </LI>
 
+<LI><B>bind_multi_address</B> (ip address)<BR>
+This parameter permit to listen several addresss of the l2tp udp protocol
+(and set several address to the tun interface).
+<BR>
+WHEN this parameter is set, It OVERWRITE the parameters "bind_address"
+and "iftun_address".
+<BR>
+these can be interesting when you want do load-balancing in cluster mode
+of the uploaded from the LAC. For example you can set a bgp.prepend(MY_AS)
+for Address1 on LNS1 and a bgp.prepend(MY_AS) for Address2 on LNS2
+(see BGP AS-path prepending).
+<BR>
+example of use with 2 address:
+<BR>
+set bind_multi_address "64.14.13.41, 64.14.13.42"
+
+</LI>
+
 <LI><B>tundevicename</B> (string)<BR>
 Name of the tun interface (default: "tun0").
 </LI>
index 21221ab..b19dfcd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@ INSTALL = install -c -D -o root -g root
 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
+    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 @@ endif
 DEFINES += -DBGP
 OBJS += bgp.o
 
-DEFINES += -DLAC
-OBJS += l2tplac.o
-
 all: programs plugins
 programs: $(PROGRAMS)
 plugins: $(PLUGINS)
@@ -129,8 +126,9 @@ radius.o: radius.c md5.h constants.h l2tpns.h plugin.h util.h cluster.h \
 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
-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
+grpsess.o: grpsess.c l2tpns.h util.h cluster.h bgp.h
+bgp.o: bgp.c l2tpns.h bgp.h util.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 --git a/arp.c b/arp.c
index 6d55756..e9c7584 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -5,6 +5,8 @@
 #include <net/ethernet.h>
 #include <net/if_arp.h>
 #include <linux/if_packet.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 
index ddc699f..5633824 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 
index 73f9a94..007f926 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 
diff --git a/bgp.c b/bgp.c
index 7c1e6c0..36894be 100644 (file)
--- a/bgp.c
+++ b/bgp.c
@@ -20,6 +20,7 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <fcntl.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #include "bgp.h"
diff --git a/cli.c b/cli.c
index 3f950e0..e5ac57c 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -21,6 +21,7 @@
 #include <dlfcn.h>
 #include <netdb.h>
 #include <libcli.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #include "constants.h"
@@ -31,9 +32,7 @@
 #ifdef BGP
 #include "bgp.h"
 #endif
-#ifdef LAC
 #include "l2tplac.h"
-#endif
 
 extern tunnelt *tunnel;
 extern bundlet *bundle;
@@ -77,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);
@@ -102,10 +102,8 @@ static int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, in
 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);
 
@@ -156,10 +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");
@@ -231,9 +228,7 @@ void init_cli()
 
        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");
@@ -542,15 +537,9 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
        }
 
        // 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",
@@ -563,11 +552,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        "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++)
@@ -578,15 +563,9 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        rem_time = session[i].timeout ? (session[i].timeout - bundle[session[i].bundle].online_time) : 0;
                else
                        rem_time = session[i].timeout ? (session[i].timeout - (time_now-session[i].opened)) : 0;
-#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),
@@ -599,11 +578,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                                (unsigned long)session[i].cin,
                                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                                (unsigned long)(rem_time),
-#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 : "*");
        }
@@ -694,11 +669,7 @@ static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int
                                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
                                );
        }
 
@@ -1309,13 +1280,11 @@ static int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int
                        cli_print(cli, "Dropping session %d", s);
                        cli_session_actions[s].action |= CLI_SESS_KILL;
                }
-#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);
@@ -3106,7 +3075,7 @@ static int cmd_show_access_list(struct cli_def *cli, char *command, char **argv,
                }
 
                if (i)
-                       cli_print(cli, "");
+                       cli_print(cli, " ");
 
                cli_print(cli, "%s IP access list %s",
                        ip_filters[f].extended ? "Extended" : "Standard",
@@ -3144,7 +3113,76 @@ static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc)
        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)
 {
@@ -3216,4 +3254,3 @@ static int cmd_show_rmtlnsconf(struct cli_def *cli, char *command, char **argv,
 
        return CLI_OK;
 }
-#endif
index 78de01b..fc4d58a 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -16,6 +16,7 @@
 #include <malloc.h>
 #include <errno.h>
 #include <libcli.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #include "cluster.h"
@@ -43,6 +44,7 @@ in_addr_t my_address = 0;             // The network address of my ethernet port.
 static int walk_session_number = 0;    // The next session to send when doing the slow table walk.
 static int walk_bundle_number = 0;     // The next bundle to send when doing the slow table walk.
 static int walk_tunnel_number = 0;     // The next tunnel to send when doing the slow table walk.
+static int walk_groupe_number = 0;     // The next groupe to send when doing the slow table walk.
 int forked = 0;                                // Sanity check: CLI must not diddle with heartbeat table
 
 #define MAX_HEART_SIZE (8192)  // Maximum size of heartbeat packet. Must be less than max IP packet size :)
@@ -87,6 +89,7 @@ int cluster_init()
        config->cluster_undefined_sessions = MAXSESSION-1;
        config->cluster_undefined_bundles = MAXBUNDLE-1;
        config->cluster_undefined_tunnels = MAXTUNNEL-1;
+       config->cluster_undefined_groupes = MAXGROUPE-1;
 
        if (!config->cluster_address)
                return 0;
@@ -228,7 +231,8 @@ static void cluster_uptodate(void)
        if (config->cluster_iam_uptodate)
                return;
 
-       if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles)
+       if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels ||
+               config->cluster_undefined_bundles || config->cluster_undefined_groupes)
                return;
 
        config->cluster_iam_uptodate = 1;
@@ -305,10 +309,11 @@ static int _forward_packet(uint8_t *data, int size, in_addr_t addr, int port, in
 // 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;
@@ -316,7 +321,7 @@ int master_forward_packet(uint8_t *data, int size, in_addr_t addr, int port)
        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)));
 }
@@ -452,7 +457,8 @@ void cluster_send_ping(time_t basetime)
 
        x.ver = 1;
        x.addr = config->bind_address;
-       x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels + config->cluster_undefined_bundles;
+       x.undef = config->cluster_undefined_sessions + config->cluster_undefined_tunnels +
+                       config->cluster_undefined_groupes + config->cluster_undefined_bundles;
        x.basetime = basetime;
 
        add_type(&p, C_PING, basetime, (uint8_t *) &x, sizeof(x));
@@ -674,6 +680,20 @@ void cluster_check_master(void)
                         config->cluster_highest_bundleid = i;
         }
 
+               //
+               // Go through and mark all the groupes as defined.
+               // Count the highest used groupe number as well.
+               //
+               config->cluster_highest_groupeid = 0;
+               for (i = 0; i < MAXGROUPE; ++i)
+               {
+                       if (grpsession[i].state == GROUPEUNDEF)
+                               grpsession[i].state = GROUPEFREE;
+
+                       if (grpsession[i].state != GROUPEFREE && i > config->cluster_highest_groupeid)
+                               config->cluster_highest_groupeid = i;
+               }
+
                //
                // Go through and mark all the sessions as being defined.
                // reset the idle timeouts.
@@ -741,6 +761,7 @@ void cluster_check_master(void)
        config->cluster_undefined_sessions = 0;
        config->cluster_undefined_bundles = 0;
        config->cluster_undefined_tunnels = 0;
+       config->cluster_undefined_groupes = 0;
        config->cluster_iam_uptodate = 1; // assume all peers are up-to-date
 
        // FIXME. We need to fix up the tunnel control message
@@ -757,7 +778,7 @@ void cluster_check_master(void)
 // we fix it up here, and we ensure that the 'first free session'
 // pointer is valid.
 //
-static void cluster_check_sessions(int highsession, int freesession_ptr, int highbundle, int hightunnel)
+static void cluster_check_sessions(int highsession, int freesession_ptr, int highbundle, int hightunnel, int highgroupe)
 {
        int i;
 
@@ -766,7 +787,8 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
        if (config->cluster_iam_uptodate)
                return;
 
-       if (highsession > config->cluster_undefined_sessions && highbundle > config->cluster_undefined_bundles && hightunnel > config->cluster_undefined_tunnels)
+       if (highsession > config->cluster_undefined_sessions && highbundle > config->cluster_undefined_bundles &&
+               highgroupe > config->cluster_undefined_groupes && hightunnel > config->cluster_undefined_tunnels)
                return;
 
                // Clear out defined sessions, counting the number of
@@ -808,10 +830,23 @@ static void cluster_check_sessions(int highsession, int freesession_ptr, int hig
                        ++config->cluster_undefined_tunnels;
        }
 
+       // Clear out defined groupe, counting the number of
+       // undefs remaining.
+       config->cluster_undefined_groupes = 0;
+       for (i = 1 ; i < MAXGROUPE; ++i) {
+               if (i > highgroupe) {
+                       if (grpsession[i].state == GROUPEUNDEF) grpsession[i].state = GROUPEFREE; // Defined.
+                       continue;
+               }
 
-       if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles) {
-               LOG(2, 0, 0, "Cleared undefined sessions/bundles/tunnels. %d sess (high %d), %d bund (high %d), %d tunn (high %d)\n",
-                       config->cluster_undefined_sessions, highsession, config->cluster_undefined_bundles, highbundle, config->cluster_undefined_tunnels, hightunnel);
+               if (grpsession[i].state == GROUPEUNDEF)
+                       ++config->cluster_undefined_groupes;
+       }
+
+       if (config->cluster_undefined_sessions || config->cluster_undefined_tunnels || config->cluster_undefined_bundles || config->cluster_undefined_groupes) {
+               LOG(2, 0, 0, "Cleared undefined sessions/bundles/tunnels. %d sess (high %d), %d bund (high %d), %d grp (high %d), %d tunn (high %d)\n",
+                       config->cluster_undefined_sessions, highsession, config->cluster_undefined_bundles, highbundle,
+                       config->cluster_undefined_groupes, highgroupe, config->cluster_undefined_tunnels, hightunnel);
                return;
        }
 
@@ -865,6 +900,27 @@ static int hb_add_type(uint8_t **p, int type, int id)
                        add_type(p, C_BUNDLE, id, (uint8_t *) &bundle[id], sizeof(bundlet));
                        break;
 
+               case C_CGROUPE: { // Compressed C_GROUPE
+                       uint8_t c[sizeof(groupsesst) * 2]; // Bigger than worst case.
+                       uint8_t *d = (uint8_t *) &grpsession[id];
+                       uint8_t *orig = d;
+                       int size;
+
+                       size = rle_compress( &d,  sizeof(groupsesst), c, sizeof(c) );
+
+                       // Did we compress the full structure, and is the size actually
+                       // reduced??
+                       if ( (d - orig) == sizeof(groupsesst) && size < sizeof(groupsesst) )
+                       {
+                               add_type(p, C_CGROUPE, id, c, size);
+                               break;
+                       }
+                       // Failed to compress : Fall through.
+               }
+               case C_GROUPE:
+                       add_type(p, C_GROUPE, id, (uint8_t *) &grpsession[id], sizeof(groupsesst));
+                       break;
+
                case C_CTUNNEL: { // Compressed C_TUNNEL
                        uint8_t c[sizeof(tunnelt) * 2]; // Bigger than worst case.
                        uint8_t *d = (uint8_t *) &tunnel[id];
@@ -897,7 +953,7 @@ static int hb_add_type(uint8_t **p, int type, int id)
 //
 void cluster_heartbeat()
 {
-       int i, count = 0, tcount = 0, bcount = 0;
+       int i, count = 0, tcount = 0, bcount = 0, gcount = 0;
        uint8_t buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ];
        heartt h;
        uint8_t *p = buff;
@@ -919,9 +975,11 @@ void cluster_heartbeat()
        h.freesession = sessionfree;
        h.hightunnel = config->cluster_highest_tunnelid;
        h.highbundle = config->cluster_highest_bundleid;
+       h.highgroupe = config->cluster_highest_groupeid;
        h.size_sess = sizeof(sessiont);         // Just in case.
        h.size_bund = sizeof(bundlet);
        h.size_tunn = sizeof(tunnelt);
+       h.nextgrpid = gnextgrpid;
        h.interval = config->cluster_hb_interval;
        h.timeout  = config->cluster_hb_timeout;
        h.table_version = config->cluster_table_version;
@@ -940,7 +998,7 @@ void cluster_heartbeat()
 
                //
                // Fill out the packet with sessions from the session table...
-               // (not forgetting to leave space so we can get some tunnels in too )
+               // (not forgetting to leave space so we can get some tunnels,bundle,groupe in too )
        while ( (p + sizeof(uint32_t) * 2 + sizeof(sessiont) * 2 ) < (buff + MAX_HEART_SIZE) ) {
 
                if (!walk_session_number)       // session #0 isn't valid.
@@ -955,40 +1013,59 @@ void cluster_heartbeat()
                ++count;                        // Count the number of extra sessions we're sending.
        }
 
-               //
-               // Fill out the packet with tunnels from the tunnel table...
-               // This effectively means we walk the tunnel table more quickly
-               // than the session table. This is good because stuffing up a 
-               // tunnel is a much bigger deal than stuffing up a session.
-               //
-       while ( (p + sizeof(uint32_t) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE) ) {
+       //
+       // Fill out the packet with tunnels from the tunnel table...
+       // This effectively means we walk the tunnel table more quickly
+       // than the session table. This is good because stuffing up a
+       // tunnel is a much bigger deal than stuffing up a session.
+       //
+       int maxsize = (sizeof(tunnelt) < sizeof(bundlet)) ? sizeof(bundlet):sizeof(tunnelt);
+       maxsize = (sizeof(groupsesst) < maxsize) ? maxsize:sizeof(groupsesst);
+       maxsize += (sizeof(uint32_t) * 2);
+
+       // Fill out the packet with tunnels,bundlets, groupes from the tables...
+       while ( (p + maxsize) < (buff + MAX_HEART_SIZE) )
+       {
+               if ((tcount >= config->cluster_highest_tunnelid) &&
+                       (bcount >= config->cluster_highest_bundleid) &&
+                       (gcount >= config->cluster_highest_groupeid))
+                               break;
 
-               if (!walk_tunnel_number)        // tunnel #0 isn't valid.
-                       ++walk_tunnel_number;
+               if ( ((p + sizeof(uint32_t) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE)) &&
+                        (tcount < config->cluster_highest_tunnelid))
+               {
+                       if (!walk_tunnel_number)        // tunnel #0 isn't valid.
+                               ++walk_tunnel_number;
 
-               if (tcount >= config->cluster_highest_tunnelid)
-                       break;
+                       hb_add_type(&p, C_CTUNNEL, walk_tunnel_number);
+                       walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1);       // +1 avoids divide by zero.
 
-               hb_add_type(&p, C_CTUNNEL, walk_tunnel_number);
-               walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1);       // +1 avoids divide by zero.
+                       ++tcount;
+               }
 
-               ++tcount;
-       }
+               if ( ((p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE)) &&
+                        (bcount < config->cluster_highest_bundleid))
+               {
+                       if (!walk_bundle_number)        // bundle #0 isn't valid.
+                               ++walk_bundle_number;
 
-               //
-               // Fill out the packet with bundles from the bundle table...
-       while ( (p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE) ) {
+                       hb_add_type(&p, C_CBUNDLE, walk_bundle_number);
+                       walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1);       // +1 avoids divide by zero.
 
-               if (!walk_bundle_number)        // bundle #0 isn't valid.
-                       ++walk_bundle_number;
+                       ++bcount;
+               }
 
-               if (bcount >= config->cluster_highest_bundleid)
-                       break;
+               if ( ((p + sizeof(uint32_t) * 2 + sizeof(groupsesst) ) < (buff + MAX_HEART_SIZE)) &&
+                        (gcount < config->cluster_highest_groupeid))
+               {
+                       if (!walk_groupe_number)        // groupe #0 isn't valid.
+                               ++walk_groupe_number;
 
-               hb_add_type(&p, C_CBUNDLE, walk_bundle_number);
-               walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1);       // +1 avoids divide by zero.
-               ++bcount;
-        }
+                       hb_add_type(&p, C_CGROUPE, walk_groupe_number);
+                       walk_groupe_number = (1+walk_groupe_number)%(config->cluster_highest_groupeid+1);       // +1 avoids divide by zero.
+                       ++gcount;
+               }
+       }
 
                //
                // Did we do something wrong?
@@ -999,10 +1076,10 @@ void cluster_heartbeat()
        }
 
        LOG(4, 0, 0, "Sending v%d heartbeat #%d, change #%" PRIu64 " with %d changes "
-                    "(%d x-sess, %d x-bundles, %d x-tunnels, %d highsess, %d highbund, %d hightun, size %d)\n",
+                    "(%d x-sess, %d x-bundles, %d x-tunnels, %d x-groupes, %d highsess, %d highbund, %d hightun, %d highgrp, size %d)\n",
            HB_VERSION, h.seq, h.table_version, config->cluster_num_changes,
-           count, bcount, tcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
-           config->cluster_highest_tunnelid, (int) (p - buff));
+           count, bcount, tcount, gcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
+           config->cluster_highest_tunnelid, config->cluster_highest_groupeid, (int) (p - buff));
 
        config->cluster_num_changes = 0;
 
@@ -1068,6 +1145,18 @@ int cluster_send_bundle(int bid)
        return type_changed(C_CBUNDLE, bid);
 }
 
+// A particular groupe has been changed!
+int cluster_send_groupe(int gid)
+{
+       if (!config->cluster_iam_master)
+       {
+               LOG(0, 0, gid, "I'm not a master, but I just tried to change a groupe!\n");
+               return -1;
+       }
+
+       return type_changed(C_CGROUPE, gid);
+}
+
 // A particular tunnel has been changed!
 int cluster_send_tunnel(int tid)
 {
@@ -1332,6 +1421,31 @@ static int cluster_recv_bundle(int more, uint8_t *p)
         return 0;
 }
 
+static int cluster_recv_groupe(int more, uint8_t *p)
+{
+       if (more >= MAXGROUPE) {
+               LOG(0, 0, 0, "DANGER: Received a group id > MAXGROUPE!\n");
+               return -1;
+       }
+
+       if (grpsession[more].state == GROUPEUNDEF) {
+               if (config->cluster_iam_uptodate) { // Sanity.
+                       LOG(0, 0, 0, "I thought I was uptodate but I just found an undefined group!\n");
+               } else {
+                       --config->cluster_undefined_groupes;
+               }
+       }
+
+       grp_cluster_load_groupe(more, (groupsesst *) p);        // Copy groupe into groupe table..
+
+       LOG(5, 0, more, "Received group update (%d undef)\n", config->cluster_undefined_groupes);
+
+       if (!config->cluster_iam_uptodate)
+               cluster_uptodate();     // Check to see if we're up to date.
+
+        return 0;
+}
+
 static int cluster_recv_tunnel(int more, uint8_t *p)
 {
        if (more >= MAXTUNNEL) {
@@ -1503,16 +1617,9 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
        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) {
@@ -1635,9 +1742,10 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
        memcpy(&past_hearts[i].data, data, size);       // Save it.
 
 
-                       // Check that we don't have too many undefined sessions, and
-                       // that the free session pointer is correct.
-       cluster_check_sessions(h->highsession, h->freesession, h->highbundle, h->hightunnel);
+       // Check that we don't have too many undefined sessions, and
+       // that the free session pointer is correct.
+       gnextgrpid = h->nextgrpid;
+       cluster_check_sessions(h->highsession, h->freesession, h->highbundle, h->hightunnel, h->highgroupe);
 
        if (h->interval != config->cluster_hb_interval)
        {
@@ -1726,12 +1834,8 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
                                size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
                                s -= (p - orig_p);
 
-#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!
@@ -1779,6 +1883,36 @@ static int cluster_process_heartbeat(uint8_t *data, int size, int more, uint8_t
                                 p += sizeof(bundle[more]);
                                 s -= sizeof(bundle[more]);
                                 break;
+
+                       case C_CGROUPE:
+                       { // Compressed Groupe structure.
+                               uint8_t c[ sizeof(groupsesst) + 2];
+                               int size;
+                               uint8_t *orig_p = p;
+
+                               size = rle_decompress((uint8_t **) &p, s, c, sizeof(c));
+                               s -= (p - orig_p);
+
+                               if (size != sizeof(groupsesst) )
+                               { // Ouch! Very very bad!
+                                       LOG(0, 0, 0, "DANGER: Received a C_CGROUPE that didn't decompress correctly!\n");
+                                       // Now what? Should exit! No-longer up to date!
+                                       break;
+                               }
+
+                               cluster_recv_groupe(more, c);
+                               break;
+                       }
+                       case C_GROUPE:
+                               if ( s < sizeof(grpsession[more]))
+                                       goto shortpacket;
+
+                               cluster_recv_groupe(more, p);
+
+                               p += sizeof(grpsession[more]);
+                               s -= sizeof(grpsession[more]);
+                       break;
+
                        default:
                                LOG(0, 0, 0, "DANGER: I received a heartbeat element where I didn't understand the type! (%d)\n", type);
                                return -1; // can't process any more of the packet!!
@@ -1854,9 +1988,11 @@ int processcluster(uint8_t *data, int size, in_addr_t addr)
                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);
 
@@ -1871,7 +2007,7 @@ int processcluster(uint8_t *data, int size, in_addr_t addr)
                                processdae(p, s, &a, sizeof(a), &local);
                        }
                        else
-                               processudp(p, s, &a);
+                               processudp(p, s, &a, indexudp);
 
                        return 0;
                }
@@ -1986,12 +2122,14 @@ int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc)
                cli_print(cli, "Next sequence number expected: %d", config->cluster_seq_number);
                cli_print(cli, "%d sessions undefined of %d", config->cluster_undefined_sessions, config->cluster_highest_sessionid);
                cli_print(cli, "%d bundles undefined of %d", config->cluster_undefined_bundles, config->cluster_highest_bundleid);
+               cli_print(cli, "%d groupes undefined of %d", config->cluster_undefined_groupes, config->cluster_highest_groupeid);
                cli_print(cli, "%d tunnels undefined of %d", config->cluster_undefined_tunnels, config->cluster_highest_tunnelid);
        } else {
                cli_print(cli, "Table version #  : %" PRIu64, config->cluster_table_version);
                cli_print(cli, "Next heartbeat # : %d", config->cluster_seq_number);
                cli_print(cli, "Highest session  : %d", config->cluster_highest_sessionid);
                cli_print(cli, "Highest bundle   : %d", config->cluster_highest_bundleid);
+               cli_print(cli, "Highest groupe   : %d", config->cluster_highest_groupeid);
                cli_print(cli, "Highest tunnel   : %d", config->cluster_highest_tunnelid);
                cli_print(cli, "%d changes queued for sending", config->cluster_num_changes);
        }
index 6a769e4..794cc9d 100644 (file)
--- a/cluster.h
+++ b/cluster.h
 #define C_CBUNDLE              18      // Compressed bundle structure.
 #define C_MPPP_FORWARD 19      // MPPP Forwarded packet..
 #define C_PPPOE_FORWARD        20      // PPPOE Forwarded packet..
+#define C_GROUPE               21      // Groupe structure.
+#define C_CGROUPE              22      // Compressed groupe structure.
+
 
-#ifdef LAC
 #define HB_VERSION             7       // Protocol version number..
-#else
-#define HB_VERSION             6       // Protocol version number..
-#endif
 #define HB_MAX_SEQ             (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
 #define HB_HISTORY_SIZE                64      // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ)
 
@@ -62,7 +61,10 @@ typedef struct {
 
        uint64_t table_version; // # state changes processed by cluster
 
-       char reserved[128 - 13*sizeof(uint32_t) - sizeof(uint64_t)];    // Pad out to 128 bytes.
+       uint32_t highgroupe;    // Id of the highest used groupe.
+       uint32_t nextgrpid;     // nextgrpid to set gnextgrpid on slave
+
+       char reserved[128 - 15*sizeof(uint32_t) - sizeof(uint64_t)];    // Pad out to 128 bytes.
 } heartt;
 
 typedef struct {               /* Used to update byte counters on the */
@@ -85,8 +87,9 @@ int cluster_init(void);
 int processcluster(uint8_t *buf, int size, in_addr_t addr);
 int cluster_send_session(int sid);
 int cluster_send_bundle(int bid);
+int cluster_send_groupe(int gid);
 int cluster_send_tunnel(int tid);
-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);
 int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port);
 int master_throttle_packet(int tid, uint8_t *data, int size);
 int master_garden_packet(sessionidt s, uint8_t *data, int size);
index 3d38b22..092ab8e 100644 (file)
--- a/control.c
+++ b/control.c
@@ -1,6 +1,9 @@
 // L2TPNS: control
 
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "control.h"
 
index 1d9c0b7..5c63054 100644 (file)
@@ -1,10 +1,17 @@
-l2tpns (2.2.1-2fdn3.6) unstable; urgency=low
+l2tpns (2.2.1-2sames3.7) unstable; urgency=low
 
+  * Merge from master
   * Fix Warning: dereferencing type-punned pointer will break strict...
   * Fix: Tunnel creation does not work when the length of the hostname is odd. (revert fix: Add a uint16_t control buffer type, as a union)
 
  -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 26 Feb 2013 09:07:16 +0100
 
+l2tpns (2.2.1-2sames3.6) unstable; urgency=low
+
+  * Sames l2tpns version.
+
+ -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 12 Feb 2013 20:20:17 +0100
+
 l2tpns (2.2.1-2fdn3.5) unstable; urgency=low
 
   * Update debian/changelog
diff --git a/dictionary.sames b/dictionary.sames
new file mode 100644 (file)
index 0000000..a8a244d
--- /dev/null
@@ -0,0 +1,18 @@
+# -*- text -*-
+##############################################################################
+#      Vendor specific attributes SamesWireless
+#      http://www.sameswireless.fr
+#
+#      $Id$
+#
+##############################################################################
+
+VENDOR         SAMES                           64520
+
+BEGIN-VENDOR   SAMES
+
+ATTRIBUTE      SAMES-Group-Framed-Route                22      string
+ATTRIBUTE      SAMES-Group-Session-Weight              23      string
+
+END-VENDOR SAMES
+
index 994ecb5..63135ea 100644 (file)
--- a/garden.c
+++ b/garden.c
@@ -3,6 +3,9 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 #include "control.h"
diff --git a/grpsess.c b/grpsess.c
new file mode 100644 (file)
index 0000000..7f897b3
--- /dev/null
+++ b/grpsess.c
@@ -0,0 +1,674 @@
+/*
+ * Fernando ALVES 2013
+ * Grouped session for load balancing and fail-over
+ * GPL licenced
+ */
+
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
+#include "l2tpns.h"
+#include "util.h"
+#include "cluster.h"
+
+#ifdef BGP
+#include "bgp.h"
+#endif
+
+union grp_iphash {
+       groupidt grp;
+       union grp_iphash *idx;
+} grp_ip_hash[256];                    // Mapping from IP address to group structures.
+
+groupidt gnextgrpid = 0;
+
+// Find gruop by IP, < 1 for not found
+//
+// Confusingly enough, this 'ip' must be
+// in _network_ order. This being the common
+// case when looking it up from IP packet headers.
+static groupidt grp_lookup_ipmap(in_addr_t ip)
+{
+       uint8_t *a = (uint8_t *) &ip;
+       union grp_iphash *h = grp_ip_hash;
+
+       if (!(h = h[*a++].idx)) return 0;
+       if (!(h = h[*a++].idx)) return 0;
+       if (!(h = h[*a++].idx)) return 0;
+
+       return h[*a].grp;
+}
+
+//
+// Take an IP address in HOST byte order and
+// add it to the grouid by IP cache.
+//
+// (It's actually cached in network order)
+//
+static void grp_cache_ipmap(in_addr_t ip, groupidt g)
+{
+       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;
+       union grp_iphash *h = grp_ip_hash;
+       int i;
+
+       for (i = 0; i < 3; i++)
+       {
+               if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union grp_iphash)))))
+                       return;
+
+               h = h[a[i]].idx;
+       }
+
+       h[a[3]].grp = g;
+
+       if (g > 0)
+               LOG(4, 0, 0, "Caching Group:%d ip address %s\n", g, fmtaddr(nip, 0));
+       else if (g == 0)
+               LOG(4, 0, 0, "Un-caching Group ip address %s\n", fmtaddr(nip, 0));
+}
+
+groupidt grp_groupbyip(in_addr_t ip)
+{
+       groupidt g = grp_lookup_ipmap(ip);
+
+       if (g > 0 && g < MAXGROUPE)
+               return g;
+
+       return 0;
+}
+
+// Add a route
+//
+// This adds it to the routing table, advertises it
+// via BGP if enabled, and stuffs it into the
+// 'groupbyip' cache.
+//
+// 'ip' must be in _host_ order.
+//
+static void grp_routeset(groupidt g, in_addr_t ip, int prefixlen, int add)
+{
+       struct {
+               struct nlmsghdr nh;
+               struct rtmsg rt;
+               char buf[32];
+       } req;
+       int i;
+       in_addr_t n_ip;
+
+       if (!prefixlen) prefixlen = 32;
+
+       ip &= 0xffffffff << (32 - prefixlen);;  // Force the ip to be the first one in the route.
+
+       memset(&req, 0, sizeof(req));
+
+       if (add)
+       {
+               req.nh.nlmsg_type = RTM_NEWROUTE;
+               req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE;
+       }
+       else
+       {
+               req.nh.nlmsg_type = RTM_DELROUTE;
+               req.nh.nlmsg_flags = NLM_F_REQUEST;
+       }
+
+       req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt));
+
+       req.rt.rtm_family = AF_INET;
+       req.rt.rtm_dst_len = prefixlen;
+       req.rt.rtm_table = RT_TABLE_MAIN;
+       req.rt.rtm_protocol = 42;
+       req.rt.rtm_scope = RT_SCOPE_LINK;
+       req.rt.rtm_type = RTN_UNICAST;
+
+       netlink_addattr(&req.nh, RTA_OIF, &tunidx, sizeof(int));
+       n_ip = htonl(ip);
+       netlink_addattr(&req.nh, RTA_DST, &n_ip, sizeof(n_ip));
+
+       LOG(3, 0, 0, "Route (Group) %s %s/%d\n", add ? "add" : "del", fmtaddr(htonl(ip), 0), prefixlen);
+
+       if (netlink_send(&req.nh) < 0)
+               LOG(0, 0, 0, "grp_routeset() error in sending netlink message: %s\n", strerror(errno));
+
+#ifdef BGP
+       if (add)
+               bgp_add_route(htonl(ip), prefixlen);
+       else
+               bgp_del_route(htonl(ip), prefixlen);
+#endif /* BGP */
+
+       // Add/Remove the IPs to the 'groupbyip' cache.
+       // Note that we add the zero address in the case of
+       // a network route. Roll on CIDR.
+
+       // Note that 'g == 0' implies this is the address pool.
+       // We still cache it here, because it will pre-fill
+       // the malloc'ed tree.
+       if (g)
+       {
+               if (!add)       // Are we deleting a route?
+                       g = 0;  // Caching the session as '0' is the same as uncaching.
+
+               for (i = ip; i < ip+(1<<(32-prefixlen)) ; ++i)
+               {
+                       grp_cache_ipmap(i, g);
+                       if (!g) cache_ipmap(i, 0);
+               }
+       }
+}
+
+// Set all route of a group
+void grp_setgrouproute(groupidt g, int add)
+{
+       int i;
+       for (i = 0; i < grpsession[g].nbroutesgrp; i++)
+       {
+               if (grpsession[g].route[i].ip != 0)
+               {
+                       grp_routeset(g, grpsession[g].route[i].ip, grpsession[g].route[i].prefixlen, add);
+               }
+       }
+}
+
+// return group id by session
+groupidt grp_groupbysession(sessionidt s)
+{
+       groupidt g = 0;
+       int i;
+       for (g = gnextgrpid; g != 0; g = grpsession[g].prev)
+       {
+               for (i = 0; i < grpsession[g].nbsession; i++)
+               {
+                       if (grpsession[g].sesslist[i].sid == s)
+                       {       // session found in group
+                               return g;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+// Remove a session to a group
+// return 1 if OK
+void grp_removesession(groupidt g, sessionidt s)
+{
+       int i;
+
+       if (grpsession[g].nbsession <= 0)
+               return;
+
+       for (i = 0; i < grpsession[g].nbsession; i++)
+       {
+               if (grpsession[g].sesslist[i].sid == s)
+               {       // session found on group
+                       --grpsession[g].nbsession;
+                       if (grpsession[g].nbsession == 0)
+                       {
+                               // Group is empty, remove it
+
+                               // Del all routes
+                               grp_setgrouproute(g, 0);
+
+                               if (gnextgrpid == g)
+                               {
+                                       gnextgrpid = grpsession[g].prev;
+                               }
+                               else
+                               {
+                                       groupidt g2;
+                                       for (g2 = gnextgrpid; g2 != 0; g2 = grpsession[g2].prev)
+                                       {
+                                               if (grpsession[g2].prev == g)
+                                               {
+                                                       grpsession[g2].prev = grpsession[g].prev;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               memset(&grpsession[g], 0, sizeof(grpsession[0]));
+                               grpsession[g].state = GROUPEFREE;
+                       }
+                       else
+                       {
+                               // remove the session
+                               memmove(&grpsession[g].sesslist[i],
+                                               &grpsession[g].sesslist[i+1],
+                                               (grpsession[g].nbsession - i) * sizeof(grpsession[g].sesslist[i]));
+                       }
+
+                       cluster_send_groupe(g);
+                       return;
+               }
+       }
+}
+
+// Add a session to a group
+// return 1 if OK
+static int grp_addsession(groupidt g, sessionidt s, uint8_t weight)
+{
+       int i;
+
+       for (i = 0; i < grpsession[g].nbsession; i++)
+       {
+               if (grpsession[g].sesslist[i].sid == s)
+               {       // already in group
+                       if ((!grpsession[g].sesslist[i].weight) || (weight > 1))
+                               grpsession[g].sesslist[i].weight = weight; // update Weight session (for load-balancing)
+
+                       return 1;
+               }
+       }
+
+       if (i >= MAXSESSINGRP)
+       {
+               LOG(1, s, session[s].tunnel, "   Too many session for Group %d\n", g);
+               return 0;
+       }
+       else
+       {       // add session id to group
+               if (i == 0)
+               {
+                       // it's the first session of the group, set to next group
+                       grpsession[g].prev = gnextgrpid;
+                       gnextgrpid = g;
+                       grpsession[g].state = GROUPEOPEN;
+               }
+               grpsession[g].sesslist[i].sid = s;
+               grpsession[g].sesslist[i].weight = weight;
+               grpsession[g].nbsession++;
+
+               return 1;
+       }
+       return 0;
+}
+
+// Add a route to a group
+// return 1 if OK
+static int grp_addroute(groupidt g, sessionidt s, in_addr_t ip, int prefixlen)
+{
+       int i;
+
+       for (i = 0; i < MAXROUTEINGRP; i++)
+       {
+               if ((i >= grpsession[g].nbroutesgrp))
+               {
+                       LOG(3, s, session[s].tunnel, "   Radius reply Group %d contains route for %s/%d\n",
+                               g, fmtaddr(htonl(ip), 0), prefixlen);
+
+                       grpsession[g].route[i].ip = ip;
+                       grpsession[g].route[i].prefixlen = prefixlen;
+                       grpsession[g].nbroutesgrp++;
+                       return 1;
+               }
+               else if ((grpsession[g].route[i].ip == ip) && (grpsession[g].route[i].prefixlen == prefixlen))
+               {
+                       // route already defined in group
+                       LOG(3, s, session[s].tunnel,
+                               "   Radius reply Group %d contains route for %s/%d (this already defined)\n",
+                               g, fmtaddr(htonl(ip), 0), prefixlen);
+
+                       return 1;
+               }
+               else if (grpsession[g].route[i].ip == 0)
+               {
+                       LOG(3, s, session[s].tunnel, "   Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
+                               g, fmtaddr(htonl(ip), 0), prefixlen);
+
+                       grpsession[g].route[i].ip = ip;
+                       grpsession[g].route[i].prefixlen = prefixlen;
+                       return 1;
+               }
+       }
+
+       if (i >= MAXROUTEINGRP)
+       {
+               LOG(1, s, session[s].tunnel, "   Too many routes for Group %d\n", g);
+       }
+       return 0;
+}
+
+// Process Sames vendor specific attribut radius
+void grp_processvendorspecific(sessionidt s, uint8_t *pvs)
+{
+       uint8_t attrib = *pvs;
+       groupidt grpid = 0;
+       uint8_t *n = pvs + 2;
+       uint8_t *e = pvs + pvs[1];
+
+       if ((attrib >= 22) && (attrib <= 23))
+       {
+               while (n < e && isdigit(*n))
+               {
+                       grpid = grpid * 10 + *n - '0';
+                       n++;
+               }
+               if ((grpid == 0) || (grpid >= MAXGROUPE))
+               {
+                       LOG(1, s, session[s].tunnel, "   Group Attribute Id %d not allowed\n", grpid);
+                       return;
+               }
+               else if (*n != ',')
+               {
+                       LOG(1, s, session[s].tunnel, "   Group Attribute Id not defined\n");
+                       return;
+               }
+
+               if (!grp_addsession(grpid, s, 1))
+               {
+                       return;
+               }
+
+               if (grpid > config->cluster_highest_groupeid)
+                       config->cluster_highest_groupeid = grpid;
+
+               n++;
+       }
+
+       //process, Sames vendor-specific 64520
+       if (attrib == 22) //SAMES-Group-Framed-Route
+       {
+               in_addr_t ip = 0;
+               uint8_t u = 0;
+               uint8_t bits = 0;
+
+               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;
+               if (*n == '/')
+               {
+                       n++;
+                       while (n < e && isdigit(*n))
+                               bits = bits * 10 + *n++ - '0';
+               }
+               else if ((ip >> 24) < 128)
+                       bits = 8;
+               else if ((ip >> 24) < 192)
+                       bits = 16;
+               else
+                       bits = 24;
+
+               if (!grp_addroute(grpid, s, ip, bits))
+                       return;
+       }
+       else if (attrib == 23) //SAMES-Group-Session-Weight
+       {
+               uint8_t weight = 0;
+
+               while (n < e && isdigit(*n))
+                       weight = weight * 10 + *n++ - '0';
+
+               if (!weight)
+               {
+                       LOG(1, s, session[s].tunnel, "   Group-Session-Weight 0 GroupId %d not allowed\n", grpid);
+                       return;
+               }
+               if (!grp_addsession(grpid, s, weight))
+               {
+                       return;
+               }
+       }
+       else
+       {
+               LOG(3, s, session[s].tunnel, "   Unknown vendor-specific: 64520, Attrib: %d\n", attrib);
+       }
+}
+
+// Init data structures
+void grp_initdata()
+{
+       int i;
+
+       // Set default value (10s)
+       config->grp_txrate_average_time = 10;
+
+       if (!(grpsession = shared_malloc(sizeof(groupsesst) * MAXGROUPE)))
+       {
+               LOG(0, 0, 0, "Error doing malloc for grouped session: %s\n", strerror(errno));
+               exit(1);
+       }
+
+       memset(grpsession, 0, sizeof(grpsession[0]) * MAXGROUPE);
+       for (i = 1; i < MAXGROUPE; i++)
+       {
+               grpsession[i].state = GROUPEUNDEF;
+       }
+}
+
+// Update time_changed of the group
+void grp_time_changed()
+{
+       groupidt g;
+
+       for (g = gnextgrpid; g != 0; g = grpsession[g].prev)
+       {
+               grpsession[g].time_changed++;
+       }
+}
+
+// Uncache all IP of a session
+static void grp_uncache_ipsession(groupidt g, sessionidt s)
+{
+       int i;
+       uint8_t *a;
+       in_addr_t ip;
+       in_addr_t n_ip, j;
+       int prefixlen;
+       union iphash *h;
+
+       for (i = 0; i < grpsession[g].nbroutesgrp; i++)
+       {
+               if (grpsession[g].route[i].ip != 0)
+               {
+                       prefixlen = grpsession[g].route[i].prefixlen;
+                       ip = grpsession[g].route[i].ip & (0xffffffff << (32 - prefixlen));      // Force the ip to be the first one in the route.
+
+                       for (j = ip; j < ip+(1<<(32-prefixlen)) ; ++j)
+                       {
+                               n_ip = htonl(j); // To network order
+                               a = (uint8_t *) &n_ip;
+                               h = ip_hash;
+
+                               if (!(h = h[*a++].idx)) continue;
+                               if (!(h = h[*a++].idx)) continue;
+                               if (!(h = h[*a++].idx)) continue;
+
+                               if (s == h[*a].sess)
+                               {
+                                       h[*a].sess = 0;
+                                       //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0));
+                               }
+                       }
+               }
+       }
+}
+
+// return the next session can be used on the group
+sessionidt grp_getnextsession(groupidt g, in_addr_t ip)
+{
+       sessionidt s = 0, s2 = 0, s3 = 0;
+       int i;
+       uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0;
+       uint32_t txrate;
+
+       if (g >= MAXGROUPE)
+               return 0;
+
+       if (grpsession[g].time_changed >= config->grp_txrate_average_time)
+       {
+               // recalculation txrate
+               ltime_changed = grpsession[g].time_changed;
+               grpsession[g].time_changed = 0;
+               for (i = 0; i < grpsession[g].nbsession; i++)
+               {
+                       if ((s2 = grpsession[g].sesslist[i].sid))
+                       {
+                               uint32_t coutgrp_delta = 0;
+
+                               if (session[s2].cout >= grpsession[g].sesslist[i].prev_coutgrp)
+                                       coutgrp_delta = session[s2].cout - grpsession[g].sesslist[i].prev_coutgrp;
+                               grpsession[g].sesslist[i].prev_coutgrp = session[s2].cout;
+
+                               grpsession[g].sesslist[i].tx_rate = coutgrp_delta/ltime_changed;
+
+                               txrate = grpsession[g].sesslist[i].tx_rate/grpsession[g].sesslist[i].weight;
+                               if (txrate < mintxrate)
+                               {
+                                       if ( session[s2].ppp.phase > Establish &&
+                                               (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
+                                       {
+                                               grpsession[g].smin = s2;
+                                               mintxrate = txrate;
+                                       }
+                               }
+
+                               if (txrate > maxtxrate)
+                               {
+                                       if ( session[s2].ppp.phase > Establish &&
+                                       (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
+                                       {
+                                               grpsession[g].smax = s2;
+                                               maxtxrate = txrate;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if ((s = sessionbyip(ip)))
+       {
+               if (s == grpsession[g].smax)
+               {
+                       s = grpsession[g].smin;
+                       grpsession[g].smax = 0;
+               }
+               else if ( (session[s].ppp.phase > Establish) &&
+                        (time_now - session[s].last_packet <= (config->echo_timeout + 1)) )
+               {
+                       return s;
+               }
+               else
+               {
+                       s = 0;
+               }
+       }
+
+       if (!s)
+       {
+               // random between 0 and nbsession-1
+               uint indexsess = (rand() % grpsession[g].nbsession);
+
+               if (indexsess >= grpsession[g].nbsession)
+                       indexsess = 0; //Sanity checks.
+
+               s2 = grpsession[g].sesslist[indexsess].sid;
+               if (s2 &&
+                       (session[s2].ppp.phase > Establish) &&
+                       (time_now - session[s2].last_packet <= (config->echo_timeout + 1)))
+               {
+                       s = s2;
+               }
+               else
+               {
+                       for (i = 0; i < grpsession[g].nbsession; i++)
+                       {
+                               if ((s2 = grpsession[g].sesslist[i].sid))
+                               {
+                                       s3 = s2;
+
+                                       if ( session[s2].ppp.phase > Establish &&
+                                               (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) )
+                                       {
+                                               s = s2;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (!s)
+               s = s3;
+
+       if (s)
+               cache_ipmap(ntohl(ip), s);
+
+       return s;
+}
+
+// load a groupe receive from master
+int grp_cluster_load_groupe(groupidt g, groupsesst *new)
+{
+       int i;
+       int updategroup = 0;
+
+       if (g >= MAXGROUPE)
+       {
+               LOG(0, 0, 0, "ERROR: Received a group id > MAXGROUPE!\n");
+               return 0;
+       }
+
+       if ((grpsession[g].nbroutesgrp != new->nbroutesgrp) ||
+               (grpsession[g].nbsession != new->nbsession))
+       {
+               updategroup = 1;
+       }
+
+       if (!updategroup)
+       {
+               // Check session list
+               for (i = 0; i < grpsession[g].nbsession; i++)
+               {
+                       if (grpsession[g].sesslist[i].sid != new->sesslist[i].sid)
+                       {
+                               updategroup = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (!updategroup)
+       {
+               // Check routes list
+               for (i = 0; i < grpsession[g].nbroutesgrp; i++)
+               {
+                       if (grpsession[g].route[i].ip != new->route[i].ip)
+                       {
+                               updategroup = 1;
+                               break;
+                       }
+               }
+       }
+
+       // needs update
+       if (updategroup)
+       {
+               // Del all routes
+               grp_setgrouproute(g, 0);
+       }
+
+       memcpy(&grpsession[g], new, sizeof(grpsession[g]));     // Copy over..
+
+       // needs update
+       if (updategroup)
+       {
+               // Add all routes
+               grp_setgrouproute(g, 1);
+       }
+
+       return 1;
+}
diff --git a/icmp.c b/icmp.c
index fa947b7..5f05127 100644 (file)
--- a/icmp.c
+++ b/icmp.c
@@ -13,6 +13,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <memory.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #include "pppoe.h"
index 6ad4683..fd3a7e0 100644 (file)
--- a/l2tplac.c
+++ b/l2tplac.c
@@ -7,6 +7,8 @@
 
 #include <errno.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
 
 #include "md5.h"
 #include "l2tpns.h"
@@ -295,6 +297,7 @@ static int lac_create_tunnelsession(tunnelidt t, sessionidt s, confrlnsidt i_con
                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));
@@ -449,7 +452,7 @@ void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out)
 }
 
 // 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
@@ -482,7 +485,7 @@ int lac_session_forward(uint8_t *buf, int len, sessionidt sess, uint16_t proto,
                         (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;
                }
        }
index 6177b0a..0ff7c5a 100644 (file)
--- a/l2tplac.h
+++ b/l2tplac.h
@@ -12,7 +12,7 @@ typedef uint16_t confrlnsidt;
 
 // l2tplac.c
 void lac_initremotelnsdata();
-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);
 int lac_conf_forwardtoremotelns(sessionidt s, char * puser);
 void lac_calc_rlns_auth(tunnelidt t, uint8_t id, uint8_t *out);
 int lac_addremotelns(char *mask, char *IP_RemoteLNS, char *Port_RemoteLNS, char *SecretRemoteLNS);
index 527175c..17c440b 100644 (file)
--- a/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 @@ int cluster_sockfd = -1;      // Intra-cluster communications socket.
 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,10 +92,7 @@ uint16_t MSS = 0;            // TCP MSS
 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;
@@ -181,17 +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 }
 };
 
@@ -220,6 +211,7 @@ tunnelt *tunnel = NULL;                     // Array of tunnel structures.
 bundlet *bundle = NULL;                        // Array of bundle structures.
 fragmentationt *frag = NULL;           // Array of fragmentation structures.
 sessiont *session = NULL;              // Array of session structures.
+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.
@@ -230,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);
@@ -261,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
@@ -621,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;
@@ -657,7 +647,7 @@ static ssize_t netlink_recv(void *buf, ssize_t len)
 }
 
 /* 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;
@@ -700,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)
        {
@@ -762,14 +752,30 @@ static void inittun(void)
                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]) {
@@ -839,28 +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));
@@ -874,6 +887,13 @@ static void initudp(void)
                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));
@@ -887,28 +907,30 @@ static void initudp(void)
                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);
 }
 
 //
@@ -1003,7 +1025,7 @@ sessionidt sessionbyipv6(struct in6_addr ip)
 //
 // (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;
@@ -1233,14 +1255,11 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t)
                        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;
        }
@@ -1388,6 +1407,7 @@ static void update_session_out_stat(sessionidt s, sessiont *sp, int len)
 void processipout(uint8_t *buf, int len)
 {
        sessionidt s;
+       groupidt g;
        sessiont *sp;
        tunnelidt t;
        in_addr_t ip;
@@ -1424,7 +1444,31 @@ void processipout(uint8_t *buf, int len)
        }
 
        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.
@@ -2055,7 +2099,7 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e
                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++)
@@ -2208,7 +2252,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
@@ -2255,6 +2299,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
@@ -2272,7 +2318,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
 
-#ifdef LAC
        if (session[s].forwardtosession)
        {
                sessionidt sess = session[s].forwardtosession;
@@ -2282,9 +2327,14 @@ void sessionkill(sessionidt s, char *reason)
                        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);
 }
@@ -2388,7 +2438,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha
 }
 
 // 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;
@@ -2479,7 +2529,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                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;
                }
 
@@ -2529,6 +2579,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].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);
@@ -2800,7 +2851,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        }
                                        break;
                                case 13:    // Response
-#ifdef LAC
                                        if (tunnel[t].isremotelns)
                                        {
                                                chapresponse = calloc(17, 1);
@@ -2808,7 +2858,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                                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;
@@ -3054,7 +3103,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                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)
                                        {
@@ -3085,7 +3133,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        {
                                                tunnelshutdown(t, "Shutting down", 6, 0, 0);
                                        }
-#endif /* LAC */
                                        break;
                                case 3:       // SCCN
                                        LOG(3, s, t, "Received SCCN\n");
@@ -3163,7 +3210,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                                        }
                                        return;
                                case 11:      // ICRP
-#ifdef LAC
                                LOG(3, s, t, "Received ICRP\n");
                                if (session[s].forwardtosession)
                                {
@@ -3179,7 +3225,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");
                                }
-#endif /* LAC */
                                        break;
                                case 12:      // ICCN
                                        LOG(3, s, t, "Received ICCN\n");
@@ -3195,7 +3240,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
 
                                        // 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);
@@ -3253,12 +3298,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                        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)
@@ -3273,14 +3317,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *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..
-                               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;
                        }
 
@@ -3292,37 +3335,37 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                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)
@@ -3336,7 +3379,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)
                        {
-                               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;
                        }
 
@@ -3354,7 +3397,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.
-                               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;
                        }
 
@@ -3371,7 +3414,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)
                        {
-                               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;
                        }
 
@@ -3380,7 +3423,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
                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
@@ -3683,7 +3726,7 @@ static void regular_cleanups(double period)
 
                // 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];
 
@@ -3949,13 +3992,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
@@ -3967,7 +4005,7 @@ static int still_busy(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 *p = buf + 32; // for the hearder of the forwarded MPPP packet (see C_MPPP_FORWARD)
                                                // and the forwarded pppoe session
@@ -3982,13 +4020,8 @@ static void mainloop(void)
                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 */
        {
@@ -4013,10 +4046,6 @@ static void mainloop(void)
                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);
@@ -4029,12 +4058,6 @@ static void mainloop(void)
                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);
@@ -4042,6 +4065,14 @@ static void mainloop(void)
                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
@@ -4103,16 +4134,12 @@ static void mainloop(void)
                        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
@@ -4146,10 +4173,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;
-                               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
@@ -4190,8 +4214,8 @@ static void mainloop(void)
 
 #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 */
 
@@ -4210,7 +4234,6 @@ static void mainloop(void)
                                                                exit(1);
                                                        }
                                                        else
-
                                                                LOG(0, 0, 0, "Got a netlink error: %s\n", strerror(-errmsg->error));
                                                }
                                                // else it's a ack
@@ -4222,7 +4245,7 @@ static void mainloop(void)
                                }
 
                                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);
                                }
                        }
 
@@ -4232,40 +4255,25 @@ static void mainloop(void)
 
                        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)
                                {
@@ -4313,18 +4321,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)
                        {
-#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++;
                        }
@@ -4659,9 +4662,9 @@ static void initdata(int optdebug, char *optconfig)
        }
 #endif /* BGP */
 
-#ifdef LAC
        lac_initremotelnsdata();
-#endif
+
+       grp_initdata();
 }
 
 static int assign_ip_address(sessionidt s)
@@ -4948,11 +4951,7 @@ void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, ui
 
 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)
@@ -4978,7 +4977,7 @@ static int dump_session(FILE **f, sessiont *s)
                        "# 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);
        }
@@ -5132,7 +5131,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();
 
@@ -5158,6 +5176,9 @@ int main(int argc, char *argv[])
                        LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
        }
 
+       //LOG(3, 0, 0, "Debug sizeof struct: sessiont %lu, tunnelt %lu, bundlet %lu, groupsesst %lu\n",
+       //      sizeof(sessiont), sizeof(tunnelt), sizeof(bundlet), sizeof(groupsesst));
+
        mainloop();
 
        /* remove plugins (so cleanup code gets run) */
@@ -5364,14 +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);
@@ -5583,6 +5652,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
        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++)
@@ -5605,6 +5675,12 @@ int sessionsetup(sessionidt s, tunnelidt t)
                }
                else
                        cache_ipmap(session[s].ip, s);
+
+               if ((g = grp_groupbysession(s)))
+               {
+                       grp_setgrouproute(g, 1);
+                       cluster_send_groupe(g);
+               }
        }
 
        sess_local[s].lcp_authtype = 0; // RADIUS authentication complete
@@ -6429,8 +6505,6 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter)
        return 0;
 }
 
-#ifdef LAC
-
 tunnelidt lac_new_tunnel()
 {
        return new_tunnel();
@@ -6476,4 +6550,3 @@ void lac_tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *
        tunnelshutdown(t, reason, result, error, msg);
 }
 
-#endif
index 092c683..bce434f 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -24,6 +24,9 @@
 #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 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"
@@ -207,6 +217,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];
 
@@ -322,17 +333,41 @@ typedef struct
        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
-#else
-       uint8_t src_hwaddr[ETH_ALEN];   // MAC addr source (for pppoe sessions 6 bytes)
-       char reserved[6];               // Space to expand structure without changing HB_VERSION
-#endif
+       char reserved[4];                               // Space to expand structure without changing HB_VERSION
 }
 sessiont;
 
+typedef struct
+{
+       uint32_t tx_rate;
+       uint32_t prev_coutgrp;
+       sessionidt sid;
+       uint8_t weight;
+}
+groupsesslistt;
+
+typedef struct
+{
+       int state;                              // current state (groupestate enum)
+       uint32_t time_changed;
+       groupidt prev;
+       sessionidt smax;
+       sessionidt smin;
+       groupsesslistt 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)
@@ -449,12 +484,9 @@ typedef struct
        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;
 
@@ -518,6 +550,13 @@ enum
        BUNDLEUNDEF,            // Undefined
 };
 
+enum
+{
+       GROUPEFREE,             // Not in use
+       GROUPEOPEN,             // Active bundle
+       GROUPEUNDEF             // Undefined
+};
+
 enum
 {
        NULLCLASS = 0,          //End Point Discriminator classes
@@ -725,8 +764,10 @@ typedef struct
        int             cluster_undefined_sessions;     // How many sessions we're yet to receive from the master.
        int             cluster_undefined_bundles;      // How many bundles we're yet to receive from the master.
        int             cluster_undefined_tunnels;      // How many tunnels we're yet to receive from the master.
+       int             cluster_undefined_groupes;      // How many groupes we're yet to receive from the master.
        int             cluster_highest_sessionid;
        int             cluster_highest_bundleid;
+       int             cluster_highest_groupeid;
        int             cluster_highest_tunnelid;
        clockt          cluster_last_hb;                // Last time we saw a heartbeat from the master.
        int             cluster_last_hb_ver;            // Heartbeat version last seen from master
@@ -765,19 +806,24 @@ typedef struct
        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 };
@@ -913,9 +959,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);
@@ -935,7 +979,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);
@@ -943,13 +987,26 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter);
 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);
+int grp_cluster_load_groupe(groupidt g, groupsesst *new);
 
 #undef LOG
 #undef LOG_HEX
@@ -985,6 +1042,8 @@ extern bundlet *bundle;
 extern sessiont *session;
 extern sessionlocalt *sess_local;
 extern ippoolt *ip_address_pool;
+extern groupsesst *grpsession;
+extern groupidt gnextgrpid;
 #define sessionfree (session[0].next)
 
 
@@ -997,6 +1056,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 {
@@ -1009,13 +1070,10 @@ struct event_data {
                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 --git a/nsctl.c b/nsctl.c
index 59ad0c6..98785cd 100644 (file)
--- a/nsctl.c
+++ b/nsctl.c
@@ -7,6 +7,8 @@
 #include <string.h>
 #include <netdb.h>
 #include <signal.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #include "control.h"
diff --git a/ppp.c b/ppp.c
index bd70e4d..cb8512f 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -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"
@@ -12,9 +15,7 @@
 #include "tbf.h"
 #include "cluster.h"
 
-#ifdef LAC
 #include "l2tplac.h"
-#endif
 #include "pppoe.h"
 
 extern tunnelt *tunnel;
@@ -105,13 +106,11 @@ void processpap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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)))
        {
@@ -264,7 +263,6 @@ void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                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);
@@ -272,7 +270,6 @@ void processchap(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
                        // Creating a tunnel/session has been started
                        return;
                }
-#endif
 
                run_plugins(PLUGIN_PRE_AUTH, &packet);
                if (!packet.continue_auth)
diff --git a/pppoe.c b/pppoe.c
index 4b196ea..61b881b 100644 (file)
--- a/pppoe.c
+++ b/pppoe.c
@@ -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"
@@ -902,7 +903,6 @@ void process_pppoe_disc(uint8_t *pack, int size)
        }
 }
 
-#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)
 {
@@ -1036,7 +1036,6 @@ void pppoe_forwardto_session_pppoe(uint8_t *pack, int size, sessionidt sess, uin
 
        tunnelsend(p, lpppoe, t); // send it....
 }
-#endif
 
 void process_pppoe_sess(uint8_t *pack, int size)
 {
@@ -1101,13 +1100,11 @@ 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)
        {
index 34806c5..266f100 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -11,6 +11,7 @@
 #include <ctype.h>
 #include <netinet/in.h>
 #include <errno.h>
+#include <linux/rtnetlink.h>
 
 #include "md5.h"
 #include "constants.h"
@@ -19,9 +20,7 @@
 #include "util.h"
 #include "cluster.h"
 
-#ifdef LAC
 #include "l2tplac.h"
-#endif
 #include "pppoe.h"
 
 extern radiust *radius;
@@ -537,9 +536,7 @@ void processrad(uint8_t *buf, int len, char socket_index)
        uint8_t routes = 0;
        int r_code;
        int r_id;
-#ifdef LAC
        int OpentunnelReq = 0;
-#endif
 
        CSTAT(processrad);
 
@@ -641,11 +638,10 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                // 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)
@@ -668,11 +664,19 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                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;
                                                }
                                        }
@@ -840,7 +844,6 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                        session[s].classlen = MAXCLASS;
                                                memcpy(session[s].class, p + 2, session[s].classlen);
                                        }
-#ifdef LAC
                                        else if (*p == 64)
                                        {
                                                // Tunnel-Type
@@ -929,7 +932,6 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                // Fill context
                                                lac_set_rad_tag_tunnel_assignment_id(tag, (char *) strtemp);
                                        }
-#endif
                                }
                        }
                        else if (r_code == AccessReject)
@@ -939,7 +941,6 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                break;
                        }
 
-#ifdef LAC
                        if ((!config->disable_lac_func) && OpentunnelReq)
                        {
                                char assignment_id[256];
@@ -996,7 +997,6 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                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)
                        {
@@ -1341,7 +1341,6 @@ void processdae(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struc
                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.
@@ -1432,4 +1431,3 @@ int rad_tunnel_pwdecode(uint8_t *pl2tpsecret, size_t *pl2tpsecretlen,
 
        return decodedlen;
 };
-#endif /* LAC */
index 805b794..a8c11bd 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 #include "control.h"
index 52fcf4a..f987378 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 
index d538a23..97d4bd0 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 #include "control.h"
index 877617e..c251dba 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 
diff --git a/tbf.c b/tbf.c
index c6273ce..8b488d6 100644 (file)
--- a/tbf.c
+++ b/tbf.c
@@ -1,6 +1,9 @@
 // L2TPNS: token bucket filters
 
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "util.h"
 #include "tbf.h"
index adcff27..bcc0478 100644 (file)
@@ -1,4 +1,7 @@
 #include <string.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+
 #include "l2tpns.h"
 #include "plugin.h"
 #include "control.h"
diff --git a/util.c b/util.c
index d4dbd4f..e412fc1 100644 (file)
--- a/util.c
+++ b/util.c
@@ -8,6 +8,7 @@
 #include <arpa/inet.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <linux/rtnetlink.h>
 
 #include "l2tpns.h"
 #ifdef BGP
@@ -51,100 +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,