]> git.proxmox.com Git - mirror_lxc.git/blob - src/include/netns_ifaddrs.c
network: add netns_getifaddrs() implementation
[mirror_lxc.git] / src / include / netns_ifaddrs.c
1 #define _GNU_SOURCE
2 #include <arpa/inet.h>
3 #include <errno.h>
4 #include <linux/if.h>
5 #include <linux/if_addr.h>
6 #include <linux/if_link.h>
7 #include <linux/if_packet.h>
8 #include <linux/netlink.h>
9 #include <linux/rtnetlink.h>
10 #include <linux/types.h>
11 #include <net/ethernet.h>
12 #include <netinet/in.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/socket.h>
18 #include <unistd.h>
19
20 #include "nl.h"
21 #include "macro.h"
22 #include "netns_ifaddrs.h"
23
24 #ifndef NETNS_RTA
25 #define NETNS_RTA(r) \
26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))))
27 #endif
28
29 #define IFADDRS_HASH_SIZE 64
30
31 #define __NETLINK_ALIGN(len) (((len) + 3) & ~3)
32
33 #define __NLMSG_OK(nlh, end) \
34 ((char *)(end) - (char *)(nlh) >= sizeof(struct nlmsghdr))
35
36 #define __NLMSG_NEXT(nlh) \
37 (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len))
38
39 #define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr)))
40
41 #define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len)
42
43 #define __NLMSG_RTA(nlh, len) \
44 ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \
45 __NETLINK_ALIGN(len)))
46
47 #define __RTA_DATALEN(rta) ((rta)->rta_len - sizeof(struct rtattr))
48
49 #define __RTA_NEXT(rta) \
50 (struct rtattr *)((char *)(rta) + __NETLINK_ALIGN((rta)->rta_len))
51
52 #define __RTA_OK(nlh, end) \
53 ((char *)(end) - (char *)(rta) >= sizeof(struct rtattr))
54
55 #define __NLMSG_RTAOK(rta, nlh) __RTA_OK(rta, __NLMSG_DATAEND(nlh))
56
57 #define __IN6_IS_ADDR_LINKLOCAL(a) \
58 ((((uint8_t *)(a))[0]) == 0xfe && (((uint8_t *)(a))[1] & 0xc0) == 0x80)
59
60 #define __IN6_IS_ADDR_MC_LINKLOCAL(a) \
61 (IN6_IS_ADDR_MULTICAST(a) && ((((uint8_t *)(a))[1] & 0xf) == 0x2))
62
63 #define __RTA_DATA(rta) ((void *)((char *)(rta) + sizeof(struct rtattr)))
64
65 /* getifaddrs() reports hardware addresses with PF_PACKET that implies struct
66 * sockaddr_ll. But e.g. Infiniband socket address length is longer than
67 * sockaddr_ll.ssl_addr[8] can hold. Use this hack struct to extend ssl_addr -
68 * callers should be able to still use it.
69 */
70 struct sockaddr_ll_hack {
71 unsigned short sll_family, sll_protocol;
72 int sll_ifindex;
73 unsigned short sll_hatype;
74 unsigned char sll_pkttype, sll_halen;
75 unsigned char sll_addr[24];
76 };
77
78 union sockany {
79 struct sockaddr sa;
80 struct sockaddr_ll_hack ll;
81 struct sockaddr_in v4;
82 struct sockaddr_in6 v6;
83 };
84
85 struct ifaddrs_storage {
86 struct netns_ifaddrs ifa;
87 struct ifaddrs_storage *hash_next;
88 union sockany addr, netmask, ifu;
89 unsigned int index;
90 char name[IFNAMSIZ + 1];
91 };
92
93 struct ifaddrs_ctx {
94 struct ifaddrs_storage *first;
95 struct ifaddrs_storage *last;
96 struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
97 };
98
99 static void copy_addr(struct sockaddr **r, int af, union sockany *sa,
100 void *addr, size_t addrlen, int ifindex)
101 {
102 uint8_t *dst;
103 size_t len;
104
105 switch (af) {
106 case AF_INET:
107 dst = (uint8_t *)&sa->v4.sin_addr;
108 len = 4;
109 break;
110 case AF_INET6:
111 dst = (uint8_t *)&sa->v6.sin6_addr;
112 len = 16;
113 if (__IN6_IS_ADDR_LINKLOCAL(addr) ||
114 __IN6_IS_ADDR_MC_LINKLOCAL(addr))
115 sa->v6.sin6_scope_id = ifindex;
116 break;
117 default:
118 return;
119 }
120
121 if (addrlen < len)
122 return;
123
124 sa->sa.sa_family = af;
125
126 memcpy(dst, addr, len);
127
128 *r = &sa->sa;
129 }
130
131 static void gen_netmask(struct sockaddr **r, int af, union sockany *sa,
132 int prefixlen)
133 {
134 uint8_t addr[16] = {0};
135 int i;
136
137 if ((size_t)prefixlen > 8 * sizeof(addr))
138 prefixlen = 8 * sizeof(addr);
139
140 i = prefixlen / 8;
141
142 memset(addr, 0xff, i);
143
144 if ((size_t)i < sizeof(addr))
145 addr[i++] = 0xff << (8 - (prefixlen % 8));
146
147 copy_addr(r, af, sa, addr, sizeof(addr), 0);
148 }
149
150 static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr,
151 size_t addrlen, int ifindex, unsigned short hatype)
152 {
153 if (addrlen > sizeof(sa->ll.sll_addr))
154 return;
155
156 sa->ll.sll_family = AF_PACKET;
157 sa->ll.sll_ifindex = ifindex;
158 sa->ll.sll_hatype = hatype;
159 sa->ll.sll_halen = addrlen;
160
161 memcpy(sa->ll.sll_addr, addr, addrlen);
162
163 *r = &sa->sa;
164 }
165
166 static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h)
167 {
168 struct ifaddrs_storage *ifs, *ifs0;
169 struct rtattr *rta;
170 int stats_len = 0;
171 struct ifinfomsg *ifi = __NLMSG_DATA(h);
172 struct ifaddrmsg *ifa = __NLMSG_DATA(h);
173 struct ifaddrs_ctx *ctx = pctx;
174
175 if (h->nlmsg_type == RTM_NEWLINK) {
176 #pragma GCC diagnostic push
177 #pragma GCC diagnostic ignored "-Wcast-align"
178 for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h);
179 rta = __RTA_NEXT(rta)) {
180 if (rta->rta_type != IFLA_STATS)
181 continue;
182
183 stats_len = __RTA_DATALEN(rta);
184 break;
185 }
186 #pragma GCC diagnostic pop
187 } else {
188 for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0;
189 ifs0 = ifs0->hash_next)
190 if (ifs0->index == ifa->ifa_index)
191 break;
192 if (!ifs0)
193 return 0;
194 }
195
196 ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
197 if (!ifs) {
198 errno = ENOMEM;
199 return -1;
200 }
201
202 #pragma GCC diagnostic push
203 #pragma GCC diagnostic ignored "-Wcast-align"
204 if (h->nlmsg_type == RTM_NEWLINK) {
205 ifs->index = ifi->ifi_index;
206 ifs->ifa.ifa_ifindex = ifi->ifi_index;
207 ifs->ifa.ifa_flags = ifi->ifi_flags;
208
209 for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h);
210 rta = __RTA_NEXT(rta)) {
211 switch (rta->rta_type) {
212 case IFLA_IFNAME:
213 if (__RTA_DATALEN(rta) < sizeof(ifs->name)) {
214 memcpy(ifs->name, __RTA_DATA(rta),
215 __RTA_DATALEN(rta));
216 ifs->ifa.ifa_name = ifs->name;
217 }
218 break;
219 case IFLA_ADDRESS:
220 copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr,
221 __RTA_DATA(rta), __RTA_DATALEN(rta),
222 ifi->ifi_index, ifi->ifi_type);
223 break;
224 case IFLA_BROADCAST:
225 copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu,
226 __RTA_DATA(rta), __RTA_DATALEN(rta),
227 ifi->ifi_index, ifi->ifi_type);
228 break;
229 case IFLA_STATS:
230 ifs->ifa.ifa_data = (void *)(ifs + 1);
231 memcpy(ifs->ifa.ifa_data, __RTA_DATA(rta),
232 __RTA_DATALEN(rta));
233 break;
234 case IFLA_MTU:
235 memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta),
236 sizeof(int));
237 break;
238 case IFLA_TARGET_NETNSID:
239 *netnsid_aware = true;
240 break;
241 }
242 }
243
244 if (ifs->ifa.ifa_name) {
245 unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
246 ifs->hash_next = ctx->hash[bucket];
247 ctx->hash[bucket] = ifs;
248 }
249 } else {
250 ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
251 ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu;
252 ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex;
253 ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
254
255 for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h);
256 rta = __RTA_NEXT(rta)) {
257 switch (rta->rta_type) {
258 case IFA_ADDRESS:
259 /* If ifa_addr is already set we, received an
260 * IFA_LOCAL before so treat this as
261 * destination address.
262 */
263 if (ifs->ifa.ifa_addr)
264 copy_addr(&ifs->ifa.__ifa_dstaddr,
265 ifa->ifa_family, &ifs->ifu,
266 __RTA_DATA(rta),
267 __RTA_DATALEN(rta),
268 ifa->ifa_index);
269 else
270 copy_addr(&ifs->ifa.ifa_addr,
271 ifa->ifa_family, &ifs->addr,
272 __RTA_DATA(rta),
273 __RTA_DATALEN(rta),
274 ifa->ifa_index);
275 break;
276 case IFA_BROADCAST:
277 copy_addr(&ifs->ifa.__ifa_broadaddr,
278 ifa->ifa_family, &ifs->ifu,
279 __RTA_DATA(rta), __RTA_DATALEN(rta),
280 ifa->ifa_index);
281 break;
282 case IFA_LOCAL:
283 /* If ifa_addr is set and we get IFA_LOCAL,
284 * assume we have a point-to-point network.
285 * Move address to correct field.
286 */
287 if (ifs->ifa.ifa_addr) {
288 ifs->ifu = ifs->addr;
289 ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa;
290
291 memset(&ifs->addr, 0, sizeof(ifs->addr));
292 }
293
294 copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family,
295 &ifs->addr, __RTA_DATA(rta),
296 __RTA_DATALEN(rta), ifa->ifa_index);
297 break;
298 case IFA_LABEL:
299 if (__RTA_DATALEN(rta) < sizeof(ifs->name)) {
300 memcpy(ifs->name, __RTA_DATA(rta),
301 __RTA_DATALEN(rta));
302 ifs->ifa.ifa_name = ifs->name;
303 }
304 break;
305 case IFA_TARGET_NETNSID:
306 *netnsid_aware = true;
307 break;
308 }
309 }
310
311 if (ifs->ifa.ifa_addr) {
312 gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family,
313 &ifs->netmask, ifa->ifa_prefixlen);
314 ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen;
315 }
316 }
317 #pragma GCC diagnostic pop
318
319 if (ifs->ifa.ifa_name) {
320 if (!ctx->first)
321 ctx->first = ifs;
322
323 if (ctx->last)
324 ctx->last->ifa.ifa_next = &ifs->ifa;
325
326 ctx->last = ifs;
327 } else {
328 free(ifs);
329 }
330
331 return 0;
332 }
333
334 static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr)
335 {
336 int ret;
337 struct sockaddr_nl nladdr;
338 struct iovec iov = {
339 .iov_base = nlmsghdr,
340 .iov_len = nlmsghdr->nlmsg_len,
341 };
342 struct msghdr msg = {
343 .msg_name = &nladdr,
344 .msg_namelen = sizeof(nladdr),
345 .msg_iov = &iov,
346 .msg_iovlen = 1,
347 };
348
349 memset(&nladdr, 0, sizeof(nladdr));
350 nladdr.nl_family = AF_NETLINK;
351 nladdr.nl_pid = 0;
352 nladdr.nl_groups = 0;
353
354 ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
355 if (ret < 0)
356 return -1;
357
358 return ret;
359 }
360
361 static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af,
362 __s32 netns_id, bool *netnsid_aware,
363 int (*cb)(void *ctx, bool *netnsid_aware,
364 struct nlmsghdr *h),
365 void *ctx)
366 {
367 char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
368 __NETLINK_ALIGN(sizeof(struct ifinfomsg)) +
369 __NETLINK_ALIGN(1024)];
370 char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
371 __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) +
372 __NETLINK_ALIGN(1024)];
373 char *buf;
374 struct nlmsghdr *hdr;
375 struct ifinfomsg *ifi_msg;
376 struct ifaddrmsg *ifa_msg;
377 union {
378 uint8_t buf[8192];
379 struct {
380 struct nlmsghdr nlh;
381 struct rtgenmsg g;
382 } req;
383 struct nlmsghdr reply;
384 } u;
385 int r, property, ret;
386
387 if (type == RTM_GETLINK)
388 buf = getlink_buf;
389 else if (type == RTM_GETADDR)
390 buf = getaddr_buf;
391 else
392 return -1;
393
394 memset(buf, 0, sizeof(*buf));
395 hdr = (struct nlmsghdr *)buf;
396 if (type == RTM_GETLINK)
397 ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr);
398 else
399 ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr);
400
401 if (type == RTM_GETLINK)
402 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg));
403 else
404 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg));
405
406 hdr->nlmsg_type = type;
407 hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
408 hdr->nlmsg_pid = 0;
409 hdr->nlmsg_seq = seq;
410 if (type == RTM_GETLINK)
411 ifi_msg->ifi_family = af;
412 else
413 ifa_msg->ifa_family = af;
414
415 errno = EINVAL;
416 if (type == RTM_GETLINK)
417 property = IFLA_TARGET_NETNSID;
418 else if (type == RTM_GETADDR)
419 property = IFA_TARGET_NETNSID;
420 else
421 return -1;
422
423 if (netns_id >= 0)
424 addattr(hdr, 1024, property, &netns_id, sizeof(netns_id));
425
426 r = __ifaddrs_netlink_send(fd, hdr);
427 if (r < 0)
428 return -1;
429
430 for (;;) {
431 r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
432 if (r <= 0)
433 return -1;
434
435 #pragma GCC diagnostic push
436 #pragma GCC diagnostic ignored "-Wcast-align"
437 for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]);
438 hdr = __NLMSG_NEXT(hdr)) {
439 if (hdr->nlmsg_type == NLMSG_DONE)
440 return 0;
441
442 if (hdr->nlmsg_type == NLMSG_ERROR) {
443 errno = EINVAL;
444 return -1;
445 }
446
447 ret = cb(ctx, netnsid_aware, hdr);
448 if (ret)
449 return ret;
450 }
451 #pragma GCC diagnostic pop
452 }
453 }
454
455 static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id,
456 bool *netnsid_aware,
457 int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h),
458 void *ctx)
459 {
460 int fd, r, saved_errno;
461 bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false;
462
463 fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
464 if (fd < 0)
465 return -1;
466
467 r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id,
468 &getlink_netnsid_aware, cb, ctx);
469 if (!r)
470 r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id,
471 &getaddr_netnsid_aware, cb, ctx);
472
473 saved_errno = errno;
474 close(fd);
475 errno = saved_errno;
476
477 if (getaddr_netnsid_aware && getlink_netnsid_aware)
478 *netnsid_aware = true;
479 else
480 *netnsid_aware = false;
481
482 return r;
483 }
484
485 /* Get a pointer to the address structure from a sockaddr. */
486 static void *get_addr_ptr(struct sockaddr *sockaddr_ptr)
487 {
488 if (sockaddr_ptr->sa_family == AF_INET)
489 return &((struct sockaddr_in *)sockaddr_ptr)->sin_addr;
490
491 if (sockaddr_ptr->sa_family == AF_INET6)
492 return &((struct sockaddr_in6 *)sockaddr_ptr)->sin6_addr;
493
494 return NULL;
495 }
496
497 static char *get_packet_address(struct sockaddr *sockaddr_ptr, char *buf, size_t buflen)
498 {
499 char *slider = buf;
500 unsigned char *m = ((struct sockaddr_ll *)sockaddr_ptr)->sll_addr;
501 unsigned char n = ((struct sockaddr_ll *)sockaddr_ptr)->sll_halen;
502
503 for (unsigned char i = 0; i < n; i++) {
504 int ret;
505
506 ret = snprintf(slider, buflen, "%02x%s", m[i], (i + 1) < n ? ":" : "");
507 if (ret < 0 || (size_t)ret >= buflen)
508 return NULL;
509
510 buflen -= ret;
511 slider = (slider + ret);
512 }
513
514 return buf;
515 }
516
517 void netns_freeifaddrs(struct netns_ifaddrs *ifp)
518 {
519 struct netns_ifaddrs *n;
520
521 while (ifp) {
522 n = ifp->ifa_next;
523 free(ifp);
524 ifp = n;
525 }
526 }
527
528 int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id,
529 bool *netnsid_aware)
530 {
531 int r, saved_errno;
532 struct ifaddrs_ctx _ctx;
533 struct ifaddrs_ctx *ctx = &_ctx;
534
535 memset(ctx, 0, sizeof *ctx);
536
537 r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware,
538 nl_msg_to_ifaddr, ctx);
539 saved_errno = errno;
540 if (r < 0)
541 netns_freeifaddrs(&ctx->first->ifa);
542 else
543 *ifap = &ctx->first->ifa;
544 errno = saved_errno;
545
546 return r;
547 }