#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"
union grp_iphash *idx;
} grp_ip_hash[256]; // Mapping from IP address to group structures.
+struct grp_ipv6radix {
+ groupidt grp;
+ struct grp_ipv6radix *branch;
+} grp_ipv6_hash[16]; // Mapping from IPv6 address to session 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
return 0;
}
+static void grp_cache_ipv6map(struct in6_addr ip, int prefixlen, groupidt g)
+{
+ int i;
+ int niblles;
+ struct grp_ipv6radix *curnode;
+ char ipv6addr[INET6_ADDRSTRLEN];
+
+ curnode = &grp_ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4];
+
+ niblles = prefixlen >> 2;
+ i = 1;
+
+ while (i < niblles)
+ {
+ if (curnode->branch == NULL)
+ {
+ if (!(curnode->branch = calloc(16, sizeof(struct grp_ipv6radix))))
+ return;
+ }
+
+ if (i & 1)
+ curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F];
+ else
+ curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4];
+
+ i++;
+ }
+
+ curnode->grp = g;
+
+ if (g > 0)
+ LOG(4, 0, 0, "Caching Group:%d ip address %s/%d\n", g,
+ inet_ntop(AF_INET6, &ip, ipv6addr,
+ INET6_ADDRSTRLEN),
+ prefixlen);
+ else if (g == 0)
+ LOG(4, 0, 0, "Un-caching Group:%d ip address %s/%d\n", g,
+ inet_ntop(AF_INET6, &ip, ipv6addr,
+ INET6_ADDRSTRLEN),
+ prefixlen);
+}
+
+static void grp_route6set(groupidt g, struct in6_addr ip, int prefixlen, int add)
+{
+ struct {
+ struct nlmsghdr nh;
+ struct rtmsg rt;
+ char buf[64];
+ } req;
+ int metric;
+ char ipv6addr[INET6_ADDRSTRLEN];
+
+ if (!config->ipv6_prefix.s6_addr[0])
+ {
+ LOG(0, 0, 0, "Asked to set IPv6 route, but IPv6 not setup.\n");
+ return;
+ }
+
+ 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_INET6;
+ 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));
+ netlink_addattr(&req.nh, RTA_DST, &ip, sizeof(ip));
+ metric = 1;
+ netlink_addattr(&req.nh, RTA_METRICS, &metric, sizeof(metric));
+
+ LOG(1, g, 0, "Route Group %s %s/%d\n",
+ add ? "add" : "del",
+ inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN),
+ prefixlen);
+
+ if (netlink_send(&req.nh) < 0)
+ LOG(0, 0, 0, "grp_route6set() error in sending netlink message: %s\n", strerror(errno));
+
+#ifdef BGP
+ if (add)
+ bgp_add_route6(ip, prefixlen);
+ else
+ bgp_del_route6(ip, prefixlen);
+#endif /* BGP */
+
+ if (g)
+ {
+ if (!add) // Are we deleting a route?
+ g = 0; // Caching the session as '0' is the same as uncaching.
+
+ grp_cache_ipv6map(ip, prefixlen, g);
+ }
+
+ return;
+}
+
+static groupidt grp_lookup_ipv6map(struct in6_addr ip)
+{
+ struct grp_ipv6radix *curnode;
+ int i;
+ int g;
+ char ipv6addr[INET6_ADDRSTRLEN];
+
+ curnode = &grp_ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4];
+ i = 1;
+ g = curnode->grp;
+
+ while (g == 0 && i < 32 && curnode->branch != NULL)
+ {
+ if (i & 1)
+ curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F];
+ else
+ curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4];
+
+ g = curnode->grp;
+ i++;
+ }
+
+ LOG(4, 0, 0, "Looking up Group address %s and got %d\n",
+ inet_ntop(AF_INET6, &ip, ipv6addr,
+ INET6_ADDRSTRLEN),
+ g);
+
+ return g;
+}
+
+groupidt grp_groupbyipv6(struct in6_addr ip)
+{
+ groupidt g = grp_lookup_ipv6map(ip);
+
+ if (g > 0 && g < MAXGROUPE)
+ return g;
+
+ return 0;
+}
+
+// Set all route of a group
+void grp_setgrouproute6(groupidt g, int add)
+{
+ int i, j;
+ int ipv6opened = 0;
+
+ if (add)
+ {
+ for (j = 0; j < grpsession[g].nbsession; j++)
+ {
+ if (grpsession[g].sesslist[j].sid != 0)
+ {
+ if (session[grpsession[g].sesslist[j].sid].ppp.ipv6cp == Opened)
+ {
+ // IPv6 opened
+ ipv6opened = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (ipv6opened || !add)
+ {
+ for (i = 0; i < grpsession[g].nbroutes6grp; i++)
+ {
+ if (grpsession[g].route6[i].ipv6prefixlen != 0)
+ {
+ grpsession[g].ipv6cp_opened = add;
+ grp_route6set(g, grpsession[g].route6[i].ipv6route, grpsession[g].route6[i].ipv6prefixlen, add);
+ }
+ }
+ }
+}
+
// Add a route
//
// This adds it to the routing table, advertises it
// via BGP if enabled, and stuffs it into the
-// 'sessionbyip' cache.
+// 'groupbyip' cache.
//
// 'ip' must be in _host_ order.
//
n_ip = htonl(ip);
netlink_addattr(&req.nh, RTA_DST, &n_ip, sizeof(n_ip));
- LOG(1, 0, 0, "Route (Group) %s %s/%d\n", add ? "add" : "del", fmtaddr(htonl(ip), 0), prefixlen);
+ 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));
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);
+ }
}
}
// Del all routes
grp_setgrouproute(g, 0);
+ grp_setgrouproute6(g, 0);
if (gnextgrpid == g)
{
- gnextgrpid = 0;
+ gnextgrpid = grpsession[g].prev;
}
else
{
}
memset(&grpsession[g], 0, sizeof(grpsession[0]));
+ grpsession[g].state = GROUPEFREE;
}
else
{
&grpsession[g].sesslist[i+1],
(grpsession[g].nbsession - i) * sizeof(grpsession[g].sesslist[i]));
}
+
+ cluster_send_groupe(g);
return;
}
}
// 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;
return 0;
}
+// Add a route to a group
+// return 1 if OK
+static int grp_addroute6(groupidt g, sessionidt s, struct in6_addr ip6, int prefixlen)
+{
+ int i;
+ char ipv6addr[INET6_ADDRSTRLEN];
+
+ for (i = 0; i < MAXROUTE6INGRP; i++)
+ {
+ if ((i >= grpsession[g].nbroutes6grp))
+ {
+ LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d\n",
+ g, inet_ntop(AF_INET6, &ip6, ipv6addr, INET6_ADDRSTRLEN), prefixlen);
+
+ grpsession[g].route6[i].ipv6route = ip6;
+ grpsession[g].route6[i].ipv6prefixlen = prefixlen;
+ grpsession[g].nbroutes6grp++;
+ return 1;
+ }
+ else if (!memcmp(&grpsession[g].route6[i].ipv6route, &ip6, sizeof(ip6)) && (grpsession[g].route6[i].ipv6prefixlen == 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, inet_ntop(AF_INET6, &ip6, ipv6addr, INET6_ADDRSTRLEN), prefixlen);
+
+ return 1;
+ }
+ else if (grpsession[g].route6[i].ipv6prefixlen == 0)
+ {
+ LOG(3, s, session[s].tunnel, " Radius reply Group %d contains route for %s/%d (find empty on list!!!)\n",
+ g, inet_ntop(AF_INET6, &ip6, ipv6addr, INET6_ADDRSTRLEN), prefixlen);
+
+ grpsession[g].route6[i].ipv6route = ip6;
+ grpsession[g].route6[i].ipv6prefixlen = prefixlen;
+ return 1;
+ }
+ }
+
+ if (i >= MAXROUTE6INGRP)
+ {
+ LOG(1, s, session[s].tunnel, " Too many IPv6 routes for Group %d\n", g);
+ }
+ return 0;
+}
+
// Process Sames vendor specific attribut radius
void grp_processvendorspecific(sessionidt s, uint8_t *pvs)
{
uint8_t *n = pvs + 2;
uint8_t *e = pvs + pvs[1];
- if ((attrib >= 22) && (attrib <= 23))
+ if ((attrib >= 22) && (attrib <= 24))
{
while (n < e && isdigit(*n))
{
return;
}
+ if (grpid > config->cluster_highest_groupeid)
+ config->cluster_highest_groupeid = grpid;
+
n++;
}
if (!grp_addroute(grpid, s, ip, bits))
return;
}
+ else if (attrib == 24)
+ {
+ struct in6_addr r6;
+ int prefixlen;
+ uint8_t *m = memchr(n, '/', e - n);
+
+ *m++ = 0;
+ inet_pton(AF_INET6, (char *) n, &r6);
+
+ prefixlen = 0;
+ while (m < e && isdigit(*m)) {
+ prefixlen = prefixlen * 10 + *m++ - '0';
+ }
+
+ if (prefixlen)
+ {
+ if (!grp_addroute6(grpid, s, r6, prefixlen))
+ return;
+ }
+ }
else if (attrib == 23) //SAMES-Group-Session-Weight
{
uint8_t weight = 0;
// Init data structures
void grp_initdata()
{
+ int i;
+
// Set default value (10s)
config->grp_txrate_average_time = 10;
}
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
}
// 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));
- }
- }
- }
- }
-}
-
+//~ 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)
+sessionidt grp_getnextsession(groupidt g, void *p_ip, void *p_ip_src, int is_ipv6)
{
+ in_addr_t *p_ipv4 = p_ip;
+ in_addr_t *p_ipv4_src = p_ip_src;
+ struct in6_addr *p_ipv6 = p_ip;
+ struct in6_addr *p_ipv6_src = p_ip_src;
sessionidt s = 0, s2 = 0, s3 = 0;
int i;
uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0;
- uint32_t txrate;
+ uint32_t txrate = 0;
if (g >= MAXGROUPE)
return 0;
{
if ((s2 = grpsession[g].sesslist[i].sid))
{
- grpsession[g].sesslist[i].tx_rate = session[s2].coutgrp_delta/ltime_changed;
- session[s2].coutgrp_delta = 0;
+ 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 ((s = sessionbyip(ip)))
+ if (!is_ipv6)
+ s = sessionbyip(*p_ipv4);
+
+ if (s || is_ipv6)
{
- if (s == grpsession[g].smax)
+ uint8_t *as;
+ uint8_t *ad;
+ uint16_t ai;
+
+ if (is_ipv6)
+ {
+ as = (uint8_t *) &p_ipv6_src[12];
+ ad = (uint8_t *) &p_ipv6[12];
+ }
+ else
+ {
+ as = (uint8_t *) p_ipv4_src;
+ ad = (uint8_t *) p_ipv4;
+ }
+
+ ai = ad[3];
+ ai <<= 8;
+ ai |= as[3];
+
+ s = grp_local[g].sid_loaddist[ai];
+ if (!s)
{
s = grpsession[g].smin;
- grpsession[g].smax = 0;
+ 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)
s = s3;
- if (s)
- cache_ipmap(ntohl(ip), s);
+ if (s && !is_ipv6)
+ {
+ cache_ipmap(ntohl(*p_ipv4), 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].nbroutes6grp != new->nbroutes6grp) ||
+ (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;
+ }
+ }
+ }
+
+ if (!updategroup)
+ {
+ // Check IPv6 routes list
+ for (i = 0; i < grpsession[g].nbroutes6grp; i++)
+ {
+ if ((grpsession[g].route6[i].ipv6prefixlen != new->route6[i].ipv6prefixlen) ||
+ memcmp(&grpsession[g].route6[i].ipv6route, &new->route6[i].ipv6route, sizeof(new->route6[i].ipv6route)))
+ {
+ updategroup = 1;
+ break;
+ }
+ }
+ }
+
+ if (grpsession[g].ipv6cp_opened != new->ipv6cp_opened)
+ {
+ updategroup = 1;
+ }
+
+ // needs update
+ if (updategroup)
+ {
+ // Del all routes
+ grp_setgrouproute(g, 0);
+ grp_setgrouproute6(g, 0);
+ }
+
+ memcpy(&grpsession[g], new, sizeof(grpsession[g])); // Copy over..
+
+ // needs update
+ if (updategroup)
+ {
+ // Add all routes
+ grp_setgrouproute(g, 1);
+ grp_setgrouproute6(g, 1);
+ }
+
+ return 1;
+}