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