Clean up the IPv6 auto-configuration daemon
[util-vserver.git] / src / vip6-autod.c
1 /*
2  * $Id$
3  * Copyright (c) 2007 The Trustees of Princeton University
4  * Author: Daniel Hokka Zakrisson <daniel@hozac.com>
5  *
6  * Licensed under the terms of the GNU General Public License
7  * version 2 or later.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include <time.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/ioctl.h>
19 #include <arpa/inet.h>
20 #include <dirent.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <signal.h>
24
25 #include <asm/types.h>
26 /* not defined for gcc -ansi */
27 typedef uint64_t __u64;
28 typedef int64_t __s64;
29 #include <netlink/netlink.h>
30 #include <netlink/route/addr.h>
31
32 typedef unsigned int nid_t;
33 typedef unsigned int xid_t;
34 #include <vserver.h>
35
36 #define HAS_ADDRESS     0x01
37 #define HAS_PREFIX      0x02
38 struct nid_list {
39         nid_t nid;
40         struct nid_list *next;
41 };
42 struct prefix_list {
43         struct prefix_list *prev;
44         struct prefix_list *next;
45         uint32_t mask;
46         int ifindex;
47         struct {
48                 struct in6_addr addr;
49                 int prefix_len;
50                 time_t valid_until;
51         } prefix;
52         struct {
53                 struct in6_addr addr;
54                 int prefix_len;
55                 time_t valid_until;
56         } address;
57         struct nid_list *nids;
58 };
59
60 struct nl_handle *handle;
61
62 /* from linux/include/net/ipv6.h */
63 static inline int ipv6_prefix_equal(struct in6_addr *prefix,
64                                     struct in6_addr *addr, int prefixlen)
65 {
66         uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
67         unsigned pdw, pbi;
68
69         /* check complete u32 in prefix */
70         pdw = prefixlen >> 5;
71         if (pdw && memcmp(a1, a2, pdw << 2))
72                 return 0;
73
74         /* check incomplete u32 in prefix */
75         pbi = prefixlen & 0x1f;
76         if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
77                 return 0;
78
79         return 1;
80 }
81
82 static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix)
83 {
84         int err = -1;
85         struct rtnl_addr *rta;
86         struct nl_addr *nl;
87
88         nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr));
89         rta = rtnl_addr_alloc();
90
91         rtnl_addr_set_family(rta, AF_INET6);
92         rtnl_addr_set_ifindex(rta, ifindex);
93         rtnl_addr_set_local(rta, nl);
94         rtnl_addr_set_prefixlen(rta, prefix);
95
96         if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST)
97                 err = 0;
98
99         rtnl_addr_free(rta);
100         nl_addr_destroy(nl);
101         return err;
102 }
103
104 static int add_nid_to_list(struct prefix_list *i, nid_t nid)
105 {
106         struct nid_list *n;
107         n = calloc(1, sizeof(struct nid_list));
108         if (!n)
109                 return -1;
110         n->nid = nid;
111         n->next = i->nids;
112         i->nids = n;
113         return 0;
114 }
115
116 static void cleanup_prefix(struct prefix_list *i)
117 {
118         struct nid_list *n;
119
120         for (n = i->nids; n; n = n->next) {
121                 struct rtnl_addr *rta;
122                 struct nl_addr *nl;
123                 struct in6_addr a;
124
125                 memcpy(&a, &i->address.addr, sizeof(a));
126                 rta = rtnl_addr_alloc();
127                 nl = nl_addr_build(AF_INET6, &a, sizeof(a));
128
129                 rtnl_addr_set_family(rta, AF_INET6);
130                 rtnl_addr_set_ifindex(rta, i->ifindex);
131                 rtnl_addr_set_local(rta, nl);
132                 rtnl_addr_set_prefixlen(rta, i->address.prefix_len);
133
134                 /* ignore errors */
135                 rtnl_addr_delete(handle, rta, 0);
136
137                 nl_addr_destroy(nl);
138                 rtnl_addr_free(rta);
139         }
140 }
141
142 static void do_slices_autoconf(struct prefix_list *head)
143 {
144         DIR *dp;
145         struct dirent *de;
146         nid_t nid;
147         struct vc_net_nx addr;
148         struct prefix_list *i;
149
150         if ((dp = opendir("/proc/virtnet")) == NULL)
151                 return;
152         while ((de = readdir(dp)) != NULL) {
153                 if (!isdigit(de->d_name[0]))
154                         continue;
155
156                 nid = strtoul(de->d_name, NULL, 10);
157                 addr.type = vcNET_IPV6;
158                 addr.count = -1;
159                 if (vc_net_remove(nid, &addr) == -1) {
160                         perror("vc_net_remove");
161                         continue;
162                 }
163
164                 for (i = head->next; i;) {
165                         /* expired */
166                         if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
167                                 struct prefix_list *tmp;
168                                 char buf[64];
169
170                                 inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf));
171                                 printf("Address %s timed out.\n", buf);
172
173                                 if (i->next)
174                                         i->next->prev = i->prev;
175                                 if (i->prev)
176                                         i->prev->next = i->next;
177                                 tmp = i->next;
178
179                                 cleanup_prefix(i);
180
181                                 free(i);
182                                 i = tmp;
183                                 continue;
184                         }
185                         if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
186                                 goto next;
187
188                         addr.count = 1;
189                         addr.mask[0] = i->prefix.prefix_len;
190                         memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr));
191                         addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f00) >> 7));
192                         addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0xff) << 25));
193                         if (vc_net_add(nid, &addr) == -1) {
194                                 perror("vc_net_add");
195                                 exit(1);
196                         }
197                         if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) {
198                                 perror("add_address_to_interface");
199                                 exit(1);
200                         }
201                         if (add_nid_to_list(i, nid) == -1) {
202                                 perror("add_nid_to_list");
203                                 exit(1);
204                         }
205 next:
206                         i = i->next;
207                 }
208         }
209         closedir(dp);
210 }
211
212 static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
213                       struct in6_addr *prefix, struct prefix_cacheinfo *cache)
214 {
215         struct prefix_list *i = head;
216         if (!msg || !prefix || !cache)
217                 return -1;
218         /* XXX IF_PREFIX_AUTOCONF == 0x02 */
219         if (!(msg->prefix_flags & 0x02))
220                 return -1;
221
222         do {
223                 if (i->next != NULL)
224                         i = i->next;
225                 if (ipv6_prefix_equal(prefix, &i->prefix.addr, msg->prefix_len) ||
226                     ipv6_prefix_equal(prefix, &i->address.addr, msg->prefix_len)) {
227                         i->mask |= HAS_PREFIX;
228                         i->ifindex = msg->prefix_ifindex;
229                         memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
230                         i->prefix.prefix_len = msg->prefix_len;
231                         i->prefix.valid_until = time(NULL) + cache->preferred_time;
232                         return 0;
233                 }
234         } while (i->next);
235
236         i->next = calloc(1, sizeof(*i));
237         if (!i->next)
238                 return -1;
239         i->next->prev = i;
240         i = i->next;
241         i->mask = HAS_PREFIX;
242         memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
243         i->prefix.prefix_len = msg->prefix_len;
244         i->prefix.valid_until = time(NULL) + cache->preferred_time;
245
246         return 1;
247 }
248
249 static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
250                               struct in6_addr *address, struct ifa_cacheinfo *cache)
251 {
252         struct prefix_list *i = head;
253         if (!msg || !address || !cache)
254                 return -1;
255
256         if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
257                 return -1;
258
259         do {
260                 if (i->next != NULL)
261                         i = i->next;
262                 if (ipv6_prefix_equal(address, &i->prefix.addr, msg->ifa_prefixlen) ||
263                     ipv6_prefix_equal(address, &i->address.addr, msg->ifa_prefixlen)) {
264                         i->mask |= HAS_ADDRESS;
265                         memcpy(&i->address.addr, address, sizeof(*address));
266                         i->address.prefix_len = msg->ifa_prefixlen;
267                         i->address.valid_until = time(NULL) + cache->ifa_prefered;
268                         return 0;
269                 }
270         } while (i->next);
271
272         i->next = calloc(1, sizeof(*i));
273         if (!i->next)
274                 return -1;
275         i->next->prev = i;
276         i = i->next;
277         i->mask = HAS_ADDRESS;
278         memcpy(&i->address.addr, address, sizeof(*address));
279         i->address.prefix_len = msg->ifa_prefixlen;
280         i->address.valid_until = time(NULL) + cache->ifa_prefered;
281
282         return 1;
283 }
284
285 static struct nla_policy addr_policy[IFA_MAX+1] = {
286         [IFA_ADDRESS]   = { .minlen = sizeof(struct in6_addr) },
287         [IFA_LABEL]     = { .type = NLA_STRING,
288                             .maxlen = IFNAMSIZ },
289         [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) },
290 };
291 static struct nla_policy prefix_policy[PREFIX_MAX+1] = {
292         [PREFIX_ADDRESS]   = { .minlen = sizeof(struct in6_addr) },
293         [PREFIX_CACHEINFO] = { .minlen = sizeof(struct prefix_cacheinfo) },
294 };
295 int handle_valid_msg(struct nl_msg *msg, void *arg)
296 {
297         struct nlmsghdr *nlh = nlmsg_hdr(msg);
298         int ret = -1;
299         char *payload;
300         struct sockaddr_nl *source = nlmsg_get_src(msg);
301
302         payload = nlmsg_data(nlh);
303         if (source->nl_groups == RTMGRP_IPV6_PREFIX) {
304                 struct prefixmsg *prefixmsg;
305                 struct in6_addr *prefix = NULL;
306                 struct prefix_cacheinfo *cacheinfo = NULL;
307                 struct nlattr *tb[PREFIX_MAX+1];
308
309                 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
310                         printf("Failed to parse prefixmsg.\n");
311                         return -1;
312                 }
313
314                 prefixmsg = (struct prefixmsg *) payload;
315                 if (tb[PREFIX_ADDRESS])
316                         prefix = nl_data_get(nla_get_data(tb[PREFIX_ADDRESS]));
317                 if (tb[PREFIX_CACHEINFO])
318                         cacheinfo = nl_data_get(nla_get_data(tb[PREFIX_CACHEINFO]));
319                 ret = add_prefix(arg, prefixmsg, prefix, cacheinfo);
320         }       
321         else if (source->nl_groups == RTMGRP_IPV6_IFADDR) {
322                 struct ifaddrmsg *ifaddrmsg;
323                 struct in6_addr *address = NULL;
324                 struct ifa_cacheinfo *cacheinfo = NULL;
325                 struct nlattr *tb[IFA_MAX+1];
326
327                 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
328                         printf("Failed to parse ifaddrmsg.\n");
329                         return -1;
330                 }
331
332                 ifaddrmsg = (struct ifaddrmsg *) payload;
333                 if (tb[IFA_ADDRESS])
334                         address = nl_data_get(nla_get_data(tb[IFA_ADDRESS]));
335                 if (tb[IFA_CACHEINFO])
336                         cacheinfo = nl_data_get(nla_get_data(tb[IFA_CACHEINFO]));
337                 ret = add_address(arg, ifaddrmsg, address, cacheinfo);
338         }
339         if (ret >= 0)
340                 do_slices_autoconf(arg);
341
342         return 0;
343 }
344
345 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
346                      void *arg)
347 {
348         perror("Got an error");
349         return 0;
350 }
351
352 int handle_no_op(struct nl_msg *msg, void *arg)
353 {
354         return 0;
355 }
356
357 /* only for access in the signal handler */
358 struct prefix_list head;
359 void signal_handler(int signal)
360 {
361         switch (signal) {
362         case SIGUSR1:
363                 do_slices_autoconf(&head);
364                 break;
365         }
366 }
367
368 int main(int argc, char *argv[])
369 {
370         struct nl_cb *cbs;
371
372         head.prev = head.next = NULL;
373         signal(SIGUSR1, signal_handler);
374
375         handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
376         cbs = nl_handle_get_cb(handle);
377         nl_cb_set(cbs, NL_CB_VALID, NL_CB_CUSTOM, handle_valid_msg, &head);
378         nl_cb_set(cbs, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, handle_no_op, NULL);
379         nl_cb_err(cbs, NL_CB_CUSTOM, handle_error_msg, &head);
380         nl_disable_sequence_check(handle);
381
382         nl_join_groups(handle, RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR);
383         if (nl_connect(handle, NETLINK_ROUTE) == -1) {
384                 perror("nl_connect");
385                 exit(1);
386         }
387
388         while (nl_recvmsgs(handle, cbs) > 0);
389
390         nl_close(handle);
391         return 0;
392 }