summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorValery Ivanov <ivalery111@gmail.com>2023-04-16 20:46:29 +0300
committerValery Ivanov <ivalery111@gmail.com>2023-05-03 22:01:02 +0300
commit4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87 (patch)
tree812bbf7f2eb20c04a0ed834692f69581b8ef0e00
parentb19e727ab52d4cd1c54f725c7d30e0c3db669c36 (diff)
downloadlibnet-4b3af3b99e5ccb82a66f5f961cc603ed16e7cb87.tar.gz
udld: initial support
Summary of changes: - Complete UDLD protocol support was added according to RFC5171 - UDLD checksum implementation was added - UDLD unit tests were added - CI has been updated to run UDLD unit tests That's how sample/udld looks in tcpdump: tcpdump: listening on lo0, link-type EN10MB (Ethernet), capture size 262144 bytes 06:23:33.536964 UDLDv1, Code Probe message (1), Flags [RT, RSY] (0x03), length 60 Checksum 0x6d85 (unverified) Device-ID TLV (0x0001) TLV, length 15, FOC1031Z7JG Port-ID TLV (0x0002) TLV, length 9, Gi0/1 Echo TLV (0x0003) TLV, length 8, ^@^@^@^@ Message Interval TLV (0x0004) TLV, length 5, 7s Timeout Interval TLV (0x0005) TLV, length 5, 5s Device Name TLV (0x0006) TLV, length 6, S1 Sequence Number TLV (0x0007) TLV, length 8, 1 0x0000: 2103 6d85 0001 000f 464f 4331 3033 315a !.m.....FOC1031Z 0x0010: 374a 4700 0200 0947 6930 2f31 0003 0008 7JG....Gi0/1.... 0x0020: 0000 0000 0004 0005 0700 0500 0505 0006 ................ 0x0030: 0006 5331 0007 0008 0000 0001 ..S1........ Signed-off-by: Valery Ivanov <ivalery111@gmail.com>
-rw-r--r--.github/workflows/build.yml1
-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_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
16 files changed, 1942 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/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_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:
+ */