1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <net/if.h>
4 #include <net/if_arp.h>
5 #include <unistd.h>
6 
7 #include "fd-util.h"
8 #include "fileio.h"
9 #include "fs-util.h"
10 #include "networkd-link.h"
11 #include "networkd-lldp-rx.h"
12 #include "networkd-lldp-tx.h"
13 #include "networkd-manager.h"
14 #include "networkd-network.h"
15 #include "string-table.h"
16 #include "string-util.h"
17 #include "strv.h"
18 #include "tmpfile-util.h"
19 
20 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
21 
22 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
23         [LLDP_MODE_NO] = "no",
24         [LLDP_MODE_YES] = "yes",
25         [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
26 };
27 
28 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
29 
link_lldp_rx_enabled(Link * link)30 static bool link_lldp_rx_enabled(Link *link) {
31         assert(link);
32 
33         if (link->flags & IFF_LOOPBACK)
34                 return false;
35 
36         if (link->iftype != ARPHRD_ETHER)
37                 return false;
38 
39         if (!link->network)
40                 return false;
41 
42         /* LLDP should be handled on bridge and bond slaves as those have a direct connection to their peers,
43          * not on the bridge/bond master. Linux doesn't even (by default) forward lldp packets to the bridge
44          * master. */
45         if (link->kind && STR_IN_SET(link->kind, "bridge", "bond"))
46                 return false;
47 
48         return link->network->lldp_mode != LLDP_MODE_NO;
49 }
50 
lldp_rx_handler(sd_lldp_rx * lldp_rx,sd_lldp_rx_event_t event,sd_lldp_neighbor * n,void * userdata)51 static void lldp_rx_handler(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata) {
52         Link *link = userdata;
53         int r;
54 
55         assert(link);
56 
57         (void) link_lldp_save(link);
58 
59         if (link->lldp_tx && event == SD_LLDP_RX_EVENT_ADDED) {
60                 /* If we received information about a new neighbor, restart the LLDP "fast" logic */
61 
62                 log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission.");
63 
64                 (void) sd_lldp_tx_stop(link->lldp_tx);
65                 r = sd_lldp_tx_start(link->lldp_tx);
66                 if (r < 0)
67                         log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m");
68         }
69 }
70 
link_lldp_rx_configure(Link * link)71 int link_lldp_rx_configure(Link *link) {
72         int r;
73 
74         if (!link_lldp_rx_enabled(link))
75                 return 0;
76 
77         if (link->lldp_rx)
78                 return -EBUSY;
79 
80         r = sd_lldp_rx_new(&link->lldp_rx);
81         if (r < 0)
82                 return r;
83 
84         r = sd_lldp_rx_attach_event(link->lldp_rx, link->manager->event, 0);
85         if (r < 0)
86                 return r;
87 
88         r = sd_lldp_rx_set_ifindex(link->lldp_rx, link->ifindex);
89         if (r < 0)
90                 return r;
91 
92         r = sd_lldp_rx_match_capabilities(link->lldp_rx,
93                                           link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ?
94                                           SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS :
95                                           SD_LLDP_SYSTEM_CAPABILITIES_ALL);
96         if (r < 0)
97                 return r;
98 
99         r = sd_lldp_rx_set_filter_address(link->lldp_rx, &link->hw_addr.ether);
100         if (r < 0)
101                 return r;
102 
103         r = sd_lldp_rx_set_callback(link->lldp_rx, lldp_rx_handler, link);
104         if (r < 0)
105                 return r;
106 
107         return 0;
108 }
109 
link_lldp_save(Link * link)110 int link_lldp_save(Link *link) {
111         _cleanup_(unlink_and_freep) char *temp_path = NULL;
112         _cleanup_fclose_ FILE *f = NULL;
113         sd_lldp_neighbor **l = NULL;
114         int n = 0, r, i;
115 
116         assert(link);
117 
118         if (isempty(link->lldp_file))
119                 return 0; /* Do not update state file when running in test mode. */
120 
121         if (!link->lldp_rx) {
122                 (void) unlink(link->lldp_file);
123                 return 0;
124         }
125 
126         r = sd_lldp_rx_get_neighbors(link->lldp_rx, &l);
127         if (r < 0)
128                 return r;
129         if (r == 0) {
130                 (void) unlink(link->lldp_file);
131                 return 0;
132         }
133 
134         n = r;
135 
136         r = fopen_temporary(link->lldp_file, &f, &temp_path);
137         if (r < 0)
138                 goto finish;
139 
140         (void) fchmod(fileno(f), 0644);
141 
142         for (i = 0; i < n; i++) {
143                 const void *p;
144                 le64_t u;
145                 size_t sz;
146 
147                 r = sd_lldp_neighbor_get_raw(l[i], &p, &sz);
148                 if (r < 0)
149                         goto finish;
150 
151                 u = htole64(sz);
152                 (void) fwrite(&u, 1, sizeof(u), f);
153                 (void) fwrite(p, 1, sz, f);
154         }
155 
156         r = fflush_and_check(f);
157         if (r < 0)
158                 goto finish;
159 
160         r = conservative_rename(temp_path, link->lldp_file);
161         if (r < 0)
162                 goto finish;
163 
164 finish:
165         if (r < 0)
166                 log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file);
167 
168         if (l) {
169                 for (i = 0; i < n; i++)
170                         sd_lldp_neighbor_unref(l[i]);
171                 free(l);
172         }
173 
174         return r;
175 }
176