]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-lldp-tx.c
Merge pull request #19870 from keszybz/install-foo-again
[thirdparty/systemd.git] / src / network / networkd-lldp-tx.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <endian.h>
4 #include <inttypes.h>
5 #include <net/if.h>
6 #include <net/if_arp.h>
7
8 #include "alloc-util.h"
9 #include "env-file.h"
10 #include "escape.h"
11 #include "fd-util.h"
12 #include "hostname-util.h"
13 #include "missing_network.h"
14 #include "networkd-link.h"
15 #include "networkd-lldp-tx.h"
16 #include "networkd-manager.h"
17 #include "parse-util.h"
18 #include "random-util.h"
19 #include "socket-util.h"
20 #include "string-table.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "unaligned.h"
24 #include "web-util.h"
25
26 /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
27 #define LLDP_TX_FAST_INIT 4U
28
29 /* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */
30 #define LLDP_TX_HOLD 4U
31
32 /* The jitter range to add, see 9.2.2. */
33 #define LLDP_JITTER_USEC (400U * USEC_PER_MSEC)
34
35 /* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */
36 #define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
37
38 /* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
39 #define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
40
41 static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = {
42 [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
43 [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
44 [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
45 };
46
47 bool link_lldp_emit_enabled(Link *link) {
48 assert(link);
49
50 if (link->flags & IFF_LOOPBACK)
51 return false;
52
53 if (link->iftype != ARPHRD_ETHER)
54 return false;
55
56 if (!link->network)
57 return false;
58
59 if (link->kind && STR_IN_SET(link->kind, "bridge", "bond"))
60 return false;
61
62 return link->network->lldp_emit != LLDP_EMIT_NO;
63 }
64
65 static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
66 assert(p);
67
68 if (id > 127)
69 return -EBADMSG;
70 if (sz > 511)
71 return -ENOBUFS;
72
73 (*p)[0] = (id << 1) | !!(sz & 256);
74 (*p)[1] = sz & 255;
75
76 *p = *p + 2;
77 return 0;
78 }
79
80 static int lldp_make_packet(
81 LLDPEmit mode,
82 const struct ether_addr *hwaddr,
83 const char *machine_id,
84 const char *ifname,
85 uint16_t ttl,
86 const char *port_description,
87 const char *hostname,
88 const char *pretty_hostname,
89 uint16_t system_capabilities,
90 uint16_t enabled_capabilities,
91 char *mud,
92 void **ret, size_t *sz) {
93
94 size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0,
95 pretty_hostname_length = 0, mud_length = 0;
96 _cleanup_free_ void *packet = NULL;
97 struct ether_header *h;
98 uint8_t *p;
99 size_t l;
100 int r;
101
102 assert(mode > LLDP_EMIT_NO);
103 assert(mode < _LLDP_EMIT_MAX);
104 assert(hwaddr);
105 assert(machine_id);
106 assert(ifname);
107 assert(ret);
108 assert(sz);
109
110 machine_id_length = strlen(machine_id);
111 ifname_length = strlen(ifname);
112
113 if (port_description)
114 port_description_length = strlen(port_description);
115
116 if (hostname)
117 hostname_length = strlen(hostname);
118
119 if (pretty_hostname)
120 pretty_hostname_length = strlen(pretty_hostname);
121
122 if (mud)
123 mud_length = strlen(mud);
124
125 l = sizeof(struct ether_header) +
126 /* Chassis ID */
127 2 + 1 + machine_id_length +
128 /* Port ID */
129 2 + 1 + ifname_length +
130 /* TTL */
131 2 + 2 +
132 /* System Capabilities */
133 2 + 4 +
134 /* End */
135 2;
136
137 /* Port Description */
138 if (port_description)
139 l += 2 + port_description_length;
140
141 /* System Name */
142 if (hostname)
143 l += 2 + hostname_length;
144
145 /* System Description */
146 if (pretty_hostname)
147 l += 2 + pretty_hostname_length;
148
149 /* MUD URL */
150 if (mud)
151 l += 2 + sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length;
152
153 packet = malloc(l);
154 if (!packet)
155 return -ENOMEM;
156
157 h = (struct ether_header*) packet;
158 h->ether_type = htobe16(ETHERTYPE_LLDP);
159 memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN);
160 memcpy(h->ether_shost, hwaddr, ETH_ALEN);
161
162 p = (uint8_t*) packet + sizeof(struct ether_header);
163
164 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length);
165 if (r < 0)
166 return r;
167 *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED;
168 p = mempcpy(p, machine_id, machine_id_length);
169
170 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length);
171 if (r < 0)
172 return r;
173 *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME;
174 p = mempcpy(p, ifname, ifname_length);
175
176 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2);
177 if (r < 0)
178 return r;
179 unaligned_write_be16(p, ttl);
180 p += 2;
181
182 if (port_description) {
183 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length);
184 if (r < 0)
185 return r;
186 p = mempcpy(p, port_description, port_description_length);
187 }
188
189 if (hostname) {
190 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length);
191 if (r < 0)
192 return r;
193 p = mempcpy(p, hostname, hostname_length);
194 }
195
196 if (pretty_hostname) {
197 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length);
198 if (r < 0)
199 return r;
200 p = mempcpy(p, pretty_hostname, pretty_hostname_length);
201 }
202
203 if (mud) {
204 uint8_t oui_mud[sizeof(SD_LLDP_OUI_MUD)] = {0x00, 0x00, 0x5E};
205 /*
206 * +--------+--------+----------+---------+--------------
207 * |TLV Type| len | OUI |subtype | MUDString
208 * | =127 | |= 00 00 5E| = 1 |
209 * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets)
210 * +--------+--------+----------+---------+--------------
211 * where:
212
213 * o TLV Type = 127 indicates a vendor-specific TLV
214 * o len = indicates the TLV string length
215 * o OUI = 00 00 5E is the organizationally unique identifier of IANA
216 * o subtype = 1 (as assigned by IANA for the MUDstring)
217 * o MUDstring = the length MUST NOT exceed 255 octets
218 */
219
220 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PRIVATE, sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length);
221 if (r < 0)
222 return r;
223
224 p = mempcpy(p, &oui_mud, sizeof(SD_LLDP_OUI_MUD));
225 *(p++) = SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION;
226 p = mempcpy(p, mud, mud_length);
227 }
228
229 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
230 if (r < 0)
231 return r;
232 unaligned_write_be16(p, system_capabilities);
233 p += 2;
234 unaligned_write_be16(p, enabled_capabilities);
235 p += 2;
236
237 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0);
238 if (r < 0)
239 return r;
240
241 assert(p == (uint8_t*) packet + l);
242
243 *ret = TAKE_PTR(packet);
244 *sz = l;
245
246 return 0;
247 }
248
249 static int lldp_send_packet(
250 int ifindex,
251 const struct ether_addr *address,
252 const void *packet,
253 size_t packet_size) {
254
255 union sockaddr_union sa = {
256 .ll.sll_family = AF_PACKET,
257 .ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
258 .ll.sll_ifindex = ifindex,
259 .ll.sll_halen = ETH_ALEN,
260 };
261
262 _cleanup_close_ int fd = -1;
263 ssize_t l;
264
265 assert(ifindex > 0);
266 assert(address);
267 assert(packet || packet_size <= 0);
268
269 memcpy(sa.ll.sll_addr, address, ETH_ALEN);
270
271 fd = socket(AF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW);
272 if (fd < 0)
273 return -errno;
274
275 l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll));
276 if (l < 0)
277 return -errno;
278
279 if ((size_t) l != packet_size)
280 return -EIO;
281
282 return 0;
283 }
284
285 static int link_send_lldp(Link *link) {
286 char machine_id_string[SD_ID128_STRING_MAX];
287 _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
288 _cleanup_free_ void *packet = NULL;
289 size_t packet_size = 0;
290 sd_id128_t machine_id;
291 uint16_t caps;
292 usec_t ttl;
293 int r;
294
295 assert(link);
296
297 if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
298 return 0;
299
300 assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
301
302 r = sd_id128_get_machine(&machine_id);
303 if (r < 0)
304 return r;
305
306 (void) gethostname_strict(&hostname);
307 (void) parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &pretty_hostname);
308
309 assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
310 ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
311
312 caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ?
313 SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
314 SD_LLDP_SYSTEM_CAPABILITIES_STATION;
315
316 r = lldp_make_packet(link->network->lldp_emit,
317 &link->hw_addr.ether,
318 sd_id128_to_string(machine_id, machine_id_string),
319 link->ifname,
320 (uint16_t) ttl,
321 link->network ? link->network->description : NULL,
322 hostname,
323 pretty_hostname,
324 SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
325 caps,
326 link->network ? link->network->lldp_mud : NULL,
327 &packet, &packet_size);
328 if (r < 0)
329 return r;
330
331 return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size);
332 }
333
334 static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
335 Link *link = userdata;
336 usec_t delay;
337 int r;
338
339 assert(s);
340 assert(userdata);
341
342 log_link_debug(link, "Sending LLDP packet...");
343
344 r = link_send_lldp(link);
345 if (r < 0)
346 log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m");
347
348 if (link->lldp_tx_fast > 0)
349 link->lldp_tx_fast--;
350
351 delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC;
352 delay = usec_add(delay, (usec_t) random_u64() % LLDP_JITTER_USEC);
353
354 r = sd_event_source_set_time_relative(s, delay);
355 if (r < 0)
356 return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m");
357
358 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
359 if (r < 0)
360 return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m");
361
362 return 0;
363 }
364
365 int link_lldp_emit_start(Link *link) {
366 usec_t next;
367 int r;
368
369 assert(link);
370
371 if (!link_lldp_emit_enabled(link)) {
372 link_lldp_emit_stop(link);
373 return 0;
374 }
375
376 /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */
377
378 link->lldp_tx_fast = LLDP_TX_FAST_INIT;
379
380 next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
381 (usec_t) random_u64() % LLDP_JITTER_USEC);
382
383 if (link->lldp_emit_event_source) {
384 usec_t old;
385
386 /* Lower the timeout, maybe */
387 r = sd_event_source_get_time(link->lldp_emit_event_source, &old);
388 if (r < 0)
389 return r;
390
391 if (old <= next)
392 return 0;
393
394 return sd_event_source_set_time(link->lldp_emit_event_source, next);
395 } else {
396 r = sd_event_add_time(
397 link->manager->event,
398 &link->lldp_emit_event_source,
399 clock_boottime_or_monotonic(),
400 next,
401 0,
402 on_lldp_timer,
403 link);
404 if (r < 0)
405 return r;
406
407 (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
408 }
409
410 return 0;
411 }
412
413 void link_lldp_emit_stop(Link *link) {
414 assert(link);
415
416 link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source);
417 }
418
419 int config_parse_lldp_mud(
420 const char *unit,
421 const char *filename,
422 unsigned line,
423 const char *section,
424 unsigned section_line,
425 const char *lvalue,
426 int ltype,
427 const char *rvalue,
428 void *data,
429 void *userdata) {
430
431 _cleanup_free_ char *unescaped = NULL;
432 Network *n = data;
433 int r;
434
435 assert(filename);
436 assert(lvalue);
437 assert(rvalue);
438
439 r = cunescape(rvalue, 0, &unescaped);
440 if (r < 0) {
441 log_syntax(unit, LOG_WARNING, filename, line, r,
442 "Failed to Failed to unescape LLDP MUD URL, ignoring: %s", rvalue);
443 return 0;
444 }
445
446 if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
447 log_syntax(unit, LOG_WARNING, filename, line, 0,
448 "Failed to parse LLDP MUD URL '%s', ignoring: %m", rvalue);
449
450 return 0;
451 }
452
453 return free_and_replace(n->lldp_mud, unescaped);
454 }
455
456 static const char * const lldp_emit_table[_LLDP_EMIT_MAX] = {
457 [LLDP_EMIT_NO] = "no",
458 [LLDP_EMIT_NEAREST_BRIDGE] = "nearest-bridge",
459 [LLDP_EMIT_NON_TPMR_BRIDGE] = "non-tpmr-bridge",
460 [LLDP_EMIT_CUSTOMER_BRIDGE] = "customer-bridge",
461 };
462
463 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(lldp_emit, LLDPEmit, LLDP_EMIT_NEAREST_BRIDGE);
464 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_lldp_emit, lldp_emit, LLDPEmit, LLDP_EMIT_NO, "Failed to parse LLDP emission setting");