diff --git a/Common++/header/MacAddress.h b/Common++/header/MacAddress.h index 1cf5852e6b..2f172c332d 100644 --- a/Common++/header/MacAddress.h +++ b/Common++/header/MacAddress.h @@ -143,6 +143,8 @@ namespace pcpp /// A static value representing a zero value of MAC address, meaning address of value "00:00:00:00:00:00" static MacAddress Zero; + /// A static value representing a broadcast MAC address, meaning address of value "ff:ff:ff:ff:ff:ff" + static MacAddress Broadcast; private: std::array m_Address{}; diff --git a/Common++/src/MacAddress.cpp b/Common++/src/MacAddress.cpp index 18886faaea..29d2449c7d 100644 --- a/Common++/src/MacAddress.cpp +++ b/Common++/src/MacAddress.cpp @@ -5,6 +5,8 @@ namespace pcpp MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0); + MacAddress MacAddress::Broadcast(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + std::string MacAddress::toString() const { char str[19]; diff --git a/Examples/ArpSpoofing/main.cpp b/Examples/ArpSpoofing/main.cpp index 538dd4c7b8..0db3a604ca 100644 --- a/Examples/ArpSpoofing/main.cpp +++ b/Examples/ArpSpoofing/main.cpp @@ -66,8 +66,7 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe pcpp::MacAddress macSrc = pDevice->getMacAddress(); pcpp::MacAddress macDst(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); pcpp::EthLayer ethLayer(macSrc, macDst, static_cast(PCPP_ETHERTYPE_ARP)); - pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, pDevice->getMacAddress(), pDevice->getMacAddress(), - pDevice->getIPv4Address(), ipAddr); + pcpp::ArpLayer arpLayer(pcpp::ArpRequest(pDevice->getMacAddress(), pDevice->getIPv4Address(), ipAddr)); arpRequest.addLayer(ðLayer); arpRequest.addLayer(&arpLayer); @@ -137,7 +136,7 @@ void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatew // Create ARP reply for the gateway pcpp::Packet gwArpReply(500); pcpp::EthLayer gwEthLayer(deviceMacAddress, gatewayMacAddr, static_cast(PCPP_ETHERTYPE_ARP)); - pcpp::ArpLayer gwArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), gatewayMacAddr, victimAddr, gatewayAddr); + pcpp::ArpLayer gwArpLayer(pcpp::ArpReply(pDevice->getMacAddress(), victimAddr, gatewayMacAddr, gatewayAddr)); gwArpReply.addLayer(&gwEthLayer); gwArpReply.addLayer(&gwArpLayer); gwArpReply.computeCalculateFields(); @@ -145,7 +144,7 @@ void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatew // Create ARP reply for the victim pcpp::Packet victimArpReply(500); pcpp::EthLayer victimEthLayer(deviceMacAddress, victimMacAddr, static_cast(PCPP_ETHERTYPE_ARP)); - pcpp::ArpLayer victimArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), victimMacAddr, gatewayAddr, victimAddr); + pcpp::ArpLayer victimArpLayer(pcpp::ArpReply(pDevice->getMacAddress(), gatewayAddr, victimMacAddr, victimAddr)); victimArpReply.addLayer(&victimEthLayer); victimArpReply.addLayer(&victimArpLayer); victimArpReply.computeCalculateFields(); diff --git a/Packet++/header/ArpLayer.h b/Packet++/header/ArpLayer.h index 0b1a3278e6..02251c4207 100644 --- a/Packet++/header/ArpLayer.h +++ b/Packet++/header/ArpLayer.h @@ -3,6 +3,7 @@ #include "Layer.h" #include "IpAddress.h" #include "MacAddress.h" +#include "DeprecationUtils.h" /// @file @@ -37,6 +38,7 @@ namespace pcpp uint32_t targetIpAddr; }; #pragma pack(pop) + static_assert(sizeof(arphdr) == 28, "arphdr size is not 28 bytes"); /// An enum for ARP message type enum ArpOpcode @@ -45,6 +47,97 @@ namespace pcpp ARP_REPLY = 0x0002 ///< ARP reply (response) }; + /// @brief An enum representing the ARP message type + enum class ArpMessageType + { + Unknown, ///< Unknown ARP message type + Request, ///< ARP request + Reply, ///< ARP reply + GratuitousRequest, ///< Gratuitous ARP request + GratuitousReply, ///< Gratuitous ARP reply + }; + + /// @brief A struct representing the build data for an ARP request + /// + /// An ARP request is a message sent by a machine to request the MAC address of another machine on the network. + struct ArpRequest + { + MacAddress senderMacAddr; + IPv4Address senderIpAddr; + IPv4Address targetIpAddr; + + /// @brief Construct a new Arp Request object + /// @param senderMacAddress The MAC address of the machine sending the query. + /// @param senderIPAddress The IP address of the machine sending the query. + /// @param targetIPAddress The IP address of the target machine being queried. + ArpRequest(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress, + IPv4Address const& targetIPAddress) + : senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress), targetIpAddr(targetIPAddress) {}; + }; + + /// @brief A struct representing the build data for an ARP reply + /// + /// An ARP reply is a message sent by a machine in response to an ARP request. It contains the MAC address of the + /// answering machine, and is sent to the IP/MAC address of the machine that sent the original ARP request. + struct ArpReply + { + MacAddress senderMacAddr; + IPv4Address senderIpAddr; + MacAddress targetMacAddr; + IPv4Address targetIpAddr; + + /// @brief Construct a new Arp Reply object + /// @param senderMacAddress The MAC address of the machine sending the reply. + /// @param senderIPAddress The IP address of the machine sending the reply. + /// @param targetMacAddress The MAC address of the target machine being replied to. + /// @param targetIPAddress The IP address of the target machine being replied to. + /// @remarks The target machine is considered the machine that sent the original ARP request. + ArpReply(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress, + MacAddress const& targetMacAddress, IPv4Address const& targetIPAddress) + : senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress), targetMacAddr(targetMacAddress), + targetIpAddr(targetIPAddress) {}; + }; + + /// @brief A struct representing the build data for a gratuitous ARP request + /// + /// A gratuitous ARP request is an ARP request that is sent by a machine to announce its presence on the network. + /// It is an ARP request that has both the sender and target IP addresses set to the IP address of the machine + /// and the target MAC address set to the broadcast address. Normally such a request will not receive a reply. + /// + /// These requests can be used to update ARP caches on other machines on the network, or to help in detecting IP + /// address conflicts. + struct GratuitousArpRequest + { + MacAddress senderMacAddr; + IPv4Address senderIpAddr; + + /// @brief Construct a new Gratuitous Arp Request object + /// @param senderMacAddress The MAC address of the machine sending the gratuitous ARP request. + /// @param senderIPAddress The IP address of the machine sending the gratuitous ARP request. + /// @remarks The target MAC address is set to the broadcast address and the target IP address is set to the + /// sender's. + GratuitousArpRequest(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress) + : senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress) {}; + }; + + /// @brief A struct representing the build data a gratuitous ARP reply + /// + /// A gratuitous ARP reply is an ARP reply that is sent by a machine to announce its presence on the network. + /// It is gratuitous in the sense that it is not in response to an ARP request, but sent unsolicited to the network. + struct GratuitousArpReply + { + MacAddress senderMacAddr; + IPv4Address senderIpAddr; + + /// @brief Construct a new Gratuitous Arp Reply object + /// @param senderMacAddress The MAC address of the machine sending the gratuitous ARP reply. + /// @param senderIPAddress The IP address of the machine sending the gratuitous ARP reply. + /// @remarks The target MAC address is set to the broadcast address and the target IP address is set to the + /// sender's. + GratuitousArpReply(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress) + : senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress) {}; + }; + /// @class ArpLayer /// Represents an ARP protocol layer. Currently only IPv4 ARP messages are supported class ArpLayer : public Layer @@ -61,15 +154,45 @@ namespace pcpp m_DataLen = sizeof(arphdr); } + /// @brief A constructor that creates an ARP header + /// @param[in] opCode ARP message type (ARP request or ARP reply) + /// @param[in] senderMacAddr The sender MAC address (will be put in arphdr#senderMacAddr) + /// @param[in] senderIpAddr The sender IP address (will be put in arphdr#senderIpAddr) + /// @param[in] targetMacAddr The target MAC address (will be put in arphdr#targetMacAddr) + /// @param[in] targetIpAddr The target IP address (will be put in arphdr#targetIpAddr) + /// @remarks No validation is done on the input parameters. The caller must ensure that the input creates a + /// valid header. + ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const IPv4Address& senderIpAddr, + const MacAddress& targetMacAddr, const IPv4Address& targetIpAddr); + /// A constructor that allocates a new ARP header /// @param[in] opCode ARP message type (ARP request or ARP reply) /// @param[in] senderMacAddr The sender MAC address (will be put in arphdr#senderMacAddr) /// @param[in] targetMacAddr The target MAC address (will be put in arphdr#targetMacAddr) /// @param[in] senderIpAddr The sender IP address (will be put in arphdr#senderIpAddr) /// @param[in] targetIpAddr The target IP address (will be put in arphdr#targetIpAddr) + /// @deprecated This constructor has been deprecated. Please use one of the other overloads. + /// @remarks This constructor zeroes the target MAC address for ARP requests to keep backward compatibility. + PCPP_DEPRECATED("This constructor has been deprecated. Please use one of the other overloads.") ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr, const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr); + /// @brief A constructor that creates an ARP request header. + /// @param arpRequest The ARP request data + explicit ArpLayer(ArpRequest const& arpRequest); + + /// @brief A constructor that creates an ARP reply header. + /// @param arpReply The ARP reply data + explicit ArpLayer(ArpReply const& arpReply); + + /// @brief A constructor that creates a gratuitous ARP request header. + /// @param gratuitousArpRequest The gratuitous ARP request data + explicit ArpLayer(GratuitousArpRequest const& gratuitousArpRequest); + + /// @brief A constructor that creates a gratuitous ARP reply header. + /// @param gratuitousArpReply The gratuitous ARP reply data + explicit ArpLayer(GratuitousArpReply const& gratuitousArpReply); + ~ArpLayer() override = default; /// Get a pointer to the ARP header. Notice this points directly to the data, so every change will change the @@ -80,6 +203,11 @@ namespace pcpp return reinterpret_cast(m_Data); } + /// Get the ARP opcode + /// @return The ARP opcode + /// @remarks The opcode may not be one of the values in @ref ArpOpcode + ArpOpcode getOpcode() const; + /// Get the sender hardware address (SHA) in the form of MacAddress /// @return A MacAddress containing the sender hardware address (SHA) inline MacAddress getSenderMacAddress() const @@ -125,9 +253,12 @@ namespace pcpp /// - @ref arphdr#hardwareSize = 6 /// - @ref arphdr#protocolType = ETHERTYPE_IP (assume IPv4 over ARP) /// - @ref arphdr#protocolSize = 4 (assume IPv4 over ARP) - /// - if it's an ARP request: @ref arphdr#targetMacAddr = MacAddress("00:00:00:00:00:00") void computeCalculateFields() override; + /// @brief Attempts to determine the ARP message type based on the header signature. + /// @return An @ref ArpMessageType representing the ARP message type. + ArpMessageType getMessageType() const; + /// Is this packet an ARP request? bool isRequest() const; diff --git a/Packet++/src/ArpLayer.cpp b/Packet++/src/ArpLayer.cpp index 4971addd37..0ae415d27f 100644 --- a/Packet++/src/ArpLayer.cpp +++ b/Packet++/src/ArpLayer.cpp @@ -6,22 +6,52 @@ namespace pcpp { - - ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr, - const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr) + ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const IPv4Address& senderIpAddr, + const MacAddress& targetMacAddr, const IPv4Address& targetIpAddr) { - const size_t headerLen = sizeof(arphdr); + constexpr size_t headerLen = sizeof(arphdr); m_DataLen = headerLen; - m_Data = new uint8_t[headerLen]; - memset(m_Data, 0, sizeof(headerLen)); + m_Data = new uint8_t[headerLen]{}; // zero-initialized m_Protocol = ARP; arphdr* arpHeader = getArpHeader(); arpHeader->opcode = htobe16(static_cast(opCode)); - targetMacAddr.copyTo(arpHeader->targetMacAddr); senderMacAddr.copyTo(arpHeader->senderMacAddr); - arpHeader->targetIpAddr = targetIpAddr.toInt(); + targetMacAddr.copyTo(arpHeader->targetMacAddr); arpHeader->senderIpAddr = senderIpAddr.toInt(); + arpHeader->targetIpAddr = targetIpAddr.toInt(); + } + + // This constructor zeroes the target MAC address for ARP requests to keep backward compatibility. + ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr, + const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr) + : ArpLayer(opCode, senderMacAddr, senderIpAddr, opCode == ARP_REQUEST ? MacAddress::Zero : targetMacAddr, + targetIpAddr) + {} + + ArpLayer::ArpLayer(ArpRequest const& arpRequest) + : ArpLayer(ARP_REQUEST, arpRequest.senderMacAddr, arpRequest.senderIpAddr, MacAddress::Zero, + arpRequest.targetIpAddr) + {} + + ArpLayer::ArpLayer(ArpReply const& arpReply) + : ArpLayer(ARP_REPLY, arpReply.senderMacAddr, arpReply.senderIpAddr, arpReply.targetMacAddr, + arpReply.targetIpAddr) + {} + + ArpLayer::ArpLayer(GratuitousArpRequest const& gratuitousArpRequest) + : ArpLayer(ARP_REQUEST, gratuitousArpRequest.senderMacAddr, gratuitousArpRequest.senderIpAddr, + MacAddress::Broadcast, gratuitousArpRequest.senderIpAddr) + {} + + ArpLayer::ArpLayer(GratuitousArpReply const& gratuitousArpReply) + : ArpLayer(ARP_REPLY, gratuitousArpReply.senderMacAddr, gratuitousArpReply.senderIpAddr, MacAddress::Broadcast, + gratuitousArpReply.senderIpAddr) + {} + + ArpOpcode ArpLayer::getOpcode() const + { + return static_cast(be16toh(getArpHeader()->opcode)); } void ArpLayer::computeCalculateFields() @@ -31,31 +61,55 @@ namespace pcpp arpHeader->hardwareSize = 6; arpHeader->protocolType = htobe16(PCPP_ETHERTYPE_IP); // assume IPv4 over ARP arpHeader->protocolSize = 4; // assume IPv4 over ARP - if (arpHeader->opcode == htobe16(ARP_REQUEST)) - MacAddress::Zero.copyTo(arpHeader->targetMacAddr); + } + + ArpMessageType ArpLayer::getMessageType() const + { + switch (getOpcode()) + { + case ArpOpcode::ARP_REQUEST: + { + if (getTargetMacAddress() == MacAddress::Broadcast && getSenderIpAddr() == getTargetIpAddr()) + { + return ArpMessageType::GratuitousRequest; + } + return ArpMessageType::Request; + } + case ArpOpcode::ARP_REPLY: + { + if (getTargetMacAddress() == MacAddress::Broadcast && getSenderIpAddr() == getTargetIpAddr()) + { + return ArpMessageType::GratuitousReply; + } + return ArpMessageType::Reply; + } + default: + return ArpMessageType::Unknown; + } } bool ArpLayer::isRequest() const { - return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REQUEST; + return getOpcode() == pcpp::ArpOpcode::ARP_REQUEST; } bool ArpLayer::isReply() const { - return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REPLY; + return getOpcode() == pcpp::ArpOpcode::ARP_REPLY; } std::string ArpLayer::toString() const { - if (be16toh(getArpHeader()->opcode) == ARP_REQUEST) + switch (getOpcode()) { + case ArpOpcode::ARP_REQUEST: return "ARP Layer, ARP request, who has " + getTargetIpAddr().toString() + " ? Tell " + getSenderIpAddr().toString(); - } - else - { + case ArpOpcode::ARP_REPLY: return "ARP Layer, ARP reply, " + getSenderIpAddr().toString() + " is at " + getSenderMacAddress().toString(); + default: + return "ARP Layer, unknown opcode (" + std::to_string(getOpcode()) + ")"; } } diff --git a/Pcap++/src/NetworkUtils.cpp b/Pcap++/src/NetworkUtils.cpp index 75a19e5de8..c6eff7b82c 100644 --- a/Pcap++/src/NetworkUtils.cpp +++ b/Pcap++/src/NetworkUtils.cpp @@ -106,10 +106,8 @@ namespace pcpp Packet arpRequest(100); - MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); - EthLayer ethLayer(sourceMac, destMac); - - ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr); + EthLayer ethLayer(sourceMac, MacAddress::Broadcast); + ArpLayer arpLayer(ArpRequest(sourceMac, sourceIP, ipAddr)); if (!arpRequest.addLayer(ðLayer)) { diff --git a/Tests/Packet++Test/Tests/EthAndArpTests.cpp b/Tests/Packet++Test/Tests/EthAndArpTests.cpp index b8faea90aa..5628c63692 100644 --- a/Tests/Packet++Test/Tests/EthAndArpTests.cpp +++ b/Tests/Packet++Test/Tests/EthAndArpTests.cpp @@ -120,32 +120,145 @@ PTF_TEST_CASE(EthAndArpPacketParsing) PTF_TEST_CASE(ArpPacketCreation) { - pcpp::MacAddress srcMac("6c:f0:49:b2:de:6e"); - pcpp::MacAddress dstMac("ff:ff:ff:ff:ff:ff"); - pcpp::EthLayer ethLayer(srcMac, dstMac, PCPP_ETHERTYPE_ARP); + { + auto const buffer = pcpp_tests::readFileIntoBuffer("PacketExamples/ArpRequestPacket.dat"); - pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, srcMac, srcMac, pcpp::IPv4Address("10.0.0.1"), - pcpp::IPv4Address("10.0.0.138")); + { + pcpp::MacAddress srcMac("6c:f0:49:b2:de:6e"); + pcpp::MacAddress dstMac("ff:ff:ff:ff:ff:ff"); + pcpp::EthLayer ethLayer(srcMac, dstMac, PCPP_ETHERTYPE_ARP); + pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, srcMac, srcMac, pcpp::IPv4Address("10.0.0.1"), + pcpp::IPv4Address("10.0.0.138")); - pcpp::Packet arpRequestPacket(1); - PTF_ASSERT_TRUE(arpRequestPacket.addLayer(ðLayer)); - PTF_ASSERT_TRUE(arpRequestPacket.addLayer(&arpLayer)); - arpRequestPacket.computeCalculateFields(); - PTF_ASSERT_EQUAL(arpRequestPacket.getRawPacket()->getRawDataLen(), 42); - - pcpp::ArpLayer* pArpLayer = arpRequestPacket.getLayerOfType(); - PTF_ASSERT_NOT_NULL(pArpLayer); - - pcpp::arphdr* arpHeader = pArpLayer->getArpHeader(); - PTF_ASSERT_EQUAL(arpHeader->hardwareSize, 6); - PTF_ASSERT_EQUAL(arpHeader->protocolType, htobe16(PCPP_ETHERTYPE_IP)); - - READ_FILE_INTO_BUFFER(1, "PacketExamples/ArpRequestPacket.dat"); - - PTF_ASSERT_EQUAL(bufferLength1, arpRequestPacket.getRawPacket()->getRawDataLen()); - PTF_ASSERT_BUF_COMPARE(arpRequestPacket.getRawPacket()->getRawData(), buffer1, bufferLength1); - - delete[] buffer1; + PTF_ASSERT_TRUE(arpLayer.getMessageType() == pcpp::ArpMessageType::Request); + + pcpp::Packet arpRequestPacket(1); + + PTF_ASSERT_TRUE(arpRequestPacket.addLayer(ðLayer)); + PTF_ASSERT_TRUE(arpRequestPacket.addLayer(&arpLayer)); + arpRequestPacket.computeCalculateFields(); + PTF_ASSERT_EQUAL(arpRequestPacket.getRawPacket()->getRawDataLen(), 42); + + pcpp::ArpLayer* pArpLayer = arpRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(pArpLayer); + + pcpp::arphdr* arpHeader = pArpLayer->getArpHeader(); + PTF_ASSERT_EQUAL(arpHeader->hardwareSize, 6); + PTF_ASSERT_EQUAL(arpHeader->protocolType, htobe16(PCPP_ETHERTYPE_IP)); + + PTF_ASSERT_EQUAL(arpRequestPacket.getRawPacket()->getRawDataLen(), buffer.size()); + PTF_ASSERT_BUF_COMPARE(arpRequestPacket.getRawPacket()->getRawData(), buffer.data(), buffer.size()); + } + + { + pcpp::MacAddress srcMac("6c:f0:49:b2:de:6e"); + pcpp::IPv4Address srcIp("10.0.0.1"); + pcpp::IPv4Address dstIp("10.0.0.138"); + + pcpp::EthLayer ethLayer(srcMac, pcpp::MacAddress::Broadcast, PCPP_ETHERTYPE_ARP); + pcpp::ArpLayer arpLayer(pcpp::ArpRequest(srcMac, srcIp, dstIp)); + + PTF_ASSERT_TRUE(arpLayer.getMessageType() == pcpp::ArpMessageType::Request); + + pcpp::Packet argRequestPacket(1); + PTF_ASSERT_TRUE(argRequestPacket.addLayer(ðLayer)); + PTF_ASSERT_TRUE(argRequestPacket.addLayer(&arpLayer)); + + argRequestPacket.computeCalculateFields(); + PTF_ASSERT_EQUAL(argRequestPacket.getRawPacket()->getRawDataLen(), 42); + + pcpp::ArpLayer* pArpLayer = argRequestPacket.getLayerOfType(); + PTF_ASSERT_NOT_NULL(pArpLayer); + + pcpp::arphdr* arpHeader = pArpLayer->getArpHeader(); + PTF_ASSERT_EQUAL(arpHeader->hardwareSize, 6); + PTF_ASSERT_EQUAL(arpHeader->protocolType, htobe16(PCPP_ETHERTYPE_IP)); + + PTF_ASSERT_EQUAL(argRequestPacket.getRawPacket()->getRawDataLen(), buffer.size()); + PTF_ASSERT_BUF_COMPARE(argRequestPacket.getRawPacket()->getRawData(), buffer.data(), buffer.size()); + } + } + + { + auto buffer = pcpp_tests::readFileIntoBuffer("PacketExamples/ArpResponsePacket.dat"); + + pcpp::MacAddress srcMac("30:46:9a:23:fb:fa"); + pcpp::IPv4Address srcIp("10.0.0.138"); + pcpp::MacAddress dstMac("6c:f0:49:b2:de:6e"); + pcpp::IPv4Address dstIp("10.0.0.1"); + + pcpp::EthLayer ethLayer(pcpp::EthLayer(srcMac, dstMac, PCPP_ETHERTYPE_ARP)); + pcpp::ArpLayer arpLayer(pcpp::ArpReply(srcMac, srcIp, dstMac, dstIp)); + + pcpp::Packet packet(1); + PTF_ASSERT_TRUE(packet.addLayer(ðLayer)); + PTF_ASSERT_TRUE(packet.addLayer(&arpLayer)); + + packet.computeCalculateFields(); + + PTF_ASSERT_EQUAL(arpLayer.getHeaderLen(), 28); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareSize, 6); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolSize, 4); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareType, htobe16(1)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolType, htobe16(PCPP_ETHERTYPE_IP)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->opcode, htobe16(pcpp::ARP_REPLY)); + PTF_ASSERT_TRUE(arpLayer.getMessageType() == pcpp::ArpMessageType::Reply); + PTF_ASSERT_EQUAL(arpLayer.getSenderMacAddress(), srcMac); + PTF_ASSERT_EQUAL(arpLayer.getSenderIpAddr(), srcIp); + PTF_ASSERT_EQUAL(arpLayer.getTargetMacAddress(), dstMac); + PTF_ASSERT_EQUAL(arpLayer.getTargetIpAddr(), dstIp); + + PTF_ASSERT_EQUAL(packet.getRawPacket()->getRawDataLen(), 42); + + pcpp::ArpLayer* pArpLayer = packet.getLayerOfType(); + PTF_ASSERT_NOT_NULL(pArpLayer); + + PTF_ASSERT_EQUAL(buffer.size(), packet.getRawPacket()->getRawDataLen() + 18 /* ethernet trailer */); + PTF_ASSERT_BUF_COMPARE(packet.getRawPacket()->getRawData(), buffer.data(), + packet.getRawPacket()->getRawDataLen()); + } + + { + // TODO: Add an actual packet to test against. + pcpp::MacAddress srcMac("02:00:00:00:00:01"); + pcpp::IPv4Address srcIp("10.0.0.1"); + + pcpp::ArpLayer arpLayer(pcpp::GratuitousArpRequest(srcMac, srcIp)); + arpLayer.computeCalculateFields(); + + PTF_ASSERT_EQUAL(arpLayer.getHeaderLen(), 28); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareSize, 6); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolSize, 4); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareType, htobe16(1)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolType, htobe16(PCPP_ETHERTYPE_IP)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->opcode, htobe16(pcpp::ARP_REQUEST)); + PTF_ASSERT_TRUE(arpLayer.getMessageType() == pcpp::ArpMessageType::GratuitousRequest); + PTF_ASSERT_EQUAL(arpLayer.getSenderMacAddress(), srcMac); + PTF_ASSERT_EQUAL(arpLayer.getSenderIpAddr(), srcIp); + PTF_ASSERT_EQUAL(arpLayer.getTargetMacAddress(), pcpp::MacAddress::Broadcast); + PTF_ASSERT_EQUAL(arpLayer.getTargetIpAddr(), srcIp); + } + + { + // TODO: Add an actual packet to test against. + pcpp::MacAddress srcMac("02:00:00:00:00:01"); + pcpp::IPv4Address srcIp("10.0.0.1"); + + pcpp::ArpLayer arpLayer(pcpp::GratuitousArpReply(srcMac, srcIp)); + arpLayer.computeCalculateFields(); + + PTF_ASSERT_EQUAL(arpLayer.getHeaderLen(), 28); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareSize, 6); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolSize, 4); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->hardwareType, htobe16(1)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->protocolType, htobe16(PCPP_ETHERTYPE_IP)); + PTF_ASSERT_EQUAL(arpLayer.getArpHeader()->opcode, htobe16(pcpp::ARP_REPLY)); + PTF_ASSERT_TRUE(arpLayer.getMessageType() == pcpp::ArpMessageType::GratuitousReply); + PTF_ASSERT_EQUAL(arpLayer.getSenderMacAddress(), srcMac); + PTF_ASSERT_EQUAL(arpLayer.getSenderIpAddr(), srcIp); + PTF_ASSERT_EQUAL(arpLayer.getTargetMacAddress(), pcpp::MacAddress::Broadcast); + PTF_ASSERT_EQUAL(arpLayer.getTargetIpAddr(), srcIp); + } } // ArpPacketCreation PTF_TEST_CASE(EthDot3LayerParsingTest) diff --git a/Tests/Packet++Test/Tests/VlanMplsTests.cpp b/Tests/Packet++Test/Tests/VlanMplsTests.cpp index 44ab437489..4a1bcc141a 100644 --- a/Tests/Packet++Test/Tests/VlanMplsTests.cpp +++ b/Tests/Packet++Test/Tests/VlanMplsTests.cpp @@ -52,8 +52,8 @@ PTF_TEST_CASE(VlanParseAndCreation) pcpp::EthLayer ethLayer(macSrc, macDest); pcpp::VlanLayer firstVlanLayer(666, 1, 5); pcpp::VlanLayer secondVlanLayer(200, 0, 2, PCPP_ETHERTYPE_ARP); - pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, macSrc, pcpp::MacAddress("00:00:00:00:00:00"), - pcpp::IPv4Address("192.168.2.200"), pcpp::IPv4Address("192.168.2.254")); + pcpp::ArpLayer arpLayer( + pcpp::ArpRequest(macSrc, pcpp::IPv4Address("192.168.2.200"), pcpp::IPv4Address("192.168.2.254"))); pcpp::Packet arpWithVlanNew(1); PTF_ASSERT_TRUE(arpWithVlanNew.addLayer(ðLayer)); PTF_ASSERT_TRUE(arpWithVlanNew.addLayer(&firstVlanLayer)); diff --git a/Tests/Packet++Test/Utils/TestUtils.cpp b/Tests/Packet++Test/Utils/TestUtils.cpp index ed30c46c0b..6d7d3b2230 100644 --- a/Tests/Packet++Test/Utils/TestUtils.cpp +++ b/Tests/Packet++Test/Utils/TestUtils.cpp @@ -19,6 +19,44 @@ namespace pcpp_tests return length; } + namespace + { + std::uint8_t hexCharToDigit(char c) + { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + throw std::invalid_argument("Invalid hex character"); + } + + std::uint8_t hexPairToByte(const char* pair) + { + return (hexCharToDigit(pair[0]) << 4) | hexCharToDigit(pair[1]); + } + } // namespace + + std::vector readFileIntoBuffer(const char* filename) + { + int fileLength = getFileLength(filename); + if (fileLength == -1) + throw std::runtime_error(std::string("Failed to open file: ") + filename); + + std::ifstream infile(filename); + if (!infile) + throw std::runtime_error(std::string("Failed to open file: ") + filename); + + std::vector buffer; + char hexPair[2]; // 0 - high, 1 - low + while (infile.read(hexPair, 2)) + { + buffer.push_back(hexPairToByte(hexPair)); + } + return buffer; + } + uint8_t* readFileIntoBuffer(const char* filename, int& bufferLength) { int fileLength = getFileLength(filename); diff --git a/Tests/Packet++Test/Utils/TestUtils.h b/Tests/Packet++Test/Utils/TestUtils.h index 7a452e2616..89c2691110 100644 --- a/Tests/Packet++Test/Utils/TestUtils.h +++ b/Tests/Packet++Test/Utils/TestUtils.h @@ -7,12 +7,14 @@ // clang-format on #include #include +#include namespace pcpp_tests { int getFileLength(const char* filename); + std::vector readFileIntoBuffer(const char* filename); uint8_t* readFileIntoBuffer(const char* filename, int& bufferLength); void printBufferDifferences(const uint8_t* buffer1, size_t buffer1Len, const uint8_t* buffer2, size_t buffer2Len);