]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-lldp-tx: introduce sd-lldp-tx
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 26 Sep 2021 08:07:34 +0000 (17:07 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 27 Sep 2021 16:35:47 +0000 (01:35 +0900)
src/libsystemd-network/meson.build
src/libsystemd-network/sd-lldp-tx.c [new file with mode: 0644]
src/systemd/meson.build
src/systemd/sd-lldp-tx.h [new file with mode: 0644]

index 1d1c4396b5440c59e5f8db1f1f6d1aa388f10ad6..54248a1e6e832094ed184f6a2d67296c1c8e8a73 100644 (file)
@@ -41,6 +41,7 @@ sources = files('''
         sd-ipv4acd.c
         sd-ipv4ll.c
         sd-lldp-rx.c
+        sd-lldp-tx.c
         sd-ndisc.c
         sd-radv.c
 '''.split())
diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c
new file mode 100644 (file)
index 0000000..24003af
--- /dev/null
@@ -0,0 +1,615 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <arpa/inet.h>
+#include <linux/sockios.h>
+#include <sys/ioctl.h>
+
+#include "sd-event.h"
+#include "sd-id128.h"
+#include "sd-lldp-tx.h"
+
+#include "alloc-util.h"
+#include "ether-addr-util.h"
+#include "fd-util.h"
+#include "hostname-util.h"
+#include "log-link.h"
+#include "network-common.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "time-util.h"
+#include "unaligned.h"
+#include "web-util.h"
+
+/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
+#define LLDP_FAST_TX_INIT 4U
+
+/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */
+#define LLDP_TX_HOLD 4U
+
+/* The jitter range to add, see 9.2.2. */
+#define LLDP_TX_JITTER_USEC (400U * USEC_PER_MSEC)
+
+/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */
+#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2)
+
+/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
+#define LLDP_FAST_TX_INTERVAL_USEC (1U * USEC_PER_SEC - LLDP_TX_JITTER_USEC / 2)
+
+#define LLDP_TX_TTL ((uint16_t) DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC))
+
+static const struct ether_addr lldp_multicast_addr[_SD_LLDP_MULTICAST_MODE_MAX] = {
+        [SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE]  = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
+        [SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
+        [SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
+};
+
+struct sd_lldp_tx {
+        unsigned n_ref;
+
+        int ifindex;
+        char *ifname;
+
+        sd_event *event;
+        int64_t event_priority;
+        sd_event_source *timer_event_source;
+
+        unsigned fast_tx;
+
+        sd_lldp_multicast_mode_t mode;
+        struct ether_addr hwaddr;
+
+        char *port_description;
+        char *hostname;
+        char *pretty_hostname;
+        char *mud_url;
+        uint16_t supported_capabilities;
+        uint16_t enabled_capabilities;
+};
+
+#define log_lldp_tx_errno(lldp_tx, error, fmt, ...)     \
+        log_interface_prefix_full_errno(                \
+                "LLDP Tx: ",                            \
+                sd_lldp_tx_get_ifname(lldp_tx),         \
+                error, fmt, ##__VA_ARGS__)
+#define log_lldp_tx(lldp_tx, fmt, ...)                  \
+        log_interface_prefix_full_errno_zerook(         \
+                "LLDP Tx: ",                            \
+                sd_lldp_tx_get_ifname(lldp_tx),         \
+                0, fmt, ##__VA_ARGS__)
+
+static sd_lldp_tx *lldp_tx_free(sd_lldp_tx *lldp_tx) {
+        if (!lldp_tx)
+                return NULL;
+
+        sd_lldp_tx_detach_event(lldp_tx);
+
+        free(lldp_tx->port_description);
+        free(lldp_tx->hostname);
+        free(lldp_tx->pretty_hostname);
+        free(lldp_tx->mud_url);
+
+        free(lldp_tx->ifname);
+        return mfree(lldp_tx);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_tx, sd_lldp_tx, lldp_tx_free);
+
+int sd_lldp_tx_new(sd_lldp_tx **ret) {
+        _cleanup_(sd_lldp_tx_unrefp) sd_lldp_tx *lldp_tx = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        lldp_tx = new(sd_lldp_tx, 1);
+        if (!lldp_tx)
+                return -ENOMEM;
+
+        *lldp_tx = (sd_lldp_tx) {
+                .n_ref = 1,
+                .mode = _SD_LLDP_MULTICAST_MODE_INVALID,
+        };
+
+        *ret = TAKE_PTR(lldp_tx);
+        return 0;
+}
+
+int sd_lldp_tx_set_ifindex(sd_lldp_tx *lldp_tx, int ifindex) {
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
+
+        lldp_tx->ifindex = ifindex;
+        return 0;
+}
+
+int sd_lldp_tx_set_ifname(sd_lldp_tx *lldp_tx, const char *ifname) {
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(ifname, -EINVAL);
+
+        if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
+                return -EINVAL;
+
+        return free_and_strdup(&lldp_tx->ifname, ifname);
+}
+
+const char *sd_lldp_tx_get_ifname(sd_lldp_tx *lldp_tx) {
+        if (!lldp_tx)
+                return NULL;
+
+        return get_ifname(lldp_tx->ifindex, &lldp_tx->ifname);
+}
+
+int sd_lldp_tx_set_multicast_mode(sd_lldp_tx *lldp_tx, sd_lldp_multicast_mode_t mode) {
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(mode >= 0 && mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL);
+
+        lldp_tx->mode = mode;
+        return 0;
+}
+
+int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr) {
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(!ether_addr_is_null(hwaddr), -EINVAL);
+
+        lldp_tx->hwaddr = *hwaddr;
+        return 0;
+}
+
+int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled) {
+        assert_return(lldp_tx, -EINVAL);
+        assert_return((enabled & ~supported) == 0, -EINVAL);
+
+        lldp_tx->supported_capabilities = supported;
+        lldp_tx->enabled_capabilities = enabled;
+        return 0;
+}
+
+int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description) {
+        assert_return(lldp_tx, -EINVAL);
+
+        /* An empty string unset the previously set hostname. */
+        if (strlen_ptr(port_description) >= 512)
+                return -EINVAL;
+
+        return free_and_strdup(&lldp_tx->port_description, empty_to_null(port_description));
+}
+
+int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname) {
+        assert_return(lldp_tx, -EINVAL);
+
+        /* An empty string unset the previously set hostname. */
+        if (!isempty(hostname)) {
+                assert_cc(HOST_NAME_MAX < 512);
+
+                if (!hostname_is_valid(hostname, 0))
+                        return -EINVAL;
+        }
+
+        return free_and_strdup(&lldp_tx->hostname, empty_to_null(hostname));
+}
+
+int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname) {
+        assert_return(lldp_tx, -EINVAL);
+
+        /* An empty string unset the previously set hostname. */
+        if (strlen_ptr(pretty_hostname) >= 512)
+                return -EINVAL;
+
+        return free_and_strdup(&lldp_tx->pretty_hostname, empty_to_null(pretty_hostname));
+}
+
+int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url) {
+        assert_return(lldp_tx, -EINVAL);
+
+        /* An empty string unset the previously set hostname. */
+        if (!isempty(mud_url)) {
+                /* Unless the maximum length of each value is 511, the MUD url must be smaller than 256.
+                 * See RFC 8520. */
+                if (strlen(mud_url) >= 256)
+                        return -EINVAL;
+
+                if (!http_url_is_valid(mud_url))
+                        return -EINVAL;
+        }
+
+        return free_and_strdup(&lldp_tx->mud_url, empty_to_null(mud_url));
+}
+
+static size_t lldp_tx_calculate_maximum_packet_size(sd_lldp_tx *lldp_tx, const char *hostname, const char *pretty_hostname) {
+        assert(lldp_tx);
+        assert(lldp_tx->ifindex > 0);
+
+        return sizeof(struct ether_header) +
+                /* Chassis ID */
+                2 + 1 + (SD_ID128_STRING_MAX - 1) +
+                /* Port ID */
+                2 + 1 + strlen_ptr(sd_lldp_tx_get_ifname(lldp_tx)) +
+                /* Port description */
+                2 + strlen_ptr(lldp_tx->port_description) +
+                /* System name */
+                2 + strlen_ptr(hostname) +
+                /* System description */
+                2 + strlen_ptr(pretty_hostname) +
+                /* MUD URL */
+                2 + sizeof(SD_LLDP_OUI_IANA_MUD) + strlen_ptr(lldp_tx->mud_url) +
+                /* TTL */
+                2 + 2 +
+                /* System Capabilities */
+                2 + 4 +
+                /* End */
+                2;
+}
+
+static int packet_append_tlv_header(uint8_t *packet, size_t packet_size, size_t *offset, uint8_t type, size_t data_len) {
+        assert(packet);
+        assert(offset);
+
+        /*
+         * +--------+--------+--------------
+         * |TLV Type|  len   |   value
+         * |(7 bits)|(9 bits)|(0-511 octets)
+         * +--------+--------+--------------
+         * where:
+         *
+         * len = indicates the length of value
+         */
+
+        /* The type field is 7-bits. */
+        if (type >= 128)
+                return -EINVAL;
+
+        /* The data length field is 9-bits. */
+        if (data_len >= 512)
+                return -EINVAL;
+
+        if (packet_size < 2 + data_len)
+                return -ENOBUFS;
+
+        if (*offset > packet_size - 2 - data_len)
+                return -ENOBUFS;
+
+        packet[(*offset)++] = (type << 1) | !!(data_len >> 8);
+        packet[(*offset)++] = data_len & (size_t) UINT8_MAX;
+
+        return 0;
+}
+
+static int 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) {
+
+        size_t len;
+        int r;
+
+        assert(packet);
+        assert(offset);
+        assert(prefix_len == 0 || prefix);
+
+        if (isempty(str))
+                return 0;
+
+        len = strlen(str);
+
+        /* Check for overflow */
+        if (len > SIZE_MAX - prefix_len)
+                return -ENOBUFS;
+
+        r = packet_append_tlv_header(packet, packet_size, offset, type, prefix_len + len);
+        if (r < 0)
+                return r;
+
+        memcpy_safe(packet + *offset, prefix, prefix_len);
+        *offset += prefix_len;
+
+        memcpy(packet + *offset, str, len);
+        *offset += len;
+
+        return 0;
+}
+
+static int packet_append_string(
+                uint8_t *packet,
+                size_t packet_size,
+                size_t *offset,
+                uint8_t type,
+                const char *str) {
+
+        return packet_append_prefixed_string(packet, packet_size, offset, type, 0, NULL, str);
+}
+
+static int lldp_tx_create_packet(sd_lldp_tx *lldp_tx, size_t *ret_packet_size, uint8_t **ret_packet) {
+        _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
+        _cleanup_free_ uint8_t *packet = NULL;
+        struct ether_header *header;
+        size_t packet_size, offset;
+        sd_id128_t machine_id;
+        int r;
+
+        assert(lldp_tx);
+        assert(lldp_tx->ifindex > 0);
+        assert(ret_packet_size);
+        assert(ret_packet);
+
+        r = sd_id128_get_machine(&machine_id);
+        if (r < 0)
+                return r;
+
+        if (!lldp_tx->hostname)
+                (void) gethostname_strict(&hostname);
+        if (!lldp_tx->pretty_hostname)
+                (void) get_pretty_hostname(&pretty_hostname);
+
+        packet_size = lldp_tx_calculate_maximum_packet_size(lldp_tx,
+                                                            lldp_tx->hostname ?: hostname,
+                                                            lldp_tx->pretty_hostname ?: pretty_hostname);
+
+        packet = new(uint8_t, packet_size);
+        if (!packet)
+                return -ENOMEM;
+
+        header = (struct ether_header*) packet;
+        header->ether_type = htobe16(ETHERTYPE_LLDP);
+        memcpy(header->ether_dhost, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN);
+        memcpy(header->ether_shost, &lldp_tx->hwaddr, ETH_ALEN);
+
+        offset = sizeof(struct ether_header);
+        r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_CHASSIS_ID,
+                                          1, (const uint8_t[]) { SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED },
+                                          SD_ID128_TO_STRING(machine_id));
+        if (r < 0)
+                return r;
+
+        r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_ID,
+                                          1, (const uint8_t[]) { SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME },
+                                          sd_lldp_tx_get_ifname(lldp_tx));
+        if (r < 0)
+                return r;
+
+        r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_PORT_DESCRIPTION,
+                                 lldp_tx->port_description);
+        if (r < 0)
+                return r;
+
+        r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_NAME,
+                                 lldp_tx->hostname ?: hostname);
+        if (r < 0)
+                return r;
+
+        r = packet_append_string(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_DESCRIPTION,
+                                 lldp_tx->pretty_hostname ?: pretty_hostname);
+        if (r < 0)
+                return r;
+
+        /* See section 12 of RFC 8520.
+         * +--------+--------+----------+---------+--------------
+         * |TLV Type|  len   |   OUI    |subtype  | MUDString
+         * |  =127  |        |= 00 00 5E|  = 1    |
+         * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets)
+         * +--------+--------+----------+---------+--------------
+         * where:
+         *
+         * o  TLV Type = 127 indicates a vendor-specific TLV
+         * o  len = indicates the TLV string length
+         * o  OUI = 00 00 5E is the organizationally unique identifier of IANA
+         * o  subtype = 1 (as assigned by IANA for the MUDstring)
+         * o  MUDstring = the length MUST NOT exceed 255 octets
+         */
+        r = packet_append_prefixed_string(packet, packet_size, &offset, SD_LLDP_TYPE_PRIVATE,
+                                          sizeof(SD_LLDP_OUI_IANA_MUD), SD_LLDP_OUI_IANA_MUD,
+                                          lldp_tx->mud_url);
+        if (r < 0)
+                return r;
+
+        r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_TTL, 2);
+        if (r < 0)
+                return r;
+
+        unaligned_write_be16(packet + offset, LLDP_TX_TTL);
+        offset += 2;
+
+        r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
+        if (r < 0)
+                return r;
+
+        unaligned_write_be16(packet + offset, lldp_tx->supported_capabilities);
+        offset += 2;
+        unaligned_write_be16(packet + offset, lldp_tx->enabled_capabilities);
+        offset += 2;
+
+        r = packet_append_tlv_header(packet, packet_size, &offset, SD_LLDP_TYPE_END, 0);
+        if (r < 0)
+                return r;
+
+        *ret_packet_size = offset;
+        *ret_packet = TAKE_PTR(packet);
+        return 0;
+}
+
+static int lldp_tx_send_packet(sd_lldp_tx *lldp_tx, size_t packet_size, const uint8_t *packet) {
+        _cleanup_close_ int fd = -1;
+        union sockaddr_union sa;
+        ssize_t l;
+
+        assert(lldp_tx);
+        assert(lldp_tx->ifindex > 0);
+        assert(packet_size > sizeof(struct ether_header));
+        assert(packet);
+
+        sa = (union sockaddr_union) {
+                .ll.sll_family = AF_PACKET,
+                .ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
+                .ll.sll_ifindex = lldp_tx->ifindex,
+                .ll.sll_halen = ETH_ALEN,
+        };
+        memcpy(sa.ll.sll_addr, lldp_multicast_addr + lldp_tx->mode, ETH_ALEN);
+
+        fd = socket(AF_PACKET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+        if (fd < 0)
+                return -errno;
+
+        l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll));
+        if (l < 0)
+                return -errno;
+
+        if ((size_t) l != packet_size)
+                return -EIO;
+
+        return 0;
+}
+
+static int lldp_tx_send(sd_lldp_tx *lldp_tx) {
+        _cleanup_free_ uint8_t *packet = NULL;
+        size_t packet_size = 0;  /* avoid false maybe-uninitialized warning */
+        int r;
+
+        assert(lldp_tx);
+
+        r = lldp_tx_create_packet(lldp_tx, &packet_size, &packet);
+        if (r < 0)
+                return r;
+
+        return lldp_tx_send_packet(lldp_tx, packet_size, packet);
+}
+
+int sd_lldp_tx_attach_event(sd_lldp_tx *lldp_tx, sd_event *event, int64_t priority) {
+        int r;
+
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(!lldp_tx->event, -EBUSY);
+
+        if (event)
+                lldp_tx->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&lldp_tx->event);
+                if (r < 0)
+                        return r;
+        }
+
+        lldp_tx->event_priority = priority;
+
+        return 0;
+}
+
+int sd_lldp_tx_detach_event(sd_lldp_tx *lldp_tx) {
+        assert_return(lldp_tx, -EINVAL);
+
+        lldp_tx->timer_event_source = sd_event_source_disable_unref(lldp_tx->timer_event_source);
+        lldp_tx->event = sd_event_unref(lldp_tx->event);
+        return 0;
+}
+
+sd_event* sd_lldp_tx_get_event(sd_lldp_tx *lldp_tx) {
+        assert_return(lldp_tx, NULL);
+
+        return lldp_tx->event;
+}
+
+static usec_t lldp_tx_get_delay(sd_lldp_tx *lldp_tx) {
+        assert(lldp_tx);
+
+        return usec_add(lldp_tx->fast_tx > 0 ? LLDP_FAST_TX_INTERVAL_USEC : LLDP_TX_INTERVAL_USEC,
+                        (usec_t) random_u64() % LLDP_TX_JITTER_USEC);
+}
+
+static int lldp_tx_reset_timer(sd_lldp_tx *lldp_tx) {
+        usec_t delay;
+        int r;
+
+        assert(lldp_tx);
+        assert(lldp_tx->timer_event_source);
+
+        delay = lldp_tx_get_delay(lldp_tx);
+
+        r = sd_event_source_set_time_relative(lldp_tx->timer_event_source, delay);
+        if (r < 0)
+                return r;
+
+        return sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_ONESHOT);
+}
+
+static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_lldp_tx *lldp_tx = userdata;
+        int r;
+
+        assert(lldp_tx);
+
+        r = lldp_tx_send(lldp_tx);
+        if (r < 0)
+                log_lldp_tx_errno(lldp_tx, r, "Failed to send packet, ignoring: %m");
+
+        if (lldp_tx->fast_tx > 0)
+                lldp_tx->fast_tx--;
+
+        r = lldp_tx_reset_timer(lldp_tx);
+        if (r < 0)
+                log_lldp_tx_errno(lldp_tx, r, "Failed to reset timer: %m");
+
+        return 0;
+}
+
+int sd_lldp_tx_is_running(sd_lldp_tx *lldp_tx) {
+        int enabled;
+
+        if (!lldp_tx)
+                return 0;
+
+        if (!lldp_tx->timer_event_source)
+                return 0;
+
+        if (sd_event_source_get_enabled(lldp_tx->timer_event_source, &enabled) < 0)
+                return 0;
+
+        return enabled == SD_EVENT_ONESHOT;
+}
+
+int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx) {
+        if (!lldp_tx)
+                return 0;
+
+        if (!lldp_tx->timer_event_source)
+                return 0;
+
+        (void) sd_event_source_set_enabled(lldp_tx->timer_event_source, SD_EVENT_OFF);
+
+        return 1;
+}
+int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) {
+        usec_t delay;
+        int r;
+
+        assert_return(lldp_tx, -EINVAL);
+        assert_return(lldp_tx->event, -EINVAL);
+        assert_return(lldp_tx->ifindex > 0, -EINVAL);
+        assert_return(lldp_tx->mode >= 0 && lldp_tx->mode < _SD_LLDP_MULTICAST_MODE_MAX, -EINVAL);
+        assert_return(!ether_addr_is_null(&lldp_tx->hwaddr), -EINVAL);
+
+        if (sd_lldp_tx_is_running(lldp_tx))
+                return 0;
+
+        lldp_tx->fast_tx = LLDP_FAST_TX_INIT;
+
+        if (lldp_tx->timer_event_source) {
+                r = lldp_tx_reset_timer(lldp_tx);
+                if (r < 0)
+                        return log_lldp_tx_errno(lldp_tx, r, "Failed to re-enable timer: %m");
+
+                return 0;
+        }
+
+        delay = lldp_tx_get_delay(lldp_tx);
+
+        r = sd_event_add_time_relative(lldp_tx->event, &lldp_tx->timer_event_source,
+                                       clock_boottime_or_monotonic(), delay, 0,
+                                       on_timer_event, lldp_tx);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(lldp_tx->timer_event_source, "lldp-tx-timer");
+        (void) sd_event_source_set_priority(lldp_tx->timer_event_source, lldp_tx->event_priority);
+
+        return 0;
+}
index bc91abeb4e30bae9f4d0cfbe0ea29a906045e677..4f40b9f57f7f91b801ad8c8be85d142ebcfd99c1 100644 (file)
@@ -29,6 +29,7 @@ _not_installed_headers = '''
         sd-ipv4acd.h
         sd-ipv4ll.h
         sd-lldp-rx.h
+        sd-lldp-tx.h
         sd-lldp.h
         sd-ndisc.h
         sd-netlink.h
diff --git a/src/systemd/sd-lldp-tx.h b/src/systemd/sd-lldp-tx.h
new file mode 100644 (file)
index 0000000..aef06ed
--- /dev/null
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosdlldptxhfoo
+#define foosdlldptxhfoo
+
+/***
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <sys/types.h>
+
+#include "sd-event.h"
+#include "sd-lldp.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_lldp_tx sd_lldp_tx;
+
+typedef enum sd_lldp_multicast_mode_t {
+        SD_LLDP_MULTICAST_MODE_NEAREST_BRIDGE,
+        SD_LLDP_MULTICAST_MODE_NON_TPMR_BRIDGE,
+        SD_LLDP_MULTICAST_MODE_CUSTOMER_BRIDGE,
+        _SD_LLDP_MULTICAST_MODE_MAX,
+        _SD_LLDP_MULTICAST_MODE_INVALID = -EINVAL,
+        _SD_ENUM_FORCE_S64(LLDP_TX_MODE),
+} sd_lldp_multicast_mode_t;
+
+int sd_lldp_tx_new(sd_lldp_tx **ret);
+sd_lldp_tx *sd_lldp_tx_ref(sd_lldp_tx *lldp_tx);
+sd_lldp_tx *sd_lldp_tx_unref(sd_lldp_tx *lldp_tx);
+
+int sd_lldp_tx_start(sd_lldp_tx *lldp_tx);
+int sd_lldp_tx_stop(sd_lldp_tx *lldp_tx);
+int sd_lldp_tx_is_running(sd_lldp_tx *lldp_tx);
+
+int sd_lldp_tx_attach_event(sd_lldp_tx *lldp_tx, sd_event *event, int64_t priority);
+int sd_lldp_tx_detach_event(sd_lldp_tx *lldp_tx);
+sd_event *sd_lldp_tx_get_event(sd_lldp_tx *lldp_tx);
+
+int sd_lldp_tx_set_ifindex(sd_lldp_tx *lldp_tx, int ifindex);
+int sd_lldp_tx_set_ifname(sd_lldp_tx *lldp_tx, const char *ifname);
+const char *sd_lldp_tx_get_ifname(sd_lldp_tx *lldp_tx);
+
+int sd_lldp_tx_set_multicast_mode(sd_lldp_tx *lldp_tx, sd_lldp_multicast_mode_t mode);
+int sd_lldp_tx_set_hwaddr(sd_lldp_tx *lldp_tx, const struct ether_addr *hwaddr);
+int sd_lldp_tx_set_port_description(sd_lldp_tx *lldp_tx, const char *port_description);
+int sd_lldp_tx_set_hostname(sd_lldp_tx *lldp_tx, const char *hostname);
+int sd_lldp_tx_set_pretty_hostname(sd_lldp_tx *lldp_tx, const char *pretty_hostname);
+int sd_lldp_tx_set_mud_url(sd_lldp_tx *lldp_tx, const char *mud_url);
+int sd_lldp_tx_set_capabilities(sd_lldp_tx *lldp_tx, uint16_t supported, uint16_t enabled);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_tx, sd_lldp_tx_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif