diff options
author | Joachim Wiberg <troglobit@gmail.com> | 2023-05-04 19:39:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-04 19:39:55 +0200 |
commit | 7b3a7d34bb4d2064cc36f3f35c6ce09ff16144bf (patch) | |
tree | bbf4c9ab47c785ae0e6d529445317a8b3a97293c | |
parent | e99af8b76ea3f412f7cab5803ba3f0e6f352fc64 (diff) | |
parent | 4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87 (diff) | |
download | libnet-master.tar.gz |
UDLD protocol support
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
-rw-r--r-- | .github/workflows/build.yml | 1 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | doc/rfc/udld/rfc5171.txt | 731 | ||||
-rw-r--r-- | include/libnet/libnet-functions.h | 115 | ||||
-rw-r--r-- | include/libnet/libnet-headers.h | 59 | ||||
-rw-r--r-- | include/libnet/libnet-structures.h | 8 | ||||
-rw-r--r-- | sample/.gitignore | 1 | ||||
-rw-r--r-- | sample/Makefile.am | 4 | ||||
-rw-r--r-- | sample/udld.c | 159 | ||||
-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 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/data/packet_captures/UDLD.cap | bin | 0 -> 3426 bytes | |||
-rw-r--r-- | tests/udld_unit_tests.c | 510 |
18 files changed, 1957 insertions, 2 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d7e974..f9fe0a3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,3 +90,4 @@ jobs: - name: Run Unit-Test run: | sudo ./tests/libnet_unit_tests + ./tests/libnet_udld_unit_tests diff --git a/configure.ac b/configure.ac index f1c592e..4f54428 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,9 @@ AC_ARG_ENABLE([tests], ) AC_MSG_RESULT([$enable_tests]) AM_CONDITIONAL([ENABLE_TESTS], [test "$enable_tests" = "yes"]) +AS_IF([test "$enable_tests" = "yes"], [ + AC_DEFINE(LIBNET_ENABLE_TESTS, 1, [Useful define for testing purposes.]) +]) # what (not) to do if the user disables shared libraries AM_CONDITIONAL([COND_SHARED], [test "x$enable_shared" != xno]) diff --git a/doc/rfc/udld/rfc5171.txt b/doc/rfc/udld/rfc5171.txt new file mode 100644 index 0000000..109cf86 --- /dev/null +++ b/doc/rfc/udld/rfc5171.txt @@ -0,0 +1,731 @@ + + + + + + +Network Working Group M. Foschiano +Request for Comments: 5171 Cisco Systems +Category: Informational April 2008 + + + Cisco Systems UniDirectional Link Detection (UDLD) Protocol + +Status of This Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +IESG Note + + This RFC is not a candidate for any level of Internet Standard. The + IETF disclaims any knowledge of the fitness of this RFC for any + purpose and in particular notes that the decision to publish is not + based on IETF review for such things as security, congestion control, + or inappropriate interaction with deployed protocols. The RFC Editor + has chosen to publish this document at its discretion. Readers of + this document should exercise caution in evaluating its value for + implementation and deployment. See RFC 3932 for more information. + +Abstract + + This document describes a Cisco Systems protocol that can be used to + detect and disable unidirectional Ethernet fiber or copper links + caused, for instance, by mis-wiring of fiber strands, interface + malfunctions, media converters' faults, etc. It operates at Layer 2 + in conjunction with IEEE 802.3's existing Layer 1 fault detection + mechanisms. + + This document explains the protocol objectives and applications, + illustrates the specific premises the protocol was based upon, and + describes the protocol architecture and related deployment issues to + serve as a possible base for future standardization. + + + + + + + + + + + + + + +Foschiano Informational [Page 1] + +RFC 5171 UDLD April 2008 + + +Table of Contents + + 1. Introduction ....................................................2 + 2. Protocol Objectives and Applications ............................3 + 3. Protocol Design Premises ........................................4 + 4. Protocol Background .............................................4 + 5. Protocol Architecture ...........................................5 + 5.1. The Basics .................................................5 + 5.2. Neighbor Database Maintenance ..............................5 + 5.3. Event-driven Detection and Echoing .........................6 + 5.4. Event-based versus Event-less Detection ....................6 + 6. Packet Format ...................................................7 + 6.1. TLV Description ............................................9 + 7. Protocol Logic .................................................10 + 7.1. Protocol Timers ...........................................10 + 8. Comparison with Bidirectional Forwarding Detection .............11 + 9. Security Considerations ........................................11 + 10. Deployment Considerations .....................................11 + 11. Normative References ..........................................12 + 12. Informative Reference .........................................12 + +1. Introduction + + Today's Ethernet-based switched networks often rely on the Spanning + Tree Protocol (STP) defined in the IEEE 802.1D standard [1] to create + a loop-free topology that is used to forward the traffic from a + source to a destination based on the Layer 2 packet information + learned by the switches and on the knowledge of the status of the + physical links along the path. + + Issues arise when, due to mis-wirings or to hardware faults, the + communication path behaves abnormally and generates forwarding + anomalies. The simplest example of such anomalies is the case of a + bidirectional link that stops passing traffic in one direction and + therefore breaks one of the most basic assumptions that high-level + protocols typically depend upon: reliable two-way communication + between peers. + + The purpose of the UDLD protocol is to detect the presence of + anomalous conditions in the Layer 2 communication channel, while + relying on the mechanisms defined by the IEEE in the 802.3 standard + [2] to properly handle conditions inherent to the physical layer. + + + + + + + + + +Foschiano Informational [Page 2] + +RFC 5171 UDLD April 2008 + + +2. Protocol Objectives and Applications + + The UniDirectional Link Detection protocol (often referred to in + short as "UDLD") is a lightweight protocol that can be used to detect + and disable one-way connections before they create dangerous + situations such as Spanning Tree loops or other protocol + malfunctions. + + The protocol's main goal is to advertise the identities of all the + capable devices attached to the same LAN segment and to collect the + information received on the ports of each device to determine if the + Layer 2 communication is happening in the appropriate fashion. + + In a network that has an extensive fiber cabling plant, problems may + arise when incorrect patching causes a switch port to have its RX + fiber strand connected to one neighbor port and its TX fiber strand + connected to another. In these cases, a port may be deemed active if + it is receiving an optical signal on its RX strand. However, the + problem is that this link does not provide a valid communication path + at Layer 2 (and above). + + If this scenario of wrongly connected fiber strands is applied to + multiple ports to create a fiber loop, each device in the loop could + directly send packets to a neighbor but would not be able to receive + from that neighbor. + + Albeit the above scenario is rather extreme, it exemplifies how the + lack of mutual identification of the neighbors can bring protocols to + the wrong assumption that during a transmission the sender and the + receiver are always properly matched. Another equally dangerous + incorrect assumption is that the lack of reception of protocol + messages on a port unmistakably indicates the absence of transmitting + protocol entities at the other end of the link. + + The UDLD protocol was implemented to help correct certain assumptions + made by other protocols, and in particular to help the Spanning Tree + Protocol to function properly so as to avoid the creation of + dangerous Layer 2 loops. It has been available on most Cisco Systems + switches for several years and is now part of numerous network design + best practices. + + + + + + + + + + + +Foschiano Informational [Page 3] + +RFC 5171 UDLD April 2008 + + +3. Protocol Design Premises + + The current implementation of UDLD is based on the following + considerations/presuppositions: + + o The protocol would have to be run in the control plane of a + network device to be flexible enough to support upgrades and + bug fixes. The control plane speed would ultimately be the + limiting factor to the capability of fast fault detection of + the protocol (CPU speed, task switching speed, event processing + speed, etc.). The transmission medium's propagation delay at + 10 Mbps speed (or higher) would instead be considered a + negligible factor. + + o Network events typically do not happen with optimal timing, but + rather at the speed determined by the software/firmware + infrastructure that controls them. (For psychological and + practical reasons, developers tend to choose round timer values + rather than determine the optimal value for the specific + software architecture in use. Also, software bugs, coding + oversights, slow process switching, implementation overhead can + all affect the control plane responsiveness and event timings.) + Hence it was deemed necessary to adopt a conservative protocol + design to minimize false positives during the detection + process. + + o If a fault were discovered, it was assumed that the user would + want to keep the faulty port down for a predetermined amount of + time to avoid unnecessary port state flapping. For that + reason, a time-based fault recovery mechanism was provided + (although alternative recovery mechanisms are not implicitly + precluded by the protocol itself). + +4. Protocol Background + + UDLD is meant to be a Layer 2 detection protocol that works on top of + the existing Layer 1 detection mechanisms defined by the IEEE + standards. For example, the Far End Fault Indication (FEFI) function + for 100BaseFX interfaces and the Auto-Negotiation function for + 100BaseTX/1000BaseX interfaces represent standard physical-layer + mechanisms to determine if the transmission media is bidirectional. + (Please see sections 24.3.2.1 and 28.2.3.5 of [2] for more details.) + The typical case of a Layer 1 "fault" indication is the "loss of + light" indication. + + UDLD differs from the above-mentioned mechanisms insofar as it + performs mutual neighbor identification; in addition, it performs + neighbor acknowledgement on top of the Logical Link Control (LLC) + + + +Foschiano Informational [Page 4] + +RFC 5171 UDLD April 2008 + + + layer and thus is able to discover logical one-way miscommunication + between neighbors even when either one of the said PHY layer + mechanisms has deemed the transmission medium bidirectional. + +5. Protocol Architecture + +5.1. The Basics + + UDLD uses two basic mechanisms: + + a. It advertises a port's identity and learns about its neighbors + on a specific LAN segment; it keeps the acquired information on + the neighbors in a cache table. + + b. It sends a train of echo messages in certain circumstances that + require fast notifications or fast resynchronization of the + cached information. + + Because of the above, the algorithm run by UDLD requires that all the + devices connected to the same LAN segment be running the protocol in + order for a potential misconfiguration to be detected and for a + prompt corrective action to be taken. + +5.2. Neighbor Database Maintenance + + UDLD sends periodical "hello" packets (also called "advertisements" + or "probes") on every active interface to keep each device informed + about its neighbors. When a hello message is received, it is cached + and kept in memory at most for a defined time interval, called + "holdtime" or "time-to-live", after which the cache entry is + considered stale and is aged out. + + If a new hello message is received when a correspondent old cache + entry has not been aged out yet, then the old entry is dropped and is + replaced by the new one with a reset time-to-live timer. Whenever an + interface gets disabled and UDLD is running, or whenever UDLD is + disabled on an interface, or whenever the device is reset, all + existing cache entries for the interfaces affected by the + configuration change are cleared, and UDLD sends at least one message + to inform the neighbors to flush the part of their caches also + affected by the status change. This mechanism is meant to keep the + caches coherent on all the connected devices. + + + + + + + + + +Foschiano Informational [Page 5] + +RFC 5171 UDLD April 2008 + + +5.3. Event-driven Detection and Echoing + + The echoing mechanism is the base of UDLD's detection algorithm: + whenever a UDLD device learns about a new neighbor or receives a + resynchronization request from an out-of-synch neighbor, it + (re)starts the detection process on its side of the connection and + sends N echo messages in reply. (This mechanism implicitly assumes + that N packets are sufficient to get through a link and reach the + other end, even though some of them might get dropped during the + transmission.) + + Since this behavior must be the same on all the neighbors, the sender + of the echoes expects to receive (after some time) an echo in reply. + If the detection process ends without the proper echo information + being received, and under specific conditions, the link is considered + to be unidirectional. + +5.4. Event-based versus Event-less Detection + + UDLD can function in two modes: normal mode and aggressive mode. + + In normal mode, a protocol determination at the end of the detection + process is always based on information received in UDLD messages: + whether it's the information about the exchange of proper neighbor + identifications or the information about the absence of such proper + identifications. Hence, albeit bound by a timer, normal mode + determinations are always based on gleaned information, and as such + are "event-based". If no such information can be obtained (e.g., + because of a bidirectional loss of connectivity), UDLD follows a + conservative approach based on the considerations in Section 3 and + deems a port to be in "undetermined" state. In other words, normal + mode will shut down a port only if it can explicitly determine that + the associated link is faulty for an extended period of time. + + In contrast, in aggressive mode, UDLD will also shut down a port if + it loses bidirectional connectivity with the neighbor for the same + extended period of time mentioned above and subsequently fails + repeated last-resort attempts to re-establish communication with the + other end of the link. This mode of operation assumes that loss of + communication with the neighbor is a meaningful network event in + itself and is a symptom of a serious connectivity problem. Because + this type of detection can be event-less, and lack of information + cannot always be associated to an actual malfunction of the link, + this mode is optional and is recommended only in certain scenarios + (typically only on point-to-point links where no communication + failure between two neighbors is admissible). + + + + + +Foschiano Informational [Page 6] + +RFC 5171 UDLD April 2008 + + +6. Packet Format + + The UDLD protocol runs on top of the LLC sub-layer of the data link + layer of the OSI model. It uses a specially assigned multicast + destination MAC address and encapsulates its messages using the + standard Subnetwork Access Protocol (SNAP) format as described in the + following: + + Destination MAC address 01-00-0C-CC-CC-CC + + UDLD SNAP format: + LLC value 0xAAAA03 + Org Id 0x00000C + HDLC protocol type 0x0111 + + UDLD's Protocol Data Unit (PDU) format is as follows: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Ver | Opcode | Flags | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | List of TLVs (variable length list) | + | ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The TLV format is the basic one described below: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TYPE | LENGTH | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VALUE | + | ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type (16 bits): If an implementation does not understand a Type + value, it should skip over it using the length field. + + Length (16 bits): Length in bytes of the Type, Length, and Value + fields. In order for this field value to be valid, it should + be greater than or equal to the minimum allowed length, 4 + bytes. If the value is less than the minimum, the whole packet + is to be considered corrupted and therefore it must be + discarded right away during the parsing process. TLVs should + not be split across packet boundaries. + + + + +Foschiano Informational [Page 7] + +RFC 5171 UDLD April 2008 + + + Value (variable length): Object contained in the TLV. + + The protocol header fields are defined as follows: + + Ver (3 bits): + 0x01: UDLD PDU version number + + Opcode (5 bits): + 0x00: Reserved + 0x01: Probe message + 0x02: Echo message + 0x03: Flush message + 0x04-0x1F: Reserved for future use + + Flags (8 bits): + bit 0: Recommended timeout flag (RT) + bit 1: ReSynch flag (RSY) + bit 2-7: Reserved for future use + + PDU Checksum (16 bits): + IP-like checksum. Take the one's complement of the one's + complement sum (with the modification that the odd byte at the + end of an odd length message is used as the low 8 bits of an + extra word, rather than as the high 8 bits.) NB: All UDLD + implementations must comply with this specification. + + The list of currently defined TLVs comprises: + + Name Type Value format + + Device-ID TLV 0x0001 ASCII character string + Port-ID TLV 0x0002 ASCII character string + Echo TLV 0x0003 List of ID pairs + Message Interval TLV 0x0004 8-bit unsigned integer + Timeout Interval TLV 0x0005 8-bit unsigned integer + Device Name TLV 0x0006 ASCII character string + Sequence Number TLV 0x0007 32-bit unsigned integer + Reserved TLVs > 0x0007 Format unknown. + To be skipped by parsing routine. + + + + + + + + + + + + +Foschiano Informational [Page 8] + +RFC 5171 UDLD April 2008 + + +6.1. TLV Description + + Device-ID TLV: + + This TLV uniquely identifies the device that is sending the UDLD + packet. The TLV length field determines the length of the carried + identifier and must be greater than zero. In version 1 of the + protocol, the lack of this ID is considered a symptom of packet + corruption that implies that the message is invalid and must be + discarded. + + Port-ID TLV: + + This TLV uniquely identifies the physical port the UDLD packet is + sent on. The TLV length field determines the length of the + carried identifier and must be greater than zero. In version 1 of + the protocol, the lack of this ID is considered a symptom of + packet corruption that implies that the message is invalid and + must be discarded. + + Echo TLV: + + This TLV contains the list of valid DeviceID/PortID pairs received + by a port from all its neighbors. If either one of the + identifiers in a pair is corrupted, the message will be ignored. + This list includes only DeviceIDs and PortIDs extracted from UDLD + messages received and cached on the same interface on which this + TLV is sent. If no UDLD messages are received, then this TLV is + sent containing zero pairs. Despite its name, this TLV must be + present in both probe and echo messages, whereas in flush messages + it's not required. + + Message Interval TLV: + + This required TLV contains the 8-bit time interval value used by a + neighbor to send UDLD probes after the linkup or detection phases. + Its time unit is 1 second. The holdtime of a cache item for a + received message is calculated as (advertised-message-interval x + R), where R is a constant called "TTL to message interval ratio". + + Timeout Interval TLV: + + This optional TLV contains the 8-bit timeout interval value (T) + used by UDLD to decide the basic length of the detection phase. + Its time unit is 1 second. If it's not present in an + advertisement, T is assumed to be a hard-coded constant. + + + + + +Foschiano Informational [Page 9] + +RFC 5171 UDLD April 2008 + + + Device Name TLV: + + This required TLV is meant to be used by the CLI or SNMP and + typically contains the user-readable device name string. + + Sequence Number TLV: + + The purpose of this optional TLV is to inform the neighbors of the + sequence number of the current message being transmitted. A + counter from 1 to 2^32-1 is supposed to keep track of the sequence + number; it is reset whenever a transition of phase occurs so that + it will restart counting from one, for instance, whenever an echo + message sequence is initiated, or whenever a linkup message train + is triggered. + + No wraparound of the counter is supposed to happen. + + The zero value is reserved and can be used as a representation of + a missing or invalid sequence number by the user interface. + Therefore, the TLV should never contain zero. (NB: The use of + this TLV is currently limited only to informational purposes.) + +7. Protocol Logic + + UDLD's protocol logic relies on specific internal timers and is + sensitive to certain network events. + + The type of messages that UDLD transmits and the timing intervals + that it uses are dependent upon the internal state of the protocol, + which changes based on network events such as: + + o Link up + o Link down + o Protocol enabled + o Protocol disabled + o New neighbor discovery + o Neighbor state change + o Neighbor resynchronization requests + +7.1. Protocol Timers + + UDLD timer values could vary within certain "safety" ranges based on + the considerations in Section 3. However, in practice, in the + current implementation, timers use only certain values verified + during testing. Their time unit is one second. + + During the detection phase, messages are exchanged at the maximum + possible rate of one per second. After that, if the protocol reaches + + + +Foschiano Informational [Page 10] + +RFC 5171 UDLD April 2008 + + + a stable state and can make a certain determination on the + "bidirectionality" of the link, the message interval is increased to + a configurable value based on a curve known as M1(t), a time-based + function. + + In case the link is deemed anything other than bidirectional at the + end of the detection, this curve is a flat line with a fixed value of + Mfast (7 seconds in the current implementation). + + In case the link is instead deemed bidirectional, the curve will use + Mfast for the first 4 subsequent message transmissions and then will + transition to an Mslow value for all other steady-state + transmissions. Mslow can be either a fixed value (60 seconds in some + obsolete implementations) or a user-configurable value (between Mfast + and 90 seconds with a default of 15 seconds in the current + implementations). + +8. Comparison with Bidirectional Forwarding Detection + + Similarly to UDLD, the Bidirectional Forwarding Detection (BFD) [3] + protocol is intended to detect faults in the path between two network + nodes. However, BFD is supposed to operate independently of media, + data protocols, and routing protocols. There is no address discovery + mechanism in BFD, which is left to the application to determine. + + On the other hand, UDLD works exclusively on top of a L2 transport + supporting the SNAP encapsulation and operates independently of the + other bridge protocols (UDLD's main "applications"), with which it + has limited interaction. It also performs full neighbor discovery on + point-to-point and point-to-multipoint media. + +9. Security Considerations + + In a heterogeneous Layer 2 network that is built with different + models of network devices or with devices running different software + images, the UDLD protocol should be supported and configured on all + ports interconnecting said devices in order to achieve a complete + coverage of its detection process. Note that UDLD is not supposed to + be used on ports connected to untrusted devices or incapable devices; + hence, it should be disabled on such ports. + +10. Deployment Considerations + + Cisco Systems has supported the UDLD protocol in its Catalyst family + of switches since 1999. + + + + + + +Foschiano Informational [Page 11] + +RFC 5171 UDLD April 2008 + + +11. Normative References + + [1] IEEE 802.1D-2004 Standard -- Media access control (MAC) Bridges + + [2] IEEE 802.3-2002 IEEE Standard -- Local and metropolitan area + networks Specific requirements--Part 3: Carrier Sense Multiple + Access with Collision Detection (CSMA/CD) Access Method and + Physical Layer Specifications + +12. Informative Reference + + [3] Katz, D., and D. Ward, "Bidirectional Forwarding Detection", + Work in Progress, March 2008. + +Author's Address + + Marco Foschiano + Cisco Systems, Inc. + Via Torri Bianche 7, + 20059 Vimercate (Mi) + Italy + + EMail: foschia@cisco.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Foschiano Informational [Page 12] + +RFC 5171 UDLD April 2008 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2008). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78 and at http://www.rfc-editor.org/copyright.html, + and except as set forth therein, the authors retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + + + + + + + + + + + + +Foschiano Informational [Page 13] + diff --git a/include/libnet/libnet-functions.h b/include/libnet/libnet-functions.h index 45e3656..c91c1d5 100644 --- a/include/libnet/libnet-functions.h +++ b/include/libnet/libnet-functions.h @@ -1464,6 +1464,121 @@ libnet_build_stp_tcn(uint16_t id, uint8_t version, uint8_t bpdu_type, const uint8_t* payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag); /** + * Builds an UniDirectional Link Detection(UDLD) header. + * UDLD frames are usually encapsulated inside of an 802.2 + 802.3 frame + * combination. + * @param version UDLD PDU version number + * @param opcode operation code + * @param flags flags + * @param checksum checksum (0 for libnet to auto-fill) + * @param payload optional payload or NULL + * @param payload_s payload length or 0 + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +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); + +/** + * Builds an UniDirectional Link Detection(UDLD) Device ID TLV. + * @param value device id(ASCII character string) + * @param value_s device id(ASCII character string) length + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +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); + +/** + * Builds an UniDirectional Link Detection(UDLD) Port ID TLV. + * @param value port id(ASCII character string) + * @param value_s port id(ASCII character string) length + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +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); + +/** + * Builds an UniDirectional Link Detection(UDLD) Echo TLV. + * @param value list of ID pairs + * @param value_s ID pairs length + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +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); + +/** + * Builds an UniDirectional Link Detection(UDLD) Message Interval TLV. + * @param value time interval(8-bit unsigned integer) + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +LIBNET_API +libnet_ptag_t +libnet_build_udld_message_interval(const uint8_t *value, libnet_t *l, +libnet_ptag_t ptag); + +/** + * Builds an UniDirectional Link Detection(UDLD) Timeout Interval TLV. + * @param value timeout interval(8-bit unsigned integer) + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +LIBNET_API +libnet_ptag_t +libnet_build_udld_timeout_interval(const uint8_t *value, libnet_t *l, +libnet_ptag_t ptag); + +/** + * Builds an UniDirectional Link Detection(UDLD) Device Name TLV. + * @param value device name(ASCII character string) + * @param value_s device name length + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +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); + +/** + * Builds an UniDirectional Link Detection(UDLD) Sequence Number TLV. + * @param value sequence number(32-bit unsigned integer) + * @param l pointer to a libnet context + * @param ptag protocol tag to modify an existing header, 0 to build a new one + * @return protocol tag value on success + * @retval -1 on error + */ +LIBNET_API +libnet_ptag_t +libnet_build_udld_sequence_number(const uint8_t *value, libnet_t *l, +libnet_ptag_t ptag); + +/** * Builds a token ring header. * @param ac access control * @param fc frame control diff --git a/include/libnet/libnet-headers.h b/include/libnet/libnet-headers.h index afbb5c3..01194c9 100644 --- a/include/libnet/libnet-headers.h +++ b/include/libnet/libnet-headers.h @@ -107,6 +107,7 @@ #define LIBNET_SEBEK_H 0x30 /* sebek header: 48 bytes */ #define LIBNET_STP_CONF_H 0x23 /**< STP conf header: 35 bytes */ #define LIBNET_STP_TCN_H 0x04 /**< STP tcn header: 4 bytes */ +#define LIBNET_UDLD_H 0x04 /**< UDLD header: 4 bytes */ #define LIBNET_TOKEN_RING_H 0x16 /**< Token Ring header: 22 bytes */ #define LIBNET_TCP_H 0x14 /**< TCP header: 20 bytes */ #define LIBNET_UDP_H 0x08 /**< UDP header: 8 bytes */ @@ -1734,6 +1735,64 @@ struct libnet_stp_tcn_hdr uint8_t stp_bpdu_type; /* bridge protocol data unit type */ }; +/* + * UDLD header + * UniDirectional Link Detection + * Base header size: 4 bytes +*/ +struct libnet_udld_hdr +{ + /* LLC Info */ +#define LIBNET_UDLD_DEST_MAC {0x01, 0x00, 0x0C, 0xCC, 0xCC, 0xCC} + + /* UDLD SNAP Format */ +#define LIBNET_UDLD_LLC_DSAP 0xAA +#define LIBNET_UDLD_LLC_SSAP 0xAA +#define LIBNET_UDLD_LLC_CONTROL 0x03 +#define LIBNET_UDLD_OID {0x00, 0x00, 0x0C} +#define LIBNET_UDLD_HDLC_PROTO_TYPE 0x0111 + + /* Protocol Data Unit (PDU) Format */ + uint8_t version_opcode; +#define LIBNET_UDLD_PDU_VERSION 0x01 +#define LIBNET_UDLD_PDU_VERSION_OFFSET (5) + +#define LIBNET_UDLD_PDU_OPCODE_RESERVED 0x00 /* Reserved opcode message */ +#define LIBNET_UDLD_PDU_OPCODE_PROBE 0x01 /* Probe opcode message */ +#define LIBNET_UDLD_PDU_OPCODE_ECHO 0x02 /* Echo opcode message */ +#define LIBNET_UDLD_PDU_OPCODE_FLUSH 0x03 /* Flush opcode message */ +#define LIBNET_UDLD_PDU_OPCODE_RESERVED_FUTURE 0x04 /* Reserved for future use 0x04-0x1F */ +#define LIBNET_UDLD_PDU_OPCODE_MASK 0x1F + + uint8_t flags; +#define LIBNET_UDLD_FLAG_RT 0x01 /* Bit 0 : Recommended timeout flag (RT) */ +#define LIBNET_UDLD_FLAG_RSY 0x02 /* Bit 1 : ReSynch flag (RSY) */ +#define LIBNET_UDLD_FLAG_RESERVED 0x03 /* Bit 2-7: Reserved for future use */ + + uint16_t checksum; /* IP-like checksum */ +#define LIBNET_PROTO_UDLD 202 + + /* TLVs */ +#define LIBNET_UDLD_TLV_HDR_SIZE 0x04 /* UDLD TLV's header size 4 bytes */ + + uint16_t tlv__type; +#define LIBNET_UDLD_DEVICE_ID 0x0001 /* Value format: ASCII character string */ +#define LIBNET_UDLD_PORT_ID 0x0002 /* Value format: ASCII character string */ +#define LIBNET_UDLD_ECHO 0x0003 /* Value format: List of ID pairs */ +#define LIBNET_UDLD_MESSAGE_INTERVAL 0x0004 /* Value format: 8-bit unsigned integer */ +#define LIBNET_UDLD_TIMEOUT_INTERVAL 0x0005 /* Value format: 8-bit unsigned integer */ +#define LIBNET_UDLD_DEVICE_NAME 0x0006 /* Value format: ASCII character string */ +#define LIBNET_UDLD_SEQUENCE_NUMBER 0x0007 /* Value format: 32-bit unsigned integer */ +/* Reserved TLVs >0x0007 Value format: To be skipped by parsing routine */ + + uint16_t tlv__length; + + /* TLV value types */ +#define LIBNET_UDLD_VALUE_TYPE_ASCII (0) +#define LIBNET_UDLD_VALUE_TYPE_ID_PAIRS (1) +#define LIBNET_UDLD_VALUE_TYPE_8_BIT_UINT (2) +#define LIBNET_UDLD_VALUE_TYPE_32_BIT_UINT (3) +}; /* * TCP header diff --git a/include/libnet/libnet-structures.h b/include/libnet/libnet-structures.h index 4fc135e..a733ecb 100644 --- a/include/libnet/libnet-structures.h +++ b/include/libnet/libnet-structures.h @@ -162,6 +162,14 @@ struct libnet_protocol_block #define LIBNET_PBLOCK_LLDP_TTL_H 0x53 /* LLDP TTL header */ #define LIBNET_PBLOCK_LLDP_END_H 0x54 /* LLDP End of LLDPDU header */ #define LIBNET_PBLOCK_LLDP_ORG_SPEC_H 0x55 /* LLDP Organization Specific header */ +#define LIBNET_PBLOCK_UDLD_H 0x56 /* UDLD header */ +#define LIBNET_PBLOCK_UDLD_DEVICE_ID_H 0x57 /* UDLD Device ID header*/ +#define LIBNET_PBLOCK_UDLD_PORT_ID_H 0x58 /* UDLD Port ID header */ +#define LIBNET_PBLOCK_UDLD_ECHO_H 0x59 /* UDLD Echo ID header */ +#define LIBNET_PBLOCK_UDLD_MSG_INTERVAL_H 0x60 /* UDLD Message Interval header */ +#define LIBNET_PBLOCK_UDLD_TMT_INTERVAL_H 0x61 /* UDLD Timeout Interval header */ +#define LIBNET_PBLOCK_UDLD_DEVICE_NAME_H 0x62 /* UDLD Device Name header*/ +#define LIBNET_PBLOCK_UDLD_SEQ_NUMBER_H 0x63 /* UDLD Sequence Number header */ uint8_t flags; /* control flags */ #define LIBNET_PBLOCK_DO_CHECKSUM 0x01 /* needs a checksum */ diff --git a/sample/.gitignore b/sample/.gitignore index 2bfd12e..78c4e37 100644 --- a/sample/.gitignore +++ b/sample/.gitignore @@ -47,3 +47,4 @@ tring_tcp1 tring_tcp2 udp1 udp2 +udld diff --git a/sample/Makefile.am b/sample/Makefile.am index fea1a19..9722c2f 100644 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -53,7 +53,8 @@ samples = arp \ test_ipv4_options \ tring_tcp1 \ tring_tcp2 \ - lldp + lldp \ + udld noinst_PROGRAMS = $(samples) @@ -106,6 +107,7 @@ tring_tcp2_SOURCES = tring_tcp2.c udp1_SOURCES = udp1.c udp2_SOURCES = udp2.c lldp_SOURCES = lldp.c +udld_SOURCES = udld.c LDADD = $(top_builddir)/src/libnet.la diff --git a/sample/udld.c b/sample/udld.c new file mode 100644 index 0000000..d6bec90 --- /dev/null +++ b/sample/udld.c @@ -0,0 +1,159 @@ +#include <stdint.h> +#if (HAVE_CONFIG_H) +#include "../include/config.h" +#endif +#include "./libnet_test.h" + +#include <assert.h> + +#define DEVICE_NAME "lo" + +int +main(int argc, char *argv[]) +{ + (void)argc; /* unused */ + + int c; + libnet_t *l; + libnet_ptag_t t; + char errbuf[LIBNET_ERRBUF_SIZE]; + size_t udld_payload_size = 0; + + l = libnet_init(LIBNET_LINK, DEVICE_NAME, errbuf); + if (l == NULL) + { + fprintf(stderr, "libnet_init() failed: %s", errbuf); + return (EXIT_FAILURE); + } + + /* [TLV SEQUENCE NUMBER ]*/ + const uint32_t sequence_number = 1; + t = libnet_build_udld_sequence_number((const uint8_t *)&sequence_number, l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Sequence Number TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint32_t)); + + /* [TLV DEVICE NAME ]*/ + const char *device_name_str = "S1"; + t = libnet_build_udld_device_name((const uint8_t *)device_name_str, strlen(device_name_str), l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Device Name TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_name_str)); + + /* [TLV TIMEOUT INTERVAL ]*/ + const uint8_t timeout_interval = 5; + t = libnet_build_udld_timeout_interval(&timeout_interval, l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Timeout Interval TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + + /* [TLV MESSAGE INTERVAL ]*/ + const uint8_t message_interval = 7; + t = libnet_build_udld_message_interval(&message_interval, l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Message Interval TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + + /* [ TLV ECHO ] */ + const uint8_t echo_id_pairs[] = {0x0, 0x0, 0x0, 0x0}; + t = libnet_build_udld_echo(echo_id_pairs, (sizeof(echo_id_pairs)/sizeof(echo_id_pairs[0])), l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Echo TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + (sizeof(echo_id_pairs)/sizeof(echo_id_pairs[0]))); + + /* [ TLV PORT ID ] */ + const char *port_id_str = "Gi0/1"; + t = libnet_build_udld_port_id((const uint8_t *)port_id_str, strlen(port_id_str), l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Port ID TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(port_id_str)); + + /* [ TLV DEVICE ID ] */ + const char *device_id_str = "FOC1031Z7JG"; + t = libnet_build_udld_device_id((const uint8_t *)device_id_str, strlen(device_id_str), l, 0); + if (t == (-1)) + { + fprintf(stderr, "Cannot build UDLD Device ID TLV: %s\n", libnet_geterror(l)); + goto bad; + } + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_id_str)); + + assert((udld_payload_size == 56) && "Incorrect UDLD payload size\n"); + + int flags = (LIBNET_UDLD_FLAG_RT | LIBNET_UDLD_FLAG_RSY); + t = libnet_build_udld_hdr(LIBNET_UDLD_PDU_VERSION, /* version */ + LIBNET_UDLD_PDU_OPCODE_PROBE, /* opcode */ + flags, /* flags */ + 0, /* checksum */ + NULL, /* payload */ + 0, /* payload_s */ + l, 0); + if (t == -1) + { + fprintf(stderr, "Can't build UDLD: %s\n", libnet_geterror(l)); + goto bad; + } + + uint8_t OUI[3] = LIBNET_UDLD_OID; + t = libnet_build_802_2snap(0xAA, /* DSAP */ + 0xAA, /* SSAP */ + 0x03, /* Control */ + OUI, /* OUI */ + LIBNET_UDLD_HDLC_PROTO_TYPE, /* Type */ + NULL, /* Payload */ + 0, /* Payload_s */ + l, + 0); + + uint8_t udld_dst_mac[6] = LIBNET_UDLD_DEST_MAC; + uint8_t udld_src_mac_dummy[6] = { 0x00, 0x19, 0x06, 0xEA, 0xB8, 0x81 }; + t = libnet_build_802_3(udld_dst_mac, /* ethernet destination */ + udld_src_mac_dummy, /* ethernet source */ + LIBNET_802_2SNAP_H + /* */ + LIBNET_UDLD_H + udld_payload_size, /* */ + NULL, /* payload */ + 0, /* payload size */ + l, /* libnet context */ + 0); /* libnet ptag */ + if (t == -1) + { + fprintf(stderr, "Can't build 802.3 header: %s\n", libnet_geterror(l)); + goto bad; + } + + /* write the packet out */ + c = libnet_write(l); + if (c == -1) + { + fprintf(stderr, "Write error: %s\n", libnet_geterror(l)); + goto bad; + } + else + { + fprintf(stderr, "Wrote %d byte LLDP frame \"%s\"\n", c, argv[2]); + } + + libnet_destroy(l); + return (EXIT_SUCCESS); + bad: + libnet_destroy(l); + return (EXIT_FAILURE); +} 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); } diff --git a/tests/Makefile.am b/tests/Makefile.am index a78c578..bb68713 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,9 @@ -bin_PROGRAMS = libnet_unit_tests +bin_PROGRAMS = libnet_unit_tests libnet_udld_unit_tests libnet_unit_tests_SOURCES = unit_tests.c +libnet_udld_unit_tests_SOURCES = udld_unit_tests.c libnet_unit_tests_LDFLAGS = -lcmocka +libnet_udld_unit_tests_LDFLAGS = -lcmocka LDADD = $(top_builddir)/src/libnet.la diff --git a/tests/data/packet_captures/UDLD.cap b/tests/data/packet_captures/UDLD.cap Binary files differnew file mode 100644 index 0000000..d8d3ff6 --- /dev/null +++ b/tests/data/packet_captures/UDLD.cap diff --git a/tests/udld_unit_tests.c b/tests/udld_unit_tests.c new file mode 100644 index 0000000..5d655dc --- /dev/null +++ b/tests/udld_unit_tests.c @@ -0,0 +1,510 @@ +// clang-format off +#include <stddef.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <libnet.h> +// clang-format on + +/* Helpers */ +#define LIBNET_TEST_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0])) + +static uint8_t tlv_length_offset = 2; +static uint8_t tlv_value_offset = 4; + +static void +libnet_build_udld__pdu_header_only(void **state) +{ + (void)state; /* unused */ + + int rv = (-1); + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + + assert_non_null(l); + + const uint8_t flags = (LIBNET_UDLD_FLAG_RT | LIBNET_UDLD_FLAG_RSY); + + libnet_ptag_t udld_ptag = libnet_build_udld_hdr(LIBNET_UDLD_PDU_VERSION, /* version */ + LIBNET_UDLD_PDU_OPCODE_PROBE, /* opcode */ + flags, /* flags */ + 0, /* do checksum */ + NULL, /* payload */ + 0, /* payload length */ + l, /* libnet context */ + 0); /* protocol tag */ + + assert_int_not_equal(udld_ptag, (-1)); + + uint8_t *header = NULL; + uint32_t header_size = 0; + + rv = libnet_adv_cull_header(l, udld_ptag, &header, &header_size); + assert_int_not_equal(rv, (-1)); + + + struct libnet_udld_hdr *udld_hdr = NULL; + + udld_hdr = (struct libnet_udld_hdr *)header; + + assert_int_equal((udld_hdr->version_opcode >> LIBNET_UDLD_PDU_VERSION_OFFSET), LIBNET_UDLD_PDU_VERSION); + assert_int_equal((udld_hdr->version_opcode & LIBNET_UDLD_PDU_OPCODE_MASK), LIBNET_UDLD_PDU_OPCODE_PROBE); + assert_int_equal(udld_hdr->flags, (LIBNET_UDLD_FLAG_RT | LIBNET_UDLD_FLAG_RSY)); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Device ID */ +static void +libnet_build_udld__tlv_device_id(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const char *device_id_str = "FOC1031Z7JG"; + libnet_ptag_t udld_tlv_device_id_ptag = libnet_build_udld_device_id((const uint8_t *)device_id_str, + strlen(device_id_str), + l, + 0); + assert_int_not_equal(udld_tlv_device_id_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_device_id_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_DEVICE_ID_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_DEVICE_ID_H); + assert_int_equal(ntohs(*type), LIBNET_UDLD_DEVICE_ID); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_id_str)); + assert_memory_equal(value, (const char []){ "FOC1031Z7JG" }, strlen(device_id_str)); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Port ID */ +static void +libnet_build_udld__tlv_port_id(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const char *origin_port_id_str = "Gi0/1"; + libnet_ptag_t udld_tlv_port_id_ptag = libnet_build_udld_port_id((const uint8_t *)origin_port_id_str, + strlen(origin_port_id_str), + l, + 0); + assert_int_not_equal(udld_tlv_port_id_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_port_id_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_PORT_ID_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_PORT_ID); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + strlen(origin_port_id_str)); + assert_memory_equal(value, (const char []){ "Gi0/1" }, strlen(origin_port_id_str)); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Echo */ +static void +libnet_build_udld__tlv_echo(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const uint8_t original_echo_id_pairs[] = { 0x01, 0x02, 0x03, 0x04 }; + const uint8_t expected_echo_id_pairs[] = { 0x01, 0x02, 0x03, 0x04 }; + libnet_ptag_t udld_tlv_echo_ptag = libnet_build_udld_echo(original_echo_id_pairs, + LIBNET_TEST_ARRAY_LENGTH(original_echo_id_pairs), + l, + 0); + assert_int_not_equal(udld_tlv_echo_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_echo_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_ECHO_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_ECHO); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + LIBNET_TEST_ARRAY_LENGTH(original_echo_id_pairs)); + assert_int_equal(ntohs(*length) - 4/* sizeof type and length */, LIBNET_TEST_ARRAY_LENGTH(expected_echo_id_pairs)); + assert_memory_equal(value, expected_echo_id_pairs, LIBNET_TEST_ARRAY_LENGTH(original_echo_id_pairs)); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Message Interval */ +static void +libnet_build_udld__tlv_message_interval(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const uint8_t message_interval = 7; + libnet_ptag_t udld_tlv_message_interval_ptag = libnet_build_udld_message_interval(&message_interval, + l, + 0); + assert_int_not_equal(udld_tlv_message_interval_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_message_interval_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_MSG_INTERVAL_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_MESSAGE_INTERVAL); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + assert_int_equal(*value, 7); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Timeout Interval */ +static void +libnet_build_udld__tlv_timeout_interval(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const uint8_t timeout_interval = 5; + libnet_ptag_t udld_tlv_timeout_interval_ptag = libnet_build_udld_timeout_interval(&timeout_interval, + l, + 0); + assert_int_not_equal(udld_tlv_timeout_interval_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_timeout_interval_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_TMT_INTERVAL_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_TIMEOUT_INTERVAL); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + assert_int_equal(*value, 5); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Device Name */ +static void +libnet_build_udld__tlv_device_name(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const char *device_name_str = "S1"; + libnet_ptag_t udld_tlv_device_name_ptag = libnet_build_udld_device_name((const uint8_t *)device_name_str, + strlen(device_name_str), + l, + 0); + assert_int_not_equal(udld_tlv_device_name_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_device_name_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_DEVICE_NAME_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint8_t *value = (const uint8_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_DEVICE_NAME); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_name_str)); + assert_memory_equal(value, (const uint8_t []){ "S1" }, strlen(device_name_str)); + + libnet_destroy(l); +} + +/* Refs: tests/data/packet_captures/UDLD.cap, Packet #1, UDLD Sequence Number */ +static void +libnet_build_udld__tlv_sequence_number(void **state) +{ + (void)state; /* unused */ + + char errbuf[LIBNET_ERRBUF_SIZE]; + + libnet_t *l = libnet_init(LIBNET_NONE, NULL, errbuf); + assert_non_null(l); + + const uint32_t sequence_number = 1; + libnet_ptag_t udld_tlv_sequence_number_ptag = libnet_build_udld_sequence_number((const uint8_t *)&sequence_number, + l, + 0); + assert_int_not_equal(udld_tlv_sequence_number_ptag, (-1)); + + libnet_pblock_t *p = libnet_pblock_find(l, udld_tlv_sequence_number_ptag); + assert_non_null(p); + assert_int_equal(p->type, LIBNET_PBLOCK_UDLD_SEQ_NUMBER_H); + + const uint16_t *type = (const uint16_t *)(p->buf); + const uint16_t *length = (const uint16_t *)(p->buf + tlv_length_offset); + const uint32_t *value = (const uint32_t *)(p->buf + tlv_value_offset); + + assert_int_equal(ntohs(*type), LIBNET_UDLD_SEQUENCE_NUMBER); + assert_int_equal(ntohs(*length), LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint32_t)); + assert_int_equal(ntohl(*value), 1); + + libnet_destroy(l); +} + + +static void +libnet_udld__checksum_calculation(void **state) +{ + (void)state; /* unused */ + + const uint8_t original_packet_hex[] = { + 0x01,0x00,0x0c,0xcc,0xcc,0xcc,0x00,0x19,0x06,0xea,0xb8,0x81,0x00,0x44, /* 14 bytes: IEEE 802.3 Ethernet */ + 0xaa,0xaa,0x03,0x00,0x00,0x0c,0x01,0x11, /* 8 bytes: LLC */ + 0x21, /* 1 bytes: UDLD: version and opcode */ + 0x03, /* 1 bytes: UDLD: flags */ + 0x00,0x00, /* 2 bytes: UDLD: checksum */ + 0x00,0x01,0x00,0x0f,0x46,0x4f,0x43,0x31,0x30,0x33,0x31,0x5a,0x37,0x4a,0x47, /* 15 bytes: UDLD: Device ID */ + 0x00,0x02,0x00,0x09,0x47,0x69,0x30,0x2f,0x31, /* 9 bytes: UDLD: Port ID */ + 0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x00, /* 8 bytes: UDLD: Echo */ + 0x00,0x04,0x00,0x05,0x07, /* 5 bytes: UDLD: Message Interval */ + 0x00,0x05,0x00,0x05,0x05, /* 5 bytes: UDLD: Timeout Interval */ + 0x00,0x06,0x00,0x06,0x53,0x31, /* 6 bytes: UDLD: Device Name */ + 0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x01 /* 8 bytes: UDLD: Sequence Number */ + }; + + const uint16_t expected_checksum = 0x6d85; + const uint16_t checksum = libnet_ip_check((uint16_t *)original_packet_hex + 11, /* UDLD packet offset*/ + 60 /* remaining bytes */ + ); + assert_int_equal(expected_checksum, htons(checksum)); +} + +/** + * Build the whole UDLD packet, including the payload and IEEE802.3 Ethernet + LLC headers. + * + * Refs: tests/data/packet_captures/UDLD.cap, Packet #2 +*/ +static void +libnet_build_udld__build_whole_packet_with_checksum(void **state) +{ + (void)state; /* unused */ + + libnet_t *l = NULL; + libnet_ptag_t udld_ptag = 0; + libnet_ptag_t udld_device_id_tlv_ptag = 0; + libnet_ptag_t udld_port_id_tlv_ptag = 0; + libnet_ptag_t udld_echo_id_tlv_ptag = 0; + libnet_ptag_t udld_message_interval_ptag = 0; + libnet_ptag_t udld_timeout_interval_ptag = 0; + libnet_ptag_t udld_device_name_ptag = 0; + libnet_ptag_t udld_sequence_number_ptag = 0; + libnet_ptag_t ieee_802_2_llc_ptag = 0; + libnet_ptag_t ieee_802_3_ptag = 0; + uint32_t udld_payload_size = 0; + char error_buffer[LIBNET_ERRBUF_SIZE]; + + l = libnet_init(LIBNET_NONE, NULL, error_buffer); + assert_non_null(l); + + /* Build UDLD */ + + /* Build UDLD Sequence Number TLV */ + const uint32_t sequence_number = 1; + udld_sequence_number_ptag = libnet_build_udld_sequence_number((const uint8_t *)&sequence_number, + l, + 0 + ); + assert_int_not_equal(udld_sequence_number_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint32_t)); + + /* Build UDLD Device Name TLV */ + const char *device_name_str = "S2"; + udld_device_name_ptag = libnet_build_udld_device_name((const uint8_t *)device_name_str, + strlen(device_name_str), + l, + 0 + ); + assert_int_not_equal(udld_device_name_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_name_str)); + + /* Build UDLD Timeout Interval TLV */ + const uint8_t timeout_interval = 5; + udld_timeout_interval_ptag = libnet_build_udld_timeout_interval(&timeout_interval, + l, + 0 + ); + assert_int_not_equal(udld_timeout_interval_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + + /* Build UDLD Message Interval TLV */ + const uint8_t message_interval = 7; + udld_message_interval_ptag = libnet_build_udld_message_interval(&message_interval, + l, + 0 + ); + assert_int_not_equal(udld_message_interval_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(uint8_t)); + + /* Build UDLD Echo TLV */ + const uint8_t echo_id_pairs[] = {0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, + 0x46, 0x4f, 0x43, 0x31, 0x30, 0x33, + 0x31, 0x5a, 0x37, 0x4a, 0x47, 0x00, + 0x05, 0x47, 0x69, 0x30, 0x2f, 0x31}; + udld_echo_id_tlv_ptag = libnet_build_udld_echo(echo_id_pairs, + LIBNET_TEST_ARRAY_LENGTH(echo_id_pairs), + l, + 0 + ); + assert_int_not_equal(udld_echo_id_tlv_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + sizeof(echo_id_pairs)); + + /* Build UDLD Port ID TLV */ + const char *port_id_str = "Fa0/1"; + udld_port_id_tlv_ptag = libnet_build_udld_port_id((const uint8_t *)port_id_str, + strlen(port_id_str), + l, + 0 + ); + assert_int_not_equal(udld_port_id_tlv_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(port_id_str)); + + /* Build UDLD Device ID TLV */ + const char *device_id_str = "FOC1025X4W3"; + udld_device_id_tlv_ptag = libnet_build_udld_device_id((const uint8_t *)device_id_str, + strlen(device_id_str), + l, + 0 + ); + assert_int_not_equal(udld_device_id_tlv_ptag, (-1)); + udld_payload_size += (LIBNET_UDLD_TLV_HDR_SIZE + strlen(device_id_str)); + + assert_int_equal(udld_payload_size, 76); + + int flags = 0; + udld_ptag = libnet_build_udld_hdr( + LIBNET_UDLD_PDU_VERSION, /* version */ + LIBNET_UDLD_PDU_OPCODE_ECHO, /* opcode */ + flags, /* flags */ + 0, /* checksum */ + NULL, /* payload*/ + 0, /* payload_s */ + l, /* libnet context */ + 0 /* libnet ptag */ + ); + assert_int_not_equal(udld_ptag, (-1)); + + /* Build IEEE 802.2 snap LLC */ + uint8_t OUI[3] = LIBNET_UDLD_OID; + ieee_802_2_llc_ptag = libnet_build_802_2snap(0xAA, /* DSAP */ + 0xAA, /* SSAP */ + 0x03, /* Control */ + OUI, /* OUI */ + LIBNET_UDLD_HDLC_PROTO_TYPE, /* Type */ + NULL, /* Payload */ + 0, /* Payload_s */ + l, + 0 + ); + assert_int_not_equal(ieee_802_2_llc_ptag, (-1)); + + /* Build IEEE 802.3 */ + uint8_t udld_dst_mac[6] = LIBNET_UDLD_DEST_MAC; + uint8_t udld_src_mac_dummy[6] = { 0x00, 0x19, 0x06, 0xEA, 0xB8, 0x81 }; + ieee_802_3_ptag = libnet_build_802_3(udld_dst_mac, /* ethernet destination */ + udld_src_mac_dummy, /* ethernet source */ + LIBNET_802_2SNAP_H + /* */ + LIBNET_UDLD_H + udld_payload_size, /* */ + NULL, /* payload */ + 0, /* payload size */ + l, /* libnet context */ + 0 + ); + assert_int_not_equal(ieee_802_3_ptag, (-1)); + + /** + * Assembly packet. + * Verify checksum correctness. + * */ + { + uint8_t *packet = NULL; + uint32_t packet_length = 0; + int rv = (-1); + + /* like libnet_write but only assembly packet, NOT sending it to the network */ + rv = libnet_pblock_coalesce(l, &packet, &packet_length); + assert_int_not_equal(rv, UINT32_MAX); + + struct libnet_udld_hdr *udld_hdr = (struct libnet_udld_hdr *)(packet + (LIBNET_802_3_H + LIBNET_802_2SNAP_H)); + + const uint32_t expected_checksum = 0x805d; + assert_int_equal(htons(udld_hdr->checksum), expected_checksum); + } + + libnet_destroy(l); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(libnet_udld__checksum_calculation), + + cmocka_unit_test(libnet_build_udld__pdu_header_only), + cmocka_unit_test(libnet_build_udld__tlv_device_id), + cmocka_unit_test(libnet_build_udld__tlv_port_id), + cmocka_unit_test(libnet_build_udld__tlv_echo), + cmocka_unit_test(libnet_build_udld__tlv_message_interval), + cmocka_unit_test(libnet_build_udld__tlv_timeout_interval), + cmocka_unit_test(libnet_build_udld__tlv_device_name), + cmocka_unit_test(libnet_build_udld__tlv_sequence_number), + cmocka_unit_test(libnet_build_udld__build_whole_packet_with_checksum), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + +/** + * Local Variables: + * indent-tabs-mode: nil + * c-file-style: "stroustrup" + * End: + */ |