Fix memory leak.
[util-vserver.git] / src / vip6-autod.c
1 /*
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>
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 /* 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>
36
37 #include <vserver.h>
38 #include "pathconfig.h"
39
40 #define HAS_ADDRESS     0x01
41 #define HAS_PREFIX      0x02
42 struct nid_list {
43         nid_t nid;
44         struct nid_list *next;
45 };
46 struct prefix_list {
47         struct prefix_list *prev;
48         struct prefix_list *next;
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         struct nid_list *nids;
62 };
63
64 struct nl_handle *handle;
65
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)
69 {
70         uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
71         unsigned pdw, pbi;
72
73         /* check complete u32 in prefix */
74         pdw = prefixlen >> 5;
75         if (pdw && memcmp(a1, a2, pdw << 2))
76                 return 0;
77
78         /* check incomplete u32 in prefix */
79         pbi = prefixlen & 0x1f;
80         if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
81                 return 0;
82
83         return 1;
84 }
85
86 static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix)
87 {
88         int err = -1;
89         struct rtnl_addr *rta;
90         struct nl_addr *nl;
91
92         nl = nl_addr_build(AF_INET6, address, sizeof(struct in6_addr));
93         rta = rtnl_addr_alloc();
94
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);
99
100         if (rtnl_addr_add(handle, rta, NLM_F_REPLACE) != -1 || errno == EEXIST)
101                 err = 0;
102
103         rtnl_addr_free(rta);
104         nl_addr_destroy(nl);
105         return err;
106 }
107
108 static int add_nid_to_list(struct nid_list **l, nid_t nid)
109 {
110         struct nid_list *n;
111         for (n = *l; n; n = n->next) {
112                 if (n->nid == nid)
113                         return 0;
114         }
115         n = calloc(1, sizeof(struct nid_list));
116         if (!n)
117                 return -1;
118         n->nid = nid;
119         n->next = *l;
120         *l = n;
121         return 1;
122 }
123
124 static void cleanup_prefix(struct prefix_list *i)
125 {
126         struct nid_list *n, *p = NULL;
127
128         for (n = i->nids; n; n = n->next) {
129                 struct rtnl_addr *rta;
130                 struct nl_addr *nl;
131                 struct in6_addr a;
132
133                 if (p)
134                         free(p);
135                 memcpy(&a, &i->address.addr, sizeof(a));
136                 rta = rtnl_addr_alloc();
137                 nl = nl_addr_build(AF_INET6, &a, sizeof(a));
138
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);
143
144                 /* ignore errors */
145                 rtnl_addr_delete(handle, rta, 0);
146
147                 nl_addr_destroy(nl);
148                 rtnl_addr_free(rta);
149
150                 p = n;
151         }
152         if (p)
153                 free(p);
154 }
155
156 static inline void free_nid_list(struct nid_list *head)
157 {
158         struct nid_list *p;
159         for (p = NULL; head; head = head->next) {
160                 if (p)
161                         free(p);
162                 p = head;
163         }
164         if (p)
165                 free(p);
166 }
167
168 static void do_slices_autoconf(struct prefix_list *head)
169 {
170         DIR *dp;
171         struct dirent *de;
172         nid_t nid;
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;
177
178         if ((dp = opendir("/proc/virtnet")) == NULL)
179                 return;
180         while ((de = readdir(dp)) != NULL) {
181                 if (!isdigit(de->d_name[0]))
182                         continue;
183
184                 nid = strtoul(de->d_name, NULL, 10);
185                 addr.type = vcNET_IPV6A;
186                 addr.count = 0;
187                 if (vc_net_remove(nid, &addr) == -1) {
188                         syslog(LOG_ERR, "vc_net_remove(%u): %s", nid, strerror(errno));
189                         continue;
190                 }
191
192                 add_nid_to_list(&current, nid);
193
194                 for (i = head->next; i;) {
195                         /* expired */
196                         if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
197                                 struct prefix_list *tmp;
198                                 char buf[64];
199
200                                 inet_ntop(AF_INET6, &i->address.addr, buf, sizeof(buf));
201                                 syslog(LOG_NOTICE, "Address %s timed out", buf);
202
203                                 if (i->next)
204                                         i->next->prev = i->prev;
205                                 if (i->prev)
206                                         i->prev->next = i->next;
207                                 tmp = i->next;
208
209                                 cleanup_prefix(i);
210
211                                 free(i);
212                                 i = tmp;
213                                 continue;
214                         }
215                         if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
216                                 goto next;
217
218                         addr.type = vcNET_IPV6;
219                         addr.count = 1;
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));
226                                 goto next;
227                         }
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));
230                                 goto next;
231                         }
232                         if (add_nid_to_list(&i->nids, nid) == -1) {
233                                 syslog(LOG_ERR, "add_nid_to_list: %s", strerror(errno));
234                                 goto next;
235                         }
236 next:
237                         i = i->next;
238                 }
239         }
240         closedir(dp);
241
242         free_nid_list(previous);
243         previous = current;
244 }
245
246 static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
247                       struct in6_addr *prefix, struct prefix_cacheinfo *cache)
248 {
249         struct prefix_list *i = head;
250         if (!msg || !prefix || !cache)
251                 return -1;
252         /* XXX IF_PREFIX_AUTOCONF == 0x02 */
253         if (!(msg->prefix_flags & 0x02))
254                 return -1;
255
256         do {
257                 if (i->next != NULL)
258                         i = i->next;
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;
266                         return 0;
267                 }
268         } while (i->next);
269
270         i->next = calloc(1, sizeof(*i));
271         if (!i->next)
272                 return -1;
273         i->next->prev = i;
274         i = i->next;
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;
279
280         return 1;
281 }
282
283 static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
284                               struct in6_addr *address, struct ifa_cacheinfo *cache)
285 {
286         struct prefix_list *i = head;
287         if (!msg || !address || !cache)
288                 return -1;
289
290         if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
291                 return -1;
292
293         do {
294                 if (i->next != NULL)
295                         i = i->next;
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;
302                         return 0;
303                 }
304         } while (i->next);
305
306         i->next = calloc(1, sizeof(*i));
307         if (!i->next)
308                 return -1;
309         i->next->prev = i;
310         i = i->next;
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;
315
316         return 1;
317 }
318
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) },
324 };
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) },
328 };
329 int handle_valid_msg(struct nl_msg *msg, void *arg)
330 {
331         struct nlmsghdr *nlh = nlmsg_hdr(msg);
332         int ret = -1;
333         char *payload;
334         struct sockaddr_nl *source = nlmsg_get_src(msg);
335
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];
342
343                 if (nlmsg_parse(nlh, sizeof(struct prefixmsg), tb, PREFIX_MAX, prefix_policy) < 0) {
344                         syslog(LOG_ERR, "Failed to parse prefixmsg");
345                         return -1;
346                 }
347
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);
354         }       
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];
360
361                 if (nlmsg_parse(nlh, sizeof(struct ifaddrmsg), tb, IFA_MAX, addr_policy) < 0) {
362                         syslog(LOG_ERR, "Failed to parse ifaddrmsg");
363                         return -1;
364                 }
365
366                 ifaddrmsg = (struct ifaddrmsg *) payload;
367                 if (tb[IFA_ADDRESS])
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);
372         }
373         if (ret >= 0)
374                 do_slices_autoconf(arg);
375
376         return 0;
377 }
378
379 int handle_error_msg(struct sockaddr_nl *source, struct nlmsgerr *err,
380                      void *arg)
381 {
382         syslog(LOG_ERR, "%s", strerror(err->error));
383         return 0;
384 }
385
386 int handle_no_op(struct nl_msg *msg, void *arg)
387 {
388         return 0;
389 }
390
391 /* only for access in the signal handler */
392 struct prefix_list head;
393 void signal_handler(int signal)
394 {
395         switch (signal) {
396         case SIGUSR1:
397                 do_slices_autoconf(&head);
398                 break;
399         }
400 }
401
402 static int write_pidfile(const char *filename)
403 {
404         FILE *fp;
405         fp = fopen(filename, "w");
406         if (!fp)
407                 return -1;
408         fprintf(fp, "%d\n", getpid());
409         fclose(fp);
410         return 0;
411 }
412
413 int main(int argc, char *argv[])
414 {
415         struct nl_cb *cbs;
416         head.prev = head.next = NULL;
417
418         openlog("vip6-autod", LOG_PERROR, LOG_DAEMON);
419
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);
426
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));
430                 exit(1);
431         }
432
433         if (daemon(0, 0) == -1)
434                 return -1;
435
436         /* XXX .. here is a hack */
437         write_pidfile(DEFAULT_PKGSTATEDIR "/../vip6-autod.pid");
438
439         signal(SIGUSR1, signal_handler);
440
441         while (nl_recvmsgs(handle, cbs) > 0);
442
443         nl_close(handle);
444         closelog();
445         return 0;
446 }