Setting tag util-vserver-pl-0.4-29
[util-vserver-pl.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 #ifdef HAVE_CONFIG_H
11 #  include <config.h>
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <stdint.h>
18 #include <string.h>
19 #include <time.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/ioctl.h>
23 #include <arpa/inet.h>
24 #include <dirent.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <syslog.h>
29
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
36 #endif
37 #include <netlink/route/addr.h>
38
39 #include <vserver.h>
40
41 #define HAS_ADDRESS     0x01
42 #define HAS_PREFIX      0x02
43
44 struct nid_list {
45         nid_t nid;
46         struct nid_list *next;
47 };
48 struct prefix {
49         uint32_t mask;
50         int ifindex;
51         struct {
52                 struct in6_addr addr;
53                 int prefix_len;
54                 time_t valid_until;
55         } prefix;
56         struct {
57                 struct in6_addr addr;
58                 int prefix_len;
59                 time_t valid_until;
60         } address;
61 };
62 struct nid_prefix_map {
63         struct {
64                 struct nid_prefix_map *prev;
65                 struct nid_prefix_map *next;
66         } n;
67         struct {
68                 struct nid_prefix_map *prev;
69                 struct nid_prefix_map *next;
70         } p;
71         struct prefix *prefix;
72         nid_t nid;
73 };
74
75 struct nl_handle *handle;
76
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)
80 {
81         uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
82         unsigned pdw, pbi;
83
84         /* check complete u32 in prefix */
85         pdw = prefixlen >> 5;
86         if (pdw && memcmp(a1, a2, pdw << 2))
87                 return 0;
88
89         /* check incomplete u32 in prefix */
90         pbi = prefixlen & 0x1f;
91         if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
92                 return 0;
93
94         return 1;
95 }
96
97 static int add_address_to_interface(int ifindex, struct in6_addr *address,
98                                     int prefix)
99 {
100         int err = -1;
101         struct rtnl_addr *rta;
102         struct nl_addr *nl;
103
104         nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr));
105         rta = rtnl_addr_alloc();
106
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);
111
112         if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST)
113                 err = 0;
114
115         rtnl_addr_put(rta);
116         nl_addr_destroy(nl);
117         return err;
118 }
119
120 static inline int remove_address_from_interface(struct nid_prefix_map *entry)
121 {
122         struct rtnl_addr *rta;
123         struct nl_addr *nl;
124         struct in6_addr a;
125         int ret;
126
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;
131         }
132
133         nl = nl_addr_build(AF_INET6, &a, sizeof(a));
134         if (!nl)
135                 return -1;
136         rta = rtnl_addr_alloc();
137         if (!rta)
138                 return -1;
139
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);
144
145         ret = rtnl_addr_delete(handle, rta, 0);
146
147         rtnl_addr_put(rta);
148         nl_addr_destroy(nl);
149
150         return ret;
151 }
152
153 static int add_to_map(struct nid_prefix_map *map, struct nid_prefix_map *new)
154 {
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)                             \
160                 ;                                                       \
161         if (i && i->member == new->member && i->om == new->om)          \
162                 return 0;                                               \
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;                                   \
168                 if (i)                                                  \
169                         i->node.prev = new;                             \
170         }                                                               \
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;                                     \
176         }                                                               \
177         /* somewhere in the middle */                                   \
178         else {                                                          \
179                 new->node.prev = i->node.prev;                          \
180                 new->node.next = i;                                     \
181                 i->node.prev->node.next = new;                          \
182                 i->node.prev = new;                                     \
183         }
184         PUT_IT_IN_PLACE(p, prefix, nid)
185         PUT_IT_IN_PLACE(n, nid, prefix)
186         return 1;
187 }
188
189 static inline void remove_from_map(struct nid_prefix_map *map,
190                                    struct nid_prefix_map *entry)
191 {
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;
200 }
201
202 static inline void remove_from_map_and_free(struct nid_prefix_map *map,
203                                             struct nid_prefix_map *entry)
204 {
205         remove_from_map(map, entry);
206         free(entry);
207 }
208
209 static int add_nid_to_map(struct nid_prefix_map *map, struct prefix *prefix,
210                           nid_t nid)
211 {
212         struct nid_prefix_map *new = calloc(1, sizeof(struct nid_prefix_map));
213         int ret;
214
215         if (!new)
216                 return -1;
217
218         new->prefix = prefix;
219         new->nid = nid;
220         ret = add_to_map(map, new);
221
222         if (ret == 0)
223                 free(new);
224
225         return ret;
226 }
227
228 static int add_prefix_to_map(struct nid_prefix_map *map, struct prefix *prefix)
229 {
230         return add_nid_to_map(map, prefix, 0);
231 }
232
233 static void cleanup_prefix(struct nid_prefix_map *map,
234                            struct nid_prefix_map *first)
235 {
236         struct nid_prefix_map *i, *p = NULL;
237
238         for (i = first; i && first->prefix == i->prefix; i = i->p.next) {
239                 if (p)
240                         remove_from_map_and_free(map, p);
241
242                 /* ignore errors */
243                 remove_address_from_interface(i);
244
245                 p = i;
246         }
247         if (p)
248                 remove_from_map_and_free(map, p);
249 }
250
251 static inline int add_nid_to_list(struct nid_list **head, nid_t nid)
252 {
253         struct nid_list *i, *new;
254
255         for (i = *head; i && i->next && i->next->nid < nid; i = i->next)
256                 ;
257         /* check if this nid is first in the list */
258         if (i && i->nid == nid)
259                 return 0;
260         /* check if it's already in the list */
261         if (i && i->next && i->next->nid == nid)
262                 return 0;
263
264         /* add it */
265         new = calloc(1, sizeof(struct nid_list));
266         if (!new)
267                 return -1;
268         new->nid = nid;
269
270         /* this is the lowest nid in the list */
271         if (i == *head) {
272                 *head = new;
273                 new->next = i;
274         }
275         /* in the middle/at the end */
276         else if (i) {
277                 new->next = i->next;
278                 i->next = new;
279         }
280         /* there was no list */
281         else
282                 *head = new;
283
284         return 1;
285 }
286
287 static inline void free_nid_list(struct nid_list *head)
288 {
289         struct nid_list *p;
290         for (p = NULL; head; head = head->next) {
291                 if (p)
292                         free(p);
293                 p = head;
294         }
295         if (p)
296                 free(p);
297 }
298
299 static inline void cleanup_nid(struct nid_prefix_map *map,
300                                nid_t nid)
301 {
302         struct nid_prefix_map *i, *p = NULL;
303         for (i = map->n.next; i->nid < nid; i = i->n.next)
304                 ;
305         /* this nid doesn't have any entries in the map */
306         if (i->nid != nid)
307                 return;
308         for (; i->nid == nid; i = i->n.next) {
309                 if (p)
310                         remove_from_map_and_free(map, p);
311                 remove_address_from_interface(i);
312                 p = i;
313         }
314         if (p)
315                 remove_from_map_and_free(map, p);
316 }
317
318 static inline void cleanup_nids(struct nid_prefix_map *map,
319                                 struct nid_list *previous,
320                                 struct nid_list *current)
321 {
322         struct nid_list *p, *pprev = NULL, *c;
323         for (p = previous, c = current; p; pprev = p, p = p->next) {
324                 if (pprev)
325                         free(pprev);
326                 while (c->nid < p->nid)
327                         c = c->next;
328                 if (c->nid == p->nid)
329                         continue;
330                 /* this context has disappeared */
331                 cleanup_nid(map, p->nid);
332         }
333         if (pprev)
334                 free(pprev);
335 }
336
337 static void do_slices_autoconf(struct nid_prefix_map *map)
338 {
339         DIR *dp;
340         struct dirent *de;
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;
345
346         if ((dp = opendir("/proc/virtnet")) == NULL)
347                 return;
348         while ((de = readdir(dp)) != NULL) {
349                 nid_t nid;
350
351                 if (!isdigit(de->d_name[0]))
352                         continue;
353
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));
358                         continue;
359                 }
360
361                 add_nid_to_list(&current, nid);
362         }
363         closedir(dp);
364
365         for (n = current; n; n = n->next) {
366                 for (i = map->p.next; i && i->nid == 0;) {
367                         /* expired */
368                         if (i->prefix->mask & HAS_PREFIX && i->prefix->prefix.valid_until < time(NULL)) {
369                                 struct nid_prefix_map *tmp;
370                                 char buf[64];
371
372                                 inet_ntop(AF_INET6, &i->prefix->address.addr, buf, sizeof(buf));
373                                 syslog(LOG_NOTICE, "Address %s timed out", buf);
374
375                                 tmp = i->p.next;
376
377                                 cleanup_prefix(map, i);
378
379                                 i = tmp;
380                                 continue;
381                         }
382                         if (i->prefix->mask != (HAS_ADDRESS|HAS_PREFIX))
383                                 goto next;
384
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;
391                         }
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));
396                                 goto next;
397                         }
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));
400                                 goto next;
401                         }
402                         if (add_nid_to_map(map, i->prefix, n->nid) == -1) {
403                                 syslog(LOG_ERR, "add_nid_to_map: %s", strerror(errno));
404                                 goto next;
405                         }
406 next:
407                         i = i->p.next;
408                 }
409         }
410
411         cleanup_nids(map, previous, current);
412         previous = current;
413 }
414
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)
418 {
419         struct nid_prefix_map *i = map;
420         struct prefix *new;
421
422         if (!msg || !prefix || !cache)
423                 return -1;
424         /* XXX IF_PREFIX_AUTOCONF == 0x02 */
425         if (!(msg->prefix_flags & 0x02))
426                 return -1;
427
428         do {
429                 if (i->p.next != NULL)
430                         i = i->p.next;
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;
438                         return 0;
439                 }
440         } while (i->p.next && i->nid == 0);
441
442         /* not yet in the map */
443         new = calloc(1, sizeof(*new));
444         if (!new)
445                 return -1;
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)
451                 return -1;
452
453         return 1;
454 }
455
456 static inline int add_address(struct nid_prefix_map *map, struct ifaddrmsg *msg,
457                               struct in6_addr *address, struct ifa_cacheinfo *cache)
458 {
459         struct nid_prefix_map *i = map;
460         struct prefix *new;
461
462         if (!msg || !address || !cache)
463                 return -1;
464
465         if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
466                 return -1;
467
468         do {
469                 if (i->p.next != NULL)
470                         i = i->p.next;
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;
477                         return 0;
478                 }
479         } while (i->p.next && i->nid == 0);
480
481         new = calloc(1, sizeof(*new));
482         if (!new)
483                 return -1;
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)
489                 return -1;
490
491         return 1;
492 }
493
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) },
499 };
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) },
503 };
504 int handle_valid_msg(struct nl_msg *msg, void *arg)
505 {
506         struct nlmsghdr *nlh = nlmsg_hdr(msg);
507         int ret = -1;
508         char *payload;
509         struct sockaddr_nl *source = nlmsg_get_src(msg);
510
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];
517
518                 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
519                         syslog(LOG_ERR, "Failed to parse prefixmsg");
520                         return -1;
521                 }
522
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);
529         }       
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];
535
536                 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
537                         syslog(LOG_ERR, "Failed to parse ifaddrmsg");
538                         return -1;
539                 }
540
541                 ifaddrmsg = (struct ifaddrmsg *) payload;
542                 if (tb[IFA_ADDRESS])
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);
547         }
548         if (ret >= 0)
549                 do_slices_autoconf(arg);
550
551         return 0;
552 }
553
554 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
555                      void *arg)
556 {
557         syslog(LOG_ERR, "%s", strerror(err->error));
558         return 0;
559 }
560
561 int handle_no_op(struct nl_msg *msg, void *arg)
562 {
563         return 0;
564 }
565
566 /* only for access in the signal handler */
567 struct nid_prefix_map map = {
568         .n = {
569                 .next = NULL,
570                 .prev = NULL,
571         },
572         .p = {
573                 .next = NULL,
574                 .prev = NULL,
575         },
576 };
577 void signal_handler(int signal)
578 {
579         switch (signal) {
580         case SIGUSR1:
581                 do_slices_autoconf(&map);
582                 break;
583         }
584 }
585
586 static int write_pidfile(const char *filename)
587 {
588         FILE *fp;
589         fp = fopen(filename, "w");
590         if (!fp)
591                 return -1;
592         fprintf(fp, "%d\n", getpid());
593         fclose(fp);
594         return 0;
595 }
596
597 int main(int argc, char *argv[])
598 {
599         struct nl_cb *cbs;
600
601         openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
602
603 #if defined(HAVE_LIBNL_1_1)
604         cbs = nl_cb_alloc(NL_CB_VERBOSE);
605         if (!cbs) {
606                 syslog(LOG_CRIT, "nl_cb_alloc: %s", strerror(errno));
607                 exit(1);
608         }
609 #elif defined(HAVE_LIBNL_1_0)
610         handle = nl_handle_alloc_nondefault(NL_CB_VERBOSE);
611         cbs = nl_handle_get_cb(handle);
612 #endif
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);
618 #endif
619         nl_disable_sequence_check(handle);
620
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));
624                 exit(1);
625         }
626
627         if (daemon(0, 0) == -1)
628                 return -1;
629
630         write_pidfile(LOCALSTATEDIR "/run/vip6-autod.pid");
631
632         signal(SIGUSR1, signal_handler);
633
634         while (nl_recvmsgs(handle, cbs) > 0);
635
636         nl_close(handle);
637         closelog();
638         return 0;
639 }