]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-lldp-tx.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / network / networkd-lldp-tx.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
8e1ad1ea
LP
2
3#include <endian.h>
4#include <inttypes.h>
5#include <string.h>
6
634f0f98 7#include "alloc-util.h"
8e1ad1ea
LP
8#include "fd-util.h"
9#include "fileio.h"
10#include "hostname-util.h"
7272b25e 11#include "networkd-lldp-tx.h"
23f53b99 12#include "networkd-manager.h"
7272b25e 13#include "parse-util.h"
8e1ad1ea
LP
14#include "random-util.h"
15#include "socket-util.h"
16#include "string-util.h"
17#include "unaligned.h"
18
19/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
20#define LLDP_TX_FAST_INIT 4U
21
22/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */
23#define LLDP_TX_HOLD 4U
24
25/* The jitter range to add, see 9.2.2. */
26#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC)
27
28/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */
29#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
30
31/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
32#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
33
7272b25e
LP
34static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = {
35 [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
36 [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
37 [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
38};
39
8e1ad1ea
LP
40static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
41 assert(p);
42
43 if (id > 127)
44 return -EBADMSG;
45 if (sz > 511)
46 return -ENOBUFS;
47
48 (*p)[0] = (id << 1) | !!(sz & 256);
49 (*p)[1] = sz & 255;
50
51 *p = *p + 2;
52 return 0;
53}
54
55static int lldp_make_packet(
7272b25e 56 LLDPEmit mode,
8e1ad1ea
LP
57 const struct ether_addr *hwaddr,
58 const char *machine_id,
59 const char *ifname,
60 uint16_t ttl,
61 const char *port_description,
62 const char *hostname,
63 const char *pretty_hostname,
64 uint16_t system_capabilities,
65 uint16_t enabled_capabilities,
66 void **ret, size_t *sz) {
67
68 size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0;
69 _cleanup_free_ void *packet = NULL;
70 struct ether_header *h;
71 uint8_t *p;
72 size_t l;
73 int r;
74
7272b25e
LP
75 assert(mode > LLDP_EMIT_NO);
76 assert(mode < _LLDP_EMIT_MAX);
8e1ad1ea
LP
77 assert(hwaddr);
78 assert(machine_id);
79 assert(ifname);
80 assert(ret);
81 assert(sz);
82
83 machine_id_length = strlen(machine_id);
84 ifname_length = strlen(ifname);
85
86 if (port_description)
87 port_description_length = strlen(port_description);
88
89 if (hostname)
90 hostname_length = strlen(hostname);
91
92 if (pretty_hostname)
93 pretty_hostname_length = strlen(pretty_hostname);
94
95 l = sizeof(struct ether_header) +
96 /* Chassis ID */
97 2 + 1 + machine_id_length +
98 /* Port ID */
99 2 + 1 + ifname_length +
100 /* TTL */
101 2 + 2 +
102 /* System Capabilities */
103 2 + 4 +
104 /* End */
105 2;
106
107 /* Port Description */
108 if (port_description)
109 l += 2 + port_description_length;
110
111 /* System Name */
112 if (hostname)
113 l += 2 + hostname_length;
114
115 /* System Description */
116 if (pretty_hostname)
117 l += 2 + pretty_hostname_length;
118
119 packet = malloc(l);
120 if (!packet)
121 return -ENOMEM;
122
123 h = (struct ether_header*) packet;
124 h->ether_type = htobe16(ETHERTYPE_LLDP);
7272b25e 125 memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN);
8e1ad1ea
LP
126 memcpy(h->ether_shost, hwaddr, ETH_ALEN);
127
128 p = (uint8_t*) packet + sizeof(struct ether_header);
129
6afa6767 130 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length);
8e1ad1ea
LP
131 if (r < 0)
132 return r;
6afa6767 133 *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED;
8e1ad1ea
LP
134 p = mempcpy(p, machine_id, machine_id_length);
135
6afa6767 136 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length);
8e1ad1ea
LP
137 if (r < 0)
138 return r;
6afa6767 139 *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME;
8e1ad1ea
LP
140 p = mempcpy(p, ifname, ifname_length);
141
6afa6767 142 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2);
8e1ad1ea
LP
143 if (r < 0)
144 return r;
145 unaligned_write_be16(p, ttl);
146 p += 2;
147
148 if (port_description) {
6afa6767 149 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length);
8e1ad1ea
LP
150 if (r < 0)
151 return r;
152 p = mempcpy(p, port_description, port_description_length);
153 }
154
155 if (hostname) {
6afa6767 156 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length);
8e1ad1ea
LP
157 if (r < 0)
158 return r;
159 p = mempcpy(p, hostname, hostname_length);
160 }
161
162 if (pretty_hostname) {
6afa6767 163 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length);
8e1ad1ea
LP
164 if (r < 0)
165 return r;
166 p = mempcpy(p, pretty_hostname, pretty_hostname_length);
167 }
168
6afa6767 169 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
8e1ad1ea
LP
170 if (r < 0)
171 return r;
172 unaligned_write_be16(p, system_capabilities);
173 p += 2;
174 unaligned_write_be16(p, enabled_capabilities);
175 p += 2;
176
6afa6767 177 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0);
8e1ad1ea
LP
178 if (r < 0)
179 return r;
180
181 assert(p == (uint8_t*) packet + l);
182
1cc6c93a 183 *ret = TAKE_PTR(packet);
8e1ad1ea
LP
184 *sz = l;
185
8e1ad1ea
LP
186 return 0;
187}
188
7272b25e
LP
189static int lldp_send_packet(
190 int ifindex,
191 const struct ether_addr *address,
192 const void *packet,
193 size_t packet_size) {
8e1ad1ea
LP
194
195 union sockaddr_union sa = {
196 .ll.sll_family = AF_PACKET,
197 .ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
198 .ll.sll_ifindex = ifindex,
199 .ll.sll_halen = ETH_ALEN,
8e1ad1ea
LP
200 };
201
202 _cleanup_close_ int fd = -1;
203 ssize_t l;
204
205 assert(ifindex > 0);
7272b25e 206 assert(address);
8e1ad1ea
LP
207 assert(packet || packet_size <= 0);
208
7272b25e
LP
209 memcpy(sa.ll.sll_addr, address, ETH_ALEN);
210
8e1ad1ea
LP
211 fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW);
212 if (fd < 0)
213 return -errno;
214
215 l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll));
216 if (l < 0)
217 return -errno;
218
219 if ((size_t) l != packet_size)
220 return -EIO;
221
222 return 0;
223}
224
225static int link_send_lldp(Link *link) {
226 char machine_id_string[SD_ID128_STRING_MAX];
227 _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
228 _cleanup_free_ void *packet = NULL;
229 size_t packet_size = 0;
230 sd_id128_t machine_id;
231 uint16_t caps;
232 usec_t ttl;
233 int r;
234
7272b25e
LP
235 assert(link);
236
237 if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
238 return 0;
239
240 assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
241
8e1ad1ea
LP
242 r = sd_id128_get_machine(&machine_id);
243 if (r < 0)
244 return r;
245
246 (void) gethostname_strict(&hostname);
1a5a177e 247 (void) parse_env_file(NULL, "/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL);
8e1ad1ea 248
c8cf4dc1 249 assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
8e1ad1ea 250 ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
8e1ad1ea
LP
251
252 caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ?
6afa6767
BG
253 SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
254 SD_LLDP_SYSTEM_CAPABILITIES_STATION;
8e1ad1ea 255
7272b25e
LP
256 r = lldp_make_packet(link->network->lldp_emit,
257 &link->mac,
8e1ad1ea
LP
258 sd_id128_to_string(machine_id, machine_id_string),
259 link->ifname,
260 (uint16_t) ttl,
261 link->network ? link->network->description : NULL,
262 hostname,
263 pretty_hostname,
6afa6767 264 SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
8e1ad1ea
LP
265 caps,
266 &packet, &packet_size);
267 if (r < 0)
268 return r;
269
7272b25e 270 return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size);
8e1ad1ea
LP
271}
272
273static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
274 Link *link = userdata;
275 usec_t current, delay, next;
276 int r;
277
278 assert(s);
279 assert(userdata);
280
281 log_link_debug(link, "Sending LLDP packet...");
282
283 r = link_send_lldp(link);
284 if (r < 0)
285 log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m");
286
287 if (link->lldp_tx_fast > 0)
288 link->lldp_tx_fast--;
289
290 assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), &current) >= 0);
291
292 delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC;
293 next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC);
294
295 r = sd_event_source_set_time(s, next);
296 if (r < 0)
297 return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m");
298
299 r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
300 if (r < 0)
301 return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m");
302
303 return 0;
304}
305
7272b25e 306int link_lldp_emit_start(Link *link) {
8e1ad1ea
LP
307 usec_t next;
308 int r;
309
310 assert(link);
311
7272b25e
LP
312 if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) {
313 link_lldp_emit_stop(link);
314 return 0;
315 }
316
8e1ad1ea
LP
317 /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */
318
319 link->lldp_tx_fast = LLDP_TX_FAST_INIT;
320
321 next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
322 (usec_t) random_u64() % LLDP_JITTER_USEC);
323
7272b25e 324 if (link->lldp_emit_event_source) {
8e1ad1ea
LP
325 usec_t old;
326
327 /* Lower the timeout, maybe */
7272b25e 328 r = sd_event_source_get_time(link->lldp_emit_event_source, &old);
8e1ad1ea
LP
329 if (r < 0)
330 return r;
331
332 if (old <= next)
333 return 0;
334
7272b25e 335 return sd_event_source_set_time(link->lldp_emit_event_source, next);
8e1ad1ea
LP
336 } else {
337 r = sd_event_add_time(
338 link->manager->event,
7272b25e 339 &link->lldp_emit_event_source,
8e1ad1ea
LP
340 clock_boottime_or_monotonic(),
341 next,
342 0,
343 on_lldp_timer,
344 link);
345 if (r < 0)
346 return r;
347
7272b25e 348 (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
8e1ad1ea
LP
349 }
350
351 return 0;
352}
353
7272b25e 354void link_lldp_emit_stop(Link *link) {
8e1ad1ea
LP
355 assert(link);
356
7272b25e
LP
357 link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source);
358}
359
360int config_parse_lldp_emit(
361 const char *unit,
362 const char *filename,
363 unsigned line,
364 const char *section,
365 unsigned section_line,
366 const char *lvalue,
367 int ltype,
368 const char *rvalue,
369 void *data,
370 void *userdata) {
371
372 LLDPEmit *emit = data;
373 int r;
374
375 assert(filename);
376 assert(lvalue);
377 assert(rvalue);
378
379 if (isempty(rvalue))
380 *emit = LLDP_EMIT_NO;
381 else if (streq(rvalue, "nearest-bridge"))
382 *emit = LLDP_EMIT_NEAREST_BRIDGE;
383 else if (streq(rvalue, "non-tpmr-bridge"))
384 *emit = LLDP_EMIT_NON_TPMR_BRIDGE;
385 else if (streq(rvalue, "customer-bridge"))
386 *emit = LLDP_EMIT_CUSTOMER_BRIDGE;
387 else {
388 r = parse_boolean(rvalue);
389 if (r < 0) {
390 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
391 return 0;
392 }
393
394 *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO;
395 }
396
397 return 0;
8e1ad1ea 398}