summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-lldp-rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-lldp-rx.c')
-rw-r--r--src/network/networkd-lldp-rx.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c
new file mode 100644
index 0000000..3a59884
--- /dev/null
+++ b/src/network/networkd-lldp-rx.c
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <unistd.h>
+
+#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;
+}