From dccdfa00e111d23069dbd94182e75ec1bd47d4fa Mon Sep 17 00:00:00 2001 From: Daniel Hokka Zakrisson Date: Sat, 24 Nov 2007 20:22:17 +0000 Subject: [PATCH] _Much_ more complex vip6-autod, now cleans up after a stopped guest too. --- src/vip6-autod.c | 411 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 295 insertions(+), 116 deletions(-) diff --git a/src/vip6-autod.c b/src/vip6-autod.c index 2d48dcd..0c0ee1a 100644 --- a/src/vip6-autod.c +++ b/src/vip6-autod.c @@ -1,5 +1,5 @@ /* - * $Id: vip6-autod.c,v 1.4 2007/07/30 14:59:11 dhozac Exp $ + * $Id$ * Copyright (c) 2007 The Trustees of Princeton University * Author: Daniel Hokka Zakrisson * @@ -28,9 +28,6 @@ #include #include -/* not defined for gcc -ansi */ -typedef unsigned long long __u64; -typedef signed long long __s64; #include #include @@ -39,13 +36,12 @@ typedef signed long long __s64; #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 { @@ -58,7 +54,18 @@ struct prefix_list { 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; @@ -83,7 +90,8 @@ static inline int ipv6_prefix_equal(struct in6_addr *prefix, 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; @@ -105,52 +113,171 @@ static int add_address_to_interface(int ifindex, struct in6_addr *address, int p return err; } -static int add_nid_to_list(struct nid_list **l, nid_t nid) +static inline int remove_address_from_interface(struct nid_prefix_map *entry) { - struct nid_list *n; - for (n = *l; n; n = n->next) { - if (n->nid == nid) - return 0; + 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; } - n = calloc(1, sizeof(struct nid_list)); - if (!n) + + nl = nl_addr_build(AF_INET6, &a, sizeof(a)); + if (!nl) return -1; - n->nid = nid; - n->next = *l; - *l = n; + 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 int add_to_map(struct nid_prefix_map *map, struct nid_prefix_map *new) +{ + 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; } -static void cleanup_prefix(struct prefix_list *i) +static inline void remove_from_map(struct nid_prefix_map *map, + struct nid_prefix_map *entry) { - struct nid_list *n, *p = NULL; + 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; +} - 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_and_free(struct nid_prefix_map *map, + struct nid_prefix_map *entry) +{ + remove_from_map(map, entry); + free(entry); +} - if (p) - free(p); - memcpy(&a, &i->address.addr, sizeof(a)); - rta = rtnl_addr_alloc(); - nl = nl_addr_build(AF_INET6, &a, sizeof(a)); +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; - 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); + if (!new) + return -1; - /* ignore errors */ - rtnl_addr_delete(handle, rta, 0); + 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; - nl_addr_destroy(nl); - rtnl_addr_free(rta); + for (i = first; i && first->prefix == i->prefix; i = i->p.next) { + if (p) + remove_from_map_and_free(map, p); + + /* ignore errors */ + remove_address_from_interface(i); - p = n; + p = i; } if (p) - free(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) @@ -165,88 +292,129 @@ static inline void free_nid_list(struct nid_list *head) free(p); } -static void do_slices_autoconf(struct prefix_list *head) +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); +} + +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 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(%u): %s", nid, strerror(errno)); continue; } add_nid_to_list(¤t, nid); + } + closedir(dp); - for (i = head->next; i;) { + 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.type = vcNET_IPV6; - 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 & 0x7f80) >> 7)); - addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0x7f) << 25)); - if (vc_net_add(nid, &addr) == -1) { - syslog(LOG_ERR, "vc_net_add(%u): %s", nid, strerror(errno)); + 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; + } + 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->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) { + 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)); goto next; } - if (add_nid_to_list(&i->nids, nid) == -1) { - syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno)); + 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); - free_nid_list(previous); + 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 */ @@ -254,36 +422,39 @@ static int add_prefix(struct prefix_list *head, struct prefixmsg *msg, 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; @@ -291,27 +462,27 @@ static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg, 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; } @@ -389,12 +560,21 @@ int handle_no_op(struct nl_msg *msg, void *arg) } /* 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; } } @@ -413,15 +593,14 @@ static int write_pidfile(const char *filename) 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); -- 2.43.0