2 * $Id: vip6-autod.c,v 1.4 2007/07/30 14:59:11 dhozac Exp $
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 /* not defined for gcc -ansi */
32 typedef unsigned long long __u64;
33 typedef signed long long __s64;
34 #include <netlink/netlink.h>
35 #include <netlink/route/addr.h>
38 #include "pathconfig.h"
40 #define HAS_ADDRESS 0x01
41 #define HAS_PREFIX 0x02
44 struct nid_list *next;
47 struct prefix_list *prev;
48 struct prefix_list *next;
61 struct nid_list *nids;
64 struct nl_handle *handle;
66 /* from linux/include/net/ipv6.h */
67 static inline int ipv6_prefix_equal(struct in6_addr *prefix,
68 struct in6_addr *addr, int prefixlen)
70 uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
73 /* check complete u32 in prefix */
75 if (pdw && memcmp(a1, a2, pdw << 2))
78 /* check incomplete u32 in prefix */
79 pbi = prefixlen & 0x1f;
80 if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
86 static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix)
89 struct rtnl_addr *rta;
92 nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr));
93 rta = rtnl_addr_alloc();
95 rtnl_addr_set_family(rta, AF_INET6);
96 rtnl_addr_set_ifindex(rta, ifindex);
97 rtnl_addr_set_local(rta, nl);
98 rtnl_addr_set_prefixlen(rta, prefix);
100 if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST)
108 static int add_nid_to_list(struct nid_list **l, nid_t nid)
111 for (n = *l; n; n = n->next) {
115 n = calloc(1, sizeof(struct nid_list));
124 static void cleanup_prefix(struct prefix_list *i)
126 struct nid_list *n, *p = NULL;
128 for (n = i->nids; n; n = n->next) {
129 struct rtnl_addr *rta;
135 memcpy(&a, &i->address.addr, sizeof(a));
136 rta = rtnl_addr_alloc();
137 nl = nl_addr_build(AF_INET6, &a, sizeof(a));
139 rtnl_addr_set_family(rta, AF_INET6);
140 rtnl_addr_set_ifindex(rta, i->ifindex);
141 rtnl_addr_set_local(rta, nl);
142 rtnl_addr_set_prefixlen(rta, i->address.prefix_len);
145 rtnl_addr_delete(handle, rta, 0);
156 static inline void free_nid_list(struct nid_list *head)
159 for (p = NULL; head; head = head->next) {
168 static void do_slices_autoconf(struct prefix_list *head)
173 struct vc_net_nx addr;
174 struct prefix_list *i;
175 struct nid_list *current = NULL, *n;
176 static struct nid_list *previous = NULL;
178 if ((dp = opendir("/proc/virtnet")) == NULL)
180 while ((de = readdir(dp)) != NULL) {
181 if (!isdigit(de->d_name[0]))
184 nid = strtoul(de->d_name, NULL, 10);
185 addr.type = vcNET_IPV6A;
187 if (vc_net_remove(nid, &addr) == -1) {
188 syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno));
192 add_nid_to_list(¤t, nid);
194 for (i = head->next; i;) {
196 if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
197 struct prefix_list *tmp;
200 inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf));
201 syslog(LOG_NOTICE, "Address %s timed out", buf);
204 i->next->prev = i->prev;
206 i->prev->next = i->next;
215 if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
218 addr.type = vcNET_IPV6;
220 addr.mask[0] = i->prefix.prefix_len;
221 memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr));
222 addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f80) >> 7));
223 addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0x7f) << 25));
224 if (vc_net_add(nid, &addr) == -1) {
225 syslog(LOG_ERR, "vc_net_add(%u): %s", nid, strerror(errno));
228 if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) {
229 syslog(LOG_ERR, "add_address_to_interface: %s", strerror(errno));
232 if (add_nid_to_list(&i->nids, nid) == -1) {
233 syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno));
242 free_nid_list(previous);
246 static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
247 struct in6_addr *prefix, struct prefix_cacheinfo *cache)
249 struct prefix_list *i = head;
250 if (!msg || !prefix || !cache)
252 /* XXX IF_PREFIX_AUTOCONF == 0x02 */
253 if (!(msg->prefix_flags & 0x02))
259 if (ipv6_prefix_equal(prefix, &i->prefix.addr, msg->prefix_len) ||
260 ipv6_prefix_equal(prefix, &i->address.addr, msg->prefix_len)) {
261 i->mask |= HAS_PREFIX;
262 i->ifindex = msg->prefix_ifindex;
263 memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
264 i->prefix.prefix_len = msg->prefix_len;
265 i->prefix.valid_until = time(NULL) + cache->preferred_time;
270 i->next = calloc(1, sizeof(*i));
275 i->mask = HAS_PREFIX;
276 memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
277 i->prefix.prefix_len = msg->prefix_len;
278 i->prefix.valid_until = time(NULL) + cache->preferred_time;
283 static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
284 struct in6_addr *address, struct ifa_cacheinfo *cache)
286 struct prefix_list *i = head;
287 if (!msg || !address || !cache)
290 if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
296 if (ipv6_prefix_equal(address, &i->prefix.addr, msg->ifa_prefixlen) ||
297 ipv6_prefix_equal(address, &i->address.addr, msg->ifa_prefixlen)) {
298 i->mask |= HAS_ADDRESS;
299 memcpy(&i->address.addr, address, sizeof(*address));
300 i->address.prefix_len = msg->ifa_prefixlen;
301 i->address.valid_until = time(NULL) + cache->ifa_prefered;
306 i->next = calloc(1, sizeof(*i));
311 i->mask = HAS_ADDRESS;
312 memcpy(&i->address.addr, address, sizeof(*address));
313 i->address.prefix_len = msg->ifa_prefixlen;
314 i->address.valid_until = time(NULL) + cache->ifa_prefered;
319 static struct nla_policy addr_policy[IFA_MAX+1] = {
320 [IFA_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
321 [IFA_LABEL] = { .type = NLA_STRING,
322 .maxlen = IFNAMSIZ },
323 [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) },
325 static struct nla_policy prefix_policy[PREFIX_MAX+1] = {
326 [PREFIX_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
327 [PREFIX_CACHEINFO] = { .minlen = sizeof(struct prefix_cacheinfo) },
329 int handle_valid_msg(struct nl_msg *msg, void *arg)
331 struct nlmsghdr *nlh = nlmsg_hdr(msg);
334 struct sockaddr_nl *source = nlmsg_get_src(msg);
336 payload = nlmsg_data(nlh);
337 if (source->nl_groups == RTMGRP_IPV6_PREFIX) {
338 struct prefixmsg *prefixmsg;
339 struct in6_addr *prefix = NULL;
340 struct prefix_cacheinfo *cacheinfo = NULL;
341 struct nlattr *tb[PREFIX_MAX+1];
343 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
344 syslog(LOG_ERR, "Failed to parse prefixmsg");
348 prefixmsg = (struct prefixmsg *) payload;
349 if (tb[PREFIX_ADDRESS])
350 prefix = nl_data_get(nla_get_data(tb[PREFIX_ADDRESS]));
351 if (tb[PREFIX_CACHEINFO])
352 cacheinfo = nl_data_get(nla_get_data(tb[PREFIX_CACHEINFO]));
353 ret = add_prefix(arg, prefixmsg, prefix, cacheinfo);
355 else if (source->nl_groups == RTMGRP_IPV6_IFADDR) {
356 struct ifaddrmsg *ifaddrmsg;
357 struct in6_addr *address = NULL;
358 struct ifa_cacheinfo *cacheinfo = NULL;
359 struct nlattr *tb[IFA_MAX+1];
361 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
362 syslog(LOG_ERR, "Failed to parse ifaddrmsg");
366 ifaddrmsg = (struct ifaddrmsg *) payload;
368 address = nl_data_get(nla_get_data(tb[IFA_ADDRESS]));
369 if (tb[IFA_CACHEINFO])
370 cacheinfo = nl_data_get(nla_get_data(tb[IFA_CACHEINFO]));
371 ret = add_address(arg, ifaddrmsg, address, cacheinfo);
374 do_slices_autoconf(arg);
379 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
382 syslog(LOG_ERR, "%s", strerror(err->error));
386 int handle_no_op(struct nl_msg *msg, void *arg)
391 /* only for access in the signal handler */
392 struct prefix_list head;
393 void signal_handler(int signal)
397 do_slices_autoconf(&head);
402 static int write_pidfile(const char *filename)
405 fp = fopen(filename, "w");
408 fprintf(fp, "%d\n", getpid());
413 int main(int argc, char *argv[])
416 head.prev = head.next = NULL;
418 openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
420 handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
421 cbs = nl_handle_get_cb(handle);
422 nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &head);
423 nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL);
424 nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &head);
425 nl_disable_sequence_check(handle);
427 nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR);
428 if (nl_connect(handle, NETLINK_ROUTE) == -1) {
429 syslog(LOG_CRIT, "nl_connect: %s", strerror(errno));
433 if (daemon(0, 0) == -1)
436 /* XXX .. here is a hack */
437 write_pidfile(DEFAULT_PKGSTATEDIR "/../vip6-autod.pid");
439 signal(SIGUSR1, signal_handler);
441 while (nl_recvmsgs(handle, cbs) > 0);