Merge from Master 2.2.1-2sames3.15
authorfendo <fendo@bi12info.com>
Fri, 19 Sep 2014 10:20:47 +0000 (12:20 +0200)
committerfendo <fendo@bi12info.com>
Fri, 19 Sep 2014 10:20:47 +0000 (12:20 +0200)
1  2 
cluster.c
cluster.h
debian/changelog
icmp.c
l2tpns.c
l2tpns.h
ppp.c
radius.c

diff --combined cluster.c
+++ b/cluster.c
@@@ -16,7 -16,6 +16,7 @@@
  #include <malloc.h>
  #include <errno.h>
  #include <libcli.h>
 +#include <linux/rtnetlink.h>
  
  #include "dhcp6.h"
  #include "l2tpns.h"
@@@ -45,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 :)
@@@ -90,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;
@@@ -232,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;
@@@ -458,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));
@@@ -681,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;
        }
  
@@@ -901,27 -867,6 +901,27 @@@ static int hb_add_type(uint8_t **p, in
                        add_type(p, C_BUNDLE, id, (uint8_t *) &bundle[id], sizeof(bundlet));
                        break;
  
 +              case C_CGROUPE: { // Compressed C_GROUPE
 +                      uint8_t c[sizeof(groupsesst) * 2]; // Bigger than worst case.
 +                      uint8_t *d = (uint8_t *) &grpsession[id];
 +                      uint8_t *orig = d;
 +                      int size;
 +
 +                      size = rle_compress( &d,  sizeof(groupsesst), c, sizeof(c) );
 +
 +                      // Did we compress the full structure, and is the size actually
 +                      // reduced??
 +                      if ( (d - orig) == sizeof(groupsesst) && size < sizeof(groupsesst) )
 +                      {
 +                              add_type(p, C_CGROUPE, id, c, size);
 +                              break;
 +                      }
 +                      // Failed to compress : Fall through.
 +              }
 +              case C_GROUPE:
 +                      add_type(p, C_GROUPE, id, (uint8_t *) &grpsession[id], sizeof(groupsesst));
 +                      break;
 +
                case C_CTUNNEL: { // Compressed C_TUNNEL
                        uint8_t c[sizeof(tunnelt) * 2]; // Bigger than worst case.
                        uint8_t *d = (uint8_t *) &tunnel[id];
  //
  void cluster_heartbeat()
  {
 -      int i, count = 0, tcount = 0, bcount = 0;
 +      int i, count = 0, tcount = 0, bcount = 0, gcount = 0;
        uint8_t buff[MAX_HEART_SIZE + sizeof(heartt) + sizeof(int) ];
        heartt h;
        uint8_t *p = buff;
        h.freesession = sessionfree;
        h.hightunnel = config->cluster_highest_tunnelid;
        h.highbundle = config->cluster_highest_bundleid;
 +      h.highgroupe = config->cluster_highest_groupeid;
        h.size_sess = sizeof(sessiont);         // Just in case.
        h.size_bund = sizeof(bundlet);
        h.size_tunn = sizeof(tunnelt);
 +      h.nextgrpid = gnextgrpid;
        h.interval = config->cluster_hb_interval;
        h.timeout  = config->cluster_hb_timeout;
        h.table_version = config->cluster_table_version;
  
                //
                // Fill out the packet with sessions from the session table...
 -              // (not forgetting to leave space so we can get some tunnels in too )
 +              // (not forgetting to leave space so we can get some tunnels,bundle,groupe in too )
        while ( (p + sizeof(uint32_t) * 2 + sizeof(sessiont) * 2 ) < (buff + MAX_HEART_SIZE) ) {
  
                if (!walk_session_number)       // session #0 isn't valid.
                ++count;                        // Count the number of extra sessions we're sending.
        }
  
 -              //
 -              // Fill out the packet with tunnels from the tunnel table...
 -              // This effectively means we walk the tunnel table more quickly
 -              // than the session table. This is good because stuffing up a 
 -              // tunnel is a much bigger deal than stuffing up a session.
 -              //
 -      while ( (p + sizeof(uint32_t) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE) ) {
 +      //
 +      // Fill out the packet with tunnels from the tunnel table...
 +      // This effectively means we walk the tunnel table more quickly
 +      // than the session table. This is good because stuffing up a
 +      // tunnel is a much bigger deal than stuffing up a session.
 +      //
 +      int maxsize = (sizeof(tunnelt) < sizeof(bundlet)) ? sizeof(bundlet):sizeof(tunnelt);
 +      maxsize = (sizeof(groupsesst) < maxsize) ? maxsize:sizeof(groupsesst);
 +      maxsize += (sizeof(uint32_t) * 2);
 +
 +      // Fill out the packet with tunnels,bundlets, groupes from the tables...
 +      while ( (p + maxsize) < (buff + MAX_HEART_SIZE) )
 +      {
 +              if ((tcount >= config->cluster_highest_tunnelid) &&
 +                      (bcount >= config->cluster_highest_bundleid) &&
 +                      (gcount >= config->cluster_highest_groupeid))
 +                              break;
  
 -              if (!walk_tunnel_number)        // tunnel #0 isn't valid.
 -                      ++walk_tunnel_number;
 +              if ( ((p + sizeof(uint32_t) * 2 + sizeof(tunnelt) ) < (buff + MAX_HEART_SIZE)) &&
 +                       (tcount < config->cluster_highest_tunnelid))
 +              {
 +                      if (!walk_tunnel_number)        // tunnel #0 isn't valid.
 +                              ++walk_tunnel_number;
  
 -              if (tcount >= config->cluster_highest_tunnelid)
 -                      break;
 +                      hb_add_type(&p, C_CTUNNEL, walk_tunnel_number);
 +                      walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1);       // +1 avoids divide by zero.
  
 -              hb_add_type(&p, C_CTUNNEL, walk_tunnel_number);
 -              walk_tunnel_number = (1+walk_tunnel_number)%(config->cluster_highest_tunnelid+1);       // +1 avoids divide by zero.
 +                      ++tcount;
 +              }
  
 -              ++tcount;
 -      }
 +              if ( ((p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE)) &&
 +                       (bcount < config->cluster_highest_bundleid))
 +              {
 +                      if (!walk_bundle_number)        // bundle #0 isn't valid.
 +                              ++walk_bundle_number;
  
 -              //
 -              // Fill out the packet with bundles from the bundle table...
 -      while ( (p + sizeof(uint32_t) * 2 + sizeof(bundlet) ) < (buff + MAX_HEART_SIZE) ) {
 +                      hb_add_type(&p, C_CBUNDLE, walk_bundle_number);
 +                      walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1);       // +1 avoids divide by zero.
  
 -              if (!walk_bundle_number)        // bundle #0 isn't valid.
 -                      ++walk_bundle_number;
 +                      ++bcount;
 +              }
  
 -              if (bcount >= config->cluster_highest_bundleid)
 -                      break;
 +              if ( ((p + sizeof(uint32_t) * 2 + sizeof(groupsesst) ) < (buff + MAX_HEART_SIZE)) &&
 +                       (gcount < config->cluster_highest_groupeid))
 +              {
 +                      if (!walk_groupe_number)        // groupe #0 isn't valid.
 +                              ++walk_groupe_number;
  
 -              hb_add_type(&p, C_CBUNDLE, walk_bundle_number);
 -              walk_bundle_number = (1+walk_bundle_number)%(config->cluster_highest_bundleid+1);       // +1 avoids divide by zero.
 -              ++bcount;
 -        }
 +                      hb_add_type(&p, C_CGROUPE, walk_groupe_number);
 +                      walk_groupe_number = (1+walk_groupe_number)%(config->cluster_highest_groupeid+1);       // +1 avoids divide by zero.
 +                      ++gcount;
 +              }
 +      }
  
                //
                // Did we do something wrong?
        }
  
        LOG(4, 0, 0, "Sending v%d heartbeat #%d, change #%" PRIu64 " with %d changes "
 -                   "(%d x-sess, %d x-bundles, %d x-tunnels, %d highsess, %d highbund, %d hightun, size %d)\n",
 +                   "(%d x-sess, %d x-bundles, %d x-tunnels, %d x-groupes, %d highsess, %d highbund, %d hightun, %d highgrp, size %d)\n",
            HB_VERSION, h.seq, h.table_version, config->cluster_num_changes,
 -          count, bcount, tcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
 -          config->cluster_highest_tunnelid, (int) (p - buff));
 +          count, bcount, tcount, gcount, config->cluster_highest_sessionid, config->cluster_highest_bundleid,
 +          config->cluster_highest_tunnelid, config->cluster_highest_groupeid, (int) (p - buff));
  
        config->cluster_num_changes = 0;
  
@@@ -1146,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)
  {
@@@ -1422,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) {
@@@ -1538,6 -1425,128 +1538,128 @@@ struct oldsession 
        char reserved_3[11];
  };
  
+ struct oldsessionV7 {
+       sessionidt next;                // next session in linked list
+       sessionidt far;                 // far end session ID
+       tunnelidt tunnel;               // near end tunnel ID
+       uint8_t flags;                  // session flags: see SESSION_*
+       struct {
+               uint8_t phase;          // PPP phase
+               uint8_t lcp:4;          //   LCP    state
+               uint8_t ipcp:4;         //   IPCP   state
+               uint8_t ipv6cp:4;       //   IPV6CP state
+               uint8_t ccp:4;          //   CCP    state
+       } ppp;
+       uint16_t mru;                   // maximum receive unit
+       in_addr_t ip;                   // IP of session set by RADIUS response (host byte order).
+       int ip_pool_index;              // index to IP pool
+       uint32_t unique_id;             // unique session id
+       uint32_t magic;                 // ppp magic number
+       uint32_t pin, pout;             // packet counts
+       uint32_t cin, cout;             // byte counts
+       uint32_t cin_wrap, cout_wrap;   // byte counter wrap count (RADIUS accounting giagawords)
+       uint32_t cin_delta, cout_delta; // byte count changes (for dump_session())
+       uint16_t throttle_in;           // upstream throttle rate (kbps)
+       uint16_t throttle_out;          // downstream throttle rate
+       uint8_t filter_in;              // input filter index (to ip_filters[N-1]; 0 if none)
+       uint8_t filter_out;             // output filter index
+       uint16_t snoop_port;            // Interception destination port
+       in_addr_t snoop_ip;             // Interception destination IP
+       clockt opened;                  // when started
+       clockt die;                     // being closed, when to finally free
+       uint32_t session_timeout;       // Maximum session time in seconds
+       uint32_t idle_timeout;          // Maximum idle time in seconds
+       time_t last_packet;             // Last packet from the user (used for idle timeouts)
+       time_t last_data;               // Last data packet to/from the user (used for idle timeouts)
+       in_addr_t dns1, dns2;           // DNS servers
+       routet route[MAXROUTE];         // static routes
+       uint16_t tbf_in;                // filter bucket for throttling in from the user.
+       uint16_t tbf_out;               // filter bucket for throttling out to the user.
+       int random_vector_length;
+       uint8_t random_vector[MAXTEL];
+       char user[MAXUSER];             // user (needed in session for radius stop messages)
+       char called[MAXTEL];            // called number
+       char calling[MAXTEL];           // calling number
+       uint32_t tx_connect_speed;
+       uint32_t rx_connect_speed;
+       clockt timeout;                 // Session timeout
+       uint32_t mrru;                  // Multilink Max-Receive-Reconstructed-Unit
+       epdist epdis;                   // Multilink Endpoint Discriminator
+       bundleidt bundle;               // Multilink Bundle Identifier
+       uint8_t mssf;                   // Multilink Short Sequence Number Header Format
+       uint8_t walled_garden;          // is this session gardened?
+       uint8_t classlen;               // class (needed for radius accounting messages)
+       char class[MAXCLASS];
+       uint8_t ipv6prefixlen;          // IPv6 route prefix length
+       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
+ };
+ struct oldsessionV8 {
+       sessionidt next;                // next session in linked list
+       sessionidt far;                 // far end session ID
+       tunnelidt tunnel;               // near end tunnel ID
+       uint8_t flags;                  // session flags: see SESSION_*
+       struct {
+               uint8_t phase;          // PPP phase
+               uint8_t lcp:4;          //   LCP    state
+               uint8_t ipcp:4;         //   IPCP   state
+               uint8_t ipv6cp:4;       //   IPV6CP state
+               uint8_t ccp:4;          //   CCP    state
+       } ppp;
+       uint16_t mru;                   // maximum receive unit
+       in_addr_t ip;                   // IP of session set by RADIUS response (host byte order).
+       int ip_pool_index;              // index to IP pool
+       uint32_t unique_id;             // unique session id
+       uint32_t magic;                 // ppp magic number
+       uint32_t pin, pout;             // packet counts
+       uint32_t cin, cout;             // byte counts
+       uint32_t cin_wrap, cout_wrap;   // byte counter wrap count (RADIUS accounting giagawords)
+       uint32_t cin_delta, cout_delta; // byte count changes (for dump_session())
+       uint16_t throttle_in;           // upstream throttle rate (kbps)
+       uint16_t throttle_out;          // downstream throttle rate
+       uint8_t filter_in;              // input filter index (to ip_filters[N-1]; 0 if none)
+       uint8_t filter_out;             // output filter index
+       uint16_t snoop_port;            // Interception destination port
+       in_addr_t snoop_ip;             // Interception destination IP
+       clockt opened;                  // when started
+       clockt die;                     // being closed, when to finally free
+       uint32_t session_timeout;       // Maximum session time in seconds
+       uint32_t idle_timeout;          // Maximum idle time in seconds
+       time_t last_packet;             // Last packet from the user (used for idle timeouts)
+       time_t last_data;               // Last data packet to/from the user (used for idle timeouts)
+       in_addr_t dns1, dns2;           // DNS servers
+       routet route[MAXROUTE];         // static routes
+       uint16_t tbf_in;                // filter bucket for throttling in from the user.
+       uint16_t tbf_out;               // filter bucket for throttling out to the user.
+       int random_vector_length;
+       uint8_t random_vector[MAXTEL];
+       char user[MAXUSER];             // user (needed in session for radius stop messages)
+       char called[MAXTEL];            // called number
+       char calling[MAXTEL];           // calling number
+       uint32_t tx_connect_speed;
+       uint32_t rx_connect_speed;
+       clockt timeout;                 // Session timeout
+       uint32_t mrru;                  // Multilink Max-Receive-Reconstructed-Unit
+       epdist epdis;                   // Multilink Endpoint Discriminator
+       bundleidt bundle;               // Multilink Bundle Identifier
+       uint8_t mssf;                   // Multilink Short Sequence Number Header Format
+       uint8_t walled_garden;          // is this session gardened?
+       uint8_t classlen;               // class (needed for radius accounting messages)
+       char class[MAXCLASS];
+       uint8_t ipv6prefixlen;          // IPv6 route prefix length
+       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)
+       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
+ };
  static uint8_t *convert_session(struct oldsession *old)
  {
        static sessiont new;
        new.snoop_ip = old->snoop_ip;
        new.snoop_port = old->snoop_port;
        new.walled_garden = old->walled_garden;
-       new.ipv6prefixlen = old->ipv6prefixlen;
-       new.ipv6route = old->ipv6route;
+       new.route6[0].ipv6prefixlen = old->ipv6prefixlen;
+       new.route6[0].ipv6route = old->ipv6route;
+       memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector));
+       memcpy(new.user, old->user, sizeof(new.user));
+       memcpy(new.called, old->called, sizeof(new.called));
+       memcpy(new.calling, old->calling, sizeof(new.calling));
+       for (i = 0; i < MAXROUTE; i++)
+               memcpy(&new.route[i], &old->route[i], sizeof(new.route[i]));
+       return (uint8_t *) &new;
+ }
  
+ static uint8_t *convert_sessionV7(struct oldsessionV7 *old)
+ {
+       static sessiont new;
+       int i;
+       memset(&new, 0, sizeof(new));
+       new.next = old->next;
+       new.far = old->far;
+       new.tunnel = old->tunnel;
+       new.flags = old->flags;
+       new.ppp.phase = old->ppp.phase;
+       new.ppp.lcp = old->ppp.lcp;
+       new.ppp.ipcp = old->ppp.ipcp;
+       new.ppp.ipv6cp = old->ppp.ipv6cp;
+       new.ppp.ccp = old->ppp.ccp;
+       new.mru = old->mru;
+       new.ip = old->ip;
+       new.ip_pool_index = old->ip_pool_index;
+       new.unique_id = old->unique_id;
+       new.magic = old->magic;
+       new.pin = old->pin;
+       new.pout = old->pout;
+       new.cin = old->cin;
+       new.cout = old->cout;
+       new.cin_wrap = old->cin_wrap;
+       new.cout_wrap = old->cout_wrap;
+       new.cin_delta = old->cin_delta;
+       new.cout_delta = old->cout_delta;
+       new.throttle_in = old->throttle_in;
+       new.throttle_out = old->throttle_out;
+       new.filter_in = old->filter_in;
+       new.filter_out = old->filter_out;
+       new.snoop_port = old->snoop_port;
+       new.snoop_ip = old->snoop_ip;
+       new.opened = old->opened;
+       new.die = old->die;
+       new.session_timeout = old->session_timeout;
+       new.idle_timeout = old->idle_timeout;
+       new.last_packet = old->last_packet;
+       new.last_data = old->last_data;
+       new.dns1 = old->dns1;
+       new.dns2 = old->dns2;
+       for (i = 0; i < MAXROUTE; i++)
+               memcpy(&new.route[i], &old->route[i], sizeof(new.route[i]));
+       new.tbf_in = old->tbf_in;
+       new.tbf_out = old->tbf_out;
+       new.random_vector_length = old->random_vector_length;
        memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector));
        memcpy(new.user, old->user, sizeof(new.user));
        memcpy(new.called, old->called, sizeof(new.called));
        memcpy(new.calling, old->calling, sizeof(new.calling));
+       new.tx_connect_speed = old->tx_connect_speed;
+       new.rx_connect_speed = old->rx_connect_speed;
+       new.timeout = old->timeout;
+       new.mrru = old->mrru;
+       new.epdis = old->epdis;
+       new.bundle = old->bundle;
+       new.mssf = old->mssf;
+       new.walled_garden = old->walled_garden;
+       new.classlen = old->classlen;
+       memcpy(new.class, old->class, sizeof(new.class));
+       new.route6[0].ipv6prefixlen = old->ipv6prefixlen;
+       new.route6[0].ipv6route = old->ipv6route;
+       new.forwardtosession = old->forwardtosession;
+       memcpy(new.src_hwaddr, old->src_hwaddr, sizeof(new.src_hwaddr));
+       return (uint8_t *) &new;
+ }
+ static uint8_t *convert_sessionV8(struct oldsessionV8 *old)
+ {
+       static sessiont new;
+       int i;
  
+       memset(&new, 0, sizeof(new));
+       new.next = old->next;
+       new.far = old->far;
+       new.tunnel = old->tunnel;
+       new.flags = old->flags;
+       new.ppp.phase = old->ppp.phase;
+       new.ppp.lcp = old->ppp.lcp;
+       new.ppp.ipcp = old->ppp.ipcp;
+       new.ppp.ipv6cp = old->ppp.ipv6cp;
+       new.ppp.ccp = old->ppp.ccp;
+       new.mru = old->mru;
+       new.ip = old->ip;
+       new.ip_pool_index = old->ip_pool_index;
+       new.unique_id = old->unique_id;
+       new.magic = old->magic;
+       new.pin = old->pin;
+       new.pout = old->pout;
+       new.cin = old->cin;
+       new.cout = old->cout;
+       new.cin_wrap = old->cin_wrap;
+       new.cout_wrap = old->cout_wrap;
+       new.cin_delta = old->cin_delta;
+       new.cout_delta = old->cout_delta;
+       new.throttle_in = old->throttle_in;
+       new.throttle_out = old->throttle_out;
+       new.filter_in = old->filter_in;
+       new.filter_out = old->filter_out;
+       new.snoop_port = old->snoop_port;
+       new.snoop_ip = old->snoop_ip;
+       new.opened = old->opened;
+       new.die = old->die;
+       new.session_timeout = old->session_timeout;
+       new.idle_timeout = old->idle_timeout;
+       new.last_packet = old->last_packet;
+       new.last_data = old->last_data;
+       new.dns1 = old->dns1;
+       new.dns2 = old->dns2;
        for (i = 0; i < MAXROUTE; i++)
                memcpy(&new.route[i], &old->route[i], sizeof(new.route[i]));
+       new.tbf_in = old->tbf_in;
+       new.tbf_out = old->tbf_out;
+       new.random_vector_length = old->random_vector_length;
+       memcpy(new.random_vector, old->random_vector, sizeof(new.random_vector));
+       memcpy(new.user, old->user, sizeof(new.user));
+       memcpy(new.called, old->called, sizeof(new.called));
+       memcpy(new.calling, old->calling, sizeof(new.calling));
+       new.tx_connect_speed = old->tx_connect_speed;
+       new.rx_connect_speed = old->rx_connect_speed;
+       new.timeout = old->timeout;
+       new.mrru = old->mrru;
+       new.epdis = old->epdis;
+       new.bundle = old->bundle;
+       new.mssf = old->mssf;
+       new.walled_garden = old->walled_garden;
+       new.classlen = old->classlen;
+       memcpy(new.class, old->class, sizeof(new.class));
+       new.route6[0].ipv6prefixlen = old->ipv6prefixlen;
+       new.route6[0].ipv6route = old->ipv6route;
+       new.forwardtosession = old->forwardtosession;
+       memcpy(new.src_hwaddr, old->src_hwaddr, sizeof(new.src_hwaddr));
+       new.dhcpv6_prefix_iaid = old->dhcpv6_prefix_iaid;
+       new.dhcpv6_iana_iaid = old->dhcpv6_iana_iaid;
+       new.ipv6address = old->ipv6address;
+       new.dhcpv6_client_id = old->dhcpv6_client_id;
  
        return (uint8_t *) &new;
  }
@@@ -1618,7 -1773,7 +1886,7 @@@ static int cluster_process_heartbeat(ui
        int i, type;
        int hb_ver = more;
  
- #if HB_VERSION != 8
+ #if HB_VERSION != 9
  # error "need to update cluster_process_heartbeat()"
  #endif
  
        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)
        {
                                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.
+                                               if ((hb_ver == 7) && (size == sizeof(struct oldsessionV7)))
+                                                       cluster_recv_session(more, convert_sessionV7((struct oldsessionV7 *) c));
+                                               else if (size == sizeof(struct oldsessionV8))
+                                                       cluster_recv_session(more, convert_sessionV8((struct oldsessionV8 *) c));
+                                               else
+                                                       LOG(0, 0, 0, "DANGER: Received a CSESSION version=%d that didn't decompress correctly!\n", hb_ver);
+                                               break;
                                        }
                                        else
                                        {
                                  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!!
@@@ -2133,14 -2260,12 +2404,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            8       // Protocol version number..
+ #define HB_VERSION            9       // 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 debian/changelog
@@@ -1,13 -1,31 +1,21 @@@
 -l2tpns (2.2.1-2fdn3.15) unstable; urgency=low
++l2tpns (2.2.1-2sames3.15) unstable; urgency=low
+   * new feature: If the user attribute "Framed-IPv6-Address" is defined then the ICMPv6_RA advertise this IPv6 address.
+   * Fix: Incorrect delegation of IPv6 prefixes when multiple of 4 bits (nibble) (eg: /44, /52 ...).
+   * Added ability to define up to 5 IPv6 prefix delegation by user.
+  -- Fernando Alves <fendo@sameswifi.fr>  Thu, 18 Sep 2014 18:36:43 +0200
 -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-2sames3.12) UNRELEASED; urgency=low
 -l2tpns (2.2.1-2fdn3.11) unstable; urgency=low
++l2tpns (2.2.1-2sames3.12) unstable; 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 icmp.c
--- 1/icmp.c
--- 2/icmp.c
+++ b/icmp.c
@@@ -5,7 -5,6 +5,7 @@@
  #include <linux/icmp.h>
  #include <netinet/icmp6.h>
  #include <unistd.h>
 +#include <linux/rtnetlink.h>
  #include <netinet/ip6.h>
  
  #include "dhcp6.h"
@@@ -144,7 -143,13 +144,13 @@@ void send_ipv6_ra(sessionidt s, tunneli
        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;
+       if (session[s].ipv6address.s6_addr[0])
+       {
+               // MSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address)
+               memcpy(&pinfo->nd_opt_pi_prefix, &session[s].ipv6address.s6_addr[0], 8);
+       }
+       else
+               pinfo->nd_opt_pi_prefix     = config->ipv6_prefix;
  
        // // Length of payload (not header)
        p_ip6_hdr->ip6_plen = htons(sizeof(*pinfo) + sizeof(*p_nra));
diff --combined l2tpns.c
+++ b/l2tpns.c
@@@ -77,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
@@@ -94,12 -94,15 +94,12 @@@ 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;
        struct ipv6radix *branch;
- } ipv6_hash[256];             // Mapping from IPv6 address to session structures.
+ } ipv6_hash[16];              // Mapping from IPv6 address to session structures.
  
  // Traffic counters.
  static uint32_t udp_rx = 0, udp_rx_pkt = 0, udp_tx = 0;
@@@ -185,7 -188,6 +185,7 @@@ config_descriptt config_values[] = 
        CONFIG("disable_sending_hello", disable_sending_hello, BOOL),
        CONFIG("disable_no_spoof", disable_no_spoof, BOOL),
        CONFIG("bind_multi_address", bind_multi_address, STRING),
 +      CONFIG("grp_txrate_average_time", grp_txrate_average_time, INT),
        CONFIG("pppoe_only_equal_svc_name", pppoe_only_equal_svc_name, BOOL),
        CONFIG("multi_hostname", multi_hostname, STRING),
        CONFIG("no_throttle_local_IP", no_throttle_local_IP, BOOL),
@@@ -223,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.
@@@ -234,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);
@@@ -262,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
@@@ -623,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;
@@@ -659,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;
@@@ -980,19 -983,24 +980,24 @@@ static sessionidt lookup_ipv6map(struc
        char ipv6addr[INET6_ADDRSTRLEN];
  
        curnode = &ipv6_hash[ip.s6_addr[0]];
+       curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4];
        i = 1;
        s = curnode->sess;
  
-       while (s == 0 && i < 15 && curnode->branch != NULL)
+       while (s == 0 && i < 32 && curnode->branch != NULL)
        {
-               curnode = &curnode->branch[ip.s6_addr[i]];
+               if (i & 1)
+                       curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F];
+               else
+                       curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4];
                s = curnode->sess;
                i++;
        }
  
        LOG(4, s, session[s].tunnel, "Looking up address %s and got %d\n",
-                       inet_ntop(AF_INET6, &ip, ipv6addr,
-                               INET6_ADDRSTRLEN),
+                       inet_ntop(AF_INET6, &ip, ipv6addr,
+                       INET6_ADDRSTRLEN),
                        s);
  
        return s;
@@@ -1033,13 -1041,26 +1038,26 @@@ sessionidt sessionbyipv6(struct in6_add
        return 0;
  }
  
+ sessionidt sessionbyipv6new(struct in6_addr ip)
+ {
+       sessionidt s;
+       CSTAT(sessionbyipv6new);
+       s = lookup_ipv6map(ip);
+       if (s > 0 && s < MAXSESSION && session[s].opened)
+               return s;
+       return 0;
+ }
  //
  // Take an IP address in HOST byte order and
  // add it to the sessionid by IP cache.
  //
  // (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;
@@@ -1072,22 -1093,28 +1090,28 @@@ static void uncache_ipmap(in_addr_t ip
  static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s)
  {
        int i;
-       int bytes;
+       int niblles;
        struct ipv6radix *curnode;
        char ipv6addr[INET6_ADDRSTRLEN];
  
-       curnode = &ipv6_hash[ip.s6_addr[0]];
+       curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4];
  
-       bytes = prefixlen >> 3;
+       niblles = prefixlen >> 2;
        i = 1;
-       while (i < bytes) {
+       while (i < niblles)
+       {
                if (curnode->branch == NULL)
                {
-                       if (!(curnode->branch = calloc(256,
-                                       sizeof (struct ipv6radix))))
+                       if (!(curnode->branch = calloc(16, sizeof (struct ipv6radix))))
                                return;
                }
-               curnode = &curnode->branch[ip.s6_addr[i]];
+               if (i & 1)
+                       curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F];
+               else
+                       curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4];
                i++;
        }
  
  
        if (s > 0)
                LOG(4, s, session[s].tunnel, "Caching ip address %s/%d\n",
-                               inet_ntop(AF_INET6, &ip, ipv6addr, 
-                                       INET6_ADDRSTRLEN),
+                               inet_ntop(AF_INET6, &ip, ipv6addr,
+                               INET6_ADDRSTRLEN),
                                prefixlen);
        else if (s == 0)
                LOG(4, 0, 0, "Un-caching ip address %s/%d\n",
-                               inet_ntop(AF_INET6, &ip, ipv6addr, 
-                                       INET6_ADDRSTRLEN),
+                               inet_ntop(AF_INET6, &ip, ipv6addr,
+                               INET6_ADDRSTRLEN),
                                prefixlen);
  }
  
@@@ -1152,7 -1179,6 +1176,6 @@@ int cmd_show_ipcache(struct cli_def *cl
        return CLI_OK;
  }
  
  // Find session by username, 0 for not found
  // walled garden users aren't authenticated, so the username is
  // reasonably useless. Ignore them to avoid incorrect actions
@@@ -1421,7 -1447,6 +1444,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.
@@@ -1714,7 -1715,6 +1737,6 @@@ static void processipv6out(uint8_t * bu
        sessionidt s;
        sessiont *sp;
        tunnelidt t;
-       in_addr_t ip;
        struct in6_addr ip6;
  
        uint8_t *data = buf;    // Keep a copy of the originals.
  
        if (s == 0)
        {
-               ip = *(uint32_t *)(buf + 32);
-               s = sessionbyip(ip);
+               s = sessionbyipv6new(ip6);
        }
-       
        if (s == 0)
        {
                // Is this a packet for a session that doesn't exist?
@@@ -2128,7 -2127,7 +2149,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].ipv6route.s6_addr[0] && session[s].ipv6prefixlen && del_routes)
-                       route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
+               for (r = 0; r < MAXROUTE6 && session[s].route6[r].ipv6route.s6_addr[0] && session[s].route6[r].ipv6prefixlen; r++)
+               {
+                       if (del_routes) route6set(s, session[s].route6[r].ipv6route, session[s].route6[r].ipv6prefixlen, 0);
+                       memset(&session[s].route6[r], 0, sizeof(session[s].route6[r]));
+               }
  
                if (session[s].ipv6address.s6_addr[0] && del_routes)
                {
                                                cache_ipmap(session[new_s].ip, new_s);
  
                                                // IPV6 route
-                                               if (session[new_s].ipv6prefixlen)
-                                                       cache_ipv6map(session[new_s].ipv6route, session[new_s].ipv6prefixlen, new_s);
+                                               for (r = 0; r < MAXROUTE6 && session[new_s].route6[r].ipv6prefixlen; r++)
+                                               {
+                                                       cache_ipv6map(session[new_s].route6[r].ipv6route, session[new_s].route6[r].ipv6prefixlen, new_s);
+                                               }
  
                                                if (session[new_s].ipv6address.s6_addr[0])
                                                {
@@@ -2338,8 -2342,6 +2364,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);
  }
@@@ -3785,7 -3781,7 +3811,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];
  
@@@ -4722,8 -4718,6 +4748,8 @@@ static void initdata(int optdebug, cha
  #endif /* BGP */
  
        lac_initremotelnsdata();
 +
 +      grp_initdata();
  }
  
  static int assign_ip_address(sessionidt s)
@@@ -5266,9 -5260,6 +5292,9 @@@ int main(int argc, char *argv[]
                        LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
        }
  
 +      //LOG(3, 0, 0, "Debug sizeof struct: sessiont %lu, tunnelt %lu, bundlet %lu, groupsesst %lu\n",
 +      //      sizeof(sessiont), sizeof(tunnelt), sizeof(bundlet), sizeof(groupsesst));
 +
        mainloop();
  
        /* remove plugins (so cleanup code gets run) */
@@@ -5780,7 -5771,6 +5806,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
@@@ -5872,7 -5856,7 +5898,7 @@@ int load_session(sessionidt s, session
        // needs update
        if (newip)
        {
-               int routed = 0;
+               int routed = 0;
  
                // remove old routes...
                for (i = 0; i < MAXROUTE && session[s].route[i].ip; i++)
                }
  
                // remove old IPV6 routes...
-               if (session[s].ipv6route.s6_addr[0] && session[s].ipv6prefixlen)
-                       route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0);
+               for (i = 0; i < MAXROUTE6 && session[s].route6[i].ipv6route.s6_addr[0] && session[s].route6[i].ipv6prefixlen; i++)
+               {
+                       route6set(s, session[s].route6[i].ipv6route, session[s].route6[i].ipv6prefixlen, 0);
+               }
  
                if (session[s].ipv6address.s6_addr[0])
                {
        }
  
        // check v6 routing
-       if (new->ipv6prefixlen && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
-               route6set(s, new->ipv6route, new->ipv6prefixlen, 1);
+       for (i = 0; i < MAXROUTE6 && new->route6[i].ipv6prefixlen; i++)
+       {
+               route6set(s, new->route6[i].ipv6route, new->route6[i].ipv6prefixlen, 1);
+       }
  
        if (new->ipv6address.s6_addr[0] && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
        {
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-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
@@@ -52,6 -49,7 +52,7 @@@
  #define MAXPLUGINS    20              // maximum number of plugins to load
  #define MAXRADSERVER  10              // max radius servers
  #define MAXROUTE      10              // max static routes per session
+ #define MAXROUTE6     5               // max static Ipv6 routes per session
  #define MAXIPPOOL     131072          // max number of ip addresses in pool
  #define RINGBUFFER_SIZE       10000           // Number of ringbuffer entries to allocate
  #define MAX_LOG_LENGTH        512             // Maximum size of log message
@@@ -219,7 -217,6 +220,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];
  
@@@ -256,6 -253,14 +257,14 @@@ typedef struct                   // rout
  }
  routet;
  
+ // structures
+ typedef struct                        // route
+ {
+       struct in6_addr ipv6route;      // Static IPv6 route
+       uint8_t ipv6prefixlen;          // IPv6 route prefix length
+ }
+ routet6;
  typedef struct controls               // control message
  {
        struct controls *next;  // next in queue
@@@ -333,47 -338,17 +342,46 @@@ typedef struc
        uint8_t walled_garden;          // is this session gardened?
        uint8_t classlen;               // class (needed for radius accounting messages)
        char class[MAXCLASS];
-       uint8_t ipv6prefixlen;          // IPv6 route prefix length
-       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)
        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)
+       routet6 route6[MAXROUTE6];              // static IPv6 routes
        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)
@@@ -556,13 -531,6 +564,13 @@@ enu
        BUNDLEUNDEF,            // Undefined
  };
  
 +enum
 +{
 +      GROUPEFREE,             // Not in use
 +      GROUPEOPEN,             // Active bundle
 +      GROUPEUNDEF             // Undefined
 +};
 +
  enum
  {
        NULLCLASS = 0,          //End Point Discriminator classes
@@@ -638,6 -606,7 +646,7 @@@ struct Tstat
      uint32_t  call_processudp;
      uint32_t  call_sessionbyip;
      uint32_t  call_sessionbyipv6;
+     uint32_t  call_sessionbyipv6new;
      uint32_t  call_sessionbyuser;
      uint32_t  call_sendarp;
      uint32_t  call_sendipcp;
@@@ -772,10 -741,8 +781,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];
@@@ -986,6 -952,7 +995,7 @@@ void send_ipv6_ra(sessionidt s, tunneli
  void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add);
  sessionidt sessionbyip(in_addr_t ip);
  sessionidt sessionbyipv6(struct in6_addr ip);
+ sessionidt sessionbyipv6new(struct in6_addr ip);
  sessionidt sessionbyuser(char *username);
  void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta);
  void random_data(uint8_t *buf, int len);
@@@ -1006,26 -973,12 +1016,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__); })
@@@ -1060,8 -1013,6 +1070,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)
  
  
@@@ -1074,8 -1025,6 +1084,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 ppp.c
--- 1/ppp.c
--- 2/ppp.c
+++ b/ppp.c
@@@ -5,9 -5,7 +5,9 @@@
  #include <unistd.h>
  #include <errno.h>
  #include <stdlib.h>
 +#include <linux/rtnetlink.h>
  #include <netinet/ip6.h>
 +
  #include "dhcp6.h"
  #include "l2tpns.h"
  #include "constants.h"
@@@ -1481,11 -1479,14 +1481,14 @@@ void processipcp(sessionidt s, tunnelid
  
  static void ipv6cp_open(sessionidt s, tunnelidt t)
  {
+       int i;
        LOG(3, s, t, "IPV6CP: Opened\n");
  
        change_state(s, ipv6cp, Opened);
-       if (session[s].ipv6prefixlen)
-               route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 1);
+       for (i = 0; i < MAXROUTE6 && session[s].route6[i].ipv6prefixlen; i++)
+       {
+               route6set(s, session[s].route6[i].ipv6route, session[s].route6[i].ipv6prefixlen, 1);
+       }
  
        if (session[s].ipv6address.s6_addr[0])
        {
@@@ -1579,8 -1580,16 +1582,16 @@@ void processipv6cp(sessionidt s, tunnel
                                gotip++; // seen address
                                if (o[1] != 10) return;
  
-                               ident[0] = htonl(session[s].ip);
-                               ident[1] = 0;
+                               if (session[s].ipv6address.s6_addr[0])
+                               {
+                                       // LSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address)
+                                       memcpy(&ident[0], &session[s].ipv6address.s6_addr[8], 8);
+                               }
+                               else
+                               {
+                                       ident[0] = htonl(session[s].ip);
+                                       ident[1] = 0;
+                               }
  
                                if (memcmp(o + 2, ident, sizeof(ident)))
                                {
@@@ -2253,7 -2262,18 +2264,18 @@@ void processipv6in(sessionidt s, tunnel
                return;
  
        // no spoof
-       if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s)
+       if (session[s].ipv6address.s6_addr[0])
+       {
+               if ((sessionbyipv6new(ip) != s) &&
+                       (ip.s6_addr[0] != 0xFE || ip.s6_addr[1] != 0x80 || ip.s6_addr16[1] != 0 || ip.s6_addr16[2] != 0 || ip.s6_addr16[3] != 0))
+               {
+                       char str[INET6_ADDRSTRLEN];
+                       LOG(5, s, t, "Dropping packet with spoofed IP %s\n",
+                                       inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN));
+                       return;
+               }
+       }
+       else if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s)
        {
                char str[INET6_ADDRSTRLEN];
                LOG(5, s, t, "Dropping packet with spoofed IP %s\n",
diff --combined radius.c
+++ b/radius.c
@@@ -11,7 -11,6 +11,7 @@@
  #include <ctype.h>
  #include <netinet/in.h>
  #include <errno.h>
 +#include <linux/rtnetlink.h>
  
  #include "md5.h"
  #include "constants.h"
@@@ -534,7 -533,8 +534,8 @@@ void processrad(uint8_t *buf, int len, 
        sessionidt s;
        tunnelidt t = 0;
        hasht hash;
-       uint8_t routes = 0;
+       int routes = 0;
+       int routes6 = 0;
        int r_code;
        int r_id;
        int OpentunnelReq = 0;
                                                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;
                                                }
                                        }
  
                                                if (prefixlen)
                                                {
-                                                       LOG(3, s, session[s].tunnel,
-                                                               "   Radius reply contains route for %s/%d\n",
-                                                               n, prefixlen);
-                                                       session[s].ipv6route = r6;
-                                                       session[s].ipv6prefixlen = prefixlen;
+                                                       if (routes6 == MAXROUTE6)
+                                                       {
+                                                               LOG(1, s, session[s].tunnel, "   Too many IPv6 routes\n");
+                                                       }
+                                                       else
+                                                       {
+                                                               LOG(3, s, session[s].tunnel, "   Radius reply contains route for %s/%d\n", n, prefixlen);
+                                                               session[s].route6[routes6].ipv6route = r6;
+                                                               session[s].route6[routes6].ipv6prefixlen = prefixlen;
+                                                               routes6++;
+                                                       }
                                                }
                                        }
                                        else if (*p == 123)
                                                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);
+                                                       if (routes6 == MAXROUTE6)
+                                                       {
+                                                               LOG(1, s, session[s].tunnel, "   Too many IPv6 routes\n");
+                                                       }
+                                                       else
+                                                       {
+                                                               memcpy(&session[s].route6[routes6].ipv6route, &p[4], p[1] - 4);
+                                                               session[s].route6[routes6].ipv6prefixlen = p[3];
+                                                               LOG(3, s, session[s].tunnel, "   Radius reply contains Delegated IPv6 Prefix %s/%d\n",
+                                                                       inet_ntop(AF_INET6, &session[s].route6[routes6].ipv6route, ipv6addr, INET6_ADDRSTRLEN), session[s].route6[routes6].ipv6prefixlen);
+                                                               routes6++;
+                                                       }
                                                }
                                        }
                                        else if (*p == 168)