+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);
+ }
+ }
+ }
+}
+