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 uint64_t __u64;
33 typedef int64_t __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 void do_slices_autoconf(struct prefix_list *head)
161 struct vc_net_nx addr;
162 struct prefix_list *i;
163 struct nid_list *current = NULL, *n;
165 if ((dp = opendir("/proc/virtnet")) == NULL)
167 while ((de = readdir(dp)) != NULL) {
168 if (!isdigit(de->d_name[0]))
171 nid = strtoul(de->d_name, NULL, 10);
172 addr.type = vcNET_IPV6A;
174 if (vc_net_remove(nid, &addr) == -1) {
175 syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno));
179 add_nid_to_list(¤t, nid);
181 for (i = head->next; i;) {
183 if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
184 struct prefix_list *tmp;
187 inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf));
188 syslog(LOG_NOTICE, "Address %s timed out", buf);
191 i->next->prev = i->prev;
193 i->prev->next = i->next;
202 if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
205 addr.type = vcNET_IPV6;
207 addr.mask[0] = i->prefix.prefix_len;
208 memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr));
209 addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f80) >> 7));
210 addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0x7f) << 25));
211 if (vc_net_add(nid, &addr) == -1) {
212 syslog(LOG_ERR, "vc_net_add(%u): %s", nid, strerror(errno));
215 if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) {
216 syslog(LOG_ERR, "add_address_to_interface: %s", strerror(errno));
219 if (add_nid_to_list(&i->nids, nid) == -1) {
220 syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno));
230 static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
231 struct in6_addr *prefix, struct prefix_cacheinfo *cache)
233 struct prefix_list *i = head;
234 if (!msg || !prefix || !cache)
236 /* XXX IF_PREFIX_AUTOCONF == 0x02 */
237 if (!(msg->prefix_flags & 0x02))
243 if (ipv6_prefix_equal(prefix, &i->prefix.addr, msg->prefix_len) ||
244 ipv6_prefix_equal(prefix, &i->address.addr, msg->prefix_len)) {
245 i->mask |= HAS_PREFIX;
246 i->ifindex = msg->prefix_ifindex;
247 memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
248 i->prefix.prefix_len = msg->prefix_len;
249 i->prefix.valid_until = time(NULL) + cache->preferred_time;
254 i->next = calloc(1, sizeof(*i));
259 i->mask = HAS_PREFIX;
260 memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
261 i->prefix.prefix_len = msg->prefix_len;
262 i->prefix.valid_until = time(NULL) + cache->preferred_time;
267 static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
268 struct in6_addr *address, struct ifa_cacheinfo *cache)
270 struct prefix_list *i = head;
271 if (!msg || !address || !cache)
274 if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
280 if (ipv6_prefix_equal(address, &i->prefix.addr, msg->ifa_prefixlen) ||
281 ipv6_prefix_equal(address, &i->address.addr, msg->ifa_prefixlen)) {
282 i->mask |= HAS_ADDRESS;
283 memcpy(&i->address.addr, address, sizeof(*address));
284 i->address.prefix_len = msg->ifa_prefixlen;
285 i->address.valid_until = time(NULL) + cache->ifa_prefered;
290 i->next = calloc(1, sizeof(*i));
295 i->mask = HAS_ADDRESS;
296 memcpy(&i->address.addr, address, sizeof(*address));
297 i->address.prefix_len = msg->ifa_prefixlen;
298 i->address.valid_until = time(NULL) + cache->ifa_prefered;
303 static struct nla_policy addr_policy[IFA_MAX+1] = {
304 [IFA_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
305 [IFA_LABEL] = { .type = NLA_STRING,
306 .maxlen = IFNAMSIZ },
307 [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) },
309 static struct nla_policy prefix_policy[PREFIX_MAX+1] = {
310 [PREFIX_ADDRESS] = { .minlen = sizeof(struct in6_addr) },
311 [PREFIX_CACHEINFO] = { .minlen = sizeof(struct prefix_cacheinfo) },
313 int handle_valid_msg(struct nl_msg *msg, void *arg)
315 struct nlmsghdr *nlh = nlmsg_hdr(msg);
318 struct sockaddr_nl *source = nlmsg_get_src(msg);
320 payload = nlmsg_data(nlh);
321 if (source->nl_groups == RTMGRP_IPV6_PREFIX) {
322 struct prefixmsg *prefixmsg;
323 struct in6_addr *prefix = NULL;
324 struct prefix_cacheinfo *cacheinfo = NULL;
325 struct nlattr *tb[PREFIX_MAX+1];
327 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
328 syslog(LOG_ERR, "Failed to parse prefixmsg");
332 prefixmsg = (struct prefixmsg *) payload;
333 if (tb[PREFIX_ADDRESS])
334 prefix = nl_data_get(nla_get_data(tb[PREFIX_ADDRESS]));
335 if (tb[PREFIX_CACHEINFO])
336 cacheinfo = nl_data_get(nla_get_data(tb[PREFIX_CACHEINFO]));
337 ret = add_prefix(arg, prefixmsg, prefix, cacheinfo);
339 else if (source->nl_groups == RTMGRP_IPV6_IFADDR) {
340 struct ifaddrmsg *ifaddrmsg;
341 struct in6_addr *address = NULL;
342 struct ifa_cacheinfo *cacheinfo = NULL;
343 struct nlattr *tb[IFA_MAX+1];
345 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
346 syslog(LOG_ERR, "Failed to parse ifaddrmsg");
350 ifaddrmsg = (struct ifaddrmsg *) payload;
352 address = nl_data_get(nla_get_data(tb[IFA_ADDRESS]));
353 if (tb[IFA_CACHEINFO])
354 cacheinfo = nl_data_get(nla_get_data(tb[IFA_CACHEINFO]));
355 ret = add_address(arg, ifaddrmsg, address, cacheinfo);
358 do_slices_autoconf(arg);
363 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
366 syslog(LOG_ERR, "%s", strerror(err->error));
370 int handle_no_op(struct nl_msg *msg, void *arg)
375 /* only for access in the signal handler */
376 struct prefix_list head;
377 void signal_handler(int signal)
381 do_slices_autoconf(&head);
386 static int write_pidfile(const char *filename)
389 fp = fopen(filename, "w");
392 fprintf(fp, "%d\n", getpid());
397 int main(int argc, char *argv[])
400 head.prev = head.next = NULL;
402 openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
404 handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
405 cbs = nl_handle_get_cb(handle);
406 nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &head);
407 nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL);
408 nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &head);
409 nl_disable_sequence_check(handle);
411 nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR);
412 if (nl_connect(handle, NETLINK_ROUTE) == -1) {
413 syslog(LOG_CRIT, "nl_connect: %s", strerror(errno));
417 if (daemon(0, 0) == -1)
420 /* XXX .. here is a hack */
421 write_pidfile(DEFAULT_PKGSTATEDIR "/../vip6-autod.pid");
423 signal(SIGUSR1, signal_handler);
425 while (nl_recvmsgs(handle, cbs) > 0);