Coverage Report

Created: 2026-05-30 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lldpd/src/daemon/interfaces-linux.c
Line
Count
Source
1
/* -*- mode: c; c-file-style: "openbsd" -*- */
2
/*
3
 * Copyright (c) 2008 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
#include "lldpd.h"
19
20
#include <stdio.h>
21
#include <unistd.h>
22
#include <inttypes.h>
23
#include <errno.h>
24
#include <sys/ioctl.h>
25
#if defined(__clang__)
26
#  pragma clang diagnostic push
27
#  pragma clang diagnostic ignored "-Wdocumentation"
28
#endif
29
#include <netinet/in.h>
30
#include <linux/if_vlan.h>
31
#include <linux/if_bonding.h>
32
#include <linux/if_bridge.h>
33
#include <linux/wireless.h>
34
#include <linux/sockios.h>
35
#include <linux/if_packet.h>
36
#include <linux/ethtool.h>
37
#if defined(__clang__)
38
#  pragma clang diagnostic pop
39
#endif
40
41
0
#define SYSFS_PATH_MAX 256
42
#define MAX_PORTS 1024
43
#define MAX_BRIDGES 1024
44
45
static int
46
iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
47
0
{
48
0
  int fd;
49
50
0
  log_debug("interfaces", "initialize ethernet device %s", hardware->h_ifname);
51
0
  if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
52
0
    return -1;
53
0
  hardware->h_sendfd = fd; /* Send */
54
55
0
  interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
56
57
0
  levent_hardware_add_fd(hardware, fd); /* Receive */
58
0
  log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
59
0
      fd);
60
0
  return 0;
61
0
}
62
63
/* Generic ethernet send/receive */
64
static int
65
iflinux_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer,
66
    size_t size)
67
0
{
68
0
  log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
69
0
      hardware->h_ifname, hardware->h_sendfd);
70
0
  return write(hardware->h_sendfd, buffer, size);
71
0
}
72
73
static int
74
iflinux_generic_recv(struct lldpd_hardware *hardware, int fd, char *buffer, size_t size,
75
    struct sockaddr_ll *from)
76
0
{
77
0
  int n, retry = 0;
78
0
  socklen_t fromlen;
79
80
0
retry:
81
0
  fromlen = sizeof(*from);
82
0
  memset(from, 0, fromlen);
83
0
  if ((n = recvfrom(fd, buffer, size, 0, (struct sockaddr *)from, &fromlen)) ==
84
0
      -1) {
85
0
    if (errno == EAGAIN && retry == 0) {
86
      /* There may be an error queued in the socket. Clear it and
87
       * retry. */
88
0
      levent_recv_error(fd, hardware->h_ifname);
89
0
      retry++;
90
0
      goto retry;
91
0
    }
92
0
    if (errno == ENETDOWN) {
93
0
      log_debug("interfaces",
94
0
          "error while receiving frame on %s (network down)",
95
0
          hardware->h_ifname);
96
0
    } else {
97
0
      log_warn("interfaces",
98
0
          "error while receiving frame on %s (retry: %d)",
99
0
          hardware->h_ifname, retry);
100
0
      hardware->h_rx_discarded_cnt++;
101
0
    }
102
0
    return -1;
103
0
  }
104
0
  if (from->sll_pkttype == PACKET_OUTGOING) return -1;
105
0
  return n;
106
0
}
107
108
static int
109
iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd,
110
    char *buffer, size_t size)
111
0
{
112
0
  int n;
113
0
  struct sockaddr_ll from;
114
115
0
  log_debug("interfaces", "receive PDU from ethernet device %s",
116
0
      hardware->h_ifname);
117
0
  if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
118
0
    return -1;
119
0
  return n;
120
0
}
121
122
static int
123
iflinux_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
124
0
{
125
0
  log_debug("interfaces", "close ethernet device %s", hardware->h_ifname);
126
0
  interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
127
0
  return 0;
128
0
}
129
130
static struct lldpd_ops eth_ops = {
131
  .send = iflinux_eth_send,
132
  .recv = iflinux_eth_recv,
133
  .cleanup = iflinux_eth_close,
134
};
135
136
static int
137
iflinux_is_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces,
138
    struct interfaces_device *iface)
139
0
{
140
#ifdef ENABLE_OLDIES
141
  struct interfaces_device *port;
142
  char path[SYSFS_PATH_MAX];
143
  int f;
144
145
  if ((snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
146
    iface->name)) >= SYSFS_PATH_MAX)
147
    log_warnx("interfaces", "path truncated");
148
  if ((f = priv_open(path)) < 0) return 0;
149
  close(f);
150
151
  /* Also grab all ports */
152
  TAILQ_FOREACH (port, interfaces, next) {
153
    if (port->upper) continue;
154
    if (snprintf(path, SYSFS_PATH_MAX,
155
      SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
156
      iface->name, port->name) >= SYSFS_PATH_MAX)
157
      log_warnx("interfaces", "path truncated");
158
    if ((f = priv_open(path)) < 0) continue;
159
    log_debug("interfaces", "port %s is bridged to %s", port->name,
160
        iface->name);
161
    port->upper = iface;
162
    close(f);
163
  }
164
165
  return 1;
166
#else
167
0
  return 0;
168
0
#endif
169
0
}
170
171
static int
172
iflinux_is_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces,
173
    struct interfaces_device *iface)
174
0
{
175
#ifdef ENABLE_OLDIES
176
  struct vlan_ioctl_args ifv = {};
177
  ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
178
  strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
179
  if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
180
    /* This is a VLAN, get the lower interface and the VID */
181
    struct interfaces_device *lower =
182
        interfaces_nametointerface(interfaces, ifv.u.device2);
183
    if (!lower) {
184
      log_debug("interfaces",
185
          "unable to find lower interface for VLAN %s", iface->name);
186
      return 0;
187
    }
188
189
    memset(&ifv, 0, sizeof(ifv));
190
    ifv.cmd = GET_VLAN_VID_CMD;
191
    strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
192
    if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
193
      log_debug("interfaces", "unable to find VID for VLAN %s",
194
          iface->name);
195
      return 0;
196
    }
197
198
    iface->lower = lower;
199
    bitmap_set(iface->vlan_bmap, ifv.u.VID);
200
    return 1;
201
  }
202
#endif
203
0
  return 0;
204
0
}
205
206
static int
207
iflinux_is_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces,
208
    struct interfaces_device *master)
209
0
{
210
#ifdef ENABLE_OLDIES
211
  /* Shortcut if we detect the new team driver. Upper and lower links
212
   * should already be set with netlink in this case.  */
213
  if (master->driver && !strcmp(master->driver, "team")) {
214
    return 1;
215
  }
216
217
  struct ifreq ifr = {};
218
  struct ifbond ifb = {};
219
  strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
220
  ifr.ifr_data = (char *)&ifb;
221
  if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
222
    while (ifb.num_slaves--) {
223
      struct ifslave ifs;
224
      memset(&ifr, 0, sizeof(ifr));
225
      memset(&ifs, 0, sizeof(ifs));
226
      strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
227
      ifr.ifr_data = (char *)&ifs;
228
      ifs.slave_id = ifb.num_slaves;
229
      if (ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) {
230
        struct interfaces_device *slave =
231
            interfaces_nametointerface(interfaces,
232
          ifs.slave_name);
233
        if (slave == NULL) continue;
234
        if (slave->upper) continue;
235
        log_debug("interfaces",
236
            "interface %s is enslaved to %s", slave->name,
237
            master->name);
238
        slave->upper = master;
239
      }
240
    }
241
    return 1;
242
  }
243
#endif
244
0
  return 0;
245
0
}
246
247
/**
248
 * Get permanent MAC from ethtool.
249
 *
250
 * Return 0 on success, -1 on error.
251
 */
252
static int
253
iflinux_get_permanent_mac_ethtool(struct lldpd *cfg,
254
    struct interfaces_device_list *interfaces, struct interfaces_device *iface)
255
0
{
256
0
  int ret = -1;
257
0
  struct ifreq ifr = {};
258
0
  struct ethtool_perm_addr *epaddr =
259
0
      calloc(1, sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN);
260
0
  if (epaddr == NULL) goto end;
261
262
0
  strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
263
0
  epaddr->cmd = ETHTOOL_GPERMADDR;
264
0
  epaddr->size = ETHER_ADDR_LEN;
265
0
  ifr.ifr_data = (caddr_t)epaddr;
266
0
  if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == -1) {
267
0
    static int once = 0;
268
0
    if (errno == EPERM && !once) {
269
0
      log_warnx("interfaces",
270
0
          "no permission to get permanent MAC address for %s (requires 2.6.19+)",
271
0
          iface->name);
272
0
      once = 1;
273
0
      goto end;
274
0
    }
275
0
    if (errno != EPERM)
276
0
      log_warn("interfaces",
277
0
          "cannot get permanent MAC address for %s", iface->name);
278
0
    goto end;
279
0
  }
280
0
  if (epaddr->data[0] != 0 || epaddr->data[1] != 0 || epaddr->data[2] != 0 ||
281
0
      epaddr->data[3] != 0 || epaddr->data[4] != 0 || epaddr->data[5] != 0) {
282
0
    memcpy(iface->address, epaddr->data, ETHER_ADDR_LEN);
283
0
    ret = 0;
284
0
    goto end;
285
0
  }
286
0
  log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface->name);
287
0
end:
288
0
  free(epaddr);
289
0
  return ret;
290
0
}
291
292
/**
293
 * Get permanent MAC address for a bond device.
294
 */
295
static void
296
iflinux_get_permanent_mac_bond(struct lldpd *cfg,
297
    struct interfaces_device_list *interfaces, struct interfaces_device *iface)
298
0
{
299
0
  struct interfaces_device *master = iface->upper;
300
0
  int f, state = 0;
301
0
  FILE *netbond;
302
0
  const char *slaveif = "Slave Interface: ";
303
0
  const char *hwaddr = "Permanent HW addr: ";
304
0
  u_int8_t mac[ETHER_ADDR_LEN];
305
0
  char path[SYSFS_PATH_MAX];
306
0
  char line[100];
307
308
  /* We have a bond, we need to query it to get real MAC addresses */
309
0
  if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s", master->name) >=
310
0
      SYSFS_PATH_MAX) {
311
0
    log_warnx("interfaces", "path truncated");
312
0
    return;
313
0
  }
314
0
  if ((f = priv_open(path)) < 0) {
315
0
    if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
316
0
      master->name) >= SYSFS_PATH_MAX) {
317
0
      log_warnx("interfaces", "path truncated");
318
0
      return;
319
0
    }
320
0
    f = priv_open(path);
321
0
  }
322
0
  if (f < 0) {
323
0
    log_warnx("interfaces", "unable to get permanent MAC address for %s",
324
0
        iface->name);
325
0
    return;
326
0
  }
327
0
  if ((netbond = fdopen(f, "r")) == NULL) {
328
0
    log_warn("interfaces", "unable to read stream from %s", path);
329
0
    close(f);
330
0
    return;
331
0
  }
332
  /* State 0:
333
     We parse the file to search "Slave Interface: ". If found, go to
334
     state 1.
335
     State 1:
336
     We parse the file to search "Permanent HW addr: ". If found, we get
337
     the mac.
338
  */
339
0
  while (fgets(line, sizeof(line), netbond)) {
340
0
    switch (state) {
341
0
    case 0:
342
0
      if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
343
0
        if (line[strlen(line) - 1] == '\n')
344
0
          line[strlen(line) - 1] = '\0';
345
0
        if (strcmp(iface->name, line + strlen(slaveif)) == 0)
346
0
          state++;
347
0
      }
348
0
      break;
349
0
    case 1:
350
0
      if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
351
0
        if (line[strlen(line) - 1] == '\n')
352
0
          line[strlen(line) - 1] = '\0';
353
0
        if (sscanf(line + strlen(hwaddr),
354
0
          "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
355
0
          &mac[0], &mac[1], &mac[2], &mac[3], &mac[4],
356
0
          &mac[5]) != ETHER_ADDR_LEN) {
357
0
          log_warn("interfaces", "unable to parse %s",
358
0
              line + strlen(hwaddr));
359
0
          fclose(netbond);
360
0
          return;
361
0
        }
362
0
        memcpy(iface->address, mac, ETHER_ADDR_LEN);
363
0
        fclose(netbond);
364
0
        return;
365
0
      }
366
0
      break;
367
0
    }
368
0
  }
369
0
  log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
370
0
      iface->name);
371
0
  fclose(netbond);
372
0
}
373
374
/**
375
 * Get permanent MAC.
376
 */
377
static void
378
iflinux_get_permanent_mac(struct lldpd *cfg, struct interfaces_device_list *interfaces,
379
    struct interfaces_device *iface)
380
0
{
381
0
  struct interfaces_device *master = iface->upper;
382
383
0
  if (master == NULL || master->type != IFACE_BOND_T) return;
384
0
  if (iflinux_get_permanent_mac_ethtool(cfg, interfaces, iface) == -1 &&
385
0
      (master->driver == NULL || !strcmp(master->driver, "bonding")))
386
    /* Fallback to old method for a bond */
387
0
    iflinux_get_permanent_mac_bond(cfg, interfaces, iface);
388
0
}
389
390
#ifdef ENABLE_DOT3
391
0
#  define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
392
#  define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
393
    uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
394
395
struct ethtool_link_usettings {
396
  struct ethtool_link_settings base;
397
  struct {
398
    ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
399
    ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
400
    ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
401
  } link_modes;
402
};
403
404
static int
405
iflinux_ethtool_link_mode_test_bit(unsigned int nr, const uint32_t *mask)
406
0
{
407
0
  if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) return 0;
408
0
  return !!(mask[nr / 32] & (1 << (nr % 32)));
409
0
}
410
static void
411
iflinux_ethtool_link_mode_unset_bit(unsigned int nr, uint32_t *mask)
412
0
{
413
0
  if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32) return;
414
0
  mask[nr / 32] &= ~(1 << (nr % 32));
415
0
}
416
static int
417
iflinux_ethtool_link_mode_is_empty(const uint32_t *mask)
418
0
{
419
0
  for (unsigned int i = 0; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i) {
420
0
    if (mask[i] != 0) return 0;
421
0
  }
422
423
0
  return 1;
424
0
}
425
426
static int
427
iflinux_ethtool_glink(struct lldpd *cfg, const char *ifname,
428
    struct ethtool_link_usettings *uset)
429
0
{
430
0
  int rc;
431
432
  /* Try with ETHTOOL_GLINKSETTINGS first */
433
0
  struct {
434
0
    struct ethtool_link_settings req;
435
0
    uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
436
0
  } ecmd;
437
0
  static int8_t nwords = 0;
438
0
  struct ifreq ifr = {};
439
0
  strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
440
441
0
  if (nwords == 0) {
442
    /* Do a handshake first. We assume that this is device-independant. */
443
0
    memset(&ecmd, 0, sizeof(ecmd));
444
0
    ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
445
0
    ifr.ifr_data = (caddr_t)&ecmd;
446
0
    rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
447
0
    if (rc == 0) {
448
0
      nwords = -ecmd.req.link_mode_masks_nwords;
449
0
      log_debug("interfaces", "glinksettings nwords is %" PRId8,
450
0
          nwords);
451
0
    } else {
452
0
      static int once = 0;
453
0
      if (errno == EPERM && !once) {
454
0
        log_warnx("interfaces",
455
0
            "cannot get ethtool link information "
456
0
            "with GLINKSETTINGS (requires 4.9+). "
457
0
            "25G+ speeds may be missing in MAC/PHY TLVs");
458
0
        once = 1;
459
0
      }
460
0
      nwords = -1;
461
0
    }
462
0
  }
463
0
  if (nwords > 0) {
464
0
    memset(&ecmd, 0, sizeof(ecmd));
465
0
    ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
466
0
    ecmd.req.link_mode_masks_nwords = nwords;
467
0
    ifr.ifr_data = (caddr_t)&ecmd;
468
0
    rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
469
0
    if (rc == 0) {
470
0
      log_debug("interfaces",
471
0
          "got ethtool results for %s with GLINKSETTINGS", ifname);
472
0
      memcpy(&uset->base, &ecmd.req, sizeof(uset->base));
473
0
      unsigned int u32_offs = 0;
474
0
      memcpy(uset->link_modes.supported,
475
0
          &ecmd.link_mode_data[u32_offs],
476
0
          4 * ecmd.req.link_mode_masks_nwords);
477
0
      u32_offs += ecmd.req.link_mode_masks_nwords;
478
0
      memcpy(uset->link_modes.advertising,
479
0
          &ecmd.link_mode_data[u32_offs],
480
0
          4 * ecmd.req.link_mode_masks_nwords);
481
0
      u32_offs += ecmd.req.link_mode_masks_nwords;
482
0
      memcpy(uset->link_modes.lp_advertising,
483
0
          &ecmd.link_mode_data[u32_offs],
484
0
          4 * ecmd.req.link_mode_masks_nwords);
485
0
      goto end;
486
0
    }
487
0
  }
488
489
  /* Try with ETHTOOL_GSET */
490
0
  struct ethtool_cmd ethc;
491
0
  memset(&ethc, 0, sizeof(ethc));
492
0
  ethc.cmd = ETHTOOL_GSET;
493
0
  ifr.ifr_data = (caddr_t)&ethc;
494
0
  rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
495
0
  if (rc == 0) {
496
    /* Do a partial copy (only what we need) */
497
0
    log_debug("interfaces", "got ethtool results for %s with GSET", ifname);
498
0
    memset(uset, 0, sizeof(*uset));
499
0
    uset->base.cmd = ETHTOOL_GSET;
500
0
    uset->base.link_mode_masks_nwords = 1;
501
0
    uset->link_modes.supported[0] = ethc.supported;
502
0
    uset->link_modes.advertising[0] = ethc.advertising;
503
0
    uset->link_modes.lp_advertising[0] = ethc.lp_advertising;
504
0
    uset->base.speed = (ethc.speed_hi << 16) | ethc.speed;
505
0
    uset->base.duplex = ethc.duplex;
506
0
    uset->base.port = ethc.port;
507
0
    uset->base.autoneg = ethc.autoneg;
508
0
  } else {
509
0
    static int once = 0;
510
0
    if (errno == EPERM && !once) {
511
0
      log_warnx("interfaces",
512
0
          "cannot get ethtool link information "
513
0
          "with GSET (requires 2.6.19+). "
514
0
          "MAC/PHY TLV will be unavailable");
515
0
      once = 1;
516
0
    }
517
0
  }
518
0
end:
519
0
  return rc;
520
0
}
521
522
struct ethtool_to_mau_type {
523
  int ethtool_bit;
524
  int mau_type;
525
};
526
527
static int
528
iflinux_ethtool_to_mau_type(const struct ethtool_to_mau_type *map,
529
    const uint32_t *supported)
530
0
{
531
0
  for (int i = 0; map[i].ethtool_bit >= 0; i++) {
532
0
    if (iflinux_ethtool_link_mode_test_bit(
533
0
      map[i].ethtool_bit, supported))
534
0
      return map[i].mau_type;
535
0
  }
536
0
  return 0;
537
0
}
538
539
static const struct ethtool_to_mau_type ethtool_10g_to_mau[] = {
540
  { ETHTOOL_LINK_MODE_10000baseT_Full_BIT, LLDP_DOT3_MAU_10GBASET },
541
  { ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, LLDP_DOT3_MAU_10GBASEKX4 },
542
  { ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, LLDP_DOT3_MAU_10GBASEKR },
543
  { ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, LLDP_DOT3_MAU_10GIGBASECX4 },
544
  { ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, LLDP_DOT3_MAU_10GIGBASESR },
545
  { ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, LLDP_DOT3_MAU_10GIGBASELR },
546
  { ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, LLDP_DOT3_MAU_10GBASELRM },
547
  { ETHTOOL_LINK_MODE_10000baseER_Full_BIT, LLDP_DOT3_MAU_10GIGBASEER },
548
  { -1, 0 }
549
};
550
551
static const struct ethtool_to_mau_type ethtool_25g_to_mau[] = {
552
  { ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, LLDP_DOT3_MAU_25GBASECR },
553
  { ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, LLDP_DOT3_MAU_25GBASEKR },
554
  { ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, LLDP_DOT3_MAU_25GBASESR },
555
  { -1, 0 }
556
};
557
558
static const struct ethtool_to_mau_type ethtool_40g_to_mau[] = {
559
  { ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, LLDP_DOT3_MAU_40GBASEKR4 },
560
  { ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, LLDP_DOT3_MAU_40GBASECR4 },
561
  { ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, LLDP_DOT3_MAU_40GBASESR4 },
562
  { ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, LLDP_DOT3_MAU_40GBASELR4 },
563
  { -1, 0 }
564
};
565
566
static const struct ethtool_to_mau_type ethtool_50g_to_mau[] = {
567
  { ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, LLDP_DOT3_MAU_50GBASECR },
568
  { ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, LLDP_DOT3_MAU_50GBASEKR },
569
  { ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, LLDP_DOT3_MAU_50GBASESR },
570
  { ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, LLDP_DOT3_MAU_50GBASEKR },
571
  { ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, LLDP_DOT3_MAU_50GBASESR },
572
  { ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, LLDP_DOT3_MAU_50GBASECR },
573
  { ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, LLDP_DOT3_MAU_50GBASELR },
574
  { -1, 0 }
575
};
576
577
static const struct ethtool_to_mau_type ethtool_100g_to_mau[] = {
578
  { ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, LLDP_DOT3_MAU_100GBASEKR4 },
579
  { ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, LLDP_DOT3_MAU_100GBASESR4 },
580
  { ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, LLDP_DOT3_MAU_100GBASECR4 },
581
  { ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, LLDP_DOT3_MAU_100GBASELR4 },
582
  { ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, LLDP_DOT3_MAU_100GBASEKR2 },
583
  { ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, LLDP_DOT3_MAU_100GBASESR2 },
584
  { ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, LLDP_DOT3_MAU_100GBASECR2 },
585
  { ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, LLDP_DOT3_MAU_100GBASER },
586
  { ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, LLDP_DOT3_MAU_100GBASEDR },
587
  { ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
588
  { ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
589
  { ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
590
  { ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, LLDP_DOT3_MAU_100GBASER },
591
  { ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, LLDP_DOT3_MAU_100GBASEDR },
592
  { -1, 0 }
593
};
594
595
static const struct ethtool_to_mau_type ethtool_200g_to_mau[] = {
596
  { ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, LLDP_DOT3_MAU_200GBASEKR4 },
597
  { ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, LLDP_DOT3_MAU_200GBASESR4 },
598
  { ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, LLDP_DOT3_MAU_200GBASELR4 },
599
  { ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, LLDP_DOT3_MAU_200GBASEDR4 },
600
  { ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, LLDP_DOT3_MAU_200GBASECR4 },
601
  { ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
602
  { ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
603
  { ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
604
  { ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
605
  { ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, LLDP_DOT3_MAU_200GBASER },
606
  { -1, 0 }
607
};
608
609
static const struct ethtool_to_mau_type ethtool_400g_to_mau[] = {
610
  { ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, LLDP_DOT3_MAU_400GBASELR8 },
611
  { ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, LLDP_DOT3_MAU_400GBASEDR4 },
612
  { ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
613
  { ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
614
  { ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
615
  { ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
616
  { ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, LLDP_DOT3_MAU_400GBASER },
617
  { ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
618
  { ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
619
  { ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, LLDP_DOT3_MAU_400GBASER },
620
  { -1, 0 }
621
};
622
623
/* Fill up MAC/PHY for a given hardware port */
624
static void
625
iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
626
0
{
627
0
  struct ethtool_link_usettings uset = {};
628
0
  struct lldpd_port *port = &hardware->h_lport;
629
0
  int j, mau;
630
0
  int advertised_ethtool_to_rfc3636[][2] = {
631
0
    { ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T },
632
0
    { ETHTOOL_LINK_MODE_10baseT_Full_BIT,
633
0
        LLDP_DOT3_LINK_AUTONEG_10BASET_FD },
634
0
    { ETHTOOL_LINK_MODE_100baseT_Half_BIT,
635
0
        LLDP_DOT3_LINK_AUTONEG_100BASE_TX },
636
0
    { ETHTOOL_LINK_MODE_100baseT_Full_BIT,
637
0
        LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD },
638
0
    { ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
639
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_T },
640
0
    { ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
641
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD },
642
0
    { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
643
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD },
644
0
    { ETHTOOL_LINK_MODE_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE },
645
0
    { ETHTOOL_LINK_MODE_Asym_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE },
646
0
    { -1, 0 }
647
0
  };
648
649
0
  log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
650
0
      hardware->h_ifname);
651
0
  if (iflinux_ethtool_glink(cfg, hardware->h_ifname, &uset) == 0) {
652
0
    port->p_macphy.autoneg_support =
653
0
        iflinux_ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
654
0
      uset.link_modes.supported);
655
0
    port->p_macphy.autoneg_enabled =
656
0
        (uset.base.autoneg == AUTONEG_DISABLE) ? 0 : 1;
657
0
    for (j = 0; advertised_ethtool_to_rfc3636[j][0] >= 0; j++) {
658
0
      if (iflinux_ethtool_link_mode_test_bit(
659
0
        advertised_ethtool_to_rfc3636[j][0],
660
0
        uset.link_modes.advertising)) {
661
0
        port->p_macphy.autoneg_advertised |=
662
0
            advertised_ethtool_to_rfc3636[j][1];
663
0
        iflinux_ethtool_link_mode_unset_bit(
664
0
            advertised_ethtool_to_rfc3636[j][0],
665
0
            uset.link_modes.advertising);
666
0
      }
667
0
    }
668
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
669
0
        uset.link_modes.advertising);
670
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT,
671
0
        uset.link_modes.advertising);
672
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT,
673
0
        uset.link_modes.advertising);
674
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT,
675
0
        uset.link_modes.advertising);
676
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
677
0
        uset.link_modes.advertising);
678
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT,
679
0
        uset.link_modes.advertising);
680
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT,
681
0
        uset.link_modes.advertising);
682
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
683
0
        uset.link_modes.advertising);
684
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT,
685
0
        uset.link_modes.advertising);
686
0
    if (!iflinux_ethtool_link_mode_is_empty(uset.link_modes.advertising)) {
687
0
      port->p_macphy.autoneg_advertised |=
688
0
          LLDP_DOT3_LINK_AUTONEG_OTHER;
689
0
    }
690
0
    switch (uset.base.speed) {
691
0
    case SPEED_10:
692
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
693
0
          LLDP_DOT3_MAU_10BASETFD :
694
0
          LLDP_DOT3_MAU_10BASETHD;
695
0
      if (uset.base.port == PORT_BNC)
696
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
697
0
      if (uset.base.port == PORT_FIBRE)
698
0
        port->p_macphy.mau_type =
699
0
            (uset.base.duplex == DUPLEX_FULL) ?
700
0
            LLDP_DOT3_MAU_10BASEFLFD :
701
0
            LLDP_DOT3_MAU_10BASEFLHD;
702
0
      break;
703
0
    case SPEED_100:
704
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
705
0
          LLDP_DOT3_MAU_100BASETXFD :
706
0
          LLDP_DOT3_MAU_100BASETXHD;
707
0
      if (uset.base.port == PORT_BNC)
708
0
        port->p_macphy.mau_type =
709
0
            (uset.base.duplex == DUPLEX_FULL) ?
710
0
            LLDP_DOT3_MAU_100BASET2FD :
711
0
            LLDP_DOT3_MAU_100BASET2HD;
712
0
      if (uset.base.port == PORT_FIBRE)
713
0
        port->p_macphy.mau_type =
714
0
            (uset.base.duplex == DUPLEX_FULL) ?
715
0
            LLDP_DOT3_MAU_100BASEFXFD :
716
0
            LLDP_DOT3_MAU_100BASEFXHD;
717
0
      break;
718
0
    case SPEED_1000:
719
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
720
0
          LLDP_DOT3_MAU_1000BASETFD :
721
0
          LLDP_DOT3_MAU_1000BASETHD;
722
0
      if (uset.base.port == PORT_FIBRE)
723
0
        port->p_macphy.mau_type =
724
0
            (uset.base.duplex == DUPLEX_FULL) ?
725
0
            LLDP_DOT3_MAU_1000BASEXFD :
726
0
            LLDP_DOT3_MAU_1000BASEXHD;
727
0
      break;
728
0
    case SPEED_2500:
729
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_2P5GIGT;
730
0
      break;
731
0
    case SPEED_5000:
732
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_5GIGT;
733
0
      break;
734
0
    case SPEED_10000:
735
0
      if ((mau = iflinux_ethtool_to_mau_type(
736
0
         ethtool_10g_to_mau, uset.link_modes.supported)))
737
0
        port->p_macphy.mau_type = mau;
738
0
      else if (uset.base.port == PORT_TP)
739
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GBASET;
740
0
      else if (uset.base.port == PORT_FIBRE)
741
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASELR;
742
0
      else if (uset.base.port == PORT_DA)
743
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASECX4;
744
0
      break;
745
0
    case SPEED_25000:
746
0
      if ((mau = iflinux_ethtool_to_mau_type(
747
0
         ethtool_25g_to_mau, uset.link_modes.supported)))
748
0
        port->p_macphy.mau_type = mau;
749
0
      else if (uset.base.port == PORT_TP)
750
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASET;
751
0
      else if (uset.base.port == PORT_FIBRE)
752
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASELR;
753
0
      else if (uset.base.port == PORT_DA)
754
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASECR;
755
0
      break;
756
0
    case SPEED_40000:
757
0
      if ((mau = iflinux_ethtool_to_mau_type(
758
0
         ethtool_40g_to_mau, uset.link_modes.supported)))
759
0
        port->p_macphy.mau_type = mau;
760
0
      else
761
0
        port->p_macphy.mau_type =
762
0
            (uset.base.port == PORT_FIBRE) ?
763
0
            LLDP_DOT3_MAU_40GBASELR4 :
764
0
            LLDP_DOT3_MAU_40GBASECR4;
765
0
      break;
766
0
    case SPEED_50000:
767
0
      if ((mau = iflinux_ethtool_to_mau_type(
768
0
         ethtool_50g_to_mau, uset.link_modes.supported)))
769
0
        port->p_macphy.mau_type = mau;
770
0
      else
771
0
        port->p_macphy.mau_type =
772
0
            (uset.base.port == PORT_FIBRE) ?
773
0
            LLDP_DOT3_MAU_50GBASELR :
774
0
            LLDP_DOT3_MAU_50GBASECR;
775
0
      break;
776
0
    case SPEED_100000:
777
0
      if ((mau = iflinux_ethtool_to_mau_type(
778
0
         ethtool_100g_to_mau, uset.link_modes.supported)))
779
0
        port->p_macphy.mau_type = mau;
780
0
      else
781
0
        port->p_macphy.mau_type =
782
0
            (uset.base.port == PORT_FIBRE) ?
783
0
            LLDP_DOT3_MAU_100GBASELR4 :
784
0
            LLDP_DOT3_MAU_100GBASECR4;
785
0
      break;
786
0
    case SPEED_200000:
787
0
      if ((mau = iflinux_ethtool_to_mau_type(
788
0
         ethtool_200g_to_mau, uset.link_modes.supported)))
789
0
        port->p_macphy.mau_type = mau;
790
0
      else
791
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_200GBASER;
792
0
      break;
793
0
    case SPEED_400000:
794
0
      if ((mau = iflinux_ethtool_to_mau_type(
795
0
         ethtool_400g_to_mau, uset.link_modes.supported)))
796
0
        port->p_macphy.mau_type = mau;
797
0
      else
798
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_400GBASER;
799
0
      break;
800
0
    }
801
0
    if (uset.base.port == PORT_AUI)
802
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
803
0
  }
804
0
}
805
#else  /* ENABLE_DOT3 */
806
static void
807
iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
808
{
809
}
810
#endif /* ENABLE_DOT3 */
811
812
#ifdef ENABLE_OLDIES
813
struct bond_master {
814
  char name[IFNAMSIZ];
815
  int index;
816
};
817
818
static int
819
iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
820
{
821
  struct bond_master *master = hardware->h_data;
822
  int fd;
823
  int un = 1;
824
825
  if (!master) return -1;
826
827
  log_debug("interfaces", "initialize enslaved device %s", hardware->h_ifname);
828
829
  /* First, we get a socket to the raw physical interface */
830
  if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
831
    return -1;
832
  hardware->h_sendfd = fd;
833
  interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
834
835
  /* Then, we open a raw interface for the master */
836
  log_debug("interfaces", "enslaved device %s has master %s(%d)",
837
      hardware->h_ifname, master->name, master->index);
838
  if ((fd = priv_iface_init(master->index, master->name)) == -1) {
839
    close(hardware->h_sendfd);
840
    return -1;
841
  }
842
  /* With bonding and older kernels (< 2.6.27) we need to listen
843
   * to bond device. We use setsockopt() PACKET_ORIGDEV to get
844
   * physical device instead of bond device (works with >=
845
   * 2.6.24). */
846
  if (setsockopt(fd, SOL_PACKET, PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
847
    log_info("interfaces",
848
        "unable to setsockopt for master bonding device of %s. "
849
        "You will get inaccurate results",
850
        hardware->h_ifname);
851
  }
852
  interfaces_setup_multicast(cfg, master->name, 0);
853
854
  levent_hardware_add_fd(hardware, hardware->h_sendfd);
855
  levent_hardware_add_fd(hardware, fd);
856
  log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
857
      hardware->h_ifname, hardware->h_sendfd, master->name, fd);
858
  return 0;
859
}
860
861
static int
862
iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd,
863
    char *buffer, size_t size)
864
{
865
  int n;
866
  struct sockaddr_ll from;
867
  struct bond_master *master = hardware->h_data;
868
869
  log_debug("interfaces", "receive PDU from enslaved device %s",
870
      hardware->h_ifname);
871
  if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
872
    return -1;
873
  if (fd == hardware->h_sendfd) /* We received this on the physical interface. */
874
    return n;
875
  /* We received this on the bonding interface. Is it really for us? */
876
  if (from.sll_ifindex == hardware->h_ifindex) /* This is for us */
877
    return n;
878
  if (from.sll_ifindex == master->index)
879
    /* We don't know from which physical interface it comes (kernel
880
     * < 2.6.24). In doubt, this is for us. */
881
    return n;
882
  return -1; /* Not for us */
883
}
884
885
static int
886
iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
887
{
888
  struct bond_master *master = hardware->h_data;
889
  log_debug("interfaces", "closing enslaved device %s", hardware->h_ifname);
890
  interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
891
  interfaces_setup_multicast(cfg, master->name, 1);
892
  free(hardware->h_data);
893
  hardware->h_data = NULL;
894
  return 0;
895
}
896
897
struct lldpd_ops bond_ops = {
898
  .send = iflinux_eth_send,
899
  .recv = iface_bond_recv,
900
  .cleanup = iface_bond_close,
901
};
902
903
static void
904
iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
905
{
906
  struct interfaces_device *iface;
907
  struct interfaces_device *master;
908
  struct lldpd_hardware *hardware;
909
  struct bond_master *bmaster;
910
  int created;
911
  TAILQ_FOREACH (iface, interfaces, next) {
912
    if (!(iface->type & IFACE_PHYSICAL_T)) continue;
913
    if (iface->ignore) continue;
914
    if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
915
916
    master = iface->upper;
917
    log_debug("interfaces",
918
        "%s is an acceptable enslaved device (master=%s)", iface->name,
919
        master->name);
920
    created = 0;
921
    if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) ==
922
        NULL) {
923
      if ((hardware = lldpd_alloc_hardware(cfg, iface->name,
924
         iface->index)) == NULL) {
925
        log_warnx("interfaces",
926
            "Unable to allocate space for %s", iface->name);
927
        continue;
928
      }
929
      created = 1;
930
    }
931
    if (hardware->h_flags) continue;
932
    if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) {
933
      if (!created) {
934
        log_debug("interfaces",
935
            "bond %s is converted from another type of interface",
936
            hardware->h_ifname);
937
        if (hardware->h_ops && hardware->h_ops->cleanup)
938
          hardware->h_ops->cleanup(cfg, hardware);
939
        levent_hardware_release(hardware);
940
        levent_hardware_init(hardware);
941
      }
942
      bmaster = hardware->h_data =
943
          calloc(1, sizeof(struct bond_master));
944
      if (!bmaster) {
945
        log_warn("interfaces", "not enough memory");
946
        lldpd_hardware_cleanup(cfg, hardware);
947
        continue;
948
      }
949
    } else
950
      bmaster = hardware->h_data;
951
    bmaster->index = master->index;
952
    strlcpy(bmaster->name, master->name, IFNAMSIZ);
953
    if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) {
954
      if (iface_bond_init(cfg, hardware) != 0) {
955
        log_warn("interfaces", "unable to initialize %s",
956
            hardware->h_ifname);
957
        lldpd_hardware_cleanup(cfg, hardware);
958
        continue;
959
      }
960
      hardware->h_ops = &bond_ops;
961
      hardware->h_mangle = 1;
962
    }
963
    if (created)
964
      interfaces_helper_add_hardware(cfg, hardware);
965
    else
966
      lldpd_port_cleanup(&hardware->h_lport, 0);
967
968
    hardware->h_flags = iface->flags;
969
    iface->ignore = 1;
970
971
    /* Get local address */
972
    memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
973
974
    /* Fill information about port */
975
    interfaces_helper_port_name_desc(cfg, hardware, iface);
976
977
    /* Fill additional info */
978
#  ifdef ENABLE_DOT3
979
    hardware->h_lport.p_aggregid = master->index;
980
#  endif
981
    hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
982
  }
983
}
984
#endif
985
986
/* Query each interface to get the appropriate driver */
987
static void
988
iflinux_add_driver(struct lldpd *cfg, struct interfaces_device_list *interfaces)
989
0
{
990
0
  struct interfaces_device *iface;
991
0
  TAILQ_FOREACH (iface, interfaces, next) {
992
0
    struct ethtool_drvinfo ethc = { .cmd = ETHTOOL_GDRVINFO };
993
0
    struct ifreq ifr = { .ifr_data = (caddr_t)&ethc };
994
0
    if (iface->driver) continue;
995
996
0
    strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
997
0
    if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
998
0
      iface->driver = strdup(ethc.driver);
999
0
      log_debug("interfaces", "driver for %s is `%s`", iface->name,
1000
0
          iface->driver);
1001
0
    }
1002
0
  }
1003
0
}
1004
1005
/* Query each interface to see if it is a wireless one */
1006
static void
1007
iflinux_add_wireless(struct lldpd *cfg, struct interfaces_device_list *interfaces)
1008
0
{
1009
0
  struct interfaces_device *iface;
1010
0
  TAILQ_FOREACH (iface, interfaces, next) {
1011
0
    if (iface->type &
1012
0
        (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T | IFACE_WIRELESS_T))
1013
0
      continue;
1014
0
    char path[IFNAMSIZ + sizeof(SYSFS_CLASS_NET) + sizeof("/wireless")];
1015
0
    snprintf(path, sizeof(path), SYSFS_CLASS_NET "%s/wireless",
1016
0
        iface->name);
1017
0
    if (priv_exist(path) == 0) {
1018
0
      log_debug("interfaces", "%s is wireless", iface->name);
1019
0
      iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
1020
0
    }
1021
0
  }
1022
0
}
1023
1024
/* Query each interface to see if it is a bridge */
1025
static void
1026
iflinux_add_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces)
1027
0
{
1028
0
  struct interfaces_device *iface;
1029
1030
0
  TAILQ_FOREACH (iface, interfaces, next) {
1031
0
    if (iface->type &
1032
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
1033
0
      continue;
1034
0
    if (iflinux_is_bridge(cfg, interfaces, iface)) {
1035
0
      log_debug("interfaces", "interface %s is a bridge",
1036
0
          iface->name);
1037
0
      iface->type |= IFACE_BRIDGE_T;
1038
0
    }
1039
0
  }
1040
0
}
1041
1042
/* Query each interface to see if it is a bond */
1043
static void
1044
iflinux_add_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
1045
0
{
1046
0
  struct interfaces_device *iface;
1047
1048
0
  TAILQ_FOREACH (iface, interfaces, next) {
1049
0
    if (iface->type &
1050
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
1051
0
      continue;
1052
0
    if (iflinux_is_bond(cfg, interfaces, iface)) {
1053
0
      log_debug("interfaces", "interface %s is a bond", iface->name);
1054
0
      iface->type |= IFACE_BOND_T;
1055
0
    }
1056
0
  }
1057
0
}
1058
1059
/* Query each interface to see if it is a vlan */
1060
static void
1061
iflinux_add_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces)
1062
0
{
1063
0
  struct interfaces_device *iface;
1064
1065
0
  TAILQ_FOREACH (iface, interfaces, next) {
1066
0
    if (iface->type &
1067
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
1068
0
      continue;
1069
0
    if (iflinux_is_vlan(cfg, interfaces, iface)) {
1070
0
      log_debug("interfaces", "interface %s is a VLAN", iface->name);
1071
0
      iface->type |= IFACE_VLAN_T;
1072
0
    }
1073
0
  }
1074
0
}
1075
1076
static void
1077
iflinux_add_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces)
1078
0
{
1079
0
  struct interfaces_device *iface;
1080
  /* Deny some drivers */
1081
0
  const char *const *rif;
1082
0
  const char *const denied_drivers[] = { "cdc_mbim", "vxlan", NULL };
1083
1084
0
  TAILQ_FOREACH (iface, interfaces, next) {
1085
0
    if (iface->type & (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
1086
0
      continue;
1087
1088
0
    iface->type &= ~IFACE_PHYSICAL_T;
1089
1090
    /* We request that the interface is able to do either multicast
1091
     * or broadcast to be able to send discovery frames. */
1092
0
    if (!(iface->flags & (IFF_MULTICAST | IFF_BROADCAST))) {
1093
0
      log_debug("interfaces",
1094
0
          "skip %s: not able to do multicast nor broadcast",
1095
0
          iface->name);
1096
0
      continue;
1097
0
    }
1098
1099
    /* Check if the driver is not denied */
1100
0
    if (iface->driver) {
1101
0
      int skip = 0;
1102
0
      for (rif = denied_drivers; *rif; rif++) {
1103
0
        if (strcmp(iface->driver, *rif) == 0) {
1104
0
          log_debug("interfaces",
1105
0
              "skip %s: denied driver", iface->name);
1106
0
          skip = 1;
1107
0
          break;
1108
0
        }
1109
0
      }
1110
0
      if (skip) continue;
1111
0
    }
1112
1113
    /* If the interface is linked to another one, skip it too. */
1114
0
    if (iface->lower &&
1115
0
        (!iface->driver ||
1116
0
      (strcmp(iface->driver, "veth") &&
1117
0
          strcmp(iface->driver, "dsa")))) {
1118
0
      log_debug("interfaces",
1119
0
          "skip %s: there is a lower interface (%s)", iface->name,
1120
0
          iface->lower->name);
1121
0
      continue;
1122
0
    }
1123
1124
    /* Get the real MAC address (for example, if the interface is enslaved)
1125
     */
1126
0
    iflinux_get_permanent_mac(cfg, interfaces, iface);
1127
1128
0
    log_debug("interfaces", "%s is a physical interface", iface->name);
1129
0
    iface->type |= IFACE_PHYSICAL_T;
1130
0
  }
1131
0
}
1132
1133
void
1134
interfaces_update(struct lldpd *cfg)
1135
0
{
1136
0
  struct lldpd_hardware *hardware;
1137
0
  struct interfaces_device_list *interfaces;
1138
0
  struct interfaces_address_list *addresses;
1139
0
  interfaces = netlink_get_interfaces(cfg);
1140
0
  addresses = netlink_get_addresses(cfg);
1141
0
  if (interfaces == NULL || addresses == NULL) {
1142
0
    log_warnx("interfaces", "cannot update the list of local interfaces");
1143
0
    return;
1144
0
  }
1145
1146
  /* Add missing bits to list of interfaces */
1147
0
  iflinux_add_driver(cfg, interfaces);
1148
0
  if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
1149
0
    iflinux_add_wireless(cfg, interfaces);
1150
0
  if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
1151
0
    iflinux_add_bridge(cfg, interfaces);
1152
0
  iflinux_add_bond(cfg, interfaces);
1153
0
  iflinux_add_vlan(cfg, interfaces);
1154
0
  iflinux_add_physical(cfg, interfaces);
1155
1156
0
  interfaces_helper_allowlist(cfg, interfaces);
1157
#ifdef ENABLE_OLDIES
1158
  iflinux_handle_bond(cfg, interfaces);
1159
#endif
1160
0
  interfaces_helper_physical(cfg, interfaces, &eth_ops, iflinux_eth_init);
1161
0
#ifdef ENABLE_DOT1
1162
0
  interfaces_helper_vlan(cfg, interfaces);
1163
0
#endif
1164
0
  interfaces_helper_mgmt(cfg, addresses, interfaces);
1165
0
  interfaces_helper_chassis(cfg, interfaces);
1166
1167
  /* Mac/PHY */
1168
0
  TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
1169
0
    if (!(hardware->h_flags & IFF_RUNNING)) continue;
1170
0
    iflinux_macphy(cfg, hardware);
1171
0
    interfaces_helper_promisc(cfg, hardware);
1172
0
  }
1173
0
}
1174
1175
void
1176
interfaces_cleanup(struct lldpd *cfg)
1177
0
{
1178
0
  netlink_cleanup(cfg);
1179
0
}