/*
- * $Id: vip6-autod.c,v 1.3 2007/07/26 23:08:00 dhozac Exp $
+ * $Id$
* Copyright (c) 2007 The Trustees of Princeton University
* Author: Daniel Hokka Zakrisson <daniel@hozac.com>
*
#include <syslog.h>
#include <asm/types.h>
-/* not defined for gcc -ansi */
-typedef uint64_t __u64;
-typedef int64_t __s64;
#include <netlink/netlink.h>
#include <netlink/route/addr.h>
#define HAS_ADDRESS 0x01
#define HAS_PREFIX 0x02
+
struct nid_list {
nid_t nid;
struct nid_list *next;
};
-struct prefix_list {
- struct prefix_list *prev;
- struct prefix_list *next;
+struct prefix {
uint32_t mask;
int ifindex;
struct {
int prefix_len;
time_t valid_until;
} address;
- struct nid_list *nids;
+};
+struct nid_prefix_map {
+ struct {
+ struct nid_prefix_map *prev;
+ struct nid_prefix_map *next;
+ } n;
+ struct {
+ struct nid_prefix_map *prev;
+ struct nid_prefix_map *next;
+ } p;
+ struct prefix *prefix;
+ nid_t nid;
};
struct nl_handle *handle;
return 1;
}
-static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix)
+static int add_address_to_interface(int ifindex, struct in6_addr *address,
+ int prefix)
{
int err = -1;
struct rtnl_addr *rta;
return err;
}
-static int add_nid_to_list(struct prefix_list *i, nid_t nid)
+static inline int remove_address_from_interface(struct nid_prefix_map *entry)
{
- struct nid_list *n;
- n = calloc(1, sizeof(struct nid_list));
- if (!n)
+ struct rtnl_addr *rta;
+ struct nl_addr *nl;
+ struct in6_addr a;
+ int ret;
+
+ memcpy(&a, &entry->prefix->address.addr, sizeof(a));
+ if (entry->nid != 0) {
+ a.s6_addr[11] = (entry->nid & 0x7f80) >> 7;
+ a.s6_addr[12] = (entry->nid & 0x7f) << 1;
+ }
+
+ nl = nl_addr_build(AF_INET6, &a, sizeof(a));
+ if (!nl)
return -1;
- n->nid = nid;
- n->next = i->nids;
- i->nids = n;
- return 0;
+ rta = rtnl_addr_alloc();
+ if (!rta)
+ return -1;
+
+ rtnl_addr_set_family(rta, AF_INET6);
+ rtnl_addr_set_ifindex(rta, entry->prefix->ifindex);
+ rtnl_addr_set_local(rta, nl);
+ rtnl_addr_set_prefixlen(rta, entry->prefix->address.prefix_len);
+
+ ret = rtnl_addr_delete(handle, rta, 0);
+
+ rtnl_addr_free(rta);
+ nl_addr_destroy(nl);
+
+ return ret;
}
-static void cleanup_prefix(struct prefix_list *i)
+static int add_to_map(struct nid_prefix_map *map, struct nid_prefix_map *new)
{
- struct nid_list *n;
+ struct nid_prefix_map *i;
+#define PUT_IT_IN_PLACE(node, member, om) \
+ /* find the correct location in the list */ \
+ for (i = map->node.next; i->node.next && i->member < \
+ new->member; i = i->node.next) \
+ ; \
+ if (i && i->member == new->member && i->om == new->om) \
+ return 0; \
+ /* first in the list */ \
+ if (!i || !i->node.prev) { \
+ new->node.prev = NULL; \
+ new->node.next = i; \
+ map->node.next = new; \
+ if (i) \
+ i->node.prev = new; \
+ } \
+ /* last in the list */ \
+ else if (i->node.next == NULL) { \
+ new->node.prev = i; \
+ new->node.next = NULL; \
+ i->node.next = new; \
+ } \
+ /* somewhere in the middle */ \
+ else { \
+ new->node.prev = i->node.prev; \
+ new->node.next = i; \
+ i->node.prev->node.next = new; \
+ i->node.prev = new; \
+ }
+ PUT_IT_IN_PLACE(p, prefix, nid)
+ PUT_IT_IN_PLACE(n, nid, prefix)
+ return 1;
+}
- for (n = i->nids; n; n = n->next) {
- struct rtnl_addr *rta;
- struct nl_addr *nl;
- struct in6_addr a;
+static inline void remove_from_map(struct nid_prefix_map *map,
+ struct nid_prefix_map *entry)
+{
+ if (map->n.next == entry)
+ map->n.next = entry->n.next;
+ if (map->n.prev == entry)
+ map->n.prev = entry->n.prev;
+ if (map->p.next == entry)
+ map->p.next = entry->p.next;
+ if (map->p.prev == entry)
+ map->p.prev = entry->p.prev;
+}
- memcpy(&a, &i->address.addr, sizeof(a));
- rta = rtnl_addr_alloc();
- nl = nl_addr_build(AF_INET6, &a, sizeof(a));
+static inline void remove_from_map_and_free(struct nid_prefix_map *map,
+ struct nid_prefix_map *entry)
+{
+ remove_from_map(map, entry);
+ free(entry);
+}
+
+static int add_nid_to_map(struct nid_prefix_map *map, struct prefix *prefix,
+ nid_t nid)
+{
+ struct nid_prefix_map *new = calloc(1, sizeof(struct nid_prefix_map));
+ int ret;
+
+ if (!new)
+ return -1;
- rtnl_addr_set_family(rta, AF_INET6);
- rtnl_addr_set_ifindex(rta, i->ifindex);
- rtnl_addr_set_local(rta, nl);
- rtnl_addr_set_prefixlen(rta, i->address.prefix_len);
+ new->prefix = prefix;
+ new->nid = nid;
+ ret = add_to_map(map, new);
+
+ if (ret == 0)
+ free(new);
+
+ return ret;
+}
+
+static int add_prefix_to_map(struct nid_prefix_map *map, struct prefix *prefix)
+{
+ return add_nid_to_map(map, prefix, 0);
+}
+
+static void cleanup_prefix(struct nid_prefix_map *map,
+ struct nid_prefix_map *first)
+{
+ struct nid_prefix_map *i, *p = NULL;
+
+ for (i = first; i && first->prefix == i->prefix; i = i->p.next) {
+ if (p)
+ remove_from_map_and_free(map, p);
/* ignore errors */
- rtnl_addr_delete(handle, rta, 0);
+ remove_address_from_interface(i);
+
+ p = i;
+ }
+ if (p)
+ remove_from_map_and_free(map, p);
+}
+
+static inline int add_nid_to_list(struct nid_list **head, nid_t nid)
+{
+ struct nid_list *i, *new;
+
+ for (i = *head; i && i->next && i->next->nid < nid; i = i->next)
+ ;
+ /* check if this nid is first in the list */
+ if (i && i->nid == nid)
+ return 0;
+ /* check if it's already in the list */
+ if (i && i->next && i->next->nid == nid)
+ return 0;
+
+ /* add it */
+ new = calloc(1, sizeof(struct nid_list));
+ if (!new)
+ return -1;
+ new->nid = nid;
+
+ /* this is the lowest nid in the list */
+ if (i == *head) {
+ *head = new;
+ new->next = i;
+ }
+ /* in the middle/at the end */
+ else if (i) {
+ new->next = i->next;
+ i->next = new;
+ }
+ /* there was no list */
+ else
+ *head = new;
+
+ return 1;
+}
+
+static inline void free_nid_list(struct nid_list *head)
+{
+ struct nid_list *p;
+ for (p = NULL; head; head = head->next) {
+ if (p)
+ free(p);
+ p = head;
+ }
+ if (p)
+ free(p);
+}
+
+static inline void cleanup_nid(struct nid_prefix_map *map,
+ nid_t nid)
+{
+ struct nid_prefix_map *i, *p = NULL;
+ for (i = map->n.next; i->nid < nid; i = i->n.next)
+ ;
+ /* this nid doesn't have any entries in the map */
+ if (i->nid != nid)
+ return;
+ for (; i->nid == nid; i = i->n.next) {
+ if (p)
+ remove_from_map_and_free(map, p);
+ remove_address_from_interface(i);
+ p = i;
+ }
+ if (p)
+ remove_from_map_and_free(map, p);
+}
- nl_addr_destroy(nl);
- rtnl_addr_free(rta);
+static inline void cleanup_nids(struct nid_prefix_map *map,
+ struct nid_list *previous,
+ struct nid_list *current)
+{
+ struct nid_list *p, *pprev = NULL, *c;
+ for (p = previous, c = current; p; pprev = p, p = p->next) {
+ if (pprev)
+ free(pprev);
+ while (c->nid < p->nid)
+ c = c->next;
+ if (c->nid == p->nid)
+ continue;
+ /* this context has disappeared */
+ cleanup_nid(map, p->nid);
}
+ if (pprev)
+ free(pprev);
}
-static void do_slices_autoconf(struct prefix_list *head)
+static void do_slices_autoconf(struct nid_prefix_map *map)
{
DIR *dp;
struct dirent *de;
- nid_t nid;
- struct vc_net_nx addr;
- struct prefix_list *i;
+ struct vc_net_addr addr;
+ struct nid_prefix_map *i;
+ struct nid_list *current = NULL, *n;
+ static struct nid_list *previous = NULL;
if ((dp = opendir("/proc/virtnet")) == NULL)
return;
while ((de = readdir(dp)) != NULL) {
+ nid_t nid;
+
if (!isdigit(de->d_name[0]))
continue;
nid = strtoul(de->d_name, NULL, 10);
- addr.type = vcNET_IPV6A;
- addr.count = 0;
+ addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
if (vc_net_remove(nid, &addr) == -1) {
- syslog(LOG_ERR, "vc_net_remove: %s", strerror(errno));
+ syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno));
continue;
}
- for (i = head->next; i;) {
+ add_nid_to_list(¤t, nid);
+ }
+ closedir(dp);
+
+ for (n = current; n; n = n->next) {
+ for (i = map->p.next; i && i->nid == 0;) {
/* expired */
- if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
- struct prefix_list *tmp;
+ if (i->prefix->mask & HAS_PREFIX && i->prefix->prefix.valid_until < time(NULL)) {
+ struct nid_prefix_map *tmp;
char buf[64];
- inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf));
+ inet_ntop(AF_INET6, &i->prefix->address.addr, buf, sizeof(buf));
syslog(LOG_NOTICE, "Address %s timed out", buf);
- if (i->next)
- i->next->prev = i->prev;
- if (i->prev)
- i->prev->next = i->next;
- tmp = i->next;
+ tmp = i->p.next;
- cleanup_prefix(i);
+ cleanup_prefix(map, i);
- free(i);
i = tmp;
continue;
}
- if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
+ if (i->prefix->mask != (HAS_ADDRESS|HAS_PREFIX))
goto next;
- addr.count = 1;
- addr.mask[0] = i->prefix.prefix_len;
- memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr));
- addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f00) >> 7));
- addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0xff) << 25));
- if (vc_net_add(nid, &addr) == -1) {
- syslog(LOG_ERR, "vc_net_add: %s", strerror(errno));
- exit(1);
+ addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ADDR;
+ memcpy(&addr.vna_v6_ip, &i->prefix->address.addr, sizeof(struct in6_addr));
+ addr.vna_prefix = i->prefix->prefix.prefix_len;
+ if (addr.vna_prefix == 64) {
+ addr.vna_v6_mask.s6_addr32[0] = addr.vna_v6_mask.s6_addr32[1] = 0xffffffff;
+ addr.vna_v6_mask.s6_addr32[2] = addr.vna_v6_mask.s6_addr32[3] = 0;
}
- if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) {
+ addr.vna_v6_ip.s6_addr[11] = (n->nid & 0x7f80) >> 7;
+ addr.vna_v6_ip.s6_addr[12] = (n->nid & 0x007f) << 1;
+ if (vc_net_add(n->nid, &addr) == -1) {
+ syslog(LOG_ERR, "vc_net_add(%u): %s", n->nid, strerror(errno));
+ goto next;
+ }
+ if (add_address_to_interface(i->prefix->ifindex, &addr.vna_v6_ip, addr.vna_prefix) == -1) {
syslog(LOG_ERR, "add_address_to_interface: %s", strerror(errno));
- exit(1);
+ goto next;
}
- if (add_nid_to_list(i, nid) == -1) {
- syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno));
- exit(1);
+ if (add_nid_to_map(map, i->prefix, n->nid) == -1) {
+ syslog(LOG_ERR, "add_nid_to_map: %s", strerror(errno));
+ goto next;
}
next:
- i = i->next;
+ i = i->p.next;
}
}
- closedir(dp);
+
+ cleanup_nids(map, previous, current);
+ previous = current;
}
-static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
+/* XXX These two functions are very similar */
+static int add_prefix(struct nid_prefix_map *map, struct prefixmsg *msg,
struct in6_addr *prefix, struct prefix_cacheinfo *cache)
{
- struct prefix_list *i = head;
+ struct nid_prefix_map *i = map;
+ struct prefix *new;
+
if (!msg || !prefix || !cache)
return -1;
/* XXX IF_PREFIX_AUTOCONF == 0x02 */
return -1;
do {
- if (i->next != NULL)
- i = i->next;
- if (ipv6_prefix_equal(prefix, &i->prefix.addr, msg->prefix_len) ||
- ipv6_prefix_equal(prefix, &i->address.addr, msg->prefix_len)) {
- i->mask |= HAS_PREFIX;
- i->ifindex = msg->prefix_ifindex;
- memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
- i->prefix.prefix_len = msg->prefix_len;
- i->prefix.valid_until = time(NULL) + cache->preferred_time;
+ if (i->p.next != NULL)
+ i = i->p.next;
+ if (ipv6_prefix_equal(prefix, &i->prefix->prefix.addr, msg->prefix_len) ||
+ ipv6_prefix_equal(prefix, &i->prefix->address.addr, msg->prefix_len)) {
+ i->prefix->mask |= HAS_PREFIX;
+ i->prefix->ifindex = msg->prefix_ifindex;
+ memcpy(&i->prefix->prefix.addr, prefix, sizeof(*prefix));
+ i->prefix->prefix.prefix_len = msg->prefix_len;
+ i->prefix->prefix.valid_until = time(NULL) + cache->preferred_time;
return 0;
}
- } while (i->next);
+ } while (i->p.next && i->nid == 0);
- i->next = calloc(1, sizeof(*i));
- if (!i->next)
+ /* not yet in the map */
+ new = calloc(1, sizeof(*new));
+ if (!new)
+ return -1;
+ new->mask = HAS_PREFIX;
+ memcpy(&new->prefix.addr, prefix, sizeof(*prefix));
+ new->prefix.prefix_len = msg->prefix_len;
+ new->prefix.valid_until = time(NULL) + cache->preferred_time;
+ if (add_prefix_to_map(map, new) == -1)
return -1;
- i->next->prev = i;
- i = i->next;
- i->mask = HAS_PREFIX;
- memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
- i->prefix.prefix_len = msg->prefix_len;
- i->prefix.valid_until = time(NULL) + cache->preferred_time;
return 1;
}
-static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
+static inline int add_address(struct nid_prefix_map *map, struct ifaddrmsg *msg,
struct in6_addr *address, struct ifa_cacheinfo *cache)
{
- struct prefix_list *i = head;
+ struct nid_prefix_map *i = map;
+ struct prefix *new;
+
if (!msg || !address || !cache)
return -1;
return -1;
do {
- if (i->next != NULL)
- i = i->next;
- if (ipv6_prefix_equal(address, &i->prefix.addr, msg->ifa_prefixlen) ||
- ipv6_prefix_equal(address, &i->address.addr, msg->ifa_prefixlen)) {
- i->mask |= HAS_ADDRESS;
- memcpy(&i->address.addr, address, sizeof(*address));
- i->address.prefix_len = msg->ifa_prefixlen;
- i->address.valid_until = time(NULL) + cache->ifa_prefered;
+ if (i->p.next != NULL)
+ i = i->p.next;
+ if (ipv6_prefix_equal(address, &i->prefix->prefix.addr, msg->ifa_prefixlen) ||
+ ipv6_prefix_equal(address, &i->prefix->address.addr, 128)) {
+ i->prefix->mask |= HAS_ADDRESS;
+ memcpy(&i->prefix->address.addr, address, sizeof(*address));
+ i->prefix->address.prefix_len = msg->ifa_prefixlen;
+ i->prefix->address.valid_until = time(NULL) + cache->ifa_prefered;
return 0;
}
- } while (i->next);
+ } while (i->p.next && i->nid == 0);
- i->next = calloc(1, sizeof(*i));
- if (!i->next)
+ new = calloc(1, sizeof(*new));
+ if (!new)
+ return -1;
+ new->mask = HAS_ADDRESS;
+ memcpy(&new->address.addr, address, sizeof(*address));
+ new->address.prefix_len = msg->ifa_prefixlen;
+ new->address.valid_until = time(NULL) + cache->ifa_prefered;
+ if (add_prefix_to_map(map, new) == -1)
return -1;
- i->next->prev = i;
- i = i->next;
- i->mask = HAS_ADDRESS;
- memcpy(&i->address.addr, address, sizeof(*address));
- i->address.prefix_len = msg->ifa_prefixlen;
- i->address.valid_until = time(NULL) + cache->ifa_prefered;
return 1;
}
}
/* only for access in the signal handler */
-struct prefix_list head;
+struct nid_prefix_map map = {
+ .n = {
+ .next = NULL,
+ .prev = NULL,
+ },
+ .p = {
+ .next = NULL,
+ .prev = NULL,
+ },
+};
void signal_handler(int signal)
{
switch (signal) {
case SIGUSR1:
- do_slices_autoconf(&head);
+ do_slices_autoconf(&map);
break;
}
}
int main(int argc, char *argv[])
{
struct nl_cb *cbs;
- head.prev = head.next = NULL;
openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
cbs = nl_handle_get_cb(handle);
- nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &head);
+ nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &map);
nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL);
- nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &head);
+ nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &map);
nl_disable_sequence_check(handle);
nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR);