Coverage Report

Created: 2024-06-18 06:28

/src/lldpd/src/daemon/interfaces-linux.c
Line
Count
Source (jump to first uncovered line)
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(sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN, 1);
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
/* Fill up MAC/PHY for a given hardware port */
523
static void
524
iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
525
0
{
526
0
  struct ethtool_link_usettings uset = {};
527
0
  struct lldpd_port *port = &hardware->h_lport;
528
0
  int j;
529
0
  int advertised_ethtool_to_rfc3636[][2] = {
530
0
    { ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T },
531
0
    { ETHTOOL_LINK_MODE_10baseT_Full_BIT,
532
0
        LLDP_DOT3_LINK_AUTONEG_10BASET_FD },
533
0
    { ETHTOOL_LINK_MODE_100baseT_Half_BIT,
534
0
        LLDP_DOT3_LINK_AUTONEG_100BASE_TX },
535
0
    { ETHTOOL_LINK_MODE_100baseT_Full_BIT,
536
0
        LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD },
537
0
    { ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
538
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_T },
539
0
    { ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
540
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD },
541
0
    { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
542
0
        LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD },
543
0
    { ETHTOOL_LINK_MODE_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE },
544
0
    { ETHTOOL_LINK_MODE_Asym_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE },
545
0
    { -1, 0 }
546
0
  };
547
548
0
  log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
549
0
      hardware->h_ifname);
550
0
  if (iflinux_ethtool_glink(cfg, hardware->h_ifname, &uset) == 0) {
551
0
    port->p_macphy.autoneg_support =
552
0
        iflinux_ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
553
0
      uset.link_modes.supported);
554
0
    port->p_macphy.autoneg_enabled =
555
0
        (uset.base.autoneg == AUTONEG_DISABLE) ? 0 : 1;
556
0
    for (j = 0; advertised_ethtool_to_rfc3636[j][0] >= 0; j++) {
557
0
      if (iflinux_ethtool_link_mode_test_bit(
558
0
        advertised_ethtool_to_rfc3636[j][0],
559
0
        uset.link_modes.advertising)) {
560
0
        port->p_macphy.autoneg_advertised |=
561
0
            advertised_ethtool_to_rfc3636[j][1];
562
0
        iflinux_ethtool_link_mode_unset_bit(
563
0
            advertised_ethtool_to_rfc3636[j][0],
564
0
            uset.link_modes.advertising);
565
0
      }
566
0
    }
567
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
568
0
        uset.link_modes.advertising);
569
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT,
570
0
        uset.link_modes.advertising);
571
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT,
572
0
        uset.link_modes.advertising);
573
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT,
574
0
        uset.link_modes.advertising);
575
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
576
0
        uset.link_modes.advertising);
577
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT,
578
0
        uset.link_modes.advertising);
579
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT,
580
0
        uset.link_modes.advertising);
581
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
582
0
        uset.link_modes.advertising);
583
0
    iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT,
584
0
        uset.link_modes.advertising);
585
0
    if (!iflinux_ethtool_link_mode_is_empty(uset.link_modes.advertising)) {
586
0
      port->p_macphy.autoneg_advertised |=
587
0
          LLDP_DOT3_LINK_AUTONEG_OTHER;
588
0
    }
589
0
    switch (uset.base.speed) {
590
0
    case SPEED_10:
591
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
592
0
          LLDP_DOT3_MAU_10BASETFD :
593
0
          LLDP_DOT3_MAU_10BASETHD;
594
0
      if (uset.base.port == PORT_BNC)
595
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
596
0
      if (uset.base.port == PORT_FIBRE)
597
0
        port->p_macphy.mau_type =
598
0
            (uset.base.duplex == DUPLEX_FULL) ?
599
0
            LLDP_DOT3_MAU_10BASEFLFD :
600
0
            LLDP_DOT3_MAU_10BASEFLHD;
601
0
      break;
602
0
    case SPEED_100:
603
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
604
0
          LLDP_DOT3_MAU_100BASETXFD :
605
0
          LLDP_DOT3_MAU_100BASETXHD;
606
0
      if (uset.base.port == PORT_BNC)
607
0
        port->p_macphy.mau_type =
608
0
            (uset.base.duplex == DUPLEX_FULL) ?
609
0
            LLDP_DOT3_MAU_100BASET2FD :
610
0
            LLDP_DOT3_MAU_100BASET2HD;
611
0
      if (uset.base.port == PORT_FIBRE)
612
0
        port->p_macphy.mau_type =
613
0
            (uset.base.duplex == DUPLEX_FULL) ?
614
0
            LLDP_DOT3_MAU_100BASEFXFD :
615
0
            LLDP_DOT3_MAU_100BASEFXHD;
616
0
      break;
617
0
    case SPEED_1000:
618
0
      port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ?
619
0
          LLDP_DOT3_MAU_1000BASETFD :
620
0
          LLDP_DOT3_MAU_1000BASETHD;
621
0
      if (uset.base.port == PORT_FIBRE)
622
0
        port->p_macphy.mau_type =
623
0
            (uset.base.duplex == DUPLEX_FULL) ?
624
0
            LLDP_DOT3_MAU_1000BASEXFD :
625
0
            LLDP_DOT3_MAU_1000BASEXHD;
626
0
      break;
627
0
    case SPEED_2500:
628
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_2P5GIGT;
629
0
      break;
630
0
    case SPEED_5000:
631
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_5GIGT;
632
0
      break;
633
0
    case SPEED_10000:
634
      // Distinguish between RJ45 BaseT, DAC BaseCX4, or Fibre BaseLR
635
0
      if (uset.base.port == PORT_TP) {
636
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GBASET;
637
0
      } else if (uset.base.port == PORT_FIBRE) {
638
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASELR;
639
0
      } else if (uset.base.port == PORT_DA) {
640
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_10GIGBASECX4;
641
0
      }
642
0
      break;
643
0
    case SPEED_25000:
644
      // Distinguish between RJ45 BaseT, DAC BaseCR, or Fibre BaseLR
645
0
      if (uset.base.port == PORT_TP) {
646
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASET;
647
0
      } else if (uset.base.port == PORT_FIBRE) {
648
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASELR;
649
0
      } else if (uset.base.port == PORT_DA) {
650
0
        port->p_macphy.mau_type = LLDP_DOT3_MAU_25GBASECR;
651
0
      }
652
0
      break;
653
0
    case SPEED_40000:
654
      // Same kind of approximation.
655
0
      port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
656
0
          LLDP_DOT3_MAU_40GBASELR4 :
657
0
          LLDP_DOT3_MAU_40GBASECR4;
658
0
      break;
659
0
    case SPEED_50000:
660
      // Same kind of approximation.
661
0
      port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
662
0
          LLDP_DOT3_MAU_50GBASELR :
663
0
          LLDP_DOT3_MAU_50GBASECR;
664
0
      break;
665
0
    case SPEED_100000:
666
      // Ditto
667
0
      port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ?
668
0
          LLDP_DOT3_MAU_100GBASELR4 :
669
0
          LLDP_DOT3_MAU_100GBASECR4;
670
0
      break;
671
0
    }
672
0
    if (uset.base.port == PORT_AUI)
673
0
      port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
674
0
  }
675
0
}
676
#else  /* ENABLE_DOT3 */
677
static void
678
iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
679
{
680
}
681
#endif /* ENABLE_DOT3 */
682
683
#ifdef ENABLE_OLDIES
684
struct bond_master {
685
  char name[IFNAMSIZ];
686
  int index;
687
};
688
689
static int
690
iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
691
{
692
  struct bond_master *master = hardware->h_data;
693
  int fd;
694
  int un = 1;
695
696
  if (!master) return -1;
697
698
  log_debug("interfaces", "initialize enslaved device %s", hardware->h_ifname);
699
700
  /* First, we get a socket to the raw physical interface */
701
  if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
702
    return -1;
703
  hardware->h_sendfd = fd;
704
  interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
705
706
  /* Then, we open a raw interface for the master */
707
  log_debug("interfaces", "enslaved device %s has master %s(%d)",
708
      hardware->h_ifname, master->name, master->index);
709
  if ((fd = priv_iface_init(master->index, master->name)) == -1) {
710
    close(hardware->h_sendfd);
711
    return -1;
712
  }
713
  /* With bonding and older kernels (< 2.6.27) we need to listen
714
   * to bond device. We use setsockopt() PACKET_ORIGDEV to get
715
   * physical device instead of bond device (works with >=
716
   * 2.6.24). */
717
  if (setsockopt(fd, SOL_PACKET, PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
718
    log_info("interfaces",
719
        "unable to setsockopt for master bonding device of %s. "
720
        "You will get inaccurate results",
721
        hardware->h_ifname);
722
  }
723
  interfaces_setup_multicast(cfg, master->name, 0);
724
725
  levent_hardware_add_fd(hardware, hardware->h_sendfd);
726
  levent_hardware_add_fd(hardware, fd);
727
  log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
728
      hardware->h_ifname, hardware->h_sendfd, master->name, fd);
729
  return 0;
730
}
731
732
static int
733
iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd,
734
    char *buffer, size_t size)
735
{
736
  int n;
737
  struct sockaddr_ll from;
738
  struct bond_master *master = hardware->h_data;
739
740
  log_debug("interfaces", "receive PDU from enslaved device %s",
741
      hardware->h_ifname);
742
  if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
743
    return -1;
744
  if (fd == hardware->h_sendfd) /* We received this on the physical interface. */
745
    return n;
746
  /* We received this on the bonding interface. Is it really for us? */
747
  if (from.sll_ifindex == hardware->h_ifindex) /* This is for us */
748
    return n;
749
  if (from.sll_ifindex == master->index)
750
    /* We don't know from which physical interface it comes (kernel
751
     * < 2.6.24). In doubt, this is for us. */
752
    return n;
753
  return -1; /* Not for us */
754
}
755
756
static int
757
iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
758
{
759
  struct bond_master *master = hardware->h_data;
760
  log_debug("interfaces", "closing enslaved device %s", hardware->h_ifname);
761
  interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
762
  interfaces_setup_multicast(cfg, master->name, 1);
763
  free(hardware->h_data);
764
  hardware->h_data = NULL;
765
  return 0;
766
}
767
768
struct lldpd_ops bond_ops = {
769
  .send = iflinux_eth_send,
770
  .recv = iface_bond_recv,
771
  .cleanup = iface_bond_close,
772
};
773
774
static void
775
iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
776
{
777
  struct interfaces_device *iface;
778
  struct interfaces_device *master;
779
  struct lldpd_hardware *hardware;
780
  struct bond_master *bmaster;
781
  int created;
782
  TAILQ_FOREACH (iface, interfaces, next) {
783
    if (!(iface->type & IFACE_PHYSICAL_T)) continue;
784
    if (iface->ignore) continue;
785
    if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
786
787
    master = iface->upper;
788
    log_debug("interfaces",
789
        "%s is an acceptable enslaved device (master=%s)", iface->name,
790
        master->name);
791
    created = 0;
792
    if ((hardware = lldpd_get_hardware(cfg, iface->name, iface->index)) ==
793
        NULL) {
794
      if ((hardware = lldpd_alloc_hardware(cfg, iface->name,
795
         iface->index)) == NULL) {
796
        log_warnx("interfaces",
797
            "Unable to allocate space for %s", iface->name);
798
        continue;
799
      }
800
      created = 1;
801
    }
802
    if (hardware->h_flags) continue;
803
    if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) {
804
      if (!created) {
805
        log_debug("interfaces",
806
            "bond %s is converted from another type of interface",
807
            hardware->h_ifname);
808
        if (hardware->h_ops && hardware->h_ops->cleanup)
809
          hardware->h_ops->cleanup(cfg, hardware);
810
        levent_hardware_release(hardware);
811
        levent_hardware_init(hardware);
812
      }
813
      bmaster = hardware->h_data =
814
          calloc(1, sizeof(struct bond_master));
815
      if (!bmaster) {
816
        log_warn("interfaces", "not enough memory");
817
        lldpd_hardware_cleanup(cfg, hardware);
818
        continue;
819
      }
820
    } else
821
      bmaster = hardware->h_data;
822
    bmaster->index = master->index;
823
    strlcpy(bmaster->name, master->name, IFNAMSIZ);
824
    if (hardware->h_ops != &bond_ops || hardware->h_ifindex_changed) {
825
      if (iface_bond_init(cfg, hardware) != 0) {
826
        log_warn("interfaces", "unable to initialize %s",
827
            hardware->h_ifname);
828
        lldpd_hardware_cleanup(cfg, hardware);
829
        continue;
830
      }
831
      hardware->h_ops = &bond_ops;
832
      hardware->h_mangle = 1;
833
    }
834
    if (created)
835
      interfaces_helper_add_hardware(cfg, hardware);
836
    else
837
      lldpd_port_cleanup(&hardware->h_lport, 0);
838
839
    hardware->h_flags = iface->flags;
840
    iface->ignore = 1;
841
842
    /* Get local address */
843
    memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
844
845
    /* Fill information about port */
846
    interfaces_helper_port_name_desc(cfg, hardware, iface);
847
848
    /* Fill additional info */
849
#  ifdef ENABLE_DOT3
850
    hardware->h_lport.p_aggregid = master->index;
851
#  endif
852
    hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
853
  }
854
}
855
#endif
856
857
/* Query each interface to get the appropriate driver */
858
static void
859
iflinux_add_driver(struct lldpd *cfg, struct interfaces_device_list *interfaces)
860
0
{
861
0
  struct interfaces_device *iface;
862
0
  TAILQ_FOREACH (iface, interfaces, next) {
863
0
    struct ethtool_drvinfo ethc = { .cmd = ETHTOOL_GDRVINFO };
864
0
    struct ifreq ifr = { .ifr_data = (caddr_t)&ethc };
865
0
    if (iface->driver) continue;
866
867
0
    strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
868
0
    if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
869
0
      iface->driver = strdup(ethc.driver);
870
0
      log_debug("interfaces", "driver for %s is `%s`", iface->name,
871
0
          iface->driver);
872
0
    }
873
0
  }
874
0
}
875
876
/* Query each interface to see if it is a wireless one */
877
static void
878
iflinux_add_wireless(struct lldpd *cfg, struct interfaces_device_list *interfaces)
879
0
{
880
#ifdef ENABLE_OLDIES
881
  struct interfaces_device *iface;
882
  TAILQ_FOREACH (iface, interfaces, next) {
883
    if (iface->type &
884
        (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T | IFACE_WIRELESS_T))
885
      continue;
886
    struct iwreq iwr = {};
887
    strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
888
    if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
889
      log_debug("interfaces", "%s is wireless", iface->name);
890
      iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
891
    }
892
  }
893
#endif
894
0
}
895
896
/* Query each interface to see if it is a bridge */
897
static void
898
iflinux_add_bridge(struct lldpd *cfg, struct interfaces_device_list *interfaces)
899
0
{
900
0
  struct interfaces_device *iface;
901
902
0
  TAILQ_FOREACH (iface, interfaces, next) {
903
0
    if (iface->type &
904
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
905
0
      continue;
906
0
    if (iflinux_is_bridge(cfg, interfaces, iface)) {
907
0
      log_debug("interfaces", "interface %s is a bridge",
908
0
          iface->name);
909
0
      iface->type |= IFACE_BRIDGE_T;
910
0
    }
911
0
  }
912
0
}
913
914
/* Query each interface to see if it is a bond */
915
static void
916
iflinux_add_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
917
0
{
918
0
  struct interfaces_device *iface;
919
920
0
  TAILQ_FOREACH (iface, interfaces, next) {
921
0
    if (iface->type &
922
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
923
0
      continue;
924
0
    if (iflinux_is_bond(cfg, interfaces, iface)) {
925
0
      log_debug("interfaces", "interface %s is a bond", iface->name);
926
0
      iface->type |= IFACE_BOND_T;
927
0
    }
928
0
  }
929
0
}
930
931
/* Query each interface to see if it is a vlan */
932
static void
933
iflinux_add_vlan(struct lldpd *cfg, struct interfaces_device_list *interfaces)
934
0
{
935
0
  struct interfaces_device *iface;
936
937
0
  TAILQ_FOREACH (iface, interfaces, next) {
938
0
    if (iface->type &
939
0
        (IFACE_PHYSICAL_T | IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
940
0
      continue;
941
0
    if (iflinux_is_vlan(cfg, interfaces, iface)) {
942
0
      log_debug("interfaces", "interface %s is a VLAN", iface->name);
943
0
      iface->type |= IFACE_VLAN_T;
944
0
    }
945
0
  }
946
0
}
947
948
static void
949
iflinux_add_physical(struct lldpd *cfg, struct interfaces_device_list *interfaces)
950
0
{
951
0
  struct interfaces_device *iface;
952
  /* Deny some drivers */
953
0
  const char *const *rif;
954
0
  const char *const denied_drivers[] = { "cdc_mbim", "vxlan", NULL };
955
956
0
  TAILQ_FOREACH (iface, interfaces, next) {
957
0
    if (iface->type & (IFACE_VLAN_T | IFACE_BOND_T | IFACE_BRIDGE_T))
958
0
      continue;
959
960
0
    iface->type &= ~IFACE_PHYSICAL_T;
961
962
    /* We request that the interface is able to do either multicast
963
     * or broadcast to be able to send discovery frames. */
964
0
    if (!(iface->flags & (IFF_MULTICAST | IFF_BROADCAST))) {
965
0
      log_debug("interfaces",
966
0
          "skip %s: not able to do multicast nor broadcast",
967
0
          iface->name);
968
0
      continue;
969
0
    }
970
971
    /* Check if the driver is not denied */
972
0
    if (iface->driver) {
973
0
      int skip = 0;
974
0
      for (rif = denied_drivers; *rif; rif++) {
975
0
        if (strcmp(iface->driver, *rif) == 0) {
976
0
          log_debug("interfaces",
977
0
              "skip %s: denied driver", iface->name);
978
0
          skip = 1;
979
0
          break;
980
0
        }
981
0
      }
982
0
      if (skip) continue;
983
0
    }
984
985
    /* If the interface is linked to another one, skip it too. */
986
0
    if (iface->lower &&
987
0
        (!iface->driver ||
988
0
      (strcmp(iface->driver, "veth") &&
989
0
          strcmp(iface->driver, "dsa")))) {
990
0
      log_debug("interfaces",
991
0
          "skip %s: there is a lower interface (%s)", iface->name,
992
0
          iface->lower->name);
993
0
      continue;
994
0
    }
995
996
    /* Get the real MAC address (for example, if the interface is enslaved)
997
     */
998
0
    iflinux_get_permanent_mac(cfg, interfaces, iface);
999
1000
0
    log_debug("interfaces", "%s is a physical interface", iface->name);
1001
0
    iface->type |= IFACE_PHYSICAL_T;
1002
0
  }
1003
0
}
1004
1005
void
1006
interfaces_update(struct lldpd *cfg)
1007
0
{
1008
0
  struct lldpd_hardware *hardware;
1009
0
  struct interfaces_device_list *interfaces;
1010
0
  struct interfaces_address_list *addresses;
1011
0
  interfaces = netlink_get_interfaces(cfg);
1012
0
  addresses = netlink_get_addresses(cfg);
1013
0
  if (interfaces == NULL || addresses == NULL) {
1014
0
    log_warnx("interfaces", "cannot update the list of local interfaces");
1015
0
    return;
1016
0
  }
1017
1018
  /* Add missing bits to list of interfaces */
1019
0
  iflinux_add_driver(cfg, interfaces);
1020
0
  if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
1021
0
    iflinux_add_wireless(cfg, interfaces);
1022
0
  if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
1023
0
    iflinux_add_bridge(cfg, interfaces);
1024
0
  iflinux_add_bond(cfg, interfaces);
1025
0
  iflinux_add_vlan(cfg, interfaces);
1026
0
  iflinux_add_physical(cfg, interfaces);
1027
1028
0
  interfaces_helper_allowlist(cfg, interfaces);
1029
#ifdef ENABLE_OLDIES
1030
  iflinux_handle_bond(cfg, interfaces);
1031
#endif
1032
0
  interfaces_helper_physical(cfg, interfaces, &eth_ops, iflinux_eth_init);
1033
0
#ifdef ENABLE_DOT1
1034
0
  interfaces_helper_vlan(cfg, interfaces);
1035
0
#endif
1036
0
  interfaces_helper_mgmt(cfg, addresses, interfaces);
1037
0
  interfaces_helper_chassis(cfg, interfaces);
1038
1039
  /* Mac/PHY */
1040
0
  TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
1041
0
    if (!hardware->h_flags) continue;
1042
0
    iflinux_macphy(cfg, hardware);
1043
0
    interfaces_helper_promisc(cfg, hardware);
1044
0
  }
1045
0
}
1046
1047
void
1048
interfaces_cleanup(struct lldpd *cfg)
1049
0
{
1050
0
  netlink_cleanup(cfg);
1051
0
}