]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-lldp-rx.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / network / networkd-lldp-rx.c
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 "networkd-link.h"
10 #include "networkd-lldp-rx.h"
11 #include "networkd-lldp-tx.h"
12 #include "networkd-manager.h"
13 #include "networkd-network.h"
14 #include "string-table.h"
15 #include "string-util.h"
16 #include "strv.h"
17 #include "tmpfile-util.h"
18
19 DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting.");
20
21 static const char* const lldp_mode_table[_LLDP_MODE_MAX] = {
22 [LLDP_MODE_NO] = "no",
23 [LLDP_MODE_YES] = "yes",
24 [LLDP_MODE_ROUTERS_ONLY] = "routers-only",
25 };
26
27 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES);
28
29 static bool link_lldp_rx_enabled(Link *link) {
30 assert(link);
31
32 if (link->flags & IFF_LOOPBACK)
33 return false;
34
35 if (link->iftype != ARPHRD_ETHER)
36 return false;
37
38 if (!link->network)
39 return false;
40
41 /* LLDP should be handled on bridge and bond slaves as those have a direct connection to their peers,
42 * not on the bridge/bond master. Linux doesn't even (by default) forward lldp packets to the bridge
43 * master.*/
44 if (link->kind && STR_IN_SET(link->kind, "bridge", "bond"))
45 return false;
46
47 return link->network->lldp_mode != LLDP_MODE_NO;
48 }
49
50 static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) {
51 Link *link = userdata;
52 int r;
53
54 assert(link);
55
56 (void) link_lldp_save(link);
57
58 if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) {
59 /* If we received information about a new neighbor, restart the LLDP "fast" logic */
60
61 log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission.");
62
63 r = link_lldp_emit_start(link);
64 if (r < 0)
65 log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m");
66 }
67 }
68
69 int link_lldp_rx_configure(Link *link) {
70 int r;
71
72 if (!link_lldp_rx_enabled(link))
73 return 0;
74
75 if (!link->lldp) {
76 r = sd_lldp_new(&link->lldp);
77 if (r < 0)
78 return r;
79
80 r = sd_lldp_attach_event(link->lldp, link->manager->event, 0);
81 if (r < 0)
82 return r;
83 }
84
85 r = sd_lldp_set_ifindex(link->lldp, link->ifindex);
86 if (r < 0)
87 return r;
88
89 r = sd_lldp_match_capabilities(link->lldp,
90 link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ?
91 SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS :
92 SD_LLDP_SYSTEM_CAPABILITIES_ALL);
93 if (r < 0)
94 return r;
95
96 r = sd_lldp_set_filter_address(link->lldp, &link->hw_addr.addr.ether);
97 if (r < 0)
98 return r;
99
100 r = sd_lldp_set_callback(link->lldp, lldp_handler, link);
101 if (r < 0)
102 return r;
103
104 r = link_update_lldp(link);
105 if (r < 0)
106 return r;
107
108 return 0;
109 }
110
111 int link_update_lldp(Link *link) {
112 int r;
113
114 assert(link);
115
116 if (!link->lldp)
117 return 0;
118
119 if (link->flags & IFF_UP) {
120 r = sd_lldp_start(link->lldp);
121 if (r < 0)
122 return log_link_warning_errno(link, r, "Failed to start LLDP: %m");
123 if (r > 0)
124 log_link_debug(link, "Started LLDP.");
125 } else {
126 r = sd_lldp_stop(link->lldp);
127 if (r < 0)
128 return log_link_warning_errno(link, r, "Failed to stop LLDP: %m");
129 if (r > 0)
130 log_link_debug(link, "Stopped LLDP.");
131 }
132
133 return r;
134 }
135
136 int link_lldp_save(Link *link) {
137 _cleanup_free_ char *temp_path = NULL;
138 _cleanup_fclose_ FILE *f = NULL;
139 sd_lldp_neighbor **l = NULL;
140 int n = 0, r, i;
141
142 assert(link);
143 assert(link->lldp_file);
144
145 if (!link->lldp) {
146 (void) unlink(link->lldp_file);
147 return 0;
148 }
149
150 r = sd_lldp_get_neighbors(link->lldp, &l);
151 if (r < 0)
152 goto finish;
153 if (r == 0) {
154 (void) unlink(link->lldp_file);
155 goto finish;
156 }
157
158 n = r;
159
160 r = fopen_temporary(link->lldp_file, &f, &temp_path);
161 if (r < 0)
162 goto finish;
163
164 fchmod(fileno(f), 0644);
165
166 for (i = 0; i < n; i++) {
167 const void *p;
168 le64_t u;
169 size_t sz;
170
171 r = sd_lldp_neighbor_get_raw(l[i], &p, &sz);
172 if (r < 0)
173 goto finish;
174
175 u = htole64(sz);
176 (void) fwrite(&u, 1, sizeof(u), f);
177 (void) fwrite(p, 1, sz, f);
178 }
179
180 r = fflush_and_check(f);
181 if (r < 0)
182 goto finish;
183
184 if (rename(temp_path, link->lldp_file) < 0) {
185 r = -errno;
186 goto finish;
187 }
188
189 finish:
190 if (r < 0) {
191 (void) unlink(link->lldp_file);
192 if (temp_path)
193 (void) unlink(temp_path);
194
195 log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file);
196 }
197
198 if (l) {
199 for (i = 0; i < n; i++)
200 sd_lldp_neighbor_unref(l[i]);
201 free(l);
202 }
203
204 return r;
205 }