Coverage Report

Created: 2025-07-18 06:58

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