Merge from master 2.2.1-2sames3.12
authorfendo <fendo@bi12info.com>
Mon, 7 Oct 2013 20:49:53 +0000 (22:49 +0200)
committerfendo <fendo@bi12info.com>
Mon, 7 Oct 2013 20:49:53 +0000 (22:49 +0200)
1  2 
Makefile
cli.c
cluster.c
cluster.h
debian/changelog
garden.c
grpsess.c
l2tpns.c
l2tpns.h
tbf.c

diff --combined Makefile
+++ b/Makefile
@@@ -18,15 -18,15 +18,15 @@@ CC = gc
  LD = gcc
  INCLUDES = -I.
  CPPFLAGS = $(INCLUDES) $(DEFINES)
- CFLAGS = -Wall -Wformat-security -Wno-format-zero-length $(OPTIM)
+ CFLAGS = -Wall -Wformat-security $(OPTIM)
  LDFLAGS =
  LDLIBS =
  INSTALL = install -c -D -o root -g root
  
- l2tpns.LIBS = -lm -lcli -ldl
+ l2tpns.LIBS = -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 l2tplac.o
 +    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o grpsess.o
  
  PROGRAMS = l2tpns nsctl
  PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
@@@ -127,7 -127,6 +127,7 @@@ tbf.o: tbf.c l2tpns.h util.h tbf.
  util.o: util.c l2tpns.h bgp.h
  pppoe.o: pppoe.c l2tpns.h cluster.h constants.h md5.h util.h
  l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h
 +grpsess.o: grpsess.c l2tpns.h util.h 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
diff --combined cli.c
--- 1/cli.c
--- 2/cli.c
+++ b/cli.c
@@@ -21,7 -21,6 +21,7 @@@
  #include <dlfcn.h>
  #include <netdb.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #include "constants.h"
@@@ -75,61 -74,60 +75,61 @@@ 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);
- static int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_set(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc);
- 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);
- 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);
+ static int cmd_show_session(struct cli_def *cli, const char *command, char **argv, int argc);
++static int cmd_show_group(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_tunnels(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_users(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_radius(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_version(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_pool(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_run(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_banana(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_plugins(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_throttle(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_write_memory(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_drop_user(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_drop_tunnel(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_drop_session(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_snoop(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_snoop(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_throttle(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_throttle(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_debug(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_debug(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_set(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_load_plugin(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_remove_plugin(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_uptime(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_shutdown(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_reload(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_setforward(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_rmtlnsconf(struct cli_def *cli, const char *command, char **argv, int argc);
  
  static int regular_stuff(struct cli_def *cli);
  
  #ifdef STATISTICS
- static int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc);
+ static int cmd_show_counters(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_clear_counters(struct cli_def *cli, const char *command, char **argv, int argc);
  #endif /* STATISTICS */
  
  #ifdef BGP
  #define MODE_CONFIG_BGP 8
- static int cmd_router_bgp(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_router_bgp_no_neighbour(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc);
+ static int cmd_router_bgp(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_router_bgp_neighbour(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_router_bgp_no_neighbour(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_bgp(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_restart_bgp(struct cli_def *cli, const char *command, char **argv, int argc);
  #endif /* BGP */
  
  #define MODE_CONFIG_NACL 9
- static int cmd_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_ip_access_list_rule(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_filter(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_no_filter(struct cli_def *cli, char *command, char **argv, int argc);
- static int cmd_show_access_list(struct cli_def *cli, char *command, char **argv, int argc);
+ static int cmd_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_ip_access_list_rule(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_filter(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_no_filter(struct cli_def *cli, const char *command, char **argv, int argc);
+ static int cmd_show_access_list(struct cli_def *cli, const char *command, char **argv, int argc);
  
  /* match if b is a substr of a */
  #define MATCH(a,b) (!strncmp((a), (b), strlen(b)))
@@@ -156,7 -154,6 +156,7 @@@ void init_cli(
        cli_register_command(cli, c, "running-config", cmd_show_run, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show the currently running configuration");
        cli_register_command(cli, c, "remotelns-conf", cmd_show_rmtlnsconf, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Show a list of remote LNS configuration");
        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");
@@@ -357,7 -354,7 +357,7 @@@ void cli_do(int sockfd
        exit(0);
  }
  
- static void cli_print_log(struct cli_def *cli, char *string)
+ static void cli_print_log(struct cli_def *cli, const char *string)
  {
        LOG(3, 0, 0, "%s\n", string);
  }
@@@ -406,7 -403,7 +406,7 @@@ int cli_arg_help(struct cli_def *cli, i
        return CLI_OK;
  }
  
- static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_session(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_tunnels(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i, x, show_all = 0;
        char *states[] = {
        return CLI_OK;
  }
  
- static int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_users(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        char sid[32][8];
        char *sargv[32];
  }
  
  #ifdef STATISTICS
- static int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_counters(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
                        GET_STAT(tun_tx_bytes),
                        GET_STAT(tun_tx_packets),
                        GET_STAT(tun_tx_errors));
-       cli_print(cli, "");
+       cli_print(cli, " ");
  
        cli_print(cli, "%-10s %10s %10s %10s %10s", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
        cli_print(cli, "%-10s %10u %10u %10u", "RX",
                        GET_STAT(tunnel_tx_packets),
                        GET_STAT(tunnel_tx_errors),
                        GET_STAT(tunnel_retries));
-       cli_print(cli, "");
+       cli_print(cli, " ");
  
        cli_print(cli, "%-30s%-10s", "Counter", "Value");
        cli_print(cli, "-----------------------------------------");
                char *p = strchr(t, '\n');
                if (p) *p = 0;
  
-               cli_print(cli, "");
+               cli_print(cli, " ");
                cli_print(cli, "Last counter reset %s", t);
        }
  
        return CLI_OK;
  }
  
- static int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_clear_counters(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
  }
  #endif /* STATISTICS */
  
- static int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_version(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
        return CLI_OK;
  }
  
- static int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_pool(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        int used = 0, free = 0, show_all = 0;
  }
  
  static FILE *save_config_fh = 0;
- static void print_save_config(struct cli_def *cli, char *string)
+ static void print_save_config(struct cli_def *cli, const char *string)
  {
        if (save_config_fh)
                fprintf(save_config_fh, "%s\n", string);
  }
  
- static int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_write_memory(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
  
  static char const *show_access_list_rule(int extended, ip_filter_rulet *rule);
  
- static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_run(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        char ipv6addr[INET6_ADDRSTRLEN];
        return CLI_OK;
  }
  
- static int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_radius(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i, free = 0, used = 0, show_all = 0;
        char *states[] = {
        return CLI_OK;
  }
  
- static int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_plugins(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_show_throttle(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_throttle(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_banana(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
        return CLI_OK;
  }
  
- static int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_drop_user(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        sessionidt s;
        return CLI_OK;
  }
  
- static int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_drop_tunnel(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        tunnelidt t;
        return CLI_OK;
  }
  
- static int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_drop_session(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        sessionidt s;
        return CLI_OK;
  }
  
- static int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_snoop(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        in_addr_t ip;
        uint16_t port;
        return CLI_OK;
  }
  
- static int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_snoop(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        sessionidt s;
        return CLI_OK;
  }
  
- static int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_throttle(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int rate_in = 0;
        int rate_out = 0;
        return CLI_OK;
  }
  
- static int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_throttle(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        sessionidt s;
        return CLI_OK;
  }
  
- static int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_debug(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_debug(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_load_plugin(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i, firstfree = 0;
  
        return CLI_OK;
  }
  
- static int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_remove_plugin(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
@@@ -1784,7 -1781,7 +1784,7 @@@ static char *duration(time_t secs
        return buf;
  }
  
- static int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_uptime(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        FILE *fh;
        char buf[100], *p = buf, *loads[3];
                return CLI_HELP_NO_ARGS;
  
        fh = fopen("/proc/loadavg", "r");
-       fgets(buf, 100, fh);
+       p = fgets(buf, 100, fh);
        fclose(fh);
  
        for (i = 0; i < 3; i++)
        return CLI_OK;
  }
  
- static int cmd_set(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_set(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
@@@ -1949,7 -1946,7 +1949,7 @@@ int regular_stuff(struct cli_def *cli
  }
  
  #ifdef BGP
- static int cmd_router_bgp(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_router_bgp(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int as;
  
@@@ -2015,7 -2012,7 +2015,7 @@@ static int find_bgp_neighbour(char cons
        return new;
  }
  
- static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_router_bgp_neighbour(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        int keepalive;
        return CLI_OK;
  }
  
- static int cmd_router_bgp_no_neighbour(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_router_bgp_no_neighbour(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_show_bgp(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_bgp(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        int hdr = 0;
  
                if (!hdr++)
                {
-                       cli_print(cli, "");
+                       cli_print(cli, " ");
                        cli_print(cli, "Peer                  AS         Address "
                            "State       Retries Retry in Route Pend    Timers");
                        cli_print(cli, "------------------ ----- --------------- "
        return CLI_OK;
  }
  
- static int cmd_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        char *addr;
        return CLI_OK;
  }
  
- static int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_suspend_bgp(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        char *addr;
        return CLI_OK;
  }
  
- static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_restart_bgp(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        char *addr;
@@@ -2441,12 -2438,12 +2441,12 @@@ static int access_list(struct cli_def *
        return CLI_OK;
  }
  
- static int cmd_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        return access_list(cli, argv, argc, 1);
  }
  
- static int cmd_no_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_ip_access_list(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        return access_list(cli, argv, argc, 0);
  }
@@@ -2532,7 -2529,7 +2532,7 @@@ static char const *show_access_list_rul
        return buf;
  }
  
- static ip_filter_rulet *access_list_rule_ext(struct cli_def *cli, char *command, char **argv, int argc)
+ static ip_filter_rulet *access_list_rule_ext(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        static ip_filter_rulet rule;
        struct in_addr addr;
        return &rule;
  }
  
- static ip_filter_rulet *access_list_rule_std(struct cli_def *cli, char *command, char **argv, int argc)
+ static ip_filter_rulet *access_list_rule_std(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        static ip_filter_rulet rule;
        struct in_addr addr;
        return &rule;
  }
  
- static int cmd_ip_access_list_rule(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_ip_access_list_rule(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        ip_filter_rulet *rule = ip_filters[filt].extended
        return CLI_OK;
  }
  
- static int cmd_filter(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_filter(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        sessionidt s;
        int i;
        return CLI_OK;
  }
  
- static int cmd_no_filter(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_no_filter(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        sessionidt s;
        return CLI_OK;
  }
  
- static int cmd_show_access_list(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_access_list(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
        return CLI_OK;
  }
  
- static int cmd_shutdown(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_shutdown(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
        return CLI_OK;
  }
  
- static int cmd_reload(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_reload(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
        return CLI_OK;
  }
  
- static int cmd_show_group(struct cli_def *cli, char *command, char **argv, int argc)
++static int cmd_show_group(struct cli_def *cli, const 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)
+ static int cmd_setforward(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int ret;
  
        return CLI_OK;
  }
  
- static int cmd_show_rmtlnsconf(struct cli_def *cli, char *command, char **argv, int argc)
+ static int cmd_show_rmtlnsconf(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        confrlnsidt idrlns;
        char strdisp[1024];
diff --combined cluster.c
+++ b/cluster.c
@@@ -16,7 -16,6 +16,7 @@@
  #include <malloc.h>
  #include <errno.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
  #include "l2tpns.h"
  #include "cluster.h"
@@@ -44,7 -43,6 +44,7 @@@ in_addr_t my_address = 0;             // The netwo
  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 :)
@@@ -89,7 -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;
@@@ -231,8 -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;
@@@ -457,8 -453,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));
@@@ -680,20 -675,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.
        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
  // 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;
  
        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
                        ++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 (grpsession[i].state == GROUPEUNDEF)
 +                      ++config->cluster_undefined_groupes;
 +      }
  
 -      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 (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;
        }
  
@@@ -900,27 -866,6 +900,27 @@@ static int hb_add_type(uint8_t **p, in
                        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];
  //
  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;
        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;
  
                //
                // 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.
                ++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?
        }
  
        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;
  
@@@ -1145,18 -1069,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)
  {
@@@ -1421,31 -1333,6 +1421,31 @@@ static int cluster_recv_bundle(int more
          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) {
@@@ -1742,10 -1629,9 +1742,10 @@@ static int cluster_process_heartbeat(ui
        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)
        {
                                  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!!
@@@ -2098,7 -1954,7 +2098,7 @@@ shortpacket
  
  //====================================================================================================
  
- int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc)
+ int cmd_show_cluster(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
  
                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);
        }
diff --combined cluster.h
+++ b/cluster.h
@@@ -25,9 -25,6 +25,9 @@@
  #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.
 +
  
  #define HB_VERSION            7       // Protocol version number..
  #define HB_MAX_SEQ            (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
@@@ -61,10 -58,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 */
@@@ -87,7 -81,6 +87,7 @@@ 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, uint16_t port, uint16_t indexudp);
  int master_forward_dae_packet(uint8_t *data, int size, in_addr_t addr, int port);
@@@ -99,7 -92,7 +99,7 @@@ void cluster_send_ping(time_t basetime)
  void cluster_heartbeat(void);
  void cluster_check_master(void);
  void cluster_check_slaves(void);
- int cmd_show_cluster(struct cli_def *cli, char *command, char **argv, int argc);
+ int cmd_show_cluster(struct cli_def *cli, const char *command, char **argv, int argc);
  int master_forward_pppoe_packet(uint8_t *data, int size, uint8_t codepad);
  
  #endif /* __CLUSTER_H__ */
diff --combined debian/changelog
@@@ -1,49 -1,54 +1,67 @@@
 -l2tpns (2.2.1-2fdn3.11) UNRELEASED; urgency=low
++l2tpns (2.2.1-2sames3.12) UNRELEASED; urgency=low
+   * Fix: throttle ipv6 out.
+   * Fix: remove old IPV6 routes on slave
+   * Fix: compiling Warning, dpkg-buildflags ...
+   * Enabled hardened build flags, thanks Moritz Muehlenhoff (closes: #657846)
+   * Packaging updates
+   * Move to 3.0 (native) source format
+   * Bump DH compat level to 8
+   * Fix ordering of stdio.h/syslog.h includes (closes: #707385)
+   * Create accounting_dir in init script if necessary (closes: #418156)
+   * Bump Standards-Version to 3.9.4.0
+   * Add build-arch/build-indep targets to debian/rules
+   * Fix: compiling Warning
++  * improved load balancing algorithm.
 - -- Fernando Alves <fernando.alves@sameswireless.fr>  Sun, 06 Oct 2013 10:38:53 +0200
++ -- Fernando Alves <fernando.alves@sameswireless.fr>  Sun, 07 Oct 2013 22:20:53 +0200
 -l2tpns (2.2.1-2fdn3.10) unstable; urgency=low
 +l2tpns (2.2.1-2sames3.11) unstable; urgency=low
 +
 +  * improved load balancing algorithm.
 +
 + -- Fernando Alves <fernando.alves@sameswireless.fr>  Fri, 02 Aug 2013 11:32:49 +0200
 +
 +l2tpns (2.2.1-2sames3.10) unstable; urgency=low
  
    * Fix: authentication success was sent 2 times.
 +  * Fix: session mismatch on group.
  
 - -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 04 Jun 2013 11:38:04 +0200
 + -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 04 Jun 2013 14:36:37 +0200
  
 -l2tpns (2.2.1-2fdn3.9) unstable; urgency=low
 +l2tpns (2.2.1-2sames3.9) unstable; urgency=low
  
    * Adding the possibility to set multiple hostname.
  
 - -- Fernando Alves <fernando.alves@sameswireless.fr>  Thu, 23 May 2013 23:58:23 +0200
 + -- Fernando Alves <fernando.alves@sameswireless.fr>  Wed, 22 May 2013 22:25:59 +0200
  
 -l2tpns (2.2.1-2fdn3.8) unstable; urgency=low
 +l2tpns (2.2.1-2sames3.8) unstable; urgency=low
  
 -  * Fix: send SCCCN requested challenge response.
 +  * Fix send SCCCN requested challenge response.
    * add accounting parameter account_all_origin.
    * Fix service_name management and add pppoe_only_equal_svc_name parameter.
 +  * Fix cluster group update.
 +  * Fix possible IPv6 spoofing.
 +  * manage groupes in cluster mode.
 +  * Adding the possibility to listening multiple IP L2TP tunnels.
 +  * Add cli show group and update rate calculation.
  
 - -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 30 Apr 2013 16:02:33 +0200
 -
 -l2tpns (2.2.1-2fdn3.7) unstable; urgency=low
 -
 -  * Adding the possibility to listening multiple IP L2TP Tunnels
 -  * Removing LAC flag.
 + -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 30 Apr 2013 19:22:11 +0200
  
 - -- Fernando Alves <fernando.alves@sameswireless.fr>  Thu, 28 Mar 2013 10:50:00 +0100
 -
 -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 --combined garden.c
+++ b/garden.c
@@@ -3,9 -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"
@@@ -160,7 -157,7 +160,7 @@@ int plugin_become_master(void
      for (i = 0; up_commands[i] && *up_commands[i]; i++)
      {
        f->log(3, 0, 0, "Running %s\n", up_commands[i]);
-       system(up_commands[i]);
+       if (-1 == system(up_commands[i])) f->log(0, 0, 0, "error command %s\n", up_commands[i]);
      }
  
      return PLUGIN_RET_OK;
@@@ -179,6 -176,7 +179,7 @@@ int garden_session(sessiont *s, int fla
  {
      char cmd[2048];
      sessionidt sess;
+       int status;
  
      if (!s) return 0;
      if (!s->opened) return 0;
            f->fmtaddr(htonl(s->ip), 0));
  
        f->log(3, sess, s->tunnel, "%s\n", cmd);
-       system(cmd);
+       status = system(cmd);
        s->walled_garden = 1;
      }
      else
        f->log(3, sess, s->tunnel, "%s\n", cmd);
        while (--count)
        {
-           int status = system(cmd);
+           status = system(cmd);
            if (WEXITSTATUS(status) != 0) break;
        }
  
@@@ -275,7 -273,7 +276,7 @@@ int plugin_init(struct pluginfuncs *fun
        for (i = 0; down_commands[i] && *down_commands[i]; i++)
        {
            f->log(3, 0, 0, "Running %s\n", down_commands[i]);
-           system(down_commands[i]);
+           if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", down_commands[i]);
        }
      }
  
@@@ -292,7 -290,7 +293,7 @@@ void plugin_done(
      for (i = 0; down_commands[i] && *down_commands[i]; i++)
      {
        f->log(3, 0, 0, "Running %s\n", down_commands[i]);
-       system(down_commands[i]);
+       if (-1 == system(down_commands[i])) f->log(0, 0, 0, "error command %s\n", down_commands[i]);
      }
  }
  
diff --combined grpsess.c
index ccaa27c,0000000..d3fae30
mode 100644,000000..100644
--- /dev/null
+++ b/grpsess.c
@@@ -1,709 -1,0 +1,709 @@@
-       uint32_t txrate;
 +/*
 + * 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;
 +
 +typedef struct
 +{
 +      sessionidt sid_loaddist[0x10000];
 +}
 +local_group;
 +
 +local_group *grp_local = NULL;                // Array of local_group structures.
 +
 +// 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;
 +      }
 +
 +      if (!(grp_local = shared_malloc(sizeof(local_group) * MAXGROUPE)))
 +      {
 +              LOG(0, 0, 0, "Error doing malloc for grp_local: %s\n", strerror(errno));
 +              exit(1);
 +      }
 +      memset(grp_local, 0, sizeof(grp_local[0]) * MAXGROUPE);
 +
 +}
 +
 +// 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));
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +uint16_t guint16_index_loadlist;
 +// return the next session can be used on the group
 +sessionidt grp_getnextsession(groupidt g, in_addr_t ip, in_addr_t ip_src)
 +{
 +      sessionidt s = 0, s2 = 0, s3 = 0;
 +      int i;
 +      uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0;
++      uint32_t txrate = 0;
 +
 +      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;
 +
 +                              txrate = (txrate + (coutgrp_delta/ltime_changed)) >> 1;
 +                              grpsession[g].sesslist[i].tx_rate = txrate;
 +
 +                              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)))
 +      {
 +              uint8_t *as = (uint8_t *) &ip_src;
 +              uint8_t *ad = (uint8_t *) &ip;
 +              uint16_t ai = ad[3];
 +              ai <<= 8;
 +              ai |= as[3];
 +
 +              s = grp_local[g].sid_loaddist[ai];
 +              if (!s)
 +              {
 +                      s = grpsession[g].smin;
 +                      grp_local[g].sid_loaddist[ai] = s;
 +              }
 +
 +              if (g != grp_groupbysession(s))
 +              {
 +                      // This session does not belong to this group
 +                      LOG(3, s, session[s].tunnel, "Warning, the session does not belong to group %d\n", g);
 +                      s = 0;
 +                      grp_local[g].sid_loaddist[ai] = 0;
 +              }
 +              else if ( (session[s].ppp.phase > Establish) &&
 +                       (time_now - session[s].last_packet <= (config->echo_timeout + 1)) )
 +              {
 +                      grp_local[g].sid_loaddist[guint16_index_loadlist++] = 0;
 +                      return s;
 +              }
 +              else
 +              {
 +                      s = 0;
 +                      grp_local[g].sid_loaddist[ai] = 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 --combined l2tpns.c
+++ b/l2tpns.c
@@@ -10,6 -10,7 +10,7 @@@
  #include <fcntl.h>
  #include <linux/if_tun.h>
  #define SYSLOG_NAMES
+ #include <stdio.h>
  #include <syslog.h>
  #include <malloc.h>
  #include <net/route.h>
@@@ -19,7 -20,6 +20,6 @@@
  #include <netinet/ip6.h>
  #include <stdarg.h>
  #include <stdlib.h>
- #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  #include <sys/ioctl.h>
@@@ -75,7 -75,7 +75,7 @@@ int cluster_sockfd = -1;      // Intra-clust
  int epollfd = -1;             // event polling
  time_t basetime = 0;          // base clock
  char hostname[MAXHOSTNAME] = "";      // us.
 -static int tunidx;            // ifr_ifindex of tun device
 +int tunidx;                           // ifr_ifindex of tun device
  int nlseqnum = 0;             // netlink sequence number
  int min_initok_nlseqnum = 0;  // minimun seq number for messages after init is ok
  static int syslog_log = 0;    // are we logging to syslog
@@@ -92,7 -92,10 +92,7 @@@ uint16_t MSS = 0;            // TCP MS
  struct cli_session_actions *cli_session_actions = NULL;       // Pending session changes requested by CLI
  struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI
  
 -union iphash {
 -      sessionidt sess;
 -      union iphash *idx;
 -} ip_hash[256];                       // Mapping from IP address to session structures.
 +union iphash ip_hash[256];    // Mapping from IP address to session structures.
  
  struct ipv6radix {
        sessionidt sess;
@@@ -183,7 -186,6 +183,7 @@@ config_descriptt config_values[] = 
        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),
        CONFIG("pppoe_only_equal_svc_name", pppoe_only_equal_svc_name, BOOL),
        CONFIG("multi_hostname", multi_hostname, STRING),
        { NULL, 0, 0, 0 }
@@@ -214,7 -216,6 +214,7 @@@ tunnelt *tunnel = NULL;                    // Array of t
  bundlet *bundle = NULL;                       // Array of bundle structures.
  fragmentationt *frag = NULL;          // Array of fragmentation structures.
  sessiont *session = NULL;             // Array of session structures.
 +groupsesst *grpsession = NULL;                // Array of groupsesst structures.
  sessionlocalt *sess_local = NULL;     // Array of local per-session counters.
  radiust *radius = NULL;                       // Array of radius structures.
  ippoolt *ip_address_pool = NULL;      // Array of dynamic IP addresses.
@@@ -225,6 -226,9 +225,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);
@@@ -253,9 -257,8 +253,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
@@@ -614,7 -617,7 +614,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;
@@@ -650,7 -653,7 +650,7 @@@ static ssize_t netlink_recv(void *buf, 
  }
  
  /* adapted from iproute2 */
 -static void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
 +void netlink_addattr(struct nlmsghdr *nh, int type, const void *data, int alen)
  {
        int len = RTA_LENGTH(alen);
        struct rtattr *rta;
@@@ -1010,8 -1013,10 +1010,10 @@@ sessionidt sessionbyipv6(struct in6_add
                 ip.s6_addr[1] == 0x80 &&
                 ip.s6_addr16[1] == 0 &&
                 ip.s6_addr16[2] == 0 &&
-                ip.s6_addr16[3] == 0)) {
-               s = lookup_ipmap(*(in_addr_t *) &ip.s6_addr[8]);
+                ip.s6_addr16[3] == 0))
+       {
+               in_addr_t *pipv4 = (in_addr_t *) &ip.s6_addr[8];
+               s = lookup_ipmap(*pipv4);
        } else {
                s = lookup_ipv6map(ip);
        }
  //
  // (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;
@@@ -1097,7 -1102,7 +1099,7 @@@ static void cache_ipv6map(struct in6_ad
  //
  // CLI list to dump current ipcache.
  //
- int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc)
+ int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        union iphash *d = ip_hash, *e, *f, *g;
        int i, j, k, l;
@@@ -1410,10 -1415,9 +1412,10 @@@ static void update_session_out_stat(ses
  void processipout(uint8_t *buf, int len)
  {
        sessionidt s;
 +      groupidt g;
        sessiont *sp;
        tunnelidt t;
 -      in_addr_t ip;
 +      in_addr_t ip, ip_src;
  
        uint8_t *data = buf;    // Keep a copy of the originals.
        int size = len;
                return;
        }
  
 +      ip_src = *(uint32_t *)(buf + 12);
        ip = *(uint32_t *)(buf + 16);
 -      if (!(s = sessionbyip(ip)))
 +      if ((g = grp_groupbyip(ip)))
 +      {
 +              s = grp_getnextsession(g, ip, ip_src);
 +              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.
@@@ -1822,6 -1801,8 +1824,8 @@@ static void send_ipout(sessionidt s, ui
  {
        sessiont *sp;
        tunnelidt t;
+       uint8_t *p;
+       uint8_t *data = buf;    // Keep a copy of the originals.
  
        uint8_t b[MAXETHER + 20];
  
        LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len);
  
        // Add on L2TP header
-       {
-               uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0);
-               if (!p) return;
-               tunnelsend(b, len + (p-b), t); // send it...
-       }
+       if (*(uint16_t *) (data + 2) == htons(PKTIPV6))
+               p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); // IPV6
+       else
+               p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0); // IPV4
+       if (!p) return;
+       tunnelsend(b, len + (p-b), t); // send it...
  
        // Snooping this session.
        if (sp->snoop_ip && sp->snoop_port)
  static void control16(controlt * c, uint16_t avp, uint16_t val, uint8_t m)
  {
        uint16_t l = (m ? 0x8008 : 0x0008);
-       *(uint16_t *) (c->buf + c->length + 0) = htons(l);
-       *(uint16_t *) (c->buf + c->length + 2) = htons(0);
-       *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
-       *(uint16_t *) (c->buf + c->length + 6) = htons(val);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       pint16[3] = htons(val);
        c->length += 8;
  }
  
  static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m)
  {
        uint16_t l = (m ? 0x800A : 0x000A);
-       *(uint16_t *) (c->buf + c->length + 0) = htons(l);
-       *(uint16_t *) (c->buf + c->length + 2) = htons(0);
-       *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
-       *(uint32_t *) (c->buf + c->length + 6) = htonl(val);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       uint32_t *pint32 = (uint32_t *) (c->buf + c->length + 6);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
+       pint32[0] = htonl(val);
        c->length += 10;
  }
  
  static void controls(controlt * c, uint16_t avp, char *val, uint8_t m)
  {
        uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6);
-       *(uint16_t *) (c->buf + c->length + 0) = htons(l);
-       *(uint16_t *) (c->buf + c->length + 2) = htons(0);
-       *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
        memcpy(c->buf + c->length + 6, val, strlen(val));
        c->length += 6 + strlen(val);
  }
  static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m)
  {
        uint16_t l = ((m ? 0x8000 : 0) + len + 6);
-       *(uint16_t *) (c->buf + c->length + 0) = htons(l);
-       *(uint16_t *) (c->buf + c->length + 2) = htons(0);
-       *(uint16_t *) (c->buf + c->length + 4) = htons(avp);
+       uint16_t *pint16 = (uint16_t *) (c->buf + c->length + 0);
+       pint16[0] = htons(l);
+       pint16[1] = htons(0);
+       pint16[2] = htons(avp);
        memcpy(c->buf + c->length + 6, val, len);
        c->length += 6 + len;
  }
@@@ -1947,10 -1936,11 +1959,11 @@@ static void controlnull(tunnelidt t
  // add a control message to a tunnel, and send if within window
  static void controladd(controlt *c, sessionidt far, tunnelidt t)
  {
-       *(uint16_t *) (c->buf + 2) = htons(c->length); // length
-       *(uint16_t *) (c->buf + 4) = htons(tunnel[t].far); // tunnel
-       *(uint16_t *) (c->buf + 6) = htons(far); // session
-       *(uint16_t *) (c->buf + 8) = htons(tunnel[t].ns); // sequence
+       uint16_t *pint16 = (uint16_t *) (c->buf + 2);
+       pint16[0] = htons(c->length); // length
+       pint16[1] = htons(tunnel[t].far); // tunnel
+       pint16[2] = htons(far); // session
+       pint16[3] = htons(tunnel[t].ns); // sequence
        tunnel[t].ns++;              // advance sequence
        // link in message in to queue
        if (tunnel[t].controlc)
@@@ -2103,7 -2093,7 +2116,7 @@@ void sessionshutdown(sessionidt s, cha
                session[s].die = TIME + 150; // Clean up in 15 seconds
  
        if (session[s].ip)
 -      {                          // IP allocated, clear and unroute
 +      {       // IP allocated, clear and unroute
                int r;
                int routed = 0;
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
@@@ -2303,8 -2293,6 +2316,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
        }
  
        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);
  }
@@@ -3736,7 -3718,7 +3749,7 @@@ static void regular_cleanups(double per
  
                // No data in ECHO_TIMEOUT seconds, send LCP ECHO
                if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= config->echo_timeout) &&
 -                      (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT))
 +                      (time_now - sess_local[s].last_echo >= config->echo_timeout))
                {
                        uint8_t b[MAXETHER];
  
@@@ -4673,8 -4655,6 +4686,8 @@@ static void initdata(int optdebug, cha
  #endif /* BGP */
  
        lac_initremotelnsdata();
 +
 +      grp_initdata();
  }
  
  static int assign_ip_address(sessionidt s)
@@@ -5075,9 -5055,9 +5088,9 @@@ int main(int argc, char *argv[]
                case 'd':
                        if (fork()) exit(0);
                        setsid();
-                       freopen("/dev/null", "r", stdin);
-                       freopen("/dev/null", "w", stdout);
-                       freopen("/dev/null", "w", stderr);
+                       if(!freopen("/dev/null", "r", stdin)) LOG(0, 0, 0, "Error freopen stdin: %s\n", strerror(errno));
+                       if(!freopen("/dev/null", "w", stdout)) LOG(0, 0, 0, "Error freopen stdout: %s\n", strerror(errno));
+                       if(!freopen("/dev/null", "w", stderr)) LOG(0, 0, 0, "Error freopen stderr: %s\n", strerror(errno));
                        break;
                case 'v':
                        optdebug++;
                        LOG(0, 0, 0, "Can't set ulimit: %s\n", strerror(errno));
  
                // Make core dumps go to /tmp
-               chdir("/tmp");
+               if(chdir("/tmp")) LOG(0, 0, 0, "Error chdir /tmp: %s\n", strerror(errno));
        }
  
        if (config->scheduler_fifo)
                        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) */
@@@ -5729,7 -5706,6 +5742,7 @@@ int sessionsetup(sessionidt s, tunnelid
        if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1))
        {
                int routed = 0;
 +              groupidt g;
  
                // Add the route for this session.
                for (r = 0; r < MAXROUTE && session[s].route[r].ip; r++)
                }
                else
                        cache_ipmap(session[s].ip, s);
 +
 +              if ((g = grp_groupbysession(s)))
 +              {
 +                      grp_setgrouproute(g, 1);
 +                      cluster_send_groupe(g);
 +              }
        }
  
        sess_local[s].lcp_authtype = 0; // RADIUS authentication complete
@@@ -5844,6 -5814,10 +5857,10 @@@ int load_session(sessionidt s, session
                                uncache_ipmap(session[s].ip);
                }
  
+               // remove old IPV6 routes...
+               if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen)
+                       route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
                routed = 0;
  
                // add new routes...
@@@ -6318,7 -6292,7 +6335,7 @@@ void become_master(void
        }
  }
  
- int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc)
+ int cmd_show_hist_idle(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int s, i;
        int count = 0;
        return CLI_OK;
  }
  
- int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc)
+ int cmd_show_hist_open(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int s, i;
        int count = 0;
diff --combined l2tpns.h
+++ b/l2tpns.h
@@@ -24,9 -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
@@@ -219,7 -216,6 +219,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];
  
@@@ -337,39 -333,10 +337,39 @@@ typedef struc
        struct in6_addr ipv6route;      // Static IPv6 route
        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
 +      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)
@@@ -552,13 -519,6 +552,13 @@@ enu
        BUNDLEUNDEF,            // Undefined
  };
  
 +enum
 +{
 +      GROUPEFREE,             // Not in use
 +      GROUPEOPEN,             // Active bundle
 +      GROUPEUNDEF             // Undefined
 +};
 +
  enum
  {
        NULLCLASS = 0,          //End Point Discriminator classes
@@@ -767,10 -727,8 +767,10 @@@ typedef struc
        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
        int nbmultiaddress; // number multi address to bind
        int indexlacudpfd;      // Index UDP LAC file handle (in udpfd[])
        int nbmultihostname;    // number hostname, normally the same number as the nbudpfd
 +      int grp_txrate_average_time; // caculation txrate average time (default 10s)
        in_addr_t bind_n_address[MAX_BINDADDR];
        in_addr_t iftun_n_address[MAX_BINDADDR];
        char bind_multi_address[256];
@@@ -991,30 -948,15 +991,29 @@@ 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);
  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);
+ int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int argc);
+ int cmd_show_hist_idle(struct cli_def *cli, const char *command, char **argv, int argc);
+ int cmd_show_hist_open(struct cli_def *cli, const char *command, char **argv, int argc);
 +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);
  
 +// grpsess.c
 +sessionidt grp_getnextsession(groupidt g, in_addr_t ip, in_addr_t ip_src);
 +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
  #define LOG(D, s, t, f, ...)  ({ if (D <= config->debug) _log(D, s, t, f, ## __VA_ARGS__); })
@@@ -1049,8 -991,6 +1048,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)
  
  
@@@ -1063,8 -1003,6 +1062,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 {
diff --combined tbf.c
--- 1/tbf.c
--- 2/tbf.c
+++ b/tbf.c
@@@ -1,9 -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"
@@@ -310,7 -307,7 +310,7 @@@ int tbf_run_timer(void
        return 1;
  }
  
- int cmd_show_tbf(struct cli_def *cli, char *command, char **argv, int argc)
+ int cmd_show_tbf(struct cli_def *cli, const char *command, char **argv, int argc)
  {
        int i;
        int count = 0;