summaryrefslogtreecommitdiff
path: root/src/libnet_build_udld.c
diff options
context:
space:
mode:
authorValery Ivanov <ivalery111@gmail.com>2023-04-16 20:46:29 +0300
committerValery Ivanov <ivalery111@gmail.com>2023-05-03 22:01:02 +0300
commit4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87 (patch)
tree812bbf7f2eb20c04a0ed834692f69581b8ef0e00 /src/libnet_build_udld.c
parentb19e727ab52d4cd1c54f725c7d30e0c3db669c36 (diff)
downloadlibnet-4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87.tar.gz
udld: initial support
Summary of changes: - Complete UDLD protocol support was added according to RFC5171 - UDLD checksum implementation was added - UDLD unit tests were added - CI has been updated to run UDLD unit tests That's how sample/udld looks in tcpdump: tcpdump: listening on lo0, link-type EN10MB (Ethernet), capture size 262144 bytes 06:23:33.536964 UDLDv1, Code Probe message (1), Flags [RT, RSY] (0x03), length 60 Checksum 0x6d85 (unverified) Device-ID TLV (0x0001) TLV, length 15, FOC1031Z7JG Port-ID TLV (0x0002) TLV, length 9, Gi0/1 Echo TLV (0x0003) TLV, length 8, ^@^@^@^@ Message Interval TLV (0x0004) TLV, length 5, 7s Timeout Interval TLV (0x0005) TLV, length 5, 5s Device Name TLV (0x0006) TLV, length 6, S1 Sequence Number TLV (0x0007) TLV, length 8, 1 0x0000: 2103 6d85 0001 000f 464f 4331 3033 315a !.m.....FOC1031Z 0x0010: 374a 4700 0200 0947 6930 2f31 0003 0008 7JG....Gi0/1.... 0x0020: 0000 0000 0004 0005 0700 0500 0505 0006 ................ 0x0030: 0006 5331 0007 0008 0000 0001 ..S1........ Signed-off-by: Valery Ivanov <ivalery111@gmail.com>
Diffstat (limited to 'src/libnet_build_udld.c')
-rw-r--r--src/libnet_build_udld.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/libnet_build_udld.c b/src/libnet_build_udld.c
new file mode 100644
index 0000000..731cffe
--- /dev/null
+++ b/src/libnet_build_udld.c
@@ -0,0 +1,307 @@
+#include "common.h"
+
+#include <assert.h>
+
+static libnet_ptag_t
+internal_build_udld_tlv(const uint16_t tlv_type, const uint8_t *value,
+const uint8_t value_s, libnet_t * l, libnet_ptag_t ptag)
+{
+ struct libnet_udld_hdr hdr;
+ uint32_t n, h;
+ libnet_pblock_t *p;
+
+ hdr.tlv__type = tlv_type;
+ hdr.tlv__length = LIBNET_UDLD_TLV_HDR_SIZE + value_s;
+
+ uint32_t host_type_and_len = 0;
+ host_type_and_len |= (hdr.tlv__type << 16);
+ host_type_and_len |= (hdr.tlv__length);
+ uint32_t network_type_and_len = htonl(host_type_and_len);
+
+ n = h = LIBNET_UDLD_TLV_HDR_SIZE + value_s;
+
+ uint8_t pblock_type = 0;
+ uint8_t value_type = 0;
+ switch(tlv_type)
+ {
+ case LIBNET_UDLD_DEVICE_ID:
+ pblock_type = LIBNET_PBLOCK_UDLD_DEVICE_ID_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_ASCII;
+ break;
+ case LIBNET_UDLD_PORT_ID:
+ pblock_type = LIBNET_PBLOCK_UDLD_PORT_ID_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_ASCII;
+ break;
+ case LIBNET_UDLD_ECHO:
+ pblock_type = LIBNET_PBLOCK_UDLD_ECHO_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_ID_PAIRS;
+ break;
+ case LIBNET_UDLD_MESSAGE_INTERVAL:
+ pblock_type = LIBNET_PBLOCK_UDLD_MSG_INTERVAL_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_8_BIT_UINT;
+ break;
+ case LIBNET_UDLD_TIMEOUT_INTERVAL:
+ pblock_type = LIBNET_PBLOCK_UDLD_TMT_INTERVAL_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_8_BIT_UINT;
+ break;
+ case LIBNET_UDLD_DEVICE_NAME:
+ pblock_type = LIBNET_PBLOCK_UDLD_DEVICE_NAME_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_ASCII;
+ break;
+ case LIBNET_UDLD_SEQUENCE_NUMBER:
+ pblock_type = LIBNET_PBLOCK_UDLD_SEQ_NUMBER_H;
+ value_type = LIBNET_UDLD_VALUE_TYPE_32_BIT_UINT;
+ break;
+ default:
+ snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
+ "%s(): incorrect TLV type", __func__);
+ goto bad;
+ }
+
+ /*
+ * Find the existing protocol block if a ptag is specified, or create
+ * a new one.
+ */
+ p = libnet_pblock_probe(l, ptag, n, pblock_type);
+ if (p == NULL)
+ {
+ return (-1);
+ }
+
+ if (libnet_pblock_append(l, p, &network_type_and_len, sizeof(network_type_and_len)) == -1)
+ {
+ goto bad;
+ }
+
+ switch(value_type)
+ {
+ case LIBNET_UDLD_VALUE_TYPE_ASCII:
+ case LIBNET_UDLD_VALUE_TYPE_ID_PAIRS:
+ {
+ if (libnet_pblock_append(l, p, value, value_s) == -1)
+ {
+ goto bad;
+ }
+ break;
+ }
+ case LIBNET_UDLD_VALUE_TYPE_8_BIT_UINT:
+ {
+ if (libnet_pblock_append(l, p, value, sizeof(uint8_t)) == -1)
+ {
+ goto bad;
+ }
+ break;
+ }
+ case LIBNET_UDLD_VALUE_TYPE_32_BIT_UINT:
+ {
+ const uint32_t sequence_number = htonl(*(const uint32_t *)value);
+ if (libnet_pblock_append(l, p, &sequence_number, sizeof(uint32_t)) == -1)
+ {
+ goto bad;
+ }
+ break;
+ }
+ default:
+ {
+ snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
+ "%s(): incorrect value type", __func__);
+ goto bad;
+ }
+ }
+
+ if (ptag)
+ {
+ return ptag;
+ }
+
+ return libnet_pblock_update(l, p, h, pblock_type);
+ bad:
+ libnet_pblock_delete(l, p);
+ return (-1);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_hdr(uint8_t version, uint8_t opcode, uint8_t flags, uint8_t checksum,
+const uint8_t *payload, uint32_t payload_s, libnet_t * l, libnet_ptag_t ptag)
+{
+
+ struct libnet_udld_hdr udld_hdr;
+ libnet_pblock_t *p = NULL;
+ uint32_t n = 0;
+ uint32_t h = 0;
+
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ n = LIBNET_UDLD_H + payload_s;
+
+ /*
+ * Find the existing protocol block if a ptag is specified, or create
+ * a new one.
+ */
+ p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_UDLD_H);
+ if (p == NULL)
+ {
+ return (-1);
+ }
+
+ memset(&udld_hdr, 0, sizeof(udld_hdr));
+ udld_hdr.version_opcode |= (version << LIBNET_UDLD_PDU_VERSION_OFFSET);
+ udld_hdr.version_opcode |= (opcode);
+ udld_hdr.flags = flags;
+ udld_hdr.checksum = checksum;
+
+ /*
+ * Appened the protocol unit to the list.
+ */
+ n = libnet_pblock_append(l, p, (u_char *) & udld_hdr, LIBNET_UDLD_H);
+ if (n == -1)
+ {
+ goto bad;
+ }
+
+ LIBNET_DO_PAYLOAD(l, p);
+
+ if (checksum == 0 && l->injection_type != LIBNET_RAW4)
+ {
+ /*
+ * If checksum is zero, by default libnet will compute a checksum
+ * for the user. The programmer can override this by calling
+ * libnet_toggle_checksum(l, ptag, 1);
+ */
+ libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
+ }
+
+ return (ptag ? ptag : libnet_pblock_update(l, p, h, LIBNET_PBLOCK_UDLD_H));
+ bad:
+ libnet_pblock_delete(l, p);
+ return (-1);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_device_id(const uint8_t *value, const uint8_t value_s, libnet_t * l, libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ if ((value && !value_s) || (!value && value_s))
+ {
+ sprintf(l->err_buf, "%s(): value inconsistency\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_DEVICE_ID, value, value_s, l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_port_id(const uint8_t *value, const uint8_t value_s, libnet_t * l, libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ if ((value && !value_s) || (!value && value_s))
+ {
+ sprintf(l->err_buf, "%s(): value inconsistency\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_PORT_ID, value, value_s, l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_echo(const uint8_t *value, const uint8_t value_s, libnet_t * l, libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ if ((value && !value_s) || (!value && value_s))
+ {
+ sprintf(l->err_buf, "%s(): value inconsistency\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_ECHO, value, value_s, l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_message_interval(const uint8_t *value, libnet_t *l,
+libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ assert(value && "value cannot be a NULL\n");
+ if (value == NULL)
+ {
+ sprintf(l->err_buf, "%s(): value pointer cannot be a NULL\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_MESSAGE_INTERVAL, value, sizeof(uint8_t), l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_timeout_interval(const uint8_t *value, libnet_t *l,
+libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ assert(value && "value cannot be a NULL\n");
+ if (value == NULL)
+ {
+ sprintf(l->err_buf, "%s(): value pointer cannot be a NULL\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_TIMEOUT_INTERVAL, (const uint8_t *)value, sizeof(uint8_t), l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_device_name(const uint8_t *value, const uint8_t value_s,
+libnet_t *l, libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ if ((value && !value_s) || (!value && value_s))
+ {
+ sprintf(l->err_buf, "%s(): value inconsistency\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_DEVICE_NAME, value, value_s, l, ptag);
+}
+
+LIBNET_API libnet_ptag_t
+libnet_build_udld_sequence_number(const uint8_t *value, libnet_t *l,
+libnet_ptag_t ptag)
+{
+ if (l == NULL)
+ {
+ return (-1);
+ }
+
+ assert(value != NULL && "value cannot be a NULL\n");
+ if (value == NULL)
+ {
+ sprintf(l->err_buf, "%s(): value pointer cannot be a NULL\n", __FUNCTION__);
+ return (-1);
+ }
+
+ return internal_build_udld_tlv(LIBNET_UDLD_SEQUENCE_NUMBER, value, sizeof(uint32_t), l, ptag);
+}