X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/517c3509bfa1987dc91fc5c15f86fcb8e5a6f7ed..refs/heads/samesversion:/grpsess.c diff --git a/grpsess.c b/grpsess.c index 68ec968..92adcec 100644 --- a/grpsess.c +++ b/grpsess.c @@ -9,9 +9,12 @@ #include #include #include +#include +#include "dhcp6.h" #include "l2tpns.h" #include "util.h" +#include "cluster.h" #ifdef BGP #include "bgp.h" @@ -22,7 +25,20 @@ union grp_iphash { union grp_iphash *idx; } grp_ip_hash[256]; // Mapping from IP address to group structures. -static groupidt gnextgrpid = 0; +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 // @@ -80,11 +96,197 @@ groupidt grp_groupbyip(in_addr_t ip) 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. // @@ -128,7 +330,7 @@ static void grp_routeset(groupidt g, in_addr_t ip, int prefixlen, int add) 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)); @@ -153,7 +355,10 @@ static void grp_routeset(groupidt g, in_addr_t ip, int prefixlen, int add) 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); + } } } @@ -209,10 +414,11 @@ void grp_removesession(groupidt g, sessionidt s) // Del all routes grp_setgrouproute(g, 0); + grp_setgrouproute6(g, 0); if (gnextgrpid == g) { - gnextgrpid = 0; + gnextgrpid = grpsession[g].prev; } else { @@ -228,6 +434,7 @@ void grp_removesession(groupidt g, sessionidt s) } memset(&grpsession[g], 0, sizeof(grpsession[0])); + grpsession[g].state = GROUPEFREE; } else { @@ -236,6 +443,8 @@ void grp_removesession(groupidt g, sessionidt s) &grpsession[g].sesslist[i+1], (grpsession[g].nbsession - i) * sizeof(grpsession[g].sesslist[i])); } + + cluster_send_groupe(g); return; } } @@ -270,6 +479,7 @@ static int grp_addsession(groupidt g, sessionidt s, uint8_t weight) // 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; @@ -325,6 +535,52 @@ static int grp_addroute(groupidt g, sessionidt s, in_addr_t ip, int prefixlen) 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) { @@ -333,7 +589,7 @@ 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)) { @@ -356,6 +612,9 @@ void grp_processvendorspecific(sessionidt s, uint8_t *pvs) return; } + if (grpid > config->cluster_highest_groupeid) + config->cluster_highest_groupeid = grpid; + n++; } @@ -394,6 +653,26 @@ void grp_processvendorspecific(sessionidt s, uint8_t *pvs) 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; @@ -420,6 +699,8 @@ void grp_processvendorspecific(sessionidt s, uint8_t *pvs) // Init data structures void grp_initdata() { + int i; + // Set default value (10s) config->grp_txrate_average_time = 10; @@ -430,6 +711,18 @@ void grp_initdata() } 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 @@ -443,79 +736,278 @@ void grp_time_changed() } } +// Uncache all IP of a session +//~ static void grp_uncache_ipsession(groupidt g, sessionidt s) +//~ { + //~ int i; + //~ uint8_t *a; + //~ in_addr_t ip; + //~ in_addr_t n_ip, j; + //~ int prefixlen; + //~ union iphash *h; +//~ + //~ for (i = 0; i < grpsession[g].nbroutesgrp; i++) + //~ { + //~ if (grpsession[g].route[i].ip != 0) + //~ { + //~ prefixlen = grpsession[g].route[i].prefixlen; + //~ ip = grpsession[g].route[i].ip & (0xffffffff << (32 - prefixlen)); // Force the ip to be the first one in the route. +//~ + //~ for (j = ip; j < ip+(1<<(32-prefixlen)) ; ++j) + //~ { + //~ n_ip = htonl(j); // To network order + //~ a = (uint8_t *) &n_ip; + //~ h = ip_hash; +//~ + //~ if (!(h = h[*a++].idx)) continue; + //~ if (!(h = h[*a++].idx)) continue; + //~ if (!(h = h[*a++].idx)) continue; +//~ + //~ if (s == h[*a].sess) + //~ { + //~ h[*a].sess = 0; + //~ //LOG(3, s, session[s].tunnel, "UnCaching ip address %s\n", fmtaddr(n_ip, 0)); + //~ } + //~ } + //~ } + //~ } +//~ } + +uint16_t guint16_index_loadlist; // return the next session can be used on the group -sessionidt grp_getnextsession(groupidt g, in_addr_t ip) +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; - uint32_t mintxrate = 0xFFFFFFFF; + uint32_t ltime_changed = 0, mintxrate = 0xFFFFFFFF, maxtxrate = 0; + uint32_t txrate = 0; if (g >= MAXGROUPE) return 0; - if ((s = sessionbyip(ip))) + if (grpsession[g].time_changed >= config->grp_txrate_average_time) { - if ( (session[s].ppp.phase > Establish) && - (time_now - session[s].last_packet <= (config->echo_timeout + 2)) ) + // recalculation txrate + ltime_changed = grpsession[g].time_changed; + grpsession[g].time_changed = 0; + for (i = 0; i < grpsession[g].nbsession; i++) { - int recaltxrate = 0; + if ((s2 = grpsession[g].sesslist[i].sid)) + { + uint32_t coutgrp_delta = 0; + if (session[s2].cout >= grpsession[g].sesslist[i].prev_coutgrp) + coutgrp_delta = session[s2].cout - grpsession[g].sesslist[i].prev_coutgrp; + grpsession[g].sesslist[i].prev_coutgrp = session[s2].cout; + + txrate = (txrate + (coutgrp_delta/ltime_changed)) >> 1; + grpsession[g].sesslist[i].tx_rate = txrate; + + txrate = grpsession[g].sesslist[i].tx_rate/grpsession[g].sesslist[i].weight; + if (txrate < mintxrate) + { + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) + { + grpsession[g].smin = s2; + mintxrate = txrate; + } + } + + if (txrate > maxtxrate) + { + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) + { + grpsession[g].smax = s2; + maxtxrate = txrate; + } + } + } + } + } + + if (!is_ipv6) + s = sessionbyip(*p_ipv4); + + if (s || is_ipv6) + { + 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; + grp_local[g].sid_loaddist[ai] = s; + } + + if (g != grp_groupbysession(s)) + { + // This session does not belong to this group + LOG(3, s, session[s].tunnel, "Warning, the session does not belong to group %d\n", g); + s = 0; + grp_local[g].sid_loaddist[ai] = 0; + } + else if ( (session[s].ppp.phase > Establish) && + (time_now - session[s].last_packet <= (config->echo_timeout + 1)) ) + { + grp_local[g].sid_loaddist[guint16_index_loadlist++] = 0; + return s; + } + else + { + s = 0; + grp_local[g].sid_loaddist[ai] = 0; + } + } + + if (!s) + { + // random between 0 and nbsession-1 + uint indexsess = (rand() % grpsession[g].nbsession); + + if (indexsess >= grpsession[g].nbsession) + indexsess = 0; //Sanity checks. + + s2 = grpsession[g].sesslist[indexsess].sid; + if (s2 && + (session[s2].ppp.phase > Establish) && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1))) + { + s = s2; + } + else + { for (i = 0; i < grpsession[g].nbsession; i++) { - if (s == grpsession[g].sesslist[i].sid) + if ((s2 = grpsession[g].sesslist[i].sid)) { - if ((time_now - grpsession[g].sesslist[i].mark_time) > config->grp_txrate_average_time) + s3 = s2; + + if ( session[s2].ppp.phase > Establish && + (time_now - session[s2].last_packet <= (config->echo_timeout + 1)) ) { - grpsession[g].sesslist[i].mark_time = time_now; - recaltxrate = 1; + s = s2; break; } } } - - if (!recaltxrate) - return s; } } - //if (grpsession[g].time_changed > config->grp_txrate_average_time) - if (grpsession[g].time_changed > 1) + if (!s) + s = s3; + + if (s && !is_ipv6) { - ltime_changed = grpsession[g].time_changed; - grpsession[g].time_changed = 1; + cache_ipmap(ntohl(*p_ipv4), s); } - for (i = 0; i < grpsession[g].nbsession; i++) + 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) { - if ((s2 = grpsession[g].sesslist[i].sid)) + 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++) { - s3 = s2; - if (ltime_changed) + if (grpsession[g].sesslist[i].sid != new->sesslist[i].sid) { - grpsession[g].sesslist[i].tx_rate = session[s2].coutgrp_delta/ltime_changed; - session[s2].coutgrp_delta = grpsession[g].sesslist[i].tx_rate; - //LOG(3, s2, session[s2].tunnel, "TX Rate: %d session weight: %d\n", grpsession[g].sesslist[i].tx_rate, grpsession[g].sesslist[i].weight); + updategroup = 1; + break; } + } + } - if ( session[s2].ppp.phase > Establish && - (time_now - session[s2].last_packet <= (config->echo_timeout + 2)) ) + if (!updategroup) + { + // Check routes list + for (i = 0; i < grpsession[g].nbroutesgrp; i++) + { + if (grpsession[g].route[i].ip != new->route[i].ip) { - uint32_t txrate = grpsession[g].sesslist[i].tx_rate/grpsession[g].sesslist[i].weight; - if (txrate < mintxrate) - { - s = s2; - mintxrate = txrate; - } + updategroup = 1; + break; } } } - if (!s) - s = s3; + 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; + } - if (s) - cache_ipmap(ntohl(ip), s); + // needs update + if (updategroup) + { + // Del all routes + grp_setgrouproute(g, 0); + grp_setgrouproute6(g, 0); + } - return s; + 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; }