/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "networkd-link.h" #include "networkd-lldp-rx.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "networkd-network.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { [LLDP_MODE_NO] = "no", [LLDP_MODE_YES] = "yes", [LLDP_MODE_ROUTERS_ONLY] = "routers-only", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); static bool link_lldp_rx_enabled(Link *link) { assert(link); if (link->flags & IFF_LOOPBACK) return false; if (link->iftype != ARPHRD_ETHER) return false; if (!link->network) return false; /* LLDP should be handled on bridge and bond slaves as those have a direct connection to their peers, * not on the bridge/bond master. Linux doesn't even (by default) forward lldp packets to the bridge * master. */ if (link->kind && STR_IN_SET(link->kind, "bridge", "bond")) return false; return link->network->lldp_mode != LLDP_MODE_NO; } static void lldp_rx_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) { Link *link = ASSERT_PTR(userdata); int r; (void) link_lldp_save(link); if (link->lldp_tx && event == SD_LLDP_RX_EVENT_ADDED) { /* If we received information about a new neighbor, restart the LLDP "fast" logic */ log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); (void) sd_lldp_tx_stop(link->lldp_tx); r = sd_lldp_tx_start(link->lldp_tx); if (r < 0) log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); } } int link_lldp_rx_configure(Link *link) { int r; if (!link_lldp_rx_enabled(link)) return 0; if (link->lldp_rx) return -EBUSY; r = sd_lldp_rx_new(&link->lldp_rx); if (r < 0) return r; r = sd_lldp_rx_attach_event(link->lldp_rx, link->manager->event, 0); if (r < 0) return r; r = sd_lldp_rx_set_ifindex(link->lldp_rx, link->ifindex); if (r < 0) return r; r = sd_lldp_rx_match_capabilities(link->lldp_rx, link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : SD_LLDP_SYSTEM_CAPABILITIES_ALL); if (r < 0) return r; r = sd_lldp_rx_set_filter_address(link->lldp_rx, &link->hw_addr.ether); if (r < 0) return r; r = sd_lldp_rx_set_callback(link->lldp_rx, lldp_rx_handler, link); if (r < 0) return r; return 0; } int link_lldp_save(Link *link) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; sd_lldp_neighbor **l = NULL; int n = 0, r, i; assert(link); if (isempty(link->lldp_file)) return 0; /* Do not update state file when running in test mode. */ if (!link->lldp_rx) { (void) unlink(link->lldp_file); return 0; } r = sd_lldp_rx_get_neighbors(link->lldp_rx, &l); if (r < 0) return r; if (r == 0) { (void) unlink(link->lldp_file); return 0; } n = r; r = fopen_temporary(link->lldp_file, &f, &temp_path); if (r < 0) goto finish; (void) fchmod(fileno(f), 0644); for (i = 0; i < n; i++) { const void *p; le64_t u; size_t sz; r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); if (r < 0) goto finish; u = htole64(sz); (void) fwrite(&u, 1, sizeof(u), f); (void) fwrite(p, 1, sz, f); } r = fflush_and_check(f); if (r < 0) goto finish; r = conservative_rename(temp_path, link->lldp_file); if (r < 0) goto finish; finish: if (r < 0) log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); if (l) { for (i = 0; i < n; i++) sd_lldp_neighbor_unref(l[i]); free(l); } return r; }