add PoC IPv6 auto-configuration daemon
[util-vserver.git] / src / vip6-autod.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdint.h>
5 #include <string.h>
6 #include <time.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/ioctl.h>
10 #include <arpa/inet.h>
11 #include <dirent.h>
12 #include <ctype.h>
13 #include <errno.h>
14
15 #include <asm/types.h>
16 #include <linux/netlink.h>
17 #include <linux/rtnetlink.h>
18 /*XXX #include <linux/ipv6.h>*/
19 struct in6_ifreq {
20         struct in6_addr ifr6_addr;
21         __u32           ifr6_prefixlen;
22         int             ifr6_ifindex;
23 };
24
25 typedef unsigned int nid_t;
26 typedef unsigned int xid_t;
27 #include <vserver.h>
28
29 #define HAS_ADDRESS     0x01
30 #define HAS_PREFIX      0x02
31 struct prefix_list {
32         struct prefix_list *prev;
33         struct prefix_list *next;
34         uint32_t mask;
35         int ifindex;
36         struct {
37                 struct in6_addr addr;
38                 int prefix_len;
39                 time_t valid_until;
40         } prefix;
41         struct {
42                 struct in6_addr addr;
43                 int prefix_len;
44                 time_t valid_until;
45         } address;
46 };
47
48 /* from linux/include/net/ipv6.h */
49 static inline int ipv6_prefix_equal(struct in6_addr *prefix,
50                                     struct in6_addr *addr, int prefixlen)
51 {
52         uint32_t *a1 = prefix->s6_addr32, *a2 = addr->s6_addr32;
53         unsigned pdw, pbi;
54
55         /* check complete u32 in prefix */
56         pdw = prefixlen >> 5;
57         if (pdw && memcmp(a1, a2, pdw << 2))
58                 return 0;
59
60         /* check incomplete u32 in prefix */
61         pbi = prefixlen & 0x1f;
62         if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
63                 return 0;
64
65         return 1;
66 }
67
68 static int add_address_to_interface(int ifindex, struct in6_addr *address, int prefix)
69 {
70         struct in6_ifreq ireq;
71         int sock;
72
73         ireq.ifr6_ifindex = ifindex;
74         ireq.ifr6_prefixlen = prefix;
75         memcpy(&ireq.ifr6_addr, address, sizeof(*address));
76
77         /* XXX should use netlink */
78         sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
79         if (sock == -1)
80                 return -1;
81
82         if (ioctl(sock, SIOCSIFADDR, &ireq) == -1 && errno != EEXIST) {
83                 close(sock);
84                 return -1;
85         }
86
87         close(sock);
88         return 0;
89 }
90
91 static void do_slices_autoconf(struct prefix_list *head)
92 {
93         DIR *dp;
94         struct dirent *de;
95         nid_t nid;
96         struct vc_net_nx addr;
97         struct prefix_list *i, *expired = NULL;
98
99         if ((dp = opendir("/proc/virtnet")) == NULL)
100                 return;
101         while ((de = readdir(dp)) != NULL) {
102                 if (!isdigit(de->d_name[0]))
103                         continue;
104
105                 nid = strtoul(de->d_name, NULL, 10);
106                 addr.type = vcNET_IPV6;
107                 addr.count = -1;
108                 if (vc_net_remove(nid, &addr) == -1)
109                         continue;
110
111                 for (i = head->next; i;) {
112                         /* expired */
113                         if (i->mask & HAS_PREFIX && i->prefix.valid_until < time(NULL)) {
114                                 struct prefix_list *tmp;
115                                 if (i->next)
116                                         i->next->prev = i->prev;
117                                 if (i->prev)
118                                         i->prev->next = i->next;
119                                 
120                                 tmp = i->next;
121                                 free(i);
122                                 i = tmp;
123                                 continue;
124                         }
125                         if (i->mask != (HAS_ADDRESS|HAS_PREFIX))
126                                 goto next;
127
128                         addr.count = 1;
129                         addr.mask[0] = i->prefix.prefix_len;
130                         memcpy(addr.ip, &i->address.addr, sizeof(struct in6_addr));
131                         addr.ip[2] = htonl((ntohl(addr.ip[2]) & 0xffffff00) | ((nid & 0x7f00) >> 7));
132                         addr.ip[3] = htonl((ntohl(addr.ip[3]) & 0x00ffffff) | ((nid & 0xff) << 25));
133                         if (vc_net_add(nid, &addr) == -1) {
134                                 perror("vc_net_add");
135                                 exit(1);
136                         }
137                         if (add_address_to_interface(i->ifindex, (struct in6_addr *) addr.ip, i->prefix.prefix_len) == -1) {
138                                 perror("add_address_to_interface");
139                                 exit(1);
140                         }
141 next:
142                         i = i->next;
143                 }
144         }
145         closedir(dp);
146 }
147
148 static int add_prefix(struct prefix_list *head, struct prefixmsg *msg,
149                       struct in6_addr *prefix, struct prefix_cacheinfo *cache)
150 {
151         struct prefix_list *i = head;
152         if (!msg || !prefix || !cache)
153                 return -1;
154
155         do {
156                 if (i->next != NULL)
157                         i = i->next;
158                 if (ipv6_prefix_equal(prefix, &i->prefix.addr, msg->prefix_len) ||
159                     ipv6_prefix_equal(prefix, &i->address.addr, msg->prefix_len)) {
160                         i->mask |= HAS_PREFIX;
161                         i->ifindex = msg->prefix_ifindex;
162                         memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
163                         i->prefix.prefix_len = msg->prefix_len;
164                         i->prefix.valid_until = time(NULL) + cache->preferred_time;
165                         return 0;
166                 }
167         } while (i->next);
168
169         i->next = calloc(1, sizeof(*i));
170         if (!i->next)
171                 return -1;
172         i->next->prev = i;
173         i = i->next;
174         i->mask = HAS_PREFIX;
175         memcpy(&i->prefix.addr, prefix, sizeof(*prefix));
176         i->prefix.prefix_len = msg->prefix_len;
177         i->prefix.valid_until = time(NULL) + cache->preferred_time;
178
179         return 1;
180 }
181
182 static inline int add_address(struct prefix_list *head, struct ifaddrmsg *msg,
183                               struct in6_addr *address, struct ifa_cacheinfo *cache)
184 {
185         struct prefix_list *i = head;
186         if (!msg || !address || !cache)
187                 return -1;
188
189         if (address->s6_addr[11] != 0xFF || address->s6_addr[12] != 0xFE)
190                 return 0;
191
192         do {
193                 if (i->next != NULL)
194                         i = i->next;
195                 if (ipv6_prefix_equal(address, &i->prefix.addr, msg->ifa_prefixlen) ||
196                     ipv6_prefix_equal(address, &i->address.addr, msg->ifa_prefixlen)) {
197                         i->mask |= HAS_ADDRESS;
198                         memcpy(&i->address.addr, address, sizeof(*address));
199                         i->address.prefix_len = msg->ifa_prefixlen;
200                         i->address.valid_until = time(NULL) + cache->ifa_prefered;
201                         return 0;
202                 }
203         } while (i->next);
204
205         i->next = calloc(1, sizeof(*i));
206         if (!i->next)
207                 return -1;
208         i->next->prev = i;
209         i = i->next;
210         i->mask = HAS_ADDRESS;
211         memcpy(&i->address.addr, address, sizeof(*address));
212         i->address.prefix_len = msg->ifa_prefixlen;
213         i->address.valid_until = time(NULL) + cache->ifa_prefered;
214
215         return 1;
216 }
217
218 int main(int argc, char *argv[])
219 {
220         int sock;
221         struct sockaddr_nl sa;
222         struct prefix_list head = { .prev = NULL, .next = NULL };
223
224         sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
225         if (sock == -1) {
226                 perror("socket()");
227                 exit(1);
228         }
229         sa.nl_family = AF_NETLINK;
230         sa.nl_groups = RTMGRP_IPV6_PREFIX|RTMGRP_IPV6_IFADDR;
231         if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
232                 perror("bind()");
233                 exit(1);
234         }
235
236         while (1) {
237                 char buf[4000], *payload;
238                 struct nlmsghdr *nlh;
239                 ssize_t len, this_len;
240                 socklen_t socklen = sizeof(sa);
241
242                 if ((len = recvfrom(sock, buf, sizeof(buf), 0, 
243                                     (struct sockaddr *) &sa, &socklen)) <= 0)
244                         break;
245                 for (nlh = (struct nlmsghdr *) buf; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
246                         struct nlattr *nla;
247
248                         if (nlh->nlmsg_type == NLMSG_DONE)
249                                 break;
250                         else if (nlh->nlmsg_type == NLMSG_ERROR)
251                                 break;
252
253                         this_len = NLMSG_ALIGN((nlh)->nlmsg_len) - NLMSG_LENGTH(0);
254                         payload = NLMSG_DATA(nlh);
255                         if (sa.nl_groups == RTMGRP_IPV6_PREFIX) {
256                                 struct prefixmsg *prefixmsg;
257                                 struct in6_addr *prefix;
258                                 struct prefix_cacheinfo *cacheinfo;
259
260                                 prefixmsg = (struct prefixmsg *) payload;
261                                 prefix = NULL;
262                                 cacheinfo = NULL;
263                                 for (nla = (struct nlattr *)(payload + sizeof(*prefixmsg)); nla < payload + this_len; nla = (char *) nla + nla->nla_len) {
264                                         if (nla->nla_type == PREFIX_ADDRESS)
265                                                 prefix = (struct in6_addr *)(((char *) nla) + sizeof(*nla));
266                                         else if (nla->nla_type == PREFIX_CACHEINFO)
267                                                 cacheinfo = (struct prefix_cacheinfo *)(((char *) nla) + sizeof(*nla));
268                                 }
269                                 if (add_prefix(&head, prefixmsg, prefix, cacheinfo) == -1) {
270                                         printf("Adding prefix failed!\n");
271                                 }
272                         }
273                         else if (sa.nl_groups == RTMGRP_IPV6_IFADDR) {
274                                 struct ifaddrmsg *ifaddrmsg;
275                                 struct in6_addr *address;
276                                 struct ifa_cacheinfo *cacheinfo;
277
278                                 ifaddrmsg = (struct ifaddrmsg *) payload;
279                                 address = NULL;
280                                 cacheinfo = NULL;
281                                 for (nla = (struct nlattr *)(payload + sizeof(*ifaddrmsg)); nla < payload + this_len; nla = (char *) nla + nla->nla_len) {
282                                         if (nla->nla_type == IFA_ADDRESS)
283                                                 address = (struct in6_addr *)(((char *) nla) + sizeof(*nla));
284                                         else if (nla->nla_type == IFA_CACHEINFO)
285                                                 cacheinfo = (struct ifa_cacheinfo *)(((char *) nla) + sizeof(*nla));
286                                 }
287                                 if (add_address(&head, ifaddrmsg, address, cacheinfo) == -1) {
288                                         printf("Adding address failed!\n");
289                                 }
290                         }
291                 }
292                 do_slices_autoconf(&head);
293         }
294
295         close(sock);
296         return 0;
297 }