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