/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(ðc, 0, sizeof(ethc)); |
492 | 0 | ethc.cmd = ETHTOOL_GSET; |
493 | 0 | ifr.ifr_data = (caddr_t)ðc; |
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)ðc }; |
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, ð_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 | } |