summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoachim Wiberg <troglobit@gmail.com>2023-05-04 19:39:55 +0200
committerGitHub <noreply@github.com>2023-05-04 19:39:55 +0200
commit7b3a7d34bb4d2064cc36f3f35c6ce09ff16144bf (patch)
treebbf4c9ab47c785ae0e6d529445317a8b3a97293c
parente99af8b76ea3f412f7cab5803ba3f0e6f352fc64 (diff)
parent4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87 (diff)
downloadlibnet-7b3a7d34bb4d2064cc36f3f35c6ce09ff16144bf.tar.gz
Merge pull request #161 from ivalery111/3-introduce-udld-supportHEADmaster
UDLD protocol support Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
-rw-r--r--.github/workflows/build.yml1
-rw-r--r--configure.ac3
-rw-r--r--doc/rfc/udld/rfc5171.txt731
-rw-r--r--include/libnet/libnet-functions.h115
-rw-r--r--include/libnet/libnet-headers.h59
-rw-r--r--include/libnet/libnet-structures.h8
-rw-r--r--sample/.gitignore1
-rw-r--r--sample/Makefile.am4
-rw-r--r--sample/udld.c159
-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
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/data/packet_captures/UDLD.capbin0 -> 3426 bytes
-rw-r--r--tests/udld_unit_tests.c510
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
new file mode 100644
index 0000000..d8d3ff6
--- /dev/null
+++ b/tests/data/packet_captures/UDLD.cap
Binary files differ
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:
+ */