]> git.proxmox.com Git - mirror_lxc.git/blame - src/include/netns_ifaddrs.c
Merge pull request #2661 from brauner/2018-09-28/relro_bind_now
[mirror_lxc.git] / src / include / netns_ifaddrs.c
CommitLineData
cc6119a0 1#include <arpa/inet.h>
4ba0d9af 2#include <errno.h>
cc6119a0
CB
3#include <linux/if.h>
4#include <linux/if_addr.h>
5#include <linux/if_link.h>
6#include <linux/if_packet.h>
4ba0d9af
SG
7#include <linux/netlink.h>
8#include <linux/rtnetlink.h>
cc6119a0
CB
9#include <linux/types.h>
10#include <net/ethernet.h>
59e9eabe 11#include <netinet/in.h>
cc6119a0
CB
12#include <stdbool.h>
13#include <stdio.h>
59e9eabe
CB
14#include <stdlib.h>
15#include <string.h>
16#include <sys/socket.h>
17#include <unistd.h>
4ba0d9af 18
a3aba110 19#include "config.h"
cc6119a0
CB
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
4ba0d9af 28
59e9eabe 29#define IFADDRS_HASH_SIZE 64
4ba0d9af 30
59e9eabe 31#define __NETLINK_ALIGN(len) (((len) + 3) & ~3)
4ba0d9af 32
59e9eabe
CB
33#define __NLMSG_OK(nlh, end) \
34 ((char *)(end) - (char *)(nlh) >= sizeof(struct nlmsghdr))
4ba0d9af 35
59e9eabe
CB
36#define __NLMSG_NEXT(nlh) \
37 (struct nlmsghdr *)((char *)(nlh) + __NETLINK_ALIGN((nlh)->nlmsg_len))
4ba0d9af 38
59e9eabe 39#define __NLMSG_DATA(nlh) ((void *)((char *)(nlh) + sizeof(struct nlmsghdr)))
4ba0d9af 40
59e9eabe 41#define __NLMSG_DATAEND(nlh) ((char *)(nlh) + (nlh)->nlmsg_len)
4ba0d9af 42
59e9eabe
CB
43#define __NLMSG_RTA(nlh, len) \
44 ((void *)((char *)(nlh) + sizeof(struct nlmsghdr) + \
45 __NETLINK_ALIGN(len)))
4ba0d9af 46
59e9eabe
CB
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)))
4ba0d9af 64
cc6119a0
CB
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 */
59e9eabe
CB
70struct 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
78union sockany {
79 struct sockaddr sa;
80 struct sockaddr_ll_hack ll;
81 struct sockaddr_in v4;
82 struct sockaddr_in6 v6;
83};
84
85struct ifaddrs_storage {
cc6119a0 86 struct netns_ifaddrs ifa;
59e9eabe
CB
87 struct ifaddrs_storage *hash_next;
88 union sockany addr, netmask, ifu;
89 unsigned int index;
90 char name[IFNAMSIZ + 1];
91};
92
93struct ifaddrs_ctx {
94 struct ifaddrs_storage *first;
95 struct ifaddrs_storage *last;
96 struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
97};
98
59e9eabe
CB
99static void copy_addr(struct sockaddr **r, int af, union sockany *sa,
100 void *addr, size_t addrlen, int ifindex)
4ba0d9af 101{
59e9eabe
CB
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;
cc6119a0
CB
113 if (__IN6_IS_ADDR_LINKLOCAL(addr) ||
114 __IN6_IS_ADDR_MC_LINKLOCAL(addr))
59e9eabe
CB
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;
4ba0d9af
SG
129}
130
59e9eabe
CB
131static void gen_netmask(struct sockaddr **r, int af, union sockany *sa,
132 int prefixlen)
4ba0d9af 133{
59e9eabe
CB
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);
4ba0d9af
SG
148}
149
59e9eabe
CB
150static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr,
151 size_t addrlen, int ifindex, unsigned short hatype)
4ba0d9af 152{
59e9eabe
CB
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;
4ba0d9af
SG
164}
165
cc6119a0 166static int nl_msg_to_ifaddr(void *pctx, bool *netnsid_aware, struct nlmsghdr *h)
4ba0d9af 167{
59e9eabe
CB
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) {
6ce39620
CB
176#pragma GCC diagnostic push
177#pragma GCC diagnostic ignored "-Wcast-align"
59e9eabe
CB
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 }
6ce39620 186#pragma GCC diagnostic pop
59e9eabe
CB
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
6ce39620
CB
202#pragma GCC diagnostic push
203#pragma GCC diagnostic ignored "-Wcast-align"
59e9eabe
CB
204 if (h->nlmsg_type == RTM_NEWLINK) {
205 ifs->index = ifi->ifi_index;
cc6119a0 206 ifs->ifa.ifa_ifindex = ifi->ifi_index;
59e9eabe
CB
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:
cc6119a0 225 copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu,
59e9eabe
CB
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;
cc6119a0
CB
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;
59e9eabe
CB
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;
cc6119a0
CB
251 ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu;
252 ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex;
59e9eabe
CB
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
cc6119a0
CB
260 * IFA_LOCAL before so treat this as
261 * destination address.
59e9eabe
CB
262 */
263 if (ifs->ifa.ifa_addr)
cc6119a0 264 copy_addr(&ifs->ifa.__ifa_dstaddr,
59e9eabe
CB
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:
cc6119a0 277 copy_addr(&ifs->ifa.__ifa_broadaddr,
59e9eabe
CB
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,
cc6119a0
CB
284 * assume we have a point-to-point network.
285 * Move address to correct field.
59e9eabe
CB
286 */
287 if (ifs->ifa.ifa_addr) {
288 ifs->ifu = ifs->addr;
cc6119a0 289 ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa;
59e9eabe
CB
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;
cc6119a0
CB
305 case IFA_TARGET_NETNSID:
306 *netnsid_aware = true;
307 break;
59e9eabe
CB
308 }
309 }
310
cc6119a0 311 if (ifs->ifa.ifa_addr) {
59e9eabe
CB
312 gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family,
313 &ifs->netmask, ifa->ifa_prefixlen);
cc6119a0
CB
314 ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen;
315 }
59e9eabe 316 }
6ce39620
CB
317#pragma GCC diagnostic pop
318
59e9eabe
CB
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;
4ba0d9af
SG
332}
333
cc6119a0
CB
334static 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
361static 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)
4ba0d9af 366{
cc6119a0
CB
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;
59e9eabe
CB
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;
cc6119a0
CB
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));
d3d5554a
CB
395
396#pragma GCC diagnostic push
397#pragma GCC diagnostic ignored "-Wcast-align"
cc6119a0
CB
398 hdr = (struct nlmsghdr *)buf;
399 if (type == RTM_GETLINK)
400 ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr);
401 else
402 ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr);
403
404 if (type == RTM_GETLINK)
405 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg));
406 else
407 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg));
d3d5554a 408#pragma GCC diagnostic pop
cc6119a0
CB
409
410 hdr->nlmsg_type = type;
411 hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
412 hdr->nlmsg_pid = 0;
413 hdr->nlmsg_seq = seq;
414 if (type == RTM_GETLINK)
415 ifi_msg->ifi_family = af;
416 else
417 ifa_msg->ifa_family = af;
418
419 errno = EINVAL;
420 if (type == RTM_GETLINK)
421 property = IFLA_TARGET_NETNSID;
422 else if (type == RTM_GETADDR)
423 property = IFA_TARGET_NETNSID;
424 else
425 return -1;
426
427 if (netns_id >= 0)
428 addattr(hdr, 1024, property, &netns_id, sizeof(netns_id));
429
430 r = __ifaddrs_netlink_send(fd, hdr);
59e9eabe 431 if (r < 0)
cc6119a0 432 return -1;
59e9eabe
CB
433
434 for (;;) {
435 r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
436 if (r <= 0)
437 return -1;
438
6ce39620
CB
439#pragma GCC diagnostic push
440#pragma GCC diagnostic ignored "-Wcast-align"
cc6119a0
CB
441 for (hdr = &u.reply; __NLMSG_OK(hdr, (void *)&u.buf[r]);
442 hdr = __NLMSG_NEXT(hdr)) {
443 if (hdr->nlmsg_type == NLMSG_DONE)
59e9eabe
CB
444 return 0;
445
cc6119a0 446 if (hdr->nlmsg_type == NLMSG_ERROR) {
59e9eabe
CB
447 errno = EINVAL;
448 return -1;
449 }
450
cc6119a0 451 ret = cb(ctx, netnsid_aware, hdr);
59e9eabe
CB
452 if (ret)
453 return ret;
454 }
6ce39620 455#pragma GCC diagnostic pop
59e9eabe 456 }
4ba0d9af
SG
457}
458
cc6119a0
CB
459static int __rtnl_enumerate(int link_af, int addr_af, __s32 netns_id,
460 bool *netnsid_aware,
461 int (*cb)(void *ctx, bool *netnsid_aware, struct nlmsghdr *h),
462 void *ctx)
4ba0d9af 463{
59e9eabe 464 int fd, r, saved_errno;
cc6119a0 465 bool getaddr_netnsid_aware = false, getlink_netnsid_aware = false;
59e9eabe
CB
466
467 fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
468 if (fd < 0)
469 return -1;
470
cc6119a0
CB
471 r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id,
472 &getlink_netnsid_aware, cb, ctx);
59e9eabe 473 if (!r)
cc6119a0
CB
474 r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id,
475 &getaddr_netnsid_aware, cb, ctx);
59e9eabe
CB
476
477 saved_errno = errno;
478 close(fd);
479 errno = saved_errno;
480
cc6119a0
CB
481 if (getaddr_netnsid_aware && getlink_netnsid_aware)
482 *netnsid_aware = true;
483 else
484 *netnsid_aware = false;
485
59e9eabe 486 return r;
4ba0d9af
SG
487}
488
d3d5554a
CB
489#pragma GCC diagnostic push
490#pragma GCC diagnostic ignored "-Wcast-align"
cc6119a0
CB
491/* Get a pointer to the address structure from a sockaddr. */
492static void *get_addr_ptr(struct sockaddr *sockaddr_ptr)
493{
494 if (sockaddr_ptr->sa_family == AF_INET)
495 return &((struct sockaddr_in *)sockaddr_ptr)->sin_addr;
496
497 if (sockaddr_ptr->sa_family == AF_INET6)
498 return &((struct sockaddr_in6 *)sockaddr_ptr)->sin6_addr;
499
500 return NULL;
501}
d3d5554a 502#pragma GCC diagnostic pop
cc6119a0
CB
503
504static char *get_packet_address(struct sockaddr *sockaddr_ptr, char *buf, size_t buflen)
505{
506 char *slider = buf;
d3d5554a
CB
507#pragma GCC diagnostic push
508#pragma GCC diagnostic ignored "-Wcast-align"
cc6119a0
CB
509 unsigned char *m = ((struct sockaddr_ll *)sockaddr_ptr)->sll_addr;
510 unsigned char n = ((struct sockaddr_ll *)sockaddr_ptr)->sll_halen;
d3d5554a 511#pragma GCC diagnostic pop
cc6119a0
CB
512
513 for (unsigned char i = 0; i < n; i++) {
514 int ret;
515
516 ret = snprintf(slider, buflen, "%02x%s", m[i], (i + 1) < n ? ":" : "");
517 if (ret < 0 || (size_t)ret >= buflen)
518 return NULL;
519
520 buflen -= ret;
521 slider = (slider + ret);
522 }
523
524 return buf;
525}
526
527void netns_freeifaddrs(struct netns_ifaddrs *ifp)
528{
529 struct netns_ifaddrs *n;
530
531 while (ifp) {
532 n = ifp->ifa_next;
533 free(ifp);
534 ifp = n;
535 }
536}
537
538int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id,
539 bool *netnsid_aware)
4ba0d9af 540{
59e9eabe
CB
541 int r, saved_errno;
542 struct ifaddrs_ctx _ctx;
543 struct ifaddrs_ctx *ctx = &_ctx;
4ba0d9af 544
59e9eabe
CB
545 memset(ctx, 0, sizeof *ctx);
546
cc6119a0
CB
547 r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware,
548 nl_msg_to_ifaddr, ctx);
59e9eabe
CB
549 saved_errno = errno;
550 if (r < 0)
cc6119a0 551 netns_freeifaddrs(&ctx->first->ifa);
59e9eabe
CB
552 else
553 *ifap = &ctx->first->ifa;
554 errno = saved_errno;
555
556 return r;
4ba0d9af 557}