1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <arpa/inet.h>
4 #include <linux/sockios.h>
5 #include <sys/ioctl.h>
6 
7 #include "sd-event.h"
8 #include "sd-id128.h"
9 #include "sd-lldp-tx.h"
10 
11 #include "alloc-util.h"
12 #include "ether-addr-util.h"
13 #include "fd-util.h"
14 #include "hostname-util.h"
15 #include "network-common.h"
16 #include "random-util.h"
17 #include "socket-util.h"
18 #include "string-util.h"
19 #include "time-util.h"
20 #include "unaligned.h"
21 #include "web-util.h"
22 
23 /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
24 #define LLDP_FAST_TX_INIT 4U
25 
26 /* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */
27 #define LLDP_TX_HOLD 4U
28 
29 /* The jitter range to add, see 9.2.2. */
30 #define LLDP_TX_JITTER_USEC (400U * USEC_PER_MSEC)
31 
32 /* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */
33 #define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2)
34 
35 /* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
36 #define LLDP_FAST_TX_INTERVAL_USEC (1U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2)
37 
38 #define LLDP_TX_TTL ((uint16_t) DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC))
39 
40 static const struct ether_addr lldp_multicast_addr[_SD_LLDP_MULTICAST_MODE_MAX] = {
41         [SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE]  = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
42         [SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
43         [SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
44 };
45 
46 struct sd_lldp_tx {
47         unsigned n_ref;
48 
49         int ifindex;
50         char *ifname;
51 
52         sd_event *event;
53         int64_t event_priority;
54         sd_event_source *timer_event_source;
55 
56         unsigned fast_tx;
57 
58         sd_lldp_multicast_mode_t mode;
59         struct ether_addr hwaddr;
60 
61         char *port_description;
62         char *hostname;
63         char *pretty_hostname;
64         char *mud_url;
65         uint16_t supported_capabilities;
66         uint16_t enabled_capabilities;
67 };
68 
69 #define log_lldp_tx_errno(lldp_tx, error, fmt, ...)     \
70         log_interface_prefix_full_errno(                \
71                 "LLDP Tx: ",                            \
72                 sd_lldp_tx, lldp_tx,                    \
73                 error, fmt, ##__VA_ARGS__)
74 #define log_lldp_tx(lldp_tx, fmt, ...)                  \
75         log_interface_prefix_full_errno_zerook(         \
76                 "LLDP Tx: ",                            \
77                 sd_lldp_tx, lldp_tx,                    \
78                 0, fmt, ##__VA_ARGS__)
79 
lldp_tx_free(sd_lldp_tx * lldp_tx)80 static sd_lldp_tx *lldp_tx_free(sd_lldp_tx *lldp_tx) {
81         if (!lldp_tx)
82                 return NULL;
83 
84         sd_lldp_tx_detach_event(lldp_tx);
85 
86         free(lldp_tx->port_description);
87         free(lldp_tx->hostname);
88         free(lldp_tx->pretty_hostname);
89         free(lldp_tx->mud_url);
90 
91         free(lldp_tx->ifname);
92         return mfree(lldp_tx);
93 }
94 
95 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_tx, sd_lldp_tx, lldp_tx_free);
96 
sd_lldp_tx_new(sd_lldp_tx ** ret)97 int sd_lldp_tx_new(sd_lldp_tx **ret) {
98         _cleanup_(sd_lldp_tx_unrefp) sd_lldp_tx *lldp_tx = NULL;
99 
100         assert_return(ret, -EINVAL);
101 
102         lldp_tx = new(sd_lldp_tx, 1);
103         if (!lldp_tx)
104                 return -ENOMEM;
105 
106         *lldp_tx = (sd_lldp_tx) {
107                 .n_ref = 1,
108                 .mode = _SD_LLDP_MULTICAST_MODE_INVALID,
109         };
110 
111         *ret = TAKE_PTR(lldp_tx);
112         return 0;
113 }
114 
sd_lldp_tx_set_ifindex(sd_lldp_tx * lldp_tx,int ifindex)115 int sd_lldp_tx_set_ifindex(sd_lldp_tx *lldp_tx, int ifindex) {
116         assert_return(lldp_tx, -EINVAL);
117         assert_return(ifindex > 0, -EINVAL);
118 
119         lldp_tx->ifindex = ifindex;
120         return 0;
121 }
122 
sd_lldp_tx_set_ifname(sd_lldp_tx * lldp_tx,const char * ifname)123 int sd_lldp_tx_set_ifname(sd_lldp_tx *lldp_tx, const char *ifname) {
124         assert_return(lldp_tx, -EINVAL);
125         assert_return(ifname, -EINVAL);
126 
127         if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
128                 return -EINVAL;
129 
130         return free_and_strdup(&lldp_tx->ifname, ifname);
131 }
132 
sd_lldp_tx_get_ifname(sd_lldp_tx * lldp_tx,const char ** ret)133 int sd_lldp_tx_get_ifname(sd_lldp_tx *lldp_tx, const char **ret) {
134         int r;
135 
136         assert_return(lldp_tx, -EINVAL);
137 
138         r = get_ifname(lldp_tx->ifindex, &lldp_tx->ifname);
139         if (r < 0)
140                 return r;
141 
142         if (ret)
143                 *ret = lldp_tx->ifname;
144 
145         return 0;
146 }
147 
sd_lldp_tx_set_multicast_mode(sd_lldp_tx * lldp_tx,sd_lldp_multicast_mode_t mode)148 int sd_lldp_tx_set_multicast_mode(sd_lldp_tx *lldp_tx, sd_lldp_multicast_mode_t mode) {
149         assert_return(lldp_tx, -EINVAL);
150         assert_return(mode >= 0 && mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL);
151 
152         lldp_tx->mode = mode;
153         return 0;
154 }
155 
sd_lldp_tx_set_hwaddr(sd_lldp_tx * lldp_tx,const struct ether_addr * hwaddr)156 int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr) {
157         assert_return(lldp_tx, -EINVAL);
158         assert_return(!ether_addr_is_null(hwaddr), -EINVAL);
159 
160         lldp_tx->hwaddr = *hwaddr;
161         return 0;
162 }
163 
sd_lldp_tx_set_capabilities(sd_lldp_tx * lldp_tx,uint16_t supported,uint16_t enabled)164 int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled) {
165         assert_return(lldp_tx, -EINVAL);
166         assert_return((enabled & ~supported) == 0, -EINVAL);
167 
168         lldp_tx->supported_capabilities = supported;
169         lldp_tx->enabled_capabilities = enabled;
170         return 0;
171 }
172 
sd_lldp_tx_set_port_description(sd_lldp_tx * lldp_tx,const char * port_description)173 int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description) {
174         assert_return(lldp_tx, -EINVAL);
175 
176         /* An empty string unset the previously set hostname. */
177         if (strlen_ptr(port_description) >= 512)
178                 return -EINVAL;
179 
180         return free_and_strdup(&lldp_tx->port_description, empty_to_null(port_description));
181 }
182 
sd_lldp_tx_set_hostname(sd_lldp_tx * lldp_tx,const char * hostname)183 int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname) {
184         assert_return(lldp_tx, -EINVAL);
185 
186         /* An empty string unset the previously set hostname. */
187         if (!isempty(hostname)) {
188                 assert_cc(HOST_NAME_MAX < 512);
189 
190                 if (!hostname_is_valid(hostname, 0))
191                         return -EINVAL;
192         }
193 
194         return free_and_strdup(&lldp_tx->hostname, empty_to_null(hostname));
195 }
196 
sd_lldp_tx_set_pretty_hostname(sd_lldp_tx * lldp_tx,const char * pretty_hostname)197 int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname) {
198         assert_return(lldp_tx, -EINVAL);
199 
200         /* An empty string unset the previously set hostname. */
201         if (strlen_ptr(pretty_hostname) >= 512)
202                 return -EINVAL;
203 
204         return free_and_strdup(&lldp_tx->pretty_hostname, empty_to_null(pretty_hostname));
205 }
206 
sd_lldp_tx_set_mud_url(sd_lldp_tx * lldp_tx,const char * mud_url)207 int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url) {
208         assert_return(lldp_tx, -EINVAL);
209 
210         /* An empty string unset the previously set hostname. */
211         if (!isempty(mud_url)) {
212                 /* Unless the maximum length of each value is 511, the MUD url must be smaller than 256.
213                  * See RFC 8520. */
214                 if (strlen(mud_url) >= 256)
215                         return -EINVAL;
216 
217                 if (!http_url_is_valid(mud_url))
218                         return -EINVAL;
219         }
220 
221         return free_and_strdup(&lldp_tx->mud_url, empty_to_null(mud_url));
222 }
223 
lldp_tx_calculate_maximum_packet_size(sd_lldp_tx * lldp_tx,const char * hostname,const char * pretty_hostname)224 static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const char *hostname, const char *pretty_hostname) {
225         assert(lldp_tx);
226         assert(lldp_tx->ifindex > 0);
227 
228         return sizeof(struct ether_header) +
229                 /* Chassis ID */
230                 2 + 1 + (SD_ID128_STRING_MAX - 1) +
231                 /* Port ID */
232                 2 + 1 + strlen_ptr(lldp_tx->ifname) +
233                 /* TTL */
234                 2 + 2 +
235                 /* Port description */
236                 2 + strlen_ptr(lldp_tx->port_description) +
237                 /* System name */
238                 2 + strlen_ptr(hostname) +
239                 /* System description */
240                 2 + strlen_ptr(pretty_hostname) +
241                 /* MUD URL */
242                 2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) +
243                 /* System Capabilities */
244                 2 + 4 +
245                 /* End */
246                 2;
247 }
248 
packet_append_tlv_header(uint8_t * packet,size_t packet_size,size_t * offset,uint8_t type,size_t data_len)249 static int packet_append_tlv_header(uint8_t *packet, size_t packet_size, size_t *offset, uint8_t type, size_t data_len) {
250         assert(packet);
251         assert(offset);
252 
253         /*
254          * +--------+--------+--------------
255          * |TLV Type|  len   |   value
256          * |(7 bits)|(9 bits)|(0-511 octets)
257          * +--------+--------+--------------
258          * where:
259          *
260          * len = indicates the length of value
261          */
262 
263         /* The type field is 7-bits. */
264         if (type >= 128)
265                 return -EINVAL;
266 
267         /* The data length field is 9-bits. */
268         if (data_len >= 512)
269                 return -EINVAL;
270 
271         if (packet_size < 2 + data_len)
272                 return -ENOBUFS;
273 
274         if (*offset > packet_size - 2 - data_len)
275                 return -ENOBUFS;
276 
277         packet[(*offset)++] = (type << 1) | !!(data_len >> 8);
278         packet[(*offset)++] = data_len & (size_t) UINT8_MAX;
279 
280         return 0;
281 }
282 
packet_append_prefixed_string(uint8_t * packet,size_t packet_size,size_t * offset,uint8_t type,size_t prefix_len,const void * prefix,const char * str)283 static int packet_append_prefixed_string(
284                 uint8_t *packet,
285                 size_t packet_size,
286                 size_t *offset,
287                 uint8_t type,
288                 size_t prefix_len,
289                 const void *prefix,
290                 const char *str) {
291 
292         size_t len;
293         int r;
294 
295         assert(packet);
296         assert(offset);
297         assert(prefix_len == 0 || prefix);
298 
299         if (isempty(str))
300                 return 0;
301 
302         len = strlen(str);
303 
304         /* Check for overflow */
305         if (len > SIZE_MAX - prefix_len)
306                 return -ENOBUFS;
307 
308         r = packet_append_tlv_header(packet, packet_size, offset, type, prefix_len + len);
309         if (r < 0)
310                 return r;
311 
312         memcpy_safe(packet + *offset, prefix, prefix_len);
313         *offset += prefix_len;
314 
315         memcpy(packet + *offset, str, len);
316         *offset += len;
317 
318         return 0;
319 }
320 
packet_append_string(uint8_t * packet,size_t packet_size,size_t * offset,uint8_t type,const char * str)321 static int packet_append_string(
322                 uint8_t *packet,
323                 size_t packet_size,
324                 size_t *offset,
325                 uint8_t type,
326                 const char *str) {
327 
328         return packet_append_prefixed_string(packet, packet_size, offset, type, 0, NULL, str);
329 }
330 
lldp_tx_create_packet(sd_lldp_tx * lldp_tx,size_t * ret_packet_size,uint8_t ** ret_packet)331 static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, uint8_t **ret_packet) {
332         _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
333         _cleanup_free_ uint8_t *packet = NULL;
334         struct ether_header *header;
335         size_t packet_size, offset;
336         sd_id128_t machine_id;
337         int r;
338 
339         assert(lldp_tx);
340         assert(lldp_tx->ifindex > 0);
341         assert(ret_packet_size);
342         assert(ret_packet);
343 
344         /* If ifname is not set yet, set ifname from ifindex. */
345         r = sd_lldp_tx_get_ifname(lldp_tx, NULL);
346         if (r < 0)
347                 return r;
348 
349         r = sd_id128_get_machine(&machine_id);
350         if (r < 0)
351                 return r;
352 
353         if (!lldp_tx->hostname)
354                 (void) gethostname_strict(&hostname);
355         if (!lldp_tx->pretty_hostname)
356                 (void) get_pretty_hostname(&pretty_hostname);
357 
358         packet_size = lldp_tx_calculate_maximum_packet_size(lldp_tx,
359                                                             lldp_tx->hostname ?: hostname,
360                                                             lldp_tx->pretty_hostname ?: pretty_hostname);
361 
362         packet = new(uint8_t, packet_size);
363         if (!packet)
364                 return -ENOMEM;
365 
366         header = (struct ether_header*) packet;
367         header->ether_type = htobe16(ETHERTYPE_LLDP);
368         memcpy(header->ether_dhost, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN);
369         memcpy(header->ether_shost, &lldp_tx->hwaddr, ETH_ALEN);
370 
371         offset = sizeof(struct ether_header);
372 
373         /* The three mandatory TLVs must appear first, in this specific order:
374          *   1. Chassis ID
375          *   2. Port ID
376          *   3. Time To Live
377          */
378 
379         r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_CHASSIS_ID,
380                                           1, (const uint8_t[]) { SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED },
381                                           SD_ID128_TO_STRING(machine_id));
382         if (r < 0)
383                 return r;
384 
385         r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_ID,
386                                           1, (const uint8_t[]) { SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME },
387                                           lldp_tx->ifname);
388         if (r < 0)
389                 return r;
390 
391         r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_TTL, 2);
392         if (r < 0)
393                 return r;
394 
395         unaligned_write_be16(packet + offset, LLDP_TX_TTL);
396         offset += 2;
397 
398         /* Optional TLVs follow, in no specific order: */
399 
400         r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_DESCRIPTION,
401                                  lldp_tx->port_description);
402         if (r < 0)
403                 return r;
404 
405         r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_NAME,
406                                  lldp_tx->hostname ?: hostname);
407         if (r < 0)
408                 return r;
409 
410         r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_DESCRIPTION,
411                                  lldp_tx->pretty_hostname ?: pretty_hostname);
412         if (r < 0)
413                 return r;
414 
415         /* See section 12 of RFC 8520.
416          * +--------+--------+----------+---------+--------------
417          * |TLV Type|  len   |   OUI    |subtype  | MUDString
418          * |  =127  |        |= 00 00 5E|  = 1    |
419          * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets)
420          * +--------+--------+----------+---------+--------------
421          * where:
422          *
423          * o  TLV Type = 127 indicates a vendor-specific TLV
424          * o  len = indicates the TLV string length
425          * o  OUI = 00 00 5E is the organizationally unique identifier of IANA
426          * o  subtype = 1 (as assigned by IANA for the MUDstring)
427          * o  MUDstring = the length MUST NOT exceed 255 octets
428          */
429         r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PRIVATE,
430                                           sizeof(SD_LLDP_OUI_IANA_MUD), SD_LLDP_OUI_IANA_MUD,
431                                           lldp_tx->mud_url);
432         if (r < 0)
433                 return r;
434 
435         r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
436         if (r < 0)
437                 return r;
438 
439         unaligned_write_be16(packet + offset, lldp_tx->supported_capabilities);
440         offset += 2;
441         unaligned_write_be16(packet + offset, lldp_tx->enabled_capabilities);
442         offset += 2;
443 
444         r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_END, 0);
445         if (r < 0)
446                 return r;
447 
448         *ret_packet_size = offset;
449         *ret_packet = TAKE_PTR(packet);
450         return 0;
451 }
452 
lldp_tx_send_packet(sd_lldp_tx * lldp_tx,size_t packet_size,const uint8_t * packet)453 static int lldp_tx_send_packet(sd_lldp_tx *lldp_tx, size_t packet_size, const uint8_t *packet) {
454         _cleanup_close_ int fd = -1;
455         union sockaddr_union sa;
456         ssize_t l;
457 
458         assert(lldp_tx);
459         assert(lldp_tx->ifindex > 0);
460         assert(packet_size > sizeof(struct ether_header));
461         assert(packet);
462 
463         sa = (union sockaddr_union) {
464                 .ll.sll_family = AF_PACKET,
465                 .ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
466                 .ll.sll_ifindex = lldp_tx->ifindex,
467                 .ll.sll_halen = ETH_ALEN,
468         };
469         memcpy(sa.ll.sll_addr, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN);
470 
471         fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
472         if (fd < 0)
473                 return -errno;
474 
475         l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll));
476         if (l < 0)
477                 return -errno;
478 
479         if ((size_t) l != packet_size)
480                 return -EIO;
481 
482         return 0;
483 }
484 
lldp_tx_send(sd_lldp_tx * lldp_tx)485 static int lldp_tx_send(sd_lldp_tx *lldp_tx) {
486         _cleanup_free_ uint8_t *packet = NULL;
487         size_t packet_size = 0;  /* avoid false maybe-uninitialized warning */
488         int r;
489 
490         assert(lldp_tx);
491 
492         r = lldp_tx_create_packet(lldp_tx, &packet_size, &packet);
493         if (r < 0)
494                 return r;
495 
496         return lldp_tx_send_packet(lldp_tx, packet_size, packet);
497 }
498 
sd_lldp_tx_attach_event(sd_lldp_tx * lldp_tx,sd_event * event,int64_t priority)499 int sd_lldp_tx_attach_event(sd_lldp_tx *lldp_tx, sd_event *event, int64_t priority) {
500         int r;
501 
502         assert_return(lldp_tx, -EINVAL);
503         assert_return(!lldp_tx->event, -EBUSY);
504 
505         if (event)
506                 lldp_tx->event = sd_event_ref(event);
507         else {
508                 r = sd_event_default(&lldp_tx->event);
509                 if (r < 0)
510                         return r;
511         }
512 
513         lldp_tx->event_priority = priority;
514 
515         return 0;
516 }
517 
sd_lldp_tx_detach_event(sd_lldp_tx * lldp_tx)518 int sd_lldp_tx_detach_event(sd_lldp_tx *lldp_tx) {
519         assert_return(lldp_tx, -EINVAL);
520 
521         lldp_tx->timer_event_source = sd_event_source_disable_unref(lldp_tx->timer_event_source);
522         lldp_tx->event = sd_event_unref(lldp_tx->event);
523         return 0;
524 }
525 
lldp_tx_get_delay(sd_lldp_tx * lldp_tx)526 static usec_t lldp_tx_get_delay(sd_lldp_tx *lldp_tx) {
527         assert(lldp_tx);
528 
529         return usec_add(lldp_tx->fast_tx > 0 ? LLDP_FAST_TX_INTERVAL_USEC : LLDP_TX_INTERVAL_USEC,
530                         (usec_t) random_u64() % LLDP_TX_JITTER_USEC);
531 }
532 
lldp_tx_reset_timer(sd_lldp_tx * lldp_tx)533 static int lldp_tx_reset_timer(sd_lldp_tx *lldp_tx) {
534         usec_t delay;
535         int r;
536 
537         assert(lldp_tx);
538         assert(lldp_tx->timer_event_source);
539 
540         delay = lldp_tx_get_delay(lldp_tx);
541 
542         r = sd_event_source_set_time_relative(lldp_tx->timer_event_source, delay);
543         if (r < 0)
544                 return r;
545 
546         return sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_ONESHOT);
547 }
548 
on_timer_event(sd_event_source * s,uint64_t usec,void * userdata)549 static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
550         sd_lldp_tx *lldp_tx = userdata;
551         int r;
552 
553         assert(lldp_tx);
554 
555         r = lldp_tx_send(lldp_tx);
556         if (r < 0)
557                 log_lldp_tx_errno(lldp_tx, r, "Failed to send packet, ignoring: %m");
558 
559         if (lldp_tx->fast_tx > 0)
560                 lldp_tx->fast_tx--;
561 
562         r = lldp_tx_reset_timer(lldp_tx);
563         if (r < 0)
564                 log_lldp_tx_errno(lldp_tx, r, "Failed to reset timer: %m");
565 
566         return 0;
567 }
568 
sd_lldp_tx_is_running(sd_lldp_tx * lldp_tx)569 int sd_lldp_tx_is_running(sd_lldp_tx *lldp_tx) {
570         int enabled;
571 
572         if (!lldp_tx)
573                 return 0;
574 
575         if (!lldp_tx->timer_event_source)
576                 return 0;
577 
578         if (sd_event_source_get_enabled(lldp_tx->timer_event_source, &enabled) < 0)
579                 return 0;
580 
581         return enabled == SD_EVENT_ONESHOT;
582 }
583 
sd_lldp_tx_stop(sd_lldp_tx * lldp_tx)584 int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) {
585         if (!lldp_tx)
586                 return 0;
587 
588         if (!lldp_tx->timer_event_source)
589                 return 0;
590 
591         (void) sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_OFF);
592 
593         return 1;
594 }
sd_lldp_tx_start(sd_lldp_tx * lldp_tx)595 int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) {
596         usec_t delay;
597         int r;
598 
599         assert_return(lldp_tx, -EINVAL);
600         assert_return(lldp_tx->event, -EINVAL);
601         assert_return(lldp_tx->ifindex > 0, -EINVAL);
602         assert_return(lldp_tx->mode >= 0 && lldp_tx->mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL);
603         assert_return(!ether_addr_is_null(&lldp_tx->hwaddr), -EINVAL);
604 
605         if (sd_lldp_tx_is_running(lldp_tx))
606                 return 0;
607 
608         lldp_tx->fast_tx = LLDP_FAST_TX_INIT;
609 
610         if (lldp_tx->timer_event_source) {
611                 r = lldp_tx_reset_timer(lldp_tx);
612                 if (r < 0)
613                         return log_lldp_tx_errno(lldp_tx, r, "Failed to re-enable timer: %m");
614 
615                 return 0;
616         }
617 
618         delay = lldp_tx_get_delay(lldp_tx);
619 
620         r = sd_event_add_time_relative(lldp_tx->event, &lldp_tx->timer_event_source,
621                                        CLOCK_BOOTTIME, delay, 0,
622                                        on_timer_event, lldp_tx);
623         if (r < 0)
624                 return r;
625 
626         (void) sd_event_source_set_description(lldp_tx->timer_event_source, "lldp-tx-timer");
627         (void) sd_event_source_set_priority(lldp_tx->timer_event_source, lldp_tx->event_priority);
628 
629         return 0;
630 }
631