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