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