diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/libnet_advanced.c | 12 | ||||
-rw-r--r-- | src/libnet_build_udld.c | 307 | ||||
-rw-r--r-- | src/libnet_checksum.c | 26 | ||||
-rw-r--r-- | src/libnet_internal.c | 16 | ||||
-rw-r--r-- | src/libnet_pblock.c | 2 |
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); } |