Coverage Report

Created: 2024-06-18 06:28

/src/lldpd/src/daemon/netlink.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c; c-file-style: "openbsd" -*- */
2
/*
3
 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4
 *
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
/* Grabbing interfaces information with netlink only. */
19
20
#include "lldpd.h"
21
22
#include <errno.h>
23
#include <sys/socket.h>
24
#include <netdb.h>
25
#include <net/if_arp.h>
26
#include <linux/netlink.h>
27
#include <linux/rtnetlink.h>
28
#include <linux/if_bridge.h>
29
30
0
#define NETLINK_BUFFER 4096
31
32
struct netlink_req {
33
  struct nlmsghdr hdr;
34
  struct ifinfomsg ifm;
35
  /* attribute has to be NLMSG aligned */
36
  struct rtattr ext_req __attribute__((aligned(NLMSG_ALIGNTO)));
37
  __u32 ext_filter_mask;
38
};
39
40
struct lldpd_netlink {
41
  int nl_socket_queries;
42
  int nl_socket_changes;
43
  int nl_socket_recv_size;
44
  /* Cache */
45
  struct interfaces_device_list *devices;
46
  struct interfaces_address_list *addresses;
47
};
48
49
/**
50
 * Set netlink socket buffer size.
51
 *
52
 * This returns the effective size on success. If the provided value is 0, this
53
 * returns the current size instead. It returns -1 on system errors and -2 if
54
 * the size was not changed appropriately (when reaching the max).
55
 */
56
static int
57
netlink_socket_set_buffer_size(int s, int optname, const char *optname_str, int bufsize)
58
0
{
59
0
  socklen_t size = sizeof(int);
60
0
  int got = 0;
61
62
0
  if (bufsize > 0 &&
63
0
      setsockopt(s, SOL_SOCKET, optname, &bufsize, sizeof(bufsize)) < 0) {
64
0
    log_warn("netlink", "unable to set %s to '%d'", optname_str, bufsize);
65
0
    return -1;
66
0
  }
67
68
  /* Now read them back from kernel.
69
   * SO_SNDBUF & SO_RCVBUF are cap-ed at sysctl `net.core.rmem_max` &
70
   * `net.core.wmem_max`. This it the easiest [probably sanest too]
71
   * to validate that our socket buffers were set properly.
72
   */
73
0
  if (getsockopt(s, SOL_SOCKET, optname, &got, &size) < 0) {
74
0
    log_warn("netlink", "unable to get %s", optname_str);
75
0
    return -1;
76
0
  }
77
0
  if (bufsize > 0 && got < bufsize) {
78
0
    log_warnx("netlink",
79
0
        "tried to set %s to '%d' "
80
0
        "but got '%d'",
81
0
        optname_str, bufsize, got);
82
0
    return -2;
83
0
  }
84
85
0
  return got;
86
0
}
87
88
/**
89
 * Connect to netlink.
90
 *
91
 * Open a Netlink socket and connect to it.
92
 *
93
 * @param groups   Which groups we want to subscribe to
94
 * @return 0 on success, -1 otherwise
95
 */
96
static int
97
netlink_connect(struct lldpd *cfg, unsigned groups)
98
0
{
99
0
  int s1 = -1, s2 = -1;
100
0
  struct sockaddr_nl local = { .nl_family = AF_NETLINK,
101
0
    .nl_pid = 0,
102
0
    .nl_groups = groups };
103
104
  /* Open Netlink socket for subscriptions */
105
0
  log_debug("netlink", "opening netlink sockets");
106
0
  s1 = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
107
0
  if (s1 == -1) {
108
0
    log_warn("netlink", "unable to open netlink socket for changes");
109
0
    goto error;
110
0
  }
111
0
  if (NETLINK_SEND_BUFSIZE &&
112
0
      netlink_socket_set_buffer_size(s1, SO_SNDBUF, "SO_SNDBUF",
113
0
    NETLINK_SEND_BUFSIZE) == -1) {
114
0
    log_warn("netlink", "unable to set send buffer size");
115
0
    goto error;
116
0
  }
117
118
0
  int rc = netlink_socket_set_buffer_size(s1, SO_RCVBUF, "SO_RCVBUF",
119
0
      NETLINK_RECEIVE_BUFSIZE);
120
0
  switch (rc) {
121
0
  case -1:
122
0
    log_warn("netlink", "unable to set receiver buffer size");
123
0
    goto error;
124
0
  case -2:
125
    /* Cannot set size */
126
0
    cfg->g_netlink->nl_socket_recv_size = 0;
127
0
    break;
128
0
  default:
129
0
    cfg->g_netlink->nl_socket_recv_size = rc;
130
0
    break;
131
0
  }
132
0
  if (groups &&
133
0
      bind(s1, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
134
0
    log_warn("netlink", "unable to bind netlink socket");
135
0
    goto error;
136
0
  }
137
138
  /* Opening Netlink socket to for queries */
139
0
  s2 = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
140
0
  if (s2 == -1) {
141
0
    log_warn("netlink", "unable to open netlink socket for queries");
142
0
    goto error;
143
0
  }
144
0
  cfg->g_netlink->nl_socket_changes = s1;
145
0
  cfg->g_netlink->nl_socket_queries = s2;
146
0
  return 0;
147
0
error:
148
0
  if (s1 != -1) close(s1);
149
0
  if (s2 != -1) close(s2);
150
0
  return -1;
151
0
}
152
153
/**
154
 * Send a netlink message.
155
 *
156
 * The type of the message can be chosen as well the route family. The
157
 * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
158
 *
159
 * @param s      the netlink socket
160
 * @param type   the request type (eg RTM_GETLINK)
161
 * @param family the rt family (eg AF_PACKET)
162
 * @return 0 on success, -1 otherwise
163
 */
164
static int
165
netlink_send(int s, int type, int family, int seq)
166
0
{
167
0
  struct netlink_req req = { .hdr = { .nlmsg_len =
168
0
            NLMSG_LENGTH(sizeof(struct ifinfomsg)),
169
0
               .nlmsg_type = type,
170
0
               .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
171
0
               .nlmsg_seq = seq,
172
0
               .nlmsg_pid = getpid() },
173
0
    .ifm = { .ifi_family = family } };
174
0
  struct iovec iov = { .iov_base = &req, .iov_len = req.hdr.nlmsg_len };
175
0
  struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
176
0
  struct msghdr rtnl_msg = { .msg_iov = &iov,
177
0
    .msg_iovlen = 1,
178
0
    .msg_name = &peer,
179
0
    .msg_namelen = sizeof(struct sockaddr_nl) };
180
181
0
  if (family == AF_BRIDGE) {
182
0
    unsigned int len = RTA_LENGTH(sizeof(__u32));
183
    /* request bridge vlan attributes */
184
0
    req.ext_req.rta_type = IFLA_EXT_MASK;
185
0
    req.ext_req.rta_len = len;
186
0
    req.ext_filter_mask = RTEXT_FILTER_BRVLAN;
187
0
    req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_ALIGN(len);
188
0
    iov.iov_len = req.hdr.nlmsg_len;
189
0
  }
190
191
  /* Send netlink message. This is synchronous but we are guaranteed
192
   * to not block. */
193
0
  log_debug("netlink", "sending netlink message");
194
0
  if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
195
0
    log_warn("netlink", "unable to send netlink message");
196
0
    return -1;
197
0
  }
198
199
0
  return 0;
200
0
}
201
202
static void
203
netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
204
0
{
205
0
  while (RTA_OK(rta, len)) {
206
0
    if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
207
0
      tb[rta->rta_type] = rta;
208
0
    rta = RTA_NEXT(rta, len);
209
0
  }
210
0
}
211
212
/**
213
 * Parse a `linkinfo` attributes.
214
 *
215
 * @param iff where to put the result
216
 * @param rta linkinfo attribute
217
 * @param len length of attributes
218
 */
219
static void
220
netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
221
0
{
222
0
  struct rtattr *link_info_attrs[IFLA_INFO_MAX + 1] = {};
223
0
  char *kind = NULL;
224
0
  uint16_t vlan_id;
225
226
0
  netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
227
228
0
  if (link_info_attrs[IFLA_INFO_KIND]) {
229
0
    kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
230
0
    if (kind) {
231
0
      if (!strcmp(kind, "vlan")) {
232
0
        log_debug("netlink", "interface %s is a VLAN",
233
0
            iff->name);
234
0
        iff->type |= IFACE_VLAN_T;
235
0
      } else if (!strcmp(kind, "bridge")) {
236
0
        log_debug("netlink", "interface %s is a bridge",
237
0
            iff->name);
238
0
        iff->type |= IFACE_BRIDGE_T;
239
0
      } else if (!strcmp(kind, "bond")) {
240
0
        log_debug("netlink", "interface %s is a bond",
241
0
            iff->name);
242
0
        iff->type |= IFACE_BOND_T;
243
0
      } else if (!strcmp(kind, "team")) {
244
0
        log_debug("netlink", "interface %s is a team",
245
0
            iff->name);
246
0
        iff->type |= IFACE_BOND_T;
247
0
      } else if (!strcmp(kind, "wlan")) {
248
0
        log_debug("netlink", "interface %s is wireless",
249
0
            iff->name);
250
0
        iff->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
251
0
      }
252
0
    }
253
0
  }
254
255
0
  if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) {
256
0
    struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX + 1] = {};
257
0
    netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX,
258
0
        RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
259
0
        RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
260
261
0
    if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) {
262
0
      vlan_id = *(uint16_t *)RTA_DATA(
263
0
          vlan_link_info_data_attrs[IFLA_VLAN_ID]);
264
0
      bitmap_set(iff->vlan_bmap, vlan_id);
265
0
      log_debug("netlink", "VLAN ID for interface %s is %d",
266
0
          iff->name, vlan_id);
267
0
    }
268
0
  }
269
270
0
  if (kind && !strcmp(kind, "bridge") && link_info_attrs[IFLA_INFO_DATA]) {
271
0
    struct rtattr *bridge_link_info_data_attrs[IFLA_BR_MAX + 1] = {};
272
0
    netlink_parse_rtattr(bridge_link_info_data_attrs, IFLA_BR_MAX,
273
0
        RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
274
0
        RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
275
276
0
    if (bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING] &&
277
0
        *(uint8_t *)RTA_DATA(
278
0
      bridge_link_info_data_attrs[IFLA_BR_VLAN_FILTERING]) > 0) {
279
0
      iff->type |= IFACE_BRIDGE_VLAN_T;
280
0
    }
281
0
  }
282
283
0
  free(kind);
284
0
}
285
286
/**
287
 * Parse a `afspec` attributes.
288
 *
289
 * @param iff where to put the result
290
 * @param rta afspec attribute
291
 * @param len length of attributes
292
 */
293
static void
294
netlink_parse_afspec(struct interfaces_device *iff, struct rtattr *rta, int len)
295
0
{
296
0
  while (RTA_OK(rta, len)) {
297
0
    struct bridge_vlan_info *vinfo;
298
0
    switch (rta->rta_type) {
299
0
    case IFLA_BRIDGE_VLAN_INFO:
300
0
      vinfo = RTA_DATA(rta);
301
0
      log_debug("netlink", "found VLAN %d on interface %s",
302
0
          vinfo->vid, iff->name ? iff->name : "(unknown)");
303
304
0
      bitmap_set(iff->vlan_bmap, vinfo->vid);
305
0
      if (vinfo->flags &
306
0
          (BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED))
307
0
        iff->pvid = vinfo->vid;
308
0
      break;
309
0
    default:
310
0
      log_debug("netlink",
311
0
          "unknown afspec attribute type %d for iface %s",
312
0
          rta->rta_type, iff->name ? iff->name : "(unknown)");
313
0
      break;
314
0
    }
315
0
    rta = RTA_NEXT(rta, len);
316
0
  }
317
  /* All enbridged interfaces will have VLAN 1 by default, ignore it */
318
0
  if (iff->vlan_bmap[0] == 2 && (bitmap_numbits(iff->vlan_bmap) == 1) &&
319
0
      iff->pvid == 1) {
320
0
    log_debug("netlink",
321
0
        "found only default VLAN 1 on interface %s, removing",
322
0
        iff->name ? iff->name : "(unknown)");
323
0
    iff->vlan_bmap[0] = iff->pvid = 0;
324
0
  }
325
0
}
326
327
/**
328
 * Parse a `link` netlink message.
329
 *
330
 * @param msg  message to be parsed
331
 * @param iff  where to put the result
332
 * return 0 if the interface is worth it, -1 otherwise
333
 */
334
static int
335
netlink_parse_link(struct nlmsghdr *msg, struct interfaces_device *iff)
336
0
{
337
0
  struct ifinfomsg *ifi;
338
0
  struct rtattr *attribute;
339
0
  int len;
340
0
  ifi = NLMSG_DATA(msg);
341
0
  len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
342
343
0
  if (ifi->ifi_type != ARPHRD_ETHER) {
344
0
    log_debug("netlink", "skip non Ethernet interface at index %d",
345
0
        ifi->ifi_index);
346
0
    return -1;
347
0
  }
348
349
0
  iff->index = ifi->ifi_index;
350
0
  iff->flags = ifi->ifi_flags;
351
0
  iff->lower_idx = -1;
352
0
  iff->upper_idx = -1;
353
354
0
  for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len);
355
0
       attribute = RTA_NEXT(attribute, len)) {
356
0
    switch (attribute->rta_type) {
357
0
    case IFLA_IFNAME:
358
      /* Interface name */
359
0
      iff->name = strdup(RTA_DATA(attribute));
360
0
      break;
361
0
    case IFLA_IFALIAS:
362
      /* Interface alias */
363
0
      iff->alias = strdup(RTA_DATA(attribute));
364
0
      break;
365
0
    case IFLA_ADDRESS:
366
      /* Interface MAC address */
367
0
      iff->address = malloc(RTA_PAYLOAD(attribute));
368
0
      if (iff->address)
369
0
        memcpy(iff->address, RTA_DATA(attribute),
370
0
            RTA_PAYLOAD(attribute));
371
0
      break;
372
0
    case IFLA_LINK:
373
      /* Index of "lower" interface */
374
0
      if (iff->lower_idx == -1) {
375
0
        iff->lower_idx = *(int *)RTA_DATA(attribute);
376
0
        log_debug("netlink", "attribute IFLA_LINK for %s: %d",
377
0
            iff->name ? iff->name : "(unknown)",
378
0
            iff->lower_idx);
379
0
      } else {
380
0
        log_debug("netlink",
381
0
            "attribute IFLA_LINK for %s: %d (ignored)",
382
0
            iff->name ? iff->name : "(unknown)",
383
0
            iff->lower_idx);
384
0
      }
385
0
      break;
386
0
    case IFLA_LINK_NETNSID:
387
      /* Is the lower interface into another namesapce? */
388
0
      iff->lower_idx = -2;
389
0
      log_debug("netlink",
390
0
          "attribute IFLA_LINK_NETNSID received for %s",
391
0
          iff->name ? iff->name : "(unknown)");
392
0
      break;
393
0
    case IFLA_MASTER:
394
      /* Index of master interface */
395
0
      iff->upper_idx = *(int *)RTA_DATA(attribute);
396
0
      break;
397
0
    case IFLA_MTU:
398
      /* Maximum Transmission Unit */
399
0
      iff->mtu = *(int *)RTA_DATA(attribute);
400
0
      break;
401
0
    case IFLA_LINKINFO:
402
0
      netlink_parse_linkinfo(iff, RTA_DATA(attribute),
403
0
          RTA_PAYLOAD(attribute));
404
0
      break;
405
0
    case IFLA_AF_SPEC:
406
0
      if (ifi->ifi_family != AF_BRIDGE) break;
407
0
      netlink_parse_afspec(iff, RTA_DATA(attribute),
408
0
          RTA_PAYLOAD(attribute));
409
0
      break;
410
0
    default:
411
0
      log_debug("netlink",
412
0
          "unhandled link attribute type %d for iface %s",
413
0
          attribute->rta_type, iff->name ? iff->name : "(unknown)");
414
0
      break;
415
0
    }
416
0
  }
417
0
  if (!iff->name || !iff->address) {
418
0
    log_debug("netlink",
419
0
        "interface %d does not have a name or an address, skip",
420
0
        iff->index);
421
0
    return -1;
422
0
  }
423
0
  if (iff->upper_idx == -1) {
424
    /* No upper interface, we cannot be enslaved. We need to clear
425
     * the flag because the appropriate information may come later
426
     * and we don't want to miss it. */
427
0
    iff->flags &= ~IFF_SLAVE;
428
0
  }
429
0
  if (iff->lower_idx == -2) iff->lower_idx = -1;
430
431
0
  if (ifi->ifi_family == AF_BRIDGE && msg->nlmsg_type == RTM_DELLINK &&
432
0
      iff->upper_idx != -1) {
433
0
    log_debug("netlink", "removal of %s from bridge %d", iff->name,
434
0
        iff->upper_idx);
435
0
    msg->nlmsg_type = RTM_NEWLINK;
436
0
    iff->upper_idx = -1;
437
0
  }
438
439
0
  log_debug("netlink", "parsed link %d (%s, flags: %d)", iff->index, iff->name,
440
0
      iff->flags);
441
0
  return 0;
442
0
}
443
444
/**
445
 * Parse a `address` netlink message.
446
 *
447
 * @param msg  message to be parsed
448
 * @param ifa  where to put the result
449
 * return 0 if the address is worth it, -1 otherwise
450
 */
451
static int
452
netlink_parse_address(struct nlmsghdr *msg, struct interfaces_address *ifa)
453
0
{
454
0
  struct ifaddrmsg *ifi;
455
0
  struct rtattr *attribute;
456
0
  int len;
457
0
  ifi = NLMSG_DATA(msg);
458
0
  len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
459
460
0
  ifa->index = ifi->ifa_index;
461
0
  ifa->flags = ifi->ifa_flags;
462
0
  switch (ifi->ifa_family) {
463
0
  case AF_INET:
464
0
  case AF_INET6:
465
0
    break;
466
0
  default:
467
0
    log_debug("netlink", "got a non IP address on if %d (family: %d)",
468
0
        ifa->index, ifi->ifa_family);
469
0
    return -1;
470
0
  }
471
472
0
  for (attribute = IFA_RTA(ifi); RTA_OK(attribute, len);
473
0
       attribute = RTA_NEXT(attribute, len)) {
474
0
    switch (attribute->rta_type) {
475
0
    case IFA_ADDRESS:
476
      /* Address */
477
0
      if (ifi->ifa_family == AF_INET) {
478
0
        struct sockaddr_in ip;
479
0
        memset(&ip, 0, sizeof(struct sockaddr_in));
480
0
        ip.sin_family = AF_INET;
481
0
        memcpy(&ip.sin_addr, RTA_DATA(attribute),
482
0
            sizeof(struct in_addr));
483
0
        memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
484
0
      } else {
485
0
        struct sockaddr_in6 ip6;
486
0
        memset(&ip6, 0, sizeof(struct sockaddr_in6));
487
0
        ip6.sin6_family = AF_INET6;
488
0
        memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
489
0
            sizeof(struct in6_addr));
490
0
        memcpy(&ifa->address, &ip6,
491
0
            sizeof(struct sockaddr_in6));
492
0
      }
493
0
      break;
494
0
    default:
495
0
      log_debug("netlink",
496
0
          "unhandled address attribute type %d for iface %d",
497
0
          attribute->rta_type, ifa->index);
498
0
      break;
499
0
    }
500
0
  }
501
0
  if (ifa->address.ss_family == AF_UNSPEC) {
502
0
    log_debug("netlink", "no IP for interface %d", ifa->index);
503
0
    return -1;
504
0
  }
505
0
  return 0;
506
0
}
507
508
/**
509
 * Merge an old interface with a new one.
510
 *
511
 * Some properties may be absent in the new interface that should be copied over
512
 * from the old one.
513
 */
514
static void
515
netlink_merge(struct interfaces_device *old, struct interfaces_device *new)
516
0
{
517
0
  if (new->alias == NULL) {
518
0
    new->alias = old->alias;
519
0
    old->alias = NULL;
520
0
  }
521
0
  if (new->address == NULL) {
522
0
    new->address = old->address;
523
0
    old->address = NULL;
524
0
  }
525
0
  if (new->mtu == 0) new->mtu = old->mtu;
526
0
  if (new->type == 0) new->type = old->type;
527
528
0
  if (bitmap_isempty(new->vlan_bmap) && new->type == IFACE_VLAN_T)
529
0
    memcpy((void *)new->vlan_bmap, (void *)old->vlan_bmap,
530
0
        sizeof(uint32_t) * VLAN_BITMAP_LEN);
531
532
  /* It's not possible for lower link to change */
533
0
  new->lower_idx = old->lower_idx;
534
0
}
535
536
/**
537
 * Receive netlink answer from the kernel.
538
 *
539
 * @param ifs  list to store interface list or NULL if we don't
540
 * @param ifas list to store address list or NULL if we don't
541
 * @return     0 on success, -1 on error
542
 */
543
static int
544
netlink_recv(struct lldpd *cfg, int s, struct interfaces_device_list *ifs,
545
    struct interfaces_address_list *ifas)
546
0
{
547
0
  int end = 0, ret = 0, flags, retry = 0;
548
0
  struct iovec iov;
549
0
  int link_update = 0;
550
551
0
  struct interfaces_device *ifdold;
552
0
  struct interfaces_device *ifdnew;
553
0
  struct interfaces_address *ifaold;
554
0
  struct interfaces_address *ifanew;
555
0
  char addr[INET6_ADDRSTRLEN + 1];
556
557
0
  iov.iov_len = NETLINK_BUFFER;
558
0
  iov.iov_base = malloc(iov.iov_len);
559
0
  if (!iov.iov_base) {
560
0
    log_warn("netlink", "not enough memory");
561
0
    return -1;
562
0
  }
563
564
0
  while (!end) {
565
0
    ssize_t len;
566
0
    struct nlmsghdr *msg;
567
0
    struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
568
0
    struct msghdr rtnl_reply = { .msg_iov = &iov,
569
0
      .msg_iovlen = 1,
570
0
      .msg_name = &peer,
571
0
      .msg_namelen = sizeof(struct sockaddr_nl) };
572
0
    flags = MSG_PEEK | MSG_TRUNC;
573
0
  retry:
574
0
    len = recvmsg(s, &rtnl_reply, flags);
575
0
    if (len == -1) {
576
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
577
0
        if (retry++ == 0) {
578
0
          levent_recv_error(s, "netlink socket");
579
0
          goto retry;
580
0
        }
581
0
        log_warnx("netlink",
582
0
            "should have received something, but didn't");
583
0
        ret = 0;
584
0
        goto out;
585
0
      }
586
0
      int rsize = cfg->g_netlink->nl_socket_recv_size;
587
0
      if (errno == ENOBUFS && rsize > 0 &&
588
0
          rsize < NETLINK_MAX_RECEIVE_BUFSIZE &&
589
0
          s == cfg->g_netlink->nl_socket_changes) {
590
        /* Try to increase buffer size, only for the
591
         * socket used to receive changes */
592
0
        rsize *= 2;
593
0
        if (rsize > NETLINK_MAX_RECEIVE_BUFSIZE) {
594
0
          rsize = NETLINK_MAX_RECEIVE_BUFSIZE;
595
0
        }
596
0
        int rc = netlink_socket_set_buffer_size(s, SO_RCVBUF,
597
0
            "SO_RCVBUF", rsize);
598
0
        if (rc < 0)
599
0
          cfg->g_netlink->nl_socket_recv_size = 0;
600
0
        else
601
0
          cfg->g_netlink->nl_socket_recv_size = rsize;
602
0
        if (rc > 0 || rc == -2) {
603
0
          log_info("netlink",
604
0
              "netlink receive buffer too small, retry with larger one (%d)",
605
0
              rsize);
606
0
          flags = 0;
607
0
          goto retry;
608
0
        }
609
0
      }
610
0
      log_warn("netlink", "unable to receive netlink answer");
611
0
      ret = -1;
612
0
      goto out;
613
0
    }
614
0
    if (!len) {
615
0
      ret = 0;
616
0
      goto out;
617
0
    }
618
619
0
    if (iov.iov_len < len || (rtnl_reply.msg_flags & MSG_TRUNC)) {
620
0
      void *tmp;
621
622
      /* Provided buffer is not large enough, enlarge it
623
       * to size of len (which should be total length of the message)
624
       * and try again. */
625
0
      iov.iov_len = len;
626
0
      tmp = realloc(iov.iov_base, iov.iov_len);
627
0
      if (!tmp) {
628
0
        log_warn("netlink", "not enough memory");
629
0
        ret = -1;
630
0
        goto out;
631
0
      }
632
0
      log_debug("netlink", "enlarge message size to %zu bytes", len);
633
0
      iov.iov_base = tmp;
634
0
      flags = 0;
635
0
      goto retry;
636
0
    }
637
638
0
    if (flags != 0) {
639
      /* Buffer is big enough, do the actual reading */
640
0
      flags = 0;
641
0
      goto retry;
642
0
    }
643
644
0
    for (msg = (struct nlmsghdr *)(void *)(iov.iov_base);
645
0
         NLMSG_OK(msg, len); msg = NLMSG_NEXT(msg, len)) {
646
0
      if (!(msg->nlmsg_flags & NLM_F_MULTI)) end = 1;
647
0
      switch (msg->nlmsg_type) {
648
0
      case NLMSG_DONE:
649
0
        log_debug("netlink", "received done message");
650
0
        end = 1;
651
0
        break;
652
0
      case RTM_NEWLINK:
653
0
      case RTM_DELLINK:
654
0
        if (!ifs) break;
655
0
        log_debug("netlink", "received link information");
656
0
        ifdnew = calloc(1, sizeof(struct interfaces_device));
657
0
        if (ifdnew == NULL) {
658
0
          log_warn("netlink",
659
0
              "not enough memory for another interface, give up what we have");
660
0
          goto end;
661
0
        }
662
0
        if (netlink_parse_link(msg, ifdnew) == 0) {
663
          /* We need to find if we already have this
664
           * interface */
665
0
          TAILQ_FOREACH (ifdold, ifs, next) {
666
0
            if (ifdold->index == ifdnew->index)
667
0
              break;
668
0
          }
669
670
0
          if (msg->nlmsg_type == RTM_NEWLINK) {
671
0
            if (ifdold == NULL) {
672
0
              log_debug("netlink",
673
0
                  "interface %s is new",
674
0
                  ifdnew->name);
675
0
              TAILQ_INSERT_TAIL(ifs, ifdnew,
676
0
                  next);
677
0
            } else {
678
0
              log_debug("netlink",
679
0
                  "interface %s/%s is updated",
680
0
                  ifdold->name, ifdnew->name);
681
0
              netlink_merge(ifdold, ifdnew);
682
0
              TAILQ_INSERT_AFTER(ifs, ifdold,
683
0
                  ifdnew, next);
684
0
              TAILQ_REMOVE(ifs, ifdold, next);
685
0
              interfaces_free_device(ifdold);
686
0
            }
687
0
          } else {
688
0
            if (ifdold == NULL) {
689
0
              log_warnx("netlink",
690
0
                  "removal request for %s, but no knowledge of it",
691
0
                  ifdnew->name);
692
0
            } else {
693
0
              log_debug("netlink",
694
0
                  "interface %s is to be removed",
695
0
                  ifdold->name);
696
0
              TAILQ_REMOVE(ifs, ifdold, next);
697
0
              interfaces_free_device(ifdold);
698
0
            }
699
0
            interfaces_free_device(ifdnew);
700
0
          }
701
0
          link_update = 1;
702
0
        } else {
703
0
          interfaces_free_device(ifdnew);
704
0
        }
705
0
        break;
706
0
      case RTM_NEWADDR:
707
0
      case RTM_DELADDR:
708
0
        if (!ifas) break;
709
0
        log_debug("netlink", "received address information");
710
0
        ifanew = calloc(1, sizeof(struct interfaces_address));
711
0
        if (ifanew == NULL) {
712
0
          log_warn("netlink",
713
0
              "not enough memory for another address, give what we have");
714
0
          goto end;
715
0
        }
716
0
        if (netlink_parse_address(msg, ifanew) == 0) {
717
0
          if (ifanew->address.ss_family == AF_INET6 &&
718
0
              ifanew->flags & IFA_F_TEMPORARY) {
719
0
            interfaces_free_address(ifanew);
720
0
            break;
721
0
          }
722
0
          TAILQ_FOREACH (ifaold, ifas, next) {
723
0
            if ((ifaold->index == ifanew->index) &&
724
0
                !memcmp(&ifaold->address,
725
0
              &ifanew->address,
726
0
              sizeof(ifaold->address)))
727
0
              break;
728
0
          }
729
0
          if (getnameinfo(
730
0
            (struct sockaddr *)&ifanew->address,
731
0
            sizeof(ifanew->address), addr,
732
0
            sizeof(addr), NULL, 0,
733
0
            NI_NUMERICHOST) != 0) {
734
0
            strlcpy(addr, "(unknown)",
735
0
                sizeof(addr));
736
0
          }
737
738
0
          if (msg->nlmsg_type == RTM_NEWADDR) {
739
0
            if (ifaold == NULL) {
740
0
              log_debug("netlink",
741
0
                  "new address %s%%%d", addr,
742
0
                  ifanew->index);
743
0
              TAILQ_INSERT_TAIL(ifas, ifanew,
744
0
                  next);
745
0
            } else {
746
0
              log_debug("netlink",
747
0
                  "updated address %s%%%d",
748
0
                  addr, ifaold->index);
749
0
              TAILQ_INSERT_AFTER(ifas, ifaold,
750
0
                  ifanew, next);
751
0
              TAILQ_REMOVE(ifas, ifaold,
752
0
                  next);
753
0
              interfaces_free_address(ifaold);
754
0
            }
755
0
          } else {
756
0
            if (ifaold == NULL) {
757
0
              log_info("netlink",
758
0
                  "removal request for address of %s%%%d, but no knowledge of it",
759
0
                  addr, ifanew->index);
760
0
            } else {
761
0
              log_debug("netlink",
762
0
                  "address %s%%%d is to be removed",
763
0
                  addr, ifaold->index);
764
0
              TAILQ_REMOVE(ifas, ifaold,
765
0
                  next);
766
0
              interfaces_free_address(ifaold);
767
0
            }
768
0
            interfaces_free_address(ifanew);
769
0
          }
770
0
        } else {
771
0
          interfaces_free_address(ifanew);
772
0
        }
773
0
        break;
774
0
      default:
775
0
        log_debug("netlink",
776
0
            "received unhandled message type %d (len: %d)",
777
0
            msg->nlmsg_type, msg->nlmsg_len);
778
0
      }
779
0
    }
780
0
  }
781
0
end:
782
0
  if (link_update) {
783
    /* Fill out lower/upper */
784
0
    struct interfaces_device *iface1, *iface2;
785
0
    TAILQ_FOREACH (iface1, ifs, next) {
786
0
      if (iface1->upper_idx != -1 &&
787
0
          iface1->upper_idx != iface1->index) {
788
0
        TAILQ_FOREACH (iface2, ifs, next) {
789
0
          if (iface1->upper_idx == iface2->index) {
790
0
            log_debug("netlink",
791
0
                "upper interface for %s is %s",
792
0
                iface1->name, iface2->name);
793
0
            iface1->upper = iface2;
794
0
            break;
795
0
          }
796
0
        }
797
0
        if (iface2 == NULL) iface1->upper = NULL;
798
0
      } else {
799
0
        iface1->upper = NULL;
800
0
      }
801
0
      if (iface1->lower_idx != -1 &&
802
0
          iface1->lower_idx != iface1->index) {
803
0
        TAILQ_FOREACH (iface2, ifs, next) {
804
0
          if (iface1->lower_idx == iface2->index) {
805
            /* Workaround a bug introduced
806
             * in Linux 4.1: a pair of veth
807
             * will be lower interface of
808
             * each other. Do not modify
809
             * index as if one of them is
810
             * updated, we will loose the
811
             * information about the
812
             * loop. */
813
0
            if (iface2->lower_idx ==
814
0
                iface1->index) {
815
0
              iface1->lower = NULL;
816
0
              log_debug("netlink",
817
0
                  "link loop detected between %s(%d) and %s(%d)",
818
0
                  iface1->name, iface1->index,
819
0
                  iface2->name,
820
0
                  iface2->index);
821
0
            } else {
822
0
              log_debug("netlink",
823
0
                  "lower interface for %s is %s",
824
0
                  iface1->name, iface2->name);
825
0
              iface1->lower = iface2;
826
0
            }
827
0
            break;
828
0
          }
829
0
        }
830
0
      } else {
831
0
        iface1->lower = NULL;
832
0
      }
833
0
    }
834
0
  }
835
836
0
out:
837
0
  free(iov.iov_base);
838
0
  return ret;
839
0
}
840
841
static int
842
netlink_group_mask(int group)
843
0
{
844
0
  return group ? (1 << (group - 1)) : 0;
845
0
}
846
847
/**
848
 * Subscribe to link changes.
849
 *
850
 * @return 0 on success, -1 otherwise
851
 */
852
static int
853
netlink_subscribe_changes(struct lldpd *cfg)
854
0
{
855
0
  unsigned int groups;
856
857
0
  log_debug("netlink", "listening on interface changes");
858
859
0
  groups = netlink_group_mask(RTNLGRP_LINK) |
860
0
      netlink_group_mask(RTNLGRP_IPV4_IFADDR) |
861
0
      netlink_group_mask(RTNLGRP_IPV6_IFADDR);
862
863
0
  return netlink_connect(cfg, groups);
864
0
}
865
866
/**
867
 * Receive changes from netlink */
868
static void
869
netlink_change_cb(struct lldpd *cfg)
870
0
{
871
0
  if (cfg->g_netlink == NULL) return;
872
0
  netlink_recv(cfg, cfg->g_netlink->nl_socket_changes, cfg->g_netlink->devices,
873
0
      cfg->g_netlink->addresses);
874
0
}
875
876
/**
877
 * Initialize netlink subsystem.
878
 *
879
 * This can be called several times but will have effect only the first time.
880
 *
881
 * @return 0 on success, -1 otherwise
882
 */
883
static int
884
netlink_initialize(struct lldpd *cfg)
885
0
{
886
0
#ifdef ENABLE_DOT1
887
0
  struct interfaces_device *iff;
888
0
#endif
889
890
0
  if (cfg->g_netlink) return 0;
891
892
0
  log_debug("netlink", "initialize netlink subsystem");
893
0
  if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) {
894
0
    log_warn("netlink", "unable to allocate memory for netlink subsystem");
895
0
    goto end;
896
0
  }
897
898
  /* Connect to netlink (by requesting to get notified on updates) and
899
   * request updated information right now */
900
0
  if (netlink_subscribe_changes(cfg) == -1) goto end;
901
902
0
  struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses =
903
0
      malloc(sizeof(struct interfaces_address_list));
904
0
  if (ifaddrs == NULL) {
905
0
    log_warn("netlink", "not enough memory for address list");
906
0
    goto end;
907
0
  }
908
0
  TAILQ_INIT(ifaddrs);
909
910
0
  struct interfaces_device_list *ifs = cfg->g_netlink->devices =
911
0
      malloc(sizeof(struct interfaces_device_list));
912
0
  if (ifs == NULL) {
913
0
    log_warn("netlink", "not enough memory for interface list");
914
0
    goto end;
915
0
  }
916
0
  TAILQ_INIT(ifs);
917
918
0
  if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETADDR, AF_UNSPEC,
919
0
    1) == -1)
920
0
    goto end;
921
0
  netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, NULL, ifaddrs);
922
0
  if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETLINK, AF_PACKET,
923
0
    2) == -1)
924
0
    goto end;
925
0
  netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, ifs, NULL);
926
0
#ifdef ENABLE_DOT1
927
  /* If we have a bridge, search for VLAN-aware bridges */
928
0
  TAILQ_FOREACH (iff, ifs, next) {
929
0
    if (iff->type & IFACE_BRIDGE_T) {
930
0
      log_debug("netlink",
931
0
          "interface %s is a bridge, check for VLANs", iff->name);
932
0
      if (netlink_send(cfg->g_netlink->nl_socket_queries, RTM_GETLINK,
933
0
        AF_BRIDGE, 3) == -1)
934
0
        goto end;
935
0
      netlink_recv(cfg, cfg->g_netlink->nl_socket_queries, ifs, NULL);
936
0
      break;
937
0
    }
938
0
  }
939
0
#endif
940
941
  /* Listen to any future change */
942
0
  cfg->g_iface_cb = netlink_change_cb;
943
0
  if (levent_iface_subscribe(cfg, cfg->g_netlink->nl_socket_changes) == -1) {
944
0
    goto end;
945
0
  }
946
947
0
  return 0;
948
0
end:
949
0
  netlink_cleanup(cfg);
950
0
  return -1;
951
0
}
952
953
/**
954
 * Cleanup netlink subsystem.
955
 */
956
void
957
netlink_cleanup(struct lldpd *cfg)
958
0
{
959
0
  if (cfg->g_netlink == NULL) return;
960
0
  if (cfg->g_netlink->nl_socket_changes != -1)
961
0
    close(cfg->g_netlink->nl_socket_changes);
962
0
  if (cfg->g_netlink->nl_socket_queries != -1)
963
0
    close(cfg->g_netlink->nl_socket_queries);
964
0
  interfaces_free_devices(cfg->g_netlink->devices);
965
0
  interfaces_free_addresses(cfg->g_netlink->addresses);
966
967
0
  free(cfg->g_netlink);
968
0
  cfg->g_netlink = NULL;
969
0
}
970
971
/**
972
 * Receive the list of interfaces.
973
 *
974
 * @return a list of interfaces.
975
 */
976
struct interfaces_device_list *
977
netlink_get_interfaces(struct lldpd *cfg)
978
0
{
979
0
  if (netlink_initialize(cfg) == -1) return NULL;
980
0
  struct interfaces_device *ifd;
981
0
  TAILQ_FOREACH (ifd, cfg->g_netlink->devices, next) {
982
0
    ifd->ignore = 0;
983
0
  }
984
0
  return cfg->g_netlink->devices;
985
0
}
986
987
/**
988
 * Receive the list of addresses.
989
 *
990
 * @return a list of addresses.
991
 */
992
struct interfaces_address_list *
993
netlink_get_addresses(struct lldpd *cfg)
994
0
{
995
0
  if (netlink_initialize(cfg) == -1) return NULL;
996
0
  return cfg->g_netlink->addresses;
997
0
}