3 * Copyright (c) 2007 The Trustees of Princeton University
4 * Author: Daniel Hokka Zakrisson <daniel@hozac.com>
6 * Licensed under the terms of the GNU General Public License
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/ioctl.h>
23 #include <arpa/inet.h>
30 #include <asm/types.h>
31 #include <netlink/netlink.h>
32 #if defined(HAVE_LIBNL_1_1)
33 # include <netlink/handlers.h>
34 #elif defined(HAVE_LIBNL_1_0)
35 # define rtnl_addr_put rtnl_addr_free
37 #include <netlink/route/addr.h>
41 #define HAS_ADDRESS 0x01
42 #define HAS_PREFIX 0x02
46 struct nid_list *next;
62 struct nid_prefix_map {
64 struct nid_prefix_map *prev;
65 struct nid_prefix_map *next;
68 struct nid_prefix_map *prev;
69 struct nid_prefix_map *next;
71 struct prefix *prefix;
75 struct nl_handle *handle;
77 /* from linux/include/net/ipv6.h */
78 static inline int ipv6_prefix_equal(struct in6_addr *prefix,
79 struct in6_addr *addr, int prefixlen)
81 uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
84 /* check complete u32 in prefix */
86 if (pdw && memcmp(a1, a2, pdw << 2))
89 /* check incomplete u32 in prefix */
90 pbi = prefixlen & 0x1f;
91 if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
97 static int add_address_to_interface(int ifindex, struct in6_addr *address,
101 struct rtnl_addr *rta;
104 nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr));
105 rta = rtnl_addr_alloc();
107 rtnl_addr_set_family(rta, AF_INET6);
108 rtnl_addr_set_ifindex(rta, ifindex);
109 rtnl_addr_set_local(rta, nl);
110 rtnl_addr_set_prefixlen(rta, prefix);
112 if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST)
120 static inline int remove_address_from_interface(struct nid_prefix_map *entry)
122 struct rtnl_addr *rta;
127 memcpy(&a, &entry->prefix->address.addr, sizeof(a));
128 if (entry->nid != 0) {
129 a.s6_addr[11] = (entry->nid & 0x7f80) >> 7;
130 a.s6_addr[12] = (entry->nid & 0x7f) << 1;
133 nl = nl_addr_build(AF_INET6, &a, sizeof(a));
136 rta = rtnl_addr_alloc();
140 rtnl_addr_set_family(rta, AF_INET6);
141 rtnl_addr_set_ifindex(rta, entry->prefix->ifindex);
142 rtnl_addr_set_local(rta, nl);
143 rtnl_addr_set_prefixlen(rta, entry->prefix->address.prefix_len);
145 ret = rtnl_addr_delete(handle, rta, 0);
153 static int add_to_map(struct nid_prefix_map *map, struct nid_prefix_map *new)
155 struct nid_prefix_map *i;
156 #define PUT_IT_IN_PLACE(node, member, om) \
157 /* find the correct location in the list */ \
158 for (i = map->node.next; i->node.next && i->member < \
159 new->member; i = i->node.next) \
161 if (i && i->member == new->member && i->om == new->om) \
163 /* first in the list */ \
164 if (!i || !i->node.prev) { \
165 new->node.prev = NULL; \
166 new->node.next = i; \
167 map->node.next = new; \
169 i->node.prev = new; \
171 /* last in the list */ \
172 else if (i->node.next == NULL) { \
173 new->node.prev = i; \
174 new->node.next = NULL; \
175 i->node.next = new; \
177 /* somewhere in the middle */ \
179 new->node.prev = i->node.prev; \
180 new->node.next = i; \
181 i->node.prev->node.next = new; \
182 i->node.prev = new; \
184 PUT_IT_IN_PLACE(p, prefix, nid)
185 PUT_IT_IN_PLACE(n, nid, prefix)
189 static inline void remove_from_map(struct nid_prefix_map *map,
190 struct nid_prefix_map *entry)
192 if (map->n.next == entry)
193 map->n.next = entry->n.next;
194 if (map->n.prev == entry)
195 map->n.prev = entry->n.prev;
196 if (map->p.next == entry)
197 map->p.next = entry->p.next;
198 if (map->p.prev == entry)
199 map->p.prev = entry->p.prev;
202 static inline void remove_from_map_and_free(struct nid_prefix_map *map,
203 struct nid_prefix_map *entry)
205 remove_from_map(map, entry);
209 static int add_nid_to_map(struct nid_prefix_map *map, struct prefix *prefix,
212 struct nid_prefix_map *new = calloc(1, sizeof(struct nid_prefix_map));
218 new->prefix = prefix;
220 ret = add_to_map(map, new);
228 static int add_prefix_to_map(struct nid_prefix_map *map, struct prefix *prefix)
230 return add_nid_to_map(map, prefix, 0);
233 static void cleanup_prefix(struct nid_prefix_map *map,
234 struct nid_prefix_map *first)
236 struct nid_prefix_map *i, *p = NULL;
238 for (i = first; i && first->prefix == i->prefix; i = i->p.next) {
240 remove_from_map_and_free(map, p);
243 remove_address_from_interface(i);
248 remove_from_map_and_free(map, p);
251 static inline int add_nid_to_list(struct nid_list **head, nid_t nid)
253 struct nid_list *i, *new;
255 for (i = *head; i && i->next && i->next->nid < nid; i = i->next)
257 /* check if this nid is first in the list */
258 if (i && i->nid == nid)
260 /* check if it's already in the list */
261 if (i && i->next && i->next->nid == nid)
265 new = calloc(1, sizeof(struct nid_list));
270 /* this is the lowest nid in the list */
275 /* in the middle/at the end */
280 /* there was no list */
287 static inline void free_nid_list(struct nid_list *head)
290 for (p = NULL; head; head = head->next) {
299 static inline void cleanup_nid(struct nid_prefix_map *map,
302 struct nid_prefix_map *i, *p = NULL;
303 for (i = map->n.next; i->nid < nid; i = i->n.next)
305 /* this nid doesn't have any entries in the map */
308 for (; i->nid == nid; i = i->n.next) {
310 remove_from_map_and_free(map, p);
311 remove_address_from_interface(i);
315 remove_from_map_and_free(map, p);
318 static inline void cleanup_nids(struct nid_prefix_map *map,
319 struct nid_list *previous,
320 struct nid_list *current)
322 struct nid_list *p, *pprev = NULL, *c;
323 for (p = previous, c = current; p; pprev = p, p = p->next) {
326 while (c->nid < p->nid)
328 if (c->nid == p->nid)
330 /* this context has disappeared */
331 cleanup_nid(map, p->nid);
337 static void do_slices_autoconf(struct nid_prefix_map *map)
341 struct vc_net_addr addr;
342 struct nid_prefix_map *i;
343 struct nid_list *current = NULL, *n;
344 static struct nid_list *previous = NULL;
346 if ((dp = opendir("/proc/virtnet")) == NULL)
348 while ((de = readdir(dp)) != NULL) {
351 if (!isdigit(de->d_name[0]))
354 nid = strtoul(de->d_name, NULL, 10);
355 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
356 if (vc_net_remove(nid, &addr) == -1) {
357 syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno));
361 add_nid_to_list(¤t, nid);
365 for (n = current; n; n = n->next) {
366 for (i = map->p.next; i && i->nid == 0;) {
368 if (i->prefix->mask & HAS_PREFIX && i->prefix->prefix.valid_until < time(NULL)) {
369 struct nid_prefix_map *tmp;
372 inet_ntop(AF_INET6, &i->prefix->address.addr, buf, sizeof(buf));
373 syslog(LOG_NOTICE, "Address %s timed out", buf);
377 cleanup_prefix(map, i);
382 if (i->prefix->mask != (HAS_ADDRESS|HAS_PREFIX))
385 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ADDR;
386 memcpy(&addr.vna_v6_ip, &i->prefix->address.addr, sizeof(struct in6_addr));
387 addr.vna_prefix = i->prefix->prefix.prefix_len;
388 if (addr.vna_prefix == 64) {
389 addr.vna_v6_mask.s6_addr32[0] = addr.vna_v6_mask.s6_addr32[1] = 0xffffffff;
390 addr.vna_v6_mask.s6_addr32[2] = addr.vna_v6_mask.s6_addr32[3] = 0;
392 addr.vna_v6_ip.s6_addr[11] = (n->nid & 0x7f80) >> 7;
393 addr.vna_v6_ip.s6_addr[12] = (n->nid & 0x007f) << 1;
394 if (vc_net_add(n->nid, &addr) == -1) {
395 syslog(LOG_ERR, "vc_net_add(%u): %s", n->nid, strerror(errno));
398 if (add_address_to_interface(i->prefix->ifindex, &addr.vna_v6_ip, addr.vna_prefix) == -1) {
399 syslog(LOG_ERR, "add_address_to_interface: %s", strerror(errno));
402 if (add_nid_to_map(map, i->prefix, n->nid) == -1) {
403 syslog(LOG_ERR, "add_nid_to_map: %s", strerror(errno));
411 cleanup_nids(map, previous, current);
415 /* XXX These two functions are very similar */
416 static int add_prefix(struct nid_prefix_map *map, struct prefixmsg *msg,
417 struct in6_addr *prefix, struct prefix_cacheinfo *cache)
419 struct nid_prefix_map *i = map;
422 if (!msg || !prefix || !cache)
424 /* XXX IF_PREFIX_AUTOCONF == 0x02 */
425 if (!(msg->prefix_flags & 0x02))
429 if (i->p.next != NULL)
431 if (ipv6_prefix_equal(prefix, &i->prefix->prefix.addr, msg->prefix_len) ||
432 ipv6_prefix_equal(prefix, &i->prefix->address.addr, msg->prefix_len)) {
433 i->prefix->mask |= HAS_PREFIX;
434 i->prefix->ifindex = msg->prefix_ifindex;
435 memcpy(&i->prefix->prefix.addr, prefix, sizeof(*prefix));
436 i->prefix->prefix.prefix_len = msg->prefix_len;
437 i->prefix->prefix.valid_until = time(NULL) + cache->preferred_time;
440 } while (i->p.next && i->nid == 0);
442 /* not yet in the map */
443 new = calloc(1, sizeof(*new));
446 new->mask = HAS_PREFIX;
447 memcpy(&new->prefix.addr, prefix, sizeof(*prefix));
448 new->prefix.prefix_len = msg->prefix_len;
449 new->prefix.valid_until = time(NULL) + cache->preferred_time;
450 if (add_prefix_to_map(map, new) == -1)
456 static inline int add_address(struct nid_prefix_map *map, struct ifaddrmsg *msg,
457 struct in6_addr *address, struct ifa_cacheinfo *cache)
459 struct nid_prefix_map *i = map;
462 if (!msg || !address || !cache)
465 if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
469 if (i->p.next != NULL)
471 if (ipv6_prefix_equal(address, &i->prefix->prefix.addr, msg->ifa_prefixlen) ||
472 ipv6_prefix_equal(address, &i->prefix->address.addr, 128)) {
473 i->prefix->mask |= HAS_ADDRESS;
474 memcpy(&i->prefix->address.addr, address, sizeof(*address));
475 i->prefix->address.prefix_len = msg->ifa_prefixlen;
476 i->prefix->address.valid_until = time(NULL) + cache->ifa_prefered;
479 } while (i->p.next && i->nid == 0);
481 new = calloc(1, sizeof(*new));
484 new->mask = HAS_ADDRESS;
485 memcpy(&new->address.addr, address, sizeof(*address));
486 new->address.prefix_len = msg->ifa_prefixlen;
487 new->address.valid_until = time(NULL) + cache->ifa_prefered;
488 if (add_prefix_to_map(map, new) == -1)
494 static struct nla_policy addr_policy[IFA_MAX+1] = {
495 [IFA_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
496 [IFA_LABEL] = { .type = NLA_STRING,
497 .maxlen = IFNAMSIZ },
498 [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) },
500 static struct nla_policy prefix_policy[PREFIX_MAX+1] = {
501 [PREFIX_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
502 [PREFIX_CACHEINFO] = { .minlen = sizeof(struct prefix_cacheinfo) },
504 int handle_valid_msg(struct nl_msg *msg, void *arg)
506 struct nlmsghdr *nlh = nlmsg_hdr(msg);
509 struct sockaddr_nl *source = nlmsg_get_src(msg);
511 payload = nlmsg_data(nlh);
512 if (source->nl_groups == RTMGRP_IPV6_PREFIX) {
513 struct prefixmsg *prefixmsg;
514 struct in6_addr *prefix = NULL;
515 struct prefix_cacheinfo *cacheinfo = NULL;
516 struct nlattr *tb[PREFIX_MAX+1];
518 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
519 syslog(LOG_ERR, "Failed to parse prefixmsg");
523 prefixmsg = (struct prefixmsg *) payload;
524 if (tb[PREFIX_ADDRESS])
525 prefix = nl_data_get(nla_get_data(tb[PREFIX_ADDRESS]));
526 if (tb[PREFIX_CACHEINFO])
527 cacheinfo = nl_data_get(nla_get_data(tb[PREFIX_CACHEINFO]));
528 ret = add_prefix(arg, prefixmsg, prefix, cacheinfo);
530 else if (source->nl_groups == RTMGRP_IPV6_IFADDR) {
531 struct ifaddrmsg *ifaddrmsg;
532 struct in6_addr *address = NULL;
533 struct ifa_cacheinfo *cacheinfo = NULL;
534 struct nlattr *tb[IFA_MAX+1];
536 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
537 syslog(LOG_ERR, "Failed to parse ifaddrmsg");
541 ifaddrmsg = (struct ifaddrmsg *) payload;
543 address = nl_data_get(nla_get_data(tb[IFA_ADDRESS]));
544 if (tb[IFA_CACHEINFO])
545 cacheinfo = nl_data_get(nla_get_data(tb[IFA_CACHEINFO]));
546 ret = add_address(arg, ifaddrmsg, address, cacheinfo);
549 do_slices_autoconf(arg);
554 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
557 syslog(LOG_ERR, "%s", strerror(err->error));
561 int handle_no_op(struct nl_msg *msg, void *arg)
566 /* only for access in the signal handler */
567 struct nid_prefix_map map = {
577 void signal_handler(int signal)
581 do_slices_autoconf(&map);
586 static int write_pidfile(const char *filename)
589 fp = fopen(filename, "w");
592 fprintf(fp, "%d\n", getpid());
597 int main(int argc, char *argv[])
601 openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
603 #if defined(HAVE_LIBNL_1_1)
604 cbs = nl_cb_alloc(NL_CB_VERBOSE);
606 syslog(LOG_CRIT, "nl_cb_alloc: %s", strerror(errno));
609 #elif defined(HAVE_LIBNL_1_0)
610 handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
611 cbs = nl_handle_get_cb(handle);
613 nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &map);
614 nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL);
615 nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &map);
616 #ifdef HAVE_LIBNL_1_1
617 handle = nl_handle_alloc_cb(cbs);
619 nl_disable_sequence_check(handle);
621 nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR);
622 if (nl_connect(handle, NETLINK_ROUTE) == -1) {
623 syslog(LOG_CRIT, "nl_connect: %s", strerror(errno));
627 if (daemon(0, 0) == -1)
630 write_pidfile(LOCALSTATEDIR "/run/vip6-autod.pid");
632 signal(SIGUSR1, signal_handler);
634 while (nl_recvmsgs(handle, cbs) > 0);