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