summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/libnet_advanced.c12
-rw-r--r--src/libnet_build_udld.c307
-rw-r--r--src/libnet_checksum.c26
-rw-r--r--src/libnet_internal.c16
-rw-r--r--src/libnet_pblock.c2
6 files changed, 364 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 141e777..329de33 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,7 @@ libnet_la_SOURCES = libnet_asn1.c \
libnet_build_sebek.c \
libnet_build_snmp.c \
libnet_build_stp.c \
+ libnet_build_udld.c \
libnet_build_tcp.c \
libnet_build_token_ring.c \
libnet_build_udp.c \
diff --git a/src/libnet_advanced.c b/src/libnet_advanced.c
index fb22ec6..5a3a610 100644
--- a/src/libnet_advanced.c
+++ b/src/libnet_advanced.c
@@ -38,12 +38,18 @@ libnet_adv_cull_packet(libnet_t *l, uint8_t **packet, uint32_t *packet_s)
*packet = NULL;
*packet_s = 0;
+#ifdef LIBNET_ENABLE_TESTS
+ /*
+ * Allow to fetch the packet without advanced mode. Useful for unit tests.
+ */
+#else
if (l->injection_type != LIBNET_LINK_ADV)
{
snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
"%s(): advanced link mode not enabled", __func__);
return (-1);
}
+#endif
/* checksums will be written in */
return (libnet_pblock_coalesce(l, packet, packet_s));
@@ -58,12 +64,18 @@ libnet_adv_cull_header(libnet_t *l, libnet_ptag_t ptag, uint8_t **header,
*header = NULL;
*header_s = 0;
+#ifdef LIBNET_ENABLE_TESTS
+ /*
+ * Allow to fetch the packet's header without advanced mode. Useful for unit tests.
+ */
+#else
if (l->injection_type != LIBNET_LINK_ADV)
{
snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
"%s(): advanced link mode not enabled", __func__);
return (-1);
}
+#endif
p = libnet_pblock_find(l, ptag);
if (p == NULL)
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);
+}
diff --git a/src/libnet_checksum.c b/src/libnet_checksum.c
index 16995de..3bbcc36 100644
--- a/src/libnet_checksum.c
+++ b/src/libnet_checksum.c
@@ -526,6 +526,32 @@ libnet_inet_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len, const
* the ISL frame itself. Use the libnet_crc function.
*/
}
+ case LIBNET_PROTO_UDLD:
+ {
+ /**
+ * Once again.
+ * iphdr points to the packet, which has the following structure:
+ * IEEE 802.3 Ethernet 14 bytes
+ * LLC 8 bytes
+ * UDLD <<<<---- udld_hdr_offset
+ */
+ /* FIXME: should we use ptrdiff_t for pointer arithmetics? */
+ int whole_packet_length = (end - iphdr); /* The length of IEEE 802.3 Ethernet + LLC + UDLD(include TLVs) */
+ if (whole_packet_length < 0)
+ {
+ snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
+ "%s(): cannot calculate packet lenght", __func__);
+ return (-1);
+ }
+ const uint8_t udld_hdr_offset = (LIBNET_802_3_H + LIBNET_802_2SNAP_H);
+ int udld_packet_length = (whole_packet_length - udld_hdr_offset);
+
+ const uint16_t checksum = libnet_ip_check((uint16_t *)iphdr + (udld_hdr_offset/sizeof(uint16_t)), udld_packet_length);
+
+ struct libnet_udld_hdr *udld_hdr = (struct libnet_udld_hdr *)(iphdr + udld_hdr_offset);
+ udld_hdr->checksum = checksum;
+ break;
+ }
default:
{
snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
diff --git a/src/libnet_internal.c b/src/libnet_internal.c
index 27d7641..fceb9b2 100644
--- a/src/libnet_internal.c
+++ b/src/libnet_internal.c
@@ -320,6 +320,22 @@ libnet_diag_dump_pblock_type(uint8_t type)
return ("lldp_end_lldpdu");
case LIBNET_PBLOCK_LLDP_ORG_SPEC_H:
return ("lldp_org_specific");
+ case LIBNET_PBLOCK_UDLD_H:
+ return ("udld");
+ case LIBNET_PBLOCK_UDLD_DEVICE_ID_H:
+ return ("udld_device_id");
+ case LIBNET_PBLOCK_UDLD_PORT_ID_H:
+ return ("udld_port_id");
+ case LIBNET_PBLOCK_UDLD_ECHO_H:
+ return ("udld_echo");
+ case LIBNET_PBLOCK_UDLD_MSG_INTERVAL_H:
+ return ("udld_message_interval");
+ case LIBNET_PBLOCK_UDLD_TMT_INTERVAL_H:
+ return ("udld_timeout_interval");
+ case LIBNET_PBLOCK_UDLD_DEVICE_NAME_H:
+ return ("udld_device_name");
+ case LIBNET_PBLOCK_UDLD_SEQ_NUMBER_H:
+ return ("udld_sequence_number");
}
return ("unrecognized pblock");
}
diff --git a/src/libnet_pblock.c b/src/libnet_pblock.c
index e3eb184..65d7217 100644
--- a/src/libnet_pblock.c
+++ b/src/libnet_pblock.c
@@ -598,6 +598,8 @@ libnet_pblock_p2p(uint8_t type)
return (IPPROTO_VRRP);
case LIBNET_PBLOCK_GRE_H:
return (IPPROTO_GRE);
+ case LIBNET_PBLOCK_UDLD_H:
+ return (LIBNET_PROTO_UDLD);
default:
return (-1);
}