Fix: IPv6 prefix routing on slave cluster
[l2tpns.git] / l2tpns.c
index cc69c2d..14892f7 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -102,7 +102,7 @@ union iphash {
 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;
@@ -982,20 +982,24 @@ static sessionidt lookup_ipv6map(struct in6_addr ip)
        int s;
        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;
@@ -1036,6 +1040,19 @@ sessionidt sessionbyipv6(struct in6_addr ip)
        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.
@@ -1075,22 +1092,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++;
        }
 
@@ -1098,13 +1121,13 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s)
 
        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);
 }
 
@@ -1155,7 +1178,6 @@ int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int
        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
@@ -1692,7 +1714,6 @@ static void processipv6out(uint8_t * buf, int len)
        sessionidt s;
        sessiont *sp;
        tunnelidt t;
-       in_addr_t ip;
        struct in6_addr ip6;
 
        uint8_t *data = buf;    // Keep a copy of the originals.
@@ -1731,10 +1752,9 @@ static void processipv6out(uint8_t * buf, int len)
 
        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?
@@ -5895,9 +5915,12 @@ int load_session(sessionidt s, sessiont *new)
        }
 
        // check v6 routing
-       for (i = 0; i < MAXROUTE6 && new->route6[i].ipv6prefixlen; i++)
+       if (new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened)
        {
-               route6set(s, new->route6[i].ipv6route, new->route6[i].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)