]> git.proxmox.com Git - mirror_lxc.git/blame - src/include/netns_ifaddrs.c
github: Update for main branch
[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 33#define __NLMSG_OK(nlh, end) \
49bdee73 34 ((size_t)((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) \
49bdee73 53 ((size_t)((char *)(end) - (char *)(rta)) >= sizeof(struct rtattr))
59e9eabe
CB
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)) {
da5efb6f
CB
180#if HAVE_STRUCT_RTNL_LINK_STATS64
181 if (rta->rta_type != IFLA_STATS64)
182#else
59e9eabe 183 if (rta->rta_type != IFLA_STATS)
da5efb6f 184#endif
59e9eabe
CB
185 continue;
186
187 stats_len = __RTA_DATALEN(rta);
188 break;
189 }
6ce39620 190#pragma GCC diagnostic pop
59e9eabe
CB
191 } else {
192 for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0;
193 ifs0 = ifs0->hash_next)
194 if (ifs0->index == ifa->ifa_index)
195 break;
196 if (!ifs0)
197 return 0;
198 }
199
200 ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
201 if (!ifs) {
202 errno = ENOMEM;
203 return -1;
204 }
205
6ce39620
CB
206#pragma GCC diagnostic push
207#pragma GCC diagnostic ignored "-Wcast-align"
59e9eabe
CB
208 if (h->nlmsg_type == RTM_NEWLINK) {
209 ifs->index = ifi->ifi_index;
cc6119a0 210 ifs->ifa.ifa_ifindex = ifi->ifi_index;
59e9eabe
CB
211 ifs->ifa.ifa_flags = ifi->ifi_flags;
212
213 for (rta = __NLMSG_RTA(h, sizeof(*ifi)); __NLMSG_RTAOK(rta, h);
214 rta = __RTA_NEXT(rta)) {
215 switch (rta->rta_type) {
216 case IFLA_IFNAME:
217 if (__RTA_DATALEN(rta) < sizeof(ifs->name)) {
218 memcpy(ifs->name, __RTA_DATA(rta),
219 __RTA_DATALEN(rta));
220 ifs->ifa.ifa_name = ifs->name;
221 }
222 break;
223 case IFLA_ADDRESS:
224 copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr,
225 __RTA_DATA(rta), __RTA_DATALEN(rta),
226 ifi->ifi_index, ifi->ifi_type);
227 break;
228 case IFLA_BROADCAST:
cc6119a0 229 copy_lladdr(&ifs->ifa.__ifa_broadaddr, &ifs->ifu,
59e9eabe
CB
230 __RTA_DATA(rta), __RTA_DATALEN(rta),
231 ifi->ifi_index, ifi->ifi_type);
232 break;
da5efb6f
CB
233#if HAVE_STRUCT_RTNL_LINK_STATS64
234 case IFLA_STATS64:
235 ifs->ifa.ifa_stats_type = IFLA_STATS64;
da5efb6f 236#else
59e9eabe 237 case IFLA_STATS:
da5efb6f 238 ifs->ifa.ifa_stats_type = IFLA_STATS;
3ccf815f
CB
239#endif
240 memcpy(&ifs->ifa.ifa_stats, __RTA_DATA(rta),
59e9eabe
CB
241 __RTA_DATALEN(rta));
242 break;
cc6119a0
CB
243 case IFLA_MTU:
244 memcpy(&ifs->ifa.ifa_mtu, __RTA_DATA(rta),
245 sizeof(int));
246 break;
247 case IFLA_TARGET_NETNSID:
248 *netnsid_aware = true;
249 break;
59e9eabe
CB
250 }
251 }
252
253 if (ifs->ifa.ifa_name) {
254 unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
255 ifs->hash_next = ctx->hash[bucket];
256 ctx->hash[bucket] = ifs;
257 }
258 } else {
259 ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
cc6119a0
CB
260 ifs->ifa.ifa_mtu = ifs0->ifa.ifa_mtu;
261 ifs->ifa.ifa_ifindex = ifs0->ifa.ifa_ifindex;
59e9eabe
CB
262 ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
263
264 for (rta = __NLMSG_RTA(h, sizeof(*ifa)); __NLMSG_RTAOK(rta, h);
265 rta = __RTA_NEXT(rta)) {
266 switch (rta->rta_type) {
267 case IFA_ADDRESS:
268 /* If ifa_addr is already set we, received an
cc6119a0
CB
269 * IFA_LOCAL before so treat this as
270 * destination address.
59e9eabe
CB
271 */
272 if (ifs->ifa.ifa_addr)
cc6119a0 273 copy_addr(&ifs->ifa.__ifa_dstaddr,
59e9eabe
CB
274 ifa->ifa_family, &ifs->ifu,
275 __RTA_DATA(rta),
276 __RTA_DATALEN(rta),
277 ifa->ifa_index);
278 else
279 copy_addr(&ifs->ifa.ifa_addr,
280 ifa->ifa_family, &ifs->addr,
281 __RTA_DATA(rta),
282 __RTA_DATALEN(rta),
283 ifa->ifa_index);
284 break;
285 case IFA_BROADCAST:
cc6119a0 286 copy_addr(&ifs->ifa.__ifa_broadaddr,
59e9eabe
CB
287 ifa->ifa_family, &ifs->ifu,
288 __RTA_DATA(rta), __RTA_DATALEN(rta),
289 ifa->ifa_index);
290 break;
291 case IFA_LOCAL:
292 /* If ifa_addr is set and we get IFA_LOCAL,
cc6119a0
CB
293 * assume we have a point-to-point network.
294 * Move address to correct field.
59e9eabe
CB
295 */
296 if (ifs->ifa.ifa_addr) {
297 ifs->ifu = ifs->addr;
cc6119a0 298 ifs->ifa.__ifa_dstaddr = &ifs->ifu.sa;
59e9eabe
CB
299
300 memset(&ifs->addr, 0, sizeof(ifs->addr));
301 }
302
303 copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family,
304 &ifs->addr, __RTA_DATA(rta),
305 __RTA_DATALEN(rta), ifa->ifa_index);
306 break;
307 case IFA_LABEL:
308 if (__RTA_DATALEN(rta) < sizeof(ifs->name)) {
309 memcpy(ifs->name, __RTA_DATA(rta),
310 __RTA_DATALEN(rta));
311 ifs->ifa.ifa_name = ifs->name;
312 }
313 break;
cc6119a0
CB
314 case IFA_TARGET_NETNSID:
315 *netnsid_aware = true;
316 break;
59e9eabe
CB
317 }
318 }
319
cc6119a0 320 if (ifs->ifa.ifa_addr) {
59e9eabe
CB
321 gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family,
322 &ifs->netmask, ifa->ifa_prefixlen);
cc6119a0
CB
323 ifs->ifa.ifa_prefixlen = ifa->ifa_prefixlen;
324 }
59e9eabe 325 }
6ce39620
CB
326#pragma GCC diagnostic pop
327
59e9eabe
CB
328 if (ifs->ifa.ifa_name) {
329 if (!ctx->first)
330 ctx->first = ifs;
331
332 if (ctx->last)
333 ctx->last->ifa.ifa_next = &ifs->ifa;
334
335 ctx->last = ifs;
336 } else {
337 free(ifs);
338 }
339
340 return 0;
4ba0d9af
SG
341}
342
cc6119a0
CB
343static int __ifaddrs_netlink_send(int fd, struct nlmsghdr *nlmsghdr)
344{
345 int ret;
346 struct sockaddr_nl nladdr;
347 struct iovec iov = {
348 .iov_base = nlmsghdr,
349 .iov_len = nlmsghdr->nlmsg_len,
350 };
351 struct msghdr msg = {
352 .msg_name = &nladdr,
353 .msg_namelen = sizeof(nladdr),
354 .msg_iov = &iov,
355 .msg_iovlen = 1,
356 };
357
358 memset(&nladdr, 0, sizeof(nladdr));
359 nladdr.nl_family = AF_NETLINK;
360 nladdr.nl_pid = 0;
361 nladdr.nl_groups = 0;
362
363 ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
364 if (ret < 0)
365 return -1;
366
367 return ret;
368}
369
370static int __ifaddrs_netlink_recv(int fd, unsigned int seq, int type, int af,
371 __s32 netns_id, bool *netnsid_aware,
372 int (*cb)(void *ctx, bool *netnsid_aware,
373 struct nlmsghdr *h),
374 void *ctx)
4ba0d9af 375{
7d60776a 376 int r, property, ret;
cc6119a0
CB
377 char *buf;
378 struct nlmsghdr *hdr;
379 struct ifinfomsg *ifi_msg;
380 struct ifaddrmsg *ifa_msg;
59e9eabe
CB
381 union {
382 uint8_t buf[8192];
383 struct {
384 struct nlmsghdr nlh;
385 struct rtgenmsg g;
386 } req;
387 struct nlmsghdr reply;
388 } u;
7d60776a
CB
389 char getlink_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
390 __NETLINK_ALIGN(sizeof(struct ifinfomsg)) +
391 __NETLINK_ALIGN(1024)] = {0};
392 char getaddr_buf[__NETLINK_ALIGN(sizeof(struct nlmsghdr)) +
393 __NETLINK_ALIGN(sizeof(struct ifaddrmsg)) +
394 __NETLINK_ALIGN(1024)] = {0};
d3d5554a
CB
395
396#pragma GCC diagnostic push
397#pragma GCC diagnostic ignored "-Wcast-align"
7d60776a
CB
398 if (type == RTM_GETLINK) {
399 buf = getlink_buf;
400 hdr = (struct nlmsghdr *)buf;
401 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifi_msg));
402
cc6119a0 403 ifi_msg = (struct ifinfomsg *)__NLMSG_DATA(hdr);
7d60776a 404 ifi_msg->ifi_family = af;
cc6119a0 405
7d60776a
CB
406 property = IFLA_TARGET_NETNSID;
407 } else if (type == RTM_GETADDR) {
408 buf = getaddr_buf;
409 hdr = (struct nlmsghdr *)buf;
cc6119a0 410 hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*ifa_msg));
7d60776a
CB
411
412 ifa_msg = (struct ifaddrmsg *)__NLMSG_DATA(hdr);
413 ifa_msg->ifa_family = af;
414
415 property = IFA_TARGET_NETNSID;
416 } else {
417 errno = EINVAL;
418 return -1;
419 }
d3d5554a 420#pragma GCC diagnostic pop
cc6119a0
CB
421
422 hdr->nlmsg_type = type;
423 hdr->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
424 hdr->nlmsg_pid = 0;
425 hdr->nlmsg_seq = seq;
cc6119a0
CB
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
c8ca5a43 471 r = setsockopt(fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &(int){1},
c6b64720
CB
472 sizeof(int));
473 if (r < 0 && netns_id >= 0) {
474 close(fd);
475 *netnsid_aware = false;
476 return -1;
477 }
478
cc6119a0
CB
479 r = __ifaddrs_netlink_recv(fd, 1, RTM_GETLINK, link_af, netns_id,
480 &getlink_netnsid_aware, cb, ctx);
59e9eabe 481 if (!r)
cc6119a0
CB
482 r = __ifaddrs_netlink_recv(fd, 2, RTM_GETADDR, addr_af, netns_id,
483 &getaddr_netnsid_aware, cb, ctx);
59e9eabe
CB
484
485 saved_errno = errno;
486 close(fd);
487 errno = saved_errno;
488
cc6119a0
CB
489 if (getaddr_netnsid_aware && getlink_netnsid_aware)
490 *netnsid_aware = true;
491 else
492 *netnsid_aware = false;
493
59e9eabe 494 return r;
4ba0d9af
SG
495}
496
cc6119a0
CB
497void netns_freeifaddrs(struct netns_ifaddrs *ifp)
498{
499 struct netns_ifaddrs *n;
500
501 while (ifp) {
502 n = ifp->ifa_next;
503 free(ifp);
504 ifp = n;
505 }
506}
507
508int netns_getifaddrs(struct netns_ifaddrs **ifap, __s32 netns_id,
509 bool *netnsid_aware)
4ba0d9af 510{
59e9eabe
CB
511 int r, saved_errno;
512 struct ifaddrs_ctx _ctx;
513 struct ifaddrs_ctx *ctx = &_ctx;
4ba0d9af 514
59e9eabe
CB
515 memset(ctx, 0, sizeof *ctx);
516
cc6119a0
CB
517 r = __rtnl_enumerate(AF_UNSPEC, AF_UNSPEC, netns_id, netnsid_aware,
518 nl_msg_to_ifaddr, ctx);
59e9eabe
CB
519 saved_errno = errno;
520 if (r < 0)
cc6119a0 521 netns_freeifaddrs(&ctx->first->ifa);
59e9eabe
CB
522 else
523 *ifap = &ctx->first->ifa;
524 errno = saved_errno;
525
526 return r;
4ba0d9af 527}