Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow gratuitous ARP requests. #1684

Open
wants to merge 16 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Common++/header/MacAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,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:
uint8_t m_Address[6] = { 0 };
Expand Down
2 changes: 2 additions & 0 deletions Common++/src/MacAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
165 changes: 164 additions & 1 deletion Packet++/header/ArpLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Layer.h"
#include "IpAddress.h"
#include "MacAddress.h"
#include "DeprecationUtils.h"

/// @file

Expand Down Expand Up @@ -42,6 +43,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
Expand All @@ -52,6 +54,115 @@ 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) {};
Dimi1010 marked this conversation as resolved.
Show resolved Hide resolved
};

/**
* @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
Expand All @@ -72,17 +183,57 @@ 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,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a new c'tor with the same params as the other c'tor but in a different order?

Copy link
Collaborator Author

@Dimi1010 Dimi1010 Jan 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ctor is because the old one still zeroes the targetMacAddress to keep people who are expecting that from forming invalid arp requests. It just does it in the ctor instead of calculateFields.

The new constructor is there because:

  • The targetMacAddr is no longer zeroed. All parameters are directly written in the header.
  • Parameters are reordered because you cant have two constructors with identical signature. Also they are now ordered in the way they appear in the header.

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,
Dimi1010 marked this conversation as resolved.
Show resolved Hide resolved
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;

/**
Expand All @@ -95,6 +246,13 @@ namespace pcpp
return reinterpret_cast<arphdr*>(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)
Expand Down Expand Up @@ -153,10 +311,15 @@ 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?
*/
Expand Down
86 changes: 70 additions & 16 deletions Packet++/src/ArpLayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint16_t>(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<ArpOpcode>(be16toh(getArpHeader()->opcode));
}

void ArpLayer::computeCalculateFields()
Expand All @@ -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()) + ")";
}
}

Expand Down
6 changes: 2 additions & 4 deletions Pcap++/src/NetworkUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(&ethLayer))
{
Expand Down
Loading
Loading