Merge from Master 2.2.1-2sames3.14
authorfendo <fendo@bi12info.com>
Sun, 14 Sep 2014 17:00:57 +0000 (19:00 +0200)
committerfendo <fendo@bi12info.com>
Sun, 14 Sep 2014 17:00:57 +0000 (19:00 +0200)
27 files changed:
1  2 
Makefile
arp.c
autosnoop.c
autothrottle.c
bgp.c
cli.c
cluster.c
cluster.h
control.c
debian/changelog
garden.c
grpsess.c
icmp.c
l2tplac.c
l2tpns.c
l2tpns.h
nsctl.c
ppp.c
pppoe.c
radius.c
sessionctl.c
setrxspeed.c
snoopctl.c
stripdomain.c
tbf.c
throttlectl.c
util.c

diff --combined Makefile
+++ b/Makefile
@@@ -26,7 -26,7 +26,7 @@@ INSTALL = install -c -D -o root -g roo
  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 grpsess.o
 -    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o dhcp6.o ipv6_u.o
++    ll.o md5.o ppp.o radius.o tbf.o util.o pppoe.o l2tplac.o grpsess.o dhcp6.o ipv6_u.o
  
  PROGRAMS = l2tpns nsctl
  PLUGINS = autosnoop.so autothrottle.so garden.so sessionctl.so \
@@@ -108,32 -108,35 +108,36 @@@ install: al
  .PHONY: all clean depend install
  
  ## Dependencies: (autogenerated) ##
- arp.o: arp.c l2tpns.h
- cli.o: cli.c l2tpns.h constants.h util.h cluster.h tbf.h ll.h bgp.h \
-  l2tplac.h
- cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h pppoe.h bgp.h
+ arp.o: arp.c dhcp6.h l2tpns.h
+ cli.o: cli.c dhcp6.h l2tpns.h constants.h util.h cluster.h tbf.h ll.h \
+  bgp.h l2tplac.h
+ cluster.o: cluster.c dhcp6.h l2tpns.h cluster.h util.h tbf.h pppoe.h \
+  bgp.h
  constants.o: constants.c constants.h
- control.o: control.c l2tpns.h control.h
- icmp.o: icmp.c l2tpns.h pppoe.h
- l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.h \
-  control.h util.h tbf.h bgp.h l2tplac.h pppoe.h
+ control.o: control.c dhcp6.h l2tpns.h control.h
+ icmp.o: icmp.c dhcp6.h l2tpns.h ipv6_u.h
+ l2tpns.o: l2tpns.c md5.h dhcp6.h l2tpns.h cluster.h plugin.h ll.h \
+  constants.h control.h util.h tbf.h bgp.h l2tplac.h pppoe.h
  ll.o: ll.c ll.h
  md5.o: md5.c md5.h
- ppp.o: ppp.c l2tpns.h constants.h plugin.h util.h tbf.h cluster.h \
+ ppp.o: ppp.c dhcp6.h l2tpns.h constants.h plugin.h util.h tbf.h cluster.h \
   l2tplac.h pppoe.h
- radius.o: radius.c md5.h constants.h l2tpns.h plugin.h util.h cluster.h \
-  l2tplac.h pppoe.h
- tbf.o: tbf.c l2tpns.h util.h tbf.h
- util.o: util.c l2tpns.h bgp.h
- pppoe.o: pppoe.c l2tpns.h cluster.h constants.h md5.h util.h
- l2tplac.o: l2tplac.c md5.h l2tpns.h util.h cluster.h l2tplac.h pppoe.h
+ radius.o: radius.c md5.h constants.h dhcp6.h l2tpns.h plugin.h util.h \
+  cluster.h l2tplac.h pppoe.h
+ tbf.o: tbf.c dhcp6.h l2tpns.h util.h tbf.h
+ util.o: util.c dhcp6.h l2tpns.h bgp.h
+ pppoe.o: pppoe.c dhcp6.h l2tpns.h cluster.h constants.h md5.h util.h
+ l2tplac.o: l2tplac.c md5.h dhcp6.h l2tpns.h util.h cluster.h l2tplac.h \
+  pppoe.h
 +grpsess.o: grpsess.c l2tpns.h util.h cluster.h bgp.h
- bgp.o: bgp.c l2tpns.h bgp.h util.h
- autosnoop.so: autosnoop.c l2tpns.h plugin.h
- autothrottle.so: autothrottle.c l2tpns.h plugin.h
- garden.so: garden.c l2tpns.h plugin.h control.h
- sessionctl.so: sessionctl.c l2tpns.h plugin.h control.h
- setrxspeed.so: setrxspeed.c l2tpns.h plugin.h
- snoopctl.so: snoopctl.c l2tpns.h plugin.h control.h
- stripdomain.so: stripdomain.c l2tpns.h plugin.h
- throttlectl.so: throttlectl.c l2tpns.h plugin.h control.h
+ dhcp6.o: dhcp6.c dhcp6.h l2tpns.h ipv6_u.h
+ ipv6_u.o: ipv6_u.c ipv6_u.h
+ bgp.o: bgp.c dhcp6.h l2tpns.h bgp.h util.h
+ autosnoop.so: autosnoop.c dhcp6.h l2tpns.h plugin.h
+ autothrottle.so: autothrottle.c dhcp6.h l2tpns.h plugin.h
+ garden.so: garden.c dhcp6.h l2tpns.h plugin.h control.h
+ sessionctl.so: sessionctl.c dhcp6.h l2tpns.h plugin.h control.h
+ setrxspeed.so: setrxspeed.c dhcp6.h l2tpns.h plugin.h
+ snoopctl.so: snoopctl.c dhcp6.h l2tpns.h plugin.h control.h
+ stripdomain.so: stripdomain.c dhcp6.h l2tpns.h plugin.h
+ throttlectl.so: throttlectl.c dhcp6.h l2tpns.h plugin.h control.h
diff --combined arp.c
--- 1/arp.c
--- 2/arp.c
+++ b/arp.c
@@@ -5,9 -5,9 +5,10 @@@
  #include <net/ethernet.h>
  #include <net/if_arp.h>
  #include <linux/if_packet.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  
  /* Most of this code is based on keepalived:vrrp_arp.c */
diff --combined autosnoop.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
+ #include "dhcp6.h"
 +
  #include "l2tpns.h"
  #include "plugin.h"
  
diff --combined autothrottle.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
+ #include "dhcp6.h"
 +
  #include "l2tpns.h"
  #include "plugin.h"
  
diff --combined bgp.c
--- 1/bgp.c
--- 2/bgp.c
+++ b/bgp.c
@@@ -20,8 -20,8 +20,9 @@@
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <fcntl.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "bgp.h"
  #include "util.h"
diff --combined cli.c
--- 1/cli.c
--- 2/cli.c
+++ b/cli.c
@@@ -21,8 -21,8 +21,9 @@@
  #include <dlfcn.h>
  #include <netdb.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "constants.h"
  #include "util.h"
@@@ -76,7 -76,6 +77,7 @@@ static char *debug_levels[] = 
  #endif
  
  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);
@@@ -156,7 -155,6 +157,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");
@@@ -3113,77 -3111,6 +3114,77 @@@ static int cmd_reload(struct cli_def *c
        return CLI_OK;
  }
  
 +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, const char *command, char **argv, int argc)
  {
        int ret;
diff --combined cluster.c
+++ b/cluster.c
@@@ -16,8 -16,8 +16,9 @@@
  #include <malloc.h>
  #include <errno.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "cluster.h"
  #include "util.h"
@@@ -44,7 -44,6 +45,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 -88,6 +90,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 -229,7 +232,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 -454,7 +458,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 -676,6 +681,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;
        }
  
@@@ -876,7 -843,7 +877,7 @@@ static int hb_add_type(uint8_t **p, in
                        // Failed to compress : Fall through.
                }
                case C_SESSION:
-                       add_type(p, C_SESSION, id, (uint8_t *) &session[id], sizeof(sessiont));
+                       add_type(p, C_SESSION, id, (uint8_t *) &session[id], sizeof(sessiont));
                        break;
  
                case C_CBUNDLE: { // Compressed C_BUNDLE
                        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];
                        // Failed to compress : Fall through.
                }
                case C_TUNNEL:
-                       add_type(p, C_TUNNEL, id, (uint8_t *) &tunnel[id], sizeof(tunnelt));
+                       add_type(p, C_TUNNEL, id, (uint8_t *) &tunnel[id], sizeof(tunnelt));
                        break;
                default:
                        LOG(0, 0, 0, "Found an invalid type in heart queue! (%d)\n", type);
  //
  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 -1070,6 +1146,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 -1334,6 +1422,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) {
@@@ -1617,11 -1505,11 +1618,11 @@@ static int cluster_process_heartbeat(ui
        int i, type;
        int hb_ver = more;
  
- #if HB_VERSION != 7
+ #if HB_VERSION != 8
  # error "need to update cluster_process_heartbeat()"
  #endif
  
-       // we handle versions 5 through 7
+       // we handle versions 5 through 8
        if (hb_ver < 5 || hb_ver > HB_VERSION) {
                LOG(0, 0, 0, "Received a heartbeat version that I don't support (%d)!\n", hb_ver);
                return -1; // Ignore it??
        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)
        {
                                        break;
                                }
  
-                               if (size != sizeof(sessiont) ) { // Ouch! Very very bad!
-                                       LOG(0, 0, 0, "DANGER: Received a CSESSION that didn't decompress correctly!\n");
-                                               // Now what? Should exit! No-longer up to date!
-                                       break;
+                               if (size != sizeof(sessiont)) { // Ouch! Very very bad!
+                                       if ((hb_ver < HB_VERSION) && (size < sizeof(sessiont)))
+                                       {
+                                               // set to 0 the unused variables
+                                               memset(&c[size], 0, (sizeof(sessiont) - size));
+                                               LOG(3, 0, 0, "WARNING: Received a CSESSION from %s hb_version %d != %d current version !\n", fmtaddr(addr, 2), hb_ver, HB_VERSION);
+                                               // New feature not activated until the master has not been upgraded.
+                                       }
+                                       else
+                                       {
+                                               LOG(0, 0, 0, "DANGER: Received a CSESSION that didn't decompress correctly!\n");
+                                                       // Now what? Should exit! No-longer up to date!
+                                               break;
+                                       }
                                }
  
                                cluster_recv_session(more, c);
                                  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!!
@@@ -2122,14 -1989,12 +2133,14 @@@ int cmd_show_cluster(struct cli_def *cl
                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
  #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_VERSION            8       // Protocol version number..
  #define HB_MAX_SEQ            (1<<30) // Maximum sequence number. (MUST BE A POWER OF 2!)
  #define HB_HISTORY_SIZE               64      // How many old heartbeats we remember?? (Must be a factor of HB_MAX_SEQ)
  
@@@ -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);
diff --combined control.c
+++ b/control.c
@@@ -1,9 -1,8 +1,10 @@@
  // L2TPNS: control
  
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
+ #include "dhcp6.h"
 +
  #include "l2tpns.h"
  #include "control.h"
  
diff --combined debian/changelog
@@@ -1,4 -1,23 +1,13 @@@
 -l2tpns (2.2.1-2fdn3.14) unstable; urgency=low
++l2tpns (2.2.1-2sames3.14) unstable; urgency=low
+   * Fix cluster slave; reset to 0, the end of the session when the master version < slave version.
+   * Fix cluster slave; no add the ipv6 route address (/128) if included in the delegated prefix.
 -
 - -- Fernando Alves <fendo@sameswifi.fr>  Fri, 12 Sep 2014 18:21:53 +0200
 -
 -l2tpns (2.2.1-2fdn3.13) unstable; urgency=low
 -
+   * Add DHCPv6 functionality
 -
 - -- Fernando Alves <fendo@sameswifi.fr>  Thu, 11 Sep 2014 16:10:38 +0200
 -
 -l2tpns (2.2.1-2fdn3.12) unstable; urgency=low
 -
+   * Fix: remove old IPV6 routes on master
 - -- Fernando Alves <fendo@sameswifi.fr>  Tue, 10 Dec 2013 23:13:20 +0100
++ -- Fernando Alves <fendo@sameswifi.fr>  Sun, 14 Sep 2014 18:27:09 +0200
 -l2tpns (2.2.1-2fdn3.11) unstable; urgency=low
 +l2tpns (2.2.1-2sames3.12) UNRELEASED; urgency=low
  
    * Fix: throttle ipv6 out.
    * Fix: remove old IPV6 routes on slave
    * 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, 07 Oct 2013 22:20:53 +0200
 +
 +l2tpns (2.2.1-2sames3.11) unstable; urgency=low
  
 - -- Fernando Alves <fendo@sameswifi.fr>  Tue, 10 Dec 2013 23:08:45 +0100
 +  * improved load balancing algorithm.
  
 -l2tpns (2.2.1-2fdn3.10) unstable; urgency=low
 + -- 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>  Thu, 28 Mar 2013 10:50:00 +0100
 + -- Fernando Alves <fernando.alves@sameswireless.fr>  Tue, 30 Apr 2013 19:22:11 +0200
  
 -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,8 +3,10 @@@
  #include <stdlib.h>
  #include <sys/wait.h>
  #include <sys/types.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  #include "control.h"
diff --combined grpsess.c
index d3fae30,0000000..5528453
mode 100644,000000..100644
--- /dev/null
+++ b/grpsess.c
@@@ -1,709 -1,0 +1,711 @@@
 +/*
 + * 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 <netinet/ip6.h>
 +
++#include "dhcp6.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 icmp.c
--- 1/icmp.c
--- 2/icmp.c
+++ b/icmp.c
@@@ -1,33 -1,18 +1,19 @@@
  // L2TPNS: icmp
  
  #include <arpa/inet.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <asm/types.h>
  #include <linux/ip.h>
  #include <linux/icmp.h>
  #include <netinet/icmp6.h>
- #include <stdio.h>
- #include <sys/socket.h>
  #include <unistd.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <memory.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
- #include "pppoe.h"
+ #include "ipv6_u.h"
  
  static uint16_t _checksum(uint8_t *addr, int count);
  
- struct ipv6_pseudo_hdr {
-       struct in6_addr src;
-       struct in6_addr dest;
-       uint32_t ulp_length;
-       uint32_t zero    : 24;
-       uint32_t nexthdr :  8;
- };
  void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, uint8_t *packet, int packet_len)
  {
        char buf[128] = {0};
@@@ -86,11 -71,11 +72,11 @@@ static uint16_t _checksum(uint8_t *addr
  
        for (; count > 1; count -= 2)
        {
-               sum += ntohs(*(uint32_t *) addr);
+               sum += ntohs(*(uint16_t *) addr);
                addr += 2;
        }
  
-       if (count > 1) sum += *(unsigned char *)addr;
+       if (count > 0) sum += *(unsigned char *)addr;
  
        // take only 16 bits out of the 32 bit sum and add up the carries
        while (sum >> 16)
  void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip)
  {
        struct nd_opt_prefix_info *pinfo;
-       struct ipv6_pseudo_hdr *phdr;
+       struct ip6_hdr *p_ip6_hdr;
+       struct nd_router_advert *p_nra;
        uint8_t b[MAXETHER + 20];
-       uint8_t c[MAXETHER + 20];
+       struct ipv6_pseudo_hdr pseudo_hdr;
        int l;
-       uint8_t *o;
  
        LOG(3, s, t, "Sending IPv6 RA\n");
-               
        memset(b, 0, sizeof(b));
-       o = makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0);
+       p_ip6_hdr = (struct ip6_hdr *) makeppp(b, sizeof(b), 0, 0, s, t, PPPIPV6, 0, 0, 0);
  
-       if (!o)
+       if (!p_ip6_hdr)
        {
                LOG(3, s, t, "failed to send IPv6 RA\n");
                return;
        }
  
-       *o = 0x60;                      // IPv6
-       *(o+1) = 0;
-       *(o+5) = 48;                    // Length of payload (not header)
-       *(o+6) = 58;                    // icmp6 is next
-       *(o+7) = 255;                   // Hop limit
-       memset(o+8, 0, 16);             // source = FE80::1
-       *(o+8) = 0xFE;
-       *(o+9) = 0x80;
-       *(o+23) = 1;
+       p_ip6_hdr->ip6_vfc = 0x60;                      // IPv6
+       p_ip6_hdr->ip6_plen = 0;                        // Length of payload (not header) (calculation below)
+       p_ip6_hdr->ip6_nxt = IPPROTO_ICMPV6;                    // icmp6 is next
+       p_ip6_hdr->ip6_hlim = 255;                      // Hop limit
+       // IPv6 0xFE80::1
+       inet_pton(AF_INET6, "FE80::1", &p_ip6_hdr->ip6_src.s6_addr);
        if (ip != NULL)
        {
-               memcpy(o+24, ip, 16);   // dest = ip
+               memcpy(p_ip6_hdr->ip6_dst.s6_addr, ip, 16);     // dest = ip
        }
        else
        {
                // FF02::1 - all hosts
-               *(o+24) = 0xFF;
-               *(o+25) = 2;
-               *(o+39) = 1;
+               inet_pton(AF_INET6, "FF02::1", &p_ip6_hdr->ip6_dst.s6_addr);
        }
-       *(o+40) = 134;                  // RA message
-       *(o+41) = 0;                    // Code
-       *(o+42) = *(o+43) = 0;          // Checksum
-       *(o+44) = 64;                   // Hop count
-       *(o+45) = 0;                    // Flags
-       *(o+46) = *(o+47) = 255;        // Lifetime
-       *(uint32_t *)(o+48) = 0;        // Reachable time
-       *(uint32_t *)(o+52) = 0;        // Retrans timer
-       pinfo = (struct nd_opt_prefix_info *)(o+56);
+       // RA message after Ipv6 header
+       p_nra = (struct nd_router_advert *) &p_ip6_hdr[1];
+       p_nra->nd_ra_type = ND_ROUTER_ADVERT; // RA message (134)
+       p_nra->nd_ra_code = 0;          // Code
+       p_nra->nd_ra_cksum = 0;         // Checksum
+       p_nra->nd_ra_curhoplimit = 64;          // Hop count
+       p_nra->nd_ra_flags_reserved = (ND_RA_FLAG_MANAGED|ND_RA_FLAG_OTHER); // Flags
+       p_nra->nd_ra_router_lifetime = 0xFFFF;  // Lifetime
+       p_nra->nd_ra_reachable = 0;     // Reachable time
+       p_nra->nd_ra_retransmit = 0;    // Retrans timer
+       // Option PI after RA message (rfc4861)
+       pinfo = (struct nd_opt_prefix_info *) &p_nra[1];
        pinfo->nd_opt_pi_type           = ND_OPT_PREFIX_INFORMATION;
        pinfo->nd_opt_pi_len            = 4;
-       pinfo->nd_opt_pi_prefix_len     = 64; // prefix length
-       pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK;
-       pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
+       pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO;
        pinfo->nd_opt_pi_valid_time     = htonl(2592000);
        pinfo->nd_opt_pi_preferred_time = htonl(604800);
        pinfo->nd_opt_pi_reserved2      = 0;
+       pinfo->nd_opt_pi_prefix_len     = 64; // prefix length
        pinfo->nd_opt_pi_prefix         = config->ipv6_prefix;
-       l = sizeof(*pinfo) + 56;
  
-       memset(c, 0, sizeof(c));
-       phdr = (struct ipv6_pseudo_hdr *) c;
-       memcpy(&phdr->src, o+8, 16);
-       memcpy(&phdr->dest, o+24, 16);
-       phdr->ulp_length = htonl(l - 40);
-       phdr->nexthdr = IPPROTO_ICMPV6;
+       // // Length of payload (not header)
+       p_ip6_hdr->ip6_plen = htons(sizeof(*pinfo) + sizeof(*p_nra));
  
-       memcpy(c + sizeof(*phdr), o + 40, l - 40);
+       l = sizeof(*pinfo) + sizeof(*p_nra) + sizeof(*p_ip6_hdr);
  
+       /* Use pseudo hearder for checksum calculation */
+       memset(&pseudo_hdr, 0, sizeof(pseudo_hdr));
+       memcpy(&pseudo_hdr.src, &p_ip6_hdr->ip6_src, 16);
+       memcpy(&pseudo_hdr.dest, &p_ip6_hdr->ip6_dst, 16);
+       pseudo_hdr.ulp_length = htonl(sizeof(*pinfo) + sizeof(*p_nra)); // Lenght whitout Ipv6 header
+       pseudo_hdr.nexthdr = IPPROTO_ICMPV6;
        // Checksum is over the icmp6 payload plus the pseudo header
-       *(uint16_t *)(o+42) = _checksum(c, l - 40 + sizeof(*phdr));
+       p_nra->nd_ra_cksum = ipv6_checksum(&pseudo_hdr, (uint8_t *) p_nra, (sizeof(*pinfo) + sizeof(*p_nra)));
  
-       tunnelsend(b, l + (o-b), t); // send it...
+       tunnelsend(b, l + (((uint8_t *) p_ip6_hdr)-b), t); // send it...
        return;
  }
diff --combined l2tplac.c
+++ b/l2tplac.c
@@@ -7,10 -7,10 +7,11 @@@
  
  #include <errno.h>
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
  
  #include "md5.h"
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "util.h"
  #include "cluster.h"
diff --combined l2tpns.c
+++ b/l2tpns.c
@@@ -40,6 -40,7 +40,7 @@@
  #include <linux/rtnetlink.h>
  
  #include "md5.h"
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "cluster.h"
  #include "plugin.h"
@@@ -55,6 -56,7 +56,7 @@@
  
  #include "l2tplac.h"
  #include "pppoe.h"
+ #include "dhcp6.h"
  
  char * Vendor_name = "Linux L2TPNS";
  uint32_t call_serial_number = 0;
@@@ -75,7 -77,7 +77,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 -94,10 +94,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,10 -188,15 +185,16 @@@ 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),
        CONFIG("no_throttle_local_IP", no_throttle_local_IP, BOOL),
+       CONFIG("dhcp6_preferred_lifetime", dhcp6_preferred_lifetime, INT),
+       CONFIG("dhcp6_valid_lifetime", dhcp6_valid_lifetime, INT),
+       CONFIG("dhcp6_server_duid", dhcp6_server_duid, INT),
+       CONFIG("primary_ipv6_dns", default_ipv6_dns1, IPv6),
+       CONFIG("secondary_ipv6_dns", default_ipv6_dns2, IPv6),
+       CONFIG("default_ipv6_domain_list", default_ipv6_domain_list, STRING),
        { NULL, 0, 0, 0 }
  };
  
@@@ -215,7 -225,6 +223,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.
@@@ -226,6 -235,9 +234,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);
@@@ -254,9 -266,8 +262,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
@@@ -615,7 -626,7 +623,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;
@@@ -651,7 -662,7 +659,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;
@@@ -1031,7 -1042,7 +1039,7 @@@ sessionidt sessionbyipv6(struct in6_add
  //
  // (It's actually cached in network order)
  //
 -static void cache_ipmap(in_addr_t ip, sessionidt s)
 +void cache_ipmap(in_addr_t ip, sessionidt s)
  {
        in_addr_t nip = htonl(ip);      // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0]
        uint8_t *a = (uint8_t *) &nip;
@@@ -1413,7 -1424,6 +1421,7 @@@ 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, ip_src;
  
        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.
@@@ -2120,7 -2106,7 +2128,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++)
                        free_ip_address(s);
  
                // unroute IPv6, if setup
-               if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen && del_routes)
+               if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen && del_routes)
                        route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
-               
+               if (session[s].ipv6address.s6_addr[0] && del_routes)
+               {
+                       route6set(s, session[s].ipv6address, 128, 0);
+               }
                if (b)
                {
                        // This session was part of a bundle
                                                // IPV6 route
                                                if (session[new_s].ipv6prefixlen)
                                                        cache_ipv6map(session[new_s].ipv6route, session[new_s].ipv6prefixlen, new_s);
+                                               if (session[new_s].ipv6address.s6_addr[0])
+                                               {
+                                                       cache_ipv6map(session[new_s].ipv6address, 128, new_s);
+                                               }
                                        }
                                }
                        }
@@@ -2320,8 -2316,6 +2338,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);
  }
@@@ -3445,6 -3433,20 +3463,20 @@@ void processudp(uint8_t *buf, int len, 
                                return;
                        }
  
+                       if (!config->cluster_iam_master)
+                       {
+                               // Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547)
+                               if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
+                                               *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
+                                               *(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 &&
+                                               *(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23)
+                               {
+                                       // DHCPV6 must be managed by the Master.
+                                       master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port, indexudpfd);
+                                       return;
+                               }
+                       }
                        processipv6in(s, t, p, l);
                }
                else if (session[s].ppp.lcp == Opened)
@@@ -3753,7 -3755,7 +3785,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];
  
@@@ -4690,8 -4692,6 +4722,8 @@@ static void initdata(int optdebug, cha
  #endif /* BGP */
  
        lac_initremotelnsdata();
 +
 +      grp_initdata();
  }
  
  static int assign_ip_address(sessionidt s)
@@@ -5134,6 -5134,7 +5166,7 @@@ int main(int argc, char *argv[]
        init_tbf(config->num_tbfs);
  
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
+       LOG(0, 0, 0, "Copyright (c) 2012, 2013, 2014 ISP FDN & SAMESWIRELESS\n");
        LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n");
        LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
        {
  
        initrad();
        initippool();
+       dhcpv6_init();
  
        // seed prng
        {
                        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) */
@@@ -5746,7 -5745,6 +5780,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
@@@ -5865,6 -5857,11 +5899,11 @@@ int load_session(sessionidt s, session
                if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen)
                        route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
  
+               if (session[s].ipv6address.s6_addr[0])
+               {
+                       route6set(s, session[s].ipv6address, 128, 0);
+               }
                routed = 0;
  
                // add new routes...
  
        // check v6 routing
        if (new->ipv6prefixlen && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
-                   route6set(s, new->ipv6route, new->ipv6prefixlen, 1);
+               route6set(s, new->ipv6route, new->ipv6prefixlen, 1);
+       if (new->ipv6address.s6_addr[0] && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
+       {
+               // Check if included in prefix
+               if (sessionbyipv6(new->ipv6address) != s)
+                       route6set(s, new->ipv6address, 128, 1);
+       }
  
        // check filters
        if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0]))
diff --combined l2tpns.h
+++ b/l2tpns.h
@@@ -15,7 -15,7 +15,7 @@@
  #include <sys/types.h>
  #include <libcli.h>
  
- #define VERSION       "2.2.1"
 -#define VERSION       "2.2.1-2fdn3.13"
++#define VERSION       "2.2.1-2sames3.14"
  
  // Limits
  #define MAXTUNNEL     500             // could be up to 65535
@@@ -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,14 +337,43 @@@ 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
+       uint32_t dhcpv6_prefix_iaid;    // prefix iaid requested by client
+       uint32_t dhcpv6_iana_iaid;              // iaid of iana requested by client
+       struct in6_addr ipv6address;    // Framed Ipv6 address
+       struct dhcp6_opt_clientid dhcpv6_client_id; // Size max (headers + DUID)
+       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 -523,6 +556,13 @@@ enu
        BUNDLEUNDEF,            // Undefined
  };
  
 +enum
 +{
 +      GROUPEFREE,             // Not in use
 +      GROUPEOPEN,             // Active bundle
 +      GROUPEUNDEF             // Undefined
 +};
 +
  enum
  {
        NULLCLASS = 0,          //End Point Discriminator classes
@@@ -660,6 -624,7 +664,7 @@@ struct Tstat
      uint32_t  call_radiussend;
      uint32_t  call_radiusretry;
      uint32_t    call_random_data;
+     uint32_t    call_dhcpv6_process;
  #endif
  };
  
@@@ -767,10 -732,8 +772,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)
        int no_throttle_local_IP;       // no throttle traffic from session to session
        in_addr_t bind_n_address[MAX_BINDADDR];
        in_addr_t iftun_n_address[MAX_BINDADDR];
        char bind_multi_address[256];
        char multi_hostname[512];
        char multi_n_hostname[MAX_NBHOSTNAME][MAXHOSTNAME];     // list hostname
+       struct in6_addr default_ipv6_dns1;
+       struct in6_addr default_ipv6_dns2;
+       uint32_t dhcp6_preferred_lifetime;              // preferred lifetime (see rfc3315)
+       uint32_t dhcp6_valid_lifetime;          // valid lifetime (see rfc3315)
+       uint32_t dhcp6_server_duid;             // DUID of dhcpv6 server (see rfc3315)
+       char default_ipv6_domain_list[255];
  } configt;
  
  enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IPv4, IPv6 };
@@@ -995,26 -963,12 +1006,26 @@@ int ip_filter(uint8_t *buf, int len, ui
  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 -1003,6 +1060,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 -1015,6 +1074,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 nsctl.c
+++ b/nsctl.c
@@@ -7,9 -7,8 +7,10 @@@
  #include <string.h>
  #include <netdb.h>
  #include <signal.h>
 +#include <sys/socket.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "control.h"
  
diff --combined ppp.c
--- 1/ppp.c
--- 2/ppp.c
+++ b/ppp.c
@@@ -5,9 -5,8 +5,10 @@@
  #include <unistd.h>
  #include <errno.h>
  #include <stdlib.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "constants.h"
  #include "plugin.h"
@@@ -1486,6 -1485,13 +1487,13 @@@ static void ipv6cp_open(sessionidt s, t
        if (session[s].ipv6prefixlen)
                route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 1);
  
+       if (session[s].ipv6address.s6_addr[0])
+       {
+               // Check if included in prefix
+               if (sessionbyipv6(session[s].ipv6address) != s)
+                       route6set(s, session[s].ipv6address, 128, 1);
+       }
        // Send an initial RA (TODO: Should we send these regularly?)
        send_ipv6_ra(s, t, NULL);
  }
@@@ -2263,6 -2269,16 +2271,16 @@@ void processipv6in(sessionidt s, tunnel
                return;
        }
  
+       // Check if DhcpV6, IP dst: FF02::1:2, Src Port 0x0222 (546), Dst Port 0x0223 (547)
+       if (*(p + 6) == 17 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
+                       *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
+                       *(uint16_t *)(p + 34) == 0 && *(p + 36) == 0 && *(p + 37) == 1 && *(p + 38) == 0 && *(p + 39) == 2 &&
+                       *(p + 40) == 2 && *(p + 41) == 0x22 && *(p + 42) == 2 && *(p + 43) == 0x23)
+       {
+               dhcpv6_process(s, t, p, l);
+               return;
+       }
        // Add on the tun header
        p -= 4;
        *(uint32_t *) p = htonl(PKTIPV6);
diff --combined pppoe.c
+++ b/pppoe.c
@@@ -21,8 -21,8 +21,9 @@@
  #include <netpacket/packet.h>
  #include <arpa/inet.h>
  #include <linux/if_pppox.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "cluster.h"
  #include "constants.h"
diff --combined radius.c
+++ b/radius.c
  #include <ctype.h>
  #include <netinet/in.h>
  #include <errno.h>
 +#include <linux/rtnetlink.h>
  
  #include "md5.h"
  #include "constants.h"
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  #include "util.h"
@@@ -630,19 -630,11 +631,19 @@@ void processrad(uint8_t *buf, int len, 
                                                else if (vendor == 529 && attrib >= 135 && attrib <= 136) // Ascend
                                                {
                                                        // handle old-format ascend DNS attributes below
 -                                                      p += 6;
 +                                                      p += 6;
 +                                              }
 +                                              else if (vendor == 64520) // Sames
 +                                              {
 +                                                      //Sames vendor-specific 64520
 +                                                      uint8_t *pvs = p + 6; // pvs set to begin to attribute
 +                                                      LOG(3, s, session[s].tunnel, "   Sames vendor-specific: %d, Attrib: %d, lenght: %d\n", vendor, attrib, attrib_length);
 +                                                      grp_processvendorspecific(s, pvs);
 +                                                      continue;
                                                }
                                                else
                                                {
 -                                                      LOG(3, s, session[s].tunnel, "      Unknown vendor-specific\n");
 +                                                      LOG(3, s, session[s].tunnel, "   Unknown vendor-specific: %d, Attrib: %d\n", vendor, attrib);
                                                        continue;
                                                }
                                        }
                                                int prefixlen;
                                                uint8_t *n = p + 2;
                                                uint8_t *e = p + p[1];
-                                               uint8_t *m = memchr(n, '/', e - p);
+                                               uint8_t *m = memchr(n, '/', e - n);
  
                                                *m++ = 0;
                                                inet_pton(AF_INET6, (char *) n, &r6);
                                                        session[s].ipv6prefixlen = prefixlen;
                                                }
                                        }
+                                       else if (*p == 123)
+                                       {
+                                               // Delegated-IPv6-Prefix
+                                               if ((p[1] > 4) && (p[3] > 0) && (p[3] <= 128))
+                                               {
+                                                       char ipv6addr[INET6_ADDRSTRLEN];
+                                                       memcpy(&session[s].ipv6route, &p[4], p[1] - 4);
+                                                       session[s].ipv6prefixlen = p[3];
+                                                       LOG(3, s, session[s].tunnel, "   Radius reply contains Delegated IPv6 Prefix %s/%d\n",
+                                                               inet_ntop(AF_INET6, &session[s].ipv6route, ipv6addr, INET6_ADDRSTRLEN), session[s].ipv6prefixlen);
+                                               }
+                                       }
+                                       else if (*p == 168)
+                                       {
+                                               // Framed-IPv6-Address
+                                               if (p[1] == 18)
+                                               {
+                                                       char ipv6addr[INET6_ADDRSTRLEN];
+                                                       memcpy(&session[s].ipv6address, &p[2], 16);
+                                                       LOG(3, s, session[s].tunnel, "   Radius reply contains Framed-IPv6-Address %s\n", inet_ntop(AF_INET6, &session[s].ipv6address, ipv6addr, INET6_ADDRSTRLEN));
+                                               }
+                                       }
                                        else if (*p == 25)
                                        {
                                                // Class
diff --combined sessionctl.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  #include "control.h"
diff --combined setrxspeed.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  
diff --combined snoopctl.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  #include "control.h"
diff --combined stripdomain.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  
diff --combined tbf.c
--- 1/tbf.c
--- 2/tbf.c
+++ b/tbf.c
@@@ -1,9 -1,8 +1,10 @@@
  // L2TPNS: token bucket filters
  
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "util.h"
  #include "tbf.h"
diff --combined throttlectl.c
@@@ -1,7 -1,6 +1,8 @@@
  #include <string.h>
- #include <sys/socket.h>
 +#include <linux/rtnetlink.h>
+ #include <netinet/ip6.h>
 +
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #include "plugin.h"
  #include "control.h"
diff --combined util.c
--- 1/util.c
--- 2/util.c
+++ b/util.c
@@@ -8,8 -8,8 +8,9 @@@
  #include <arpa/inet.h>
  #include <string.h>
  #include <sys/mman.h>
 +#include <linux/rtnetlink.h>
  
+ #include "dhcp6.h"
  #include "l2tpns.h"
  #ifdef BGP
  #include "bgp.h"