Skip to content

Commit 231cba2

Browse files
authored
Allow gratuitous ARP requests. (#1684)
1 parent 4f650ff commit 231cba2

File tree

10 files changed

+390
-51
lines changed

10 files changed

+390
-51
lines changed

Common++/header/MacAddress.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ namespace pcpp
143143

144144
/// A static value representing a zero value of MAC address, meaning address of value "00:00:00:00:00:00"
145145
static MacAddress Zero;
146+
/// A static value representing a broadcast MAC address, meaning address of value "ff:ff:ff:ff:ff:ff"
147+
static MacAddress Broadcast;
146148

147149
private:
148150
std::array<uint8_t, 6> m_Address{};

Common++/src/MacAddress.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace pcpp
55

66
MacAddress MacAddress::Zero(0, 0, 0, 0, 0, 0);
77

8+
MacAddress MacAddress::Broadcast(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
9+
810
std::string MacAddress::toString() const
911
{
1012
char str[19];

Examples/ArpSpoofing/main.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ pcpp::MacAddress getMacAddress(const pcpp::IPv4Address& ipAddr, pcpp::PcapLiveDe
6666
pcpp::MacAddress macSrc = pDevice->getMacAddress();
6767
pcpp::MacAddress macDst(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
6868
pcpp::EthLayer ethLayer(macSrc, macDst, static_cast<uint16_t>(PCPP_ETHERTYPE_ARP));
69-
pcpp::ArpLayer arpLayer(pcpp::ARP_REQUEST, pDevice->getMacAddress(), pDevice->getMacAddress(),
70-
pDevice->getIPv4Address(), ipAddr);
69+
pcpp::ArpLayer arpLayer(pcpp::ArpRequest(pDevice->getMacAddress(), pDevice->getIPv4Address(), ipAddr));
7170

7271
arpRequest.addLayer(&ethLayer);
7372
arpRequest.addLayer(&arpLayer);
@@ -137,15 +136,15 @@ void doArpSpoofing(pcpp::PcapLiveDevice* pDevice, const pcpp::IPv4Address& gatew
137136
// Create ARP reply for the gateway
138137
pcpp::Packet gwArpReply(500);
139138
pcpp::EthLayer gwEthLayer(deviceMacAddress, gatewayMacAddr, static_cast<uint16_t>(PCPP_ETHERTYPE_ARP));
140-
pcpp::ArpLayer gwArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), gatewayMacAddr, victimAddr, gatewayAddr);
139+
pcpp::ArpLayer gwArpLayer(pcpp::ArpReply(pDevice->getMacAddress(), victimAddr, gatewayMacAddr, gatewayAddr));
141140
gwArpReply.addLayer(&gwEthLayer);
142141
gwArpReply.addLayer(&gwArpLayer);
143142
gwArpReply.computeCalculateFields();
144143

145144
// Create ARP reply for the victim
146145
pcpp::Packet victimArpReply(500);
147146
pcpp::EthLayer victimEthLayer(deviceMacAddress, victimMacAddr, static_cast<uint16_t>(PCPP_ETHERTYPE_ARP));
148-
pcpp::ArpLayer victimArpLayer(pcpp::ARP_REPLY, pDevice->getMacAddress(), victimMacAddr, gatewayAddr, victimAddr);
147+
pcpp::ArpLayer victimArpLayer(pcpp::ArpReply(pDevice->getMacAddress(), gatewayAddr, victimMacAddr, victimAddr));
149148
victimArpReply.addLayer(&victimEthLayer);
150149
victimArpReply.addLayer(&victimArpLayer);
151150
victimArpReply.computeCalculateFields();

Packet++/header/ArpLayer.h

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Layer.h"
44
#include "IpAddress.h"
55
#include "MacAddress.h"
6+
#include "DeprecationUtils.h"
67

78
/// @file
89

@@ -37,6 +38,7 @@ namespace pcpp
3738
uint32_t targetIpAddr;
3839
};
3940
#pragma pack(pop)
41+
static_assert(sizeof(arphdr) == 28, "arphdr size is not 28 bytes");
4042

4143
/// An enum for ARP message type
4244
enum ArpOpcode
@@ -45,6 +47,97 @@ namespace pcpp
4547
ARP_REPLY = 0x0002 ///< ARP reply (response)
4648
};
4749

50+
/// @brief An enum representing the ARP message type
51+
enum class ArpMessageType
52+
{
53+
Unknown, ///< Unknown ARP message type
54+
Request, ///< ARP request
55+
Reply, ///< ARP reply
56+
GratuitousRequest, ///< Gratuitous ARP request
57+
GratuitousReply, ///< Gratuitous ARP reply
58+
};
59+
60+
/// @brief A struct representing the build data for an ARP request
61+
///
62+
/// An ARP request is a message sent by a machine to request the MAC address of another machine on the network.
63+
struct ArpRequest
64+
{
65+
MacAddress senderMacAddr;
66+
IPv4Address senderIpAddr;
67+
IPv4Address targetIpAddr;
68+
69+
/// @brief Construct a new Arp Request object
70+
/// @param senderMacAddress The MAC address of the machine sending the query.
71+
/// @param senderIPAddress The IP address of the machine sending the query.
72+
/// @param targetIPAddress The IP address of the target machine being queried.
73+
ArpRequest(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress,
74+
IPv4Address const& targetIPAddress)
75+
: senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress), targetIpAddr(targetIPAddress) {};
76+
};
77+
78+
/// @brief A struct representing the build data for an ARP reply
79+
///
80+
/// An ARP reply is a message sent by a machine in response to an ARP request. It contains the MAC address of the
81+
/// answering machine, and is sent to the IP/MAC address of the machine that sent the original ARP request.
82+
struct ArpReply
83+
{
84+
MacAddress senderMacAddr;
85+
IPv4Address senderIpAddr;
86+
MacAddress targetMacAddr;
87+
IPv4Address targetIpAddr;
88+
89+
/// @brief Construct a new Arp Reply object
90+
/// @param senderMacAddress The MAC address of the machine sending the reply.
91+
/// @param senderIPAddress The IP address of the machine sending the reply.
92+
/// @param targetMacAddress The MAC address of the target machine being replied to.
93+
/// @param targetIPAddress The IP address of the target machine being replied to.
94+
/// @remarks The target machine is considered the machine that sent the original ARP request.
95+
ArpReply(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress,
96+
MacAddress const& targetMacAddress, IPv4Address const& targetIPAddress)
97+
: senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress), targetMacAddr(targetMacAddress),
98+
targetIpAddr(targetIPAddress) {};
99+
};
100+
101+
/// @brief A struct representing the build data for a gratuitous ARP request
102+
///
103+
/// A gratuitous ARP request is an ARP request that is sent by a machine to announce its presence on the network.
104+
/// It is an ARP request that has both the sender and target IP addresses set to the IP address of the machine
105+
/// and the target MAC address set to the broadcast address. Normally such a request will not receive a reply.
106+
///
107+
/// These requests can be used to update ARP caches on other machines on the network, or to help in detecting IP
108+
/// address conflicts.
109+
struct GratuitousArpRequest
110+
{
111+
MacAddress senderMacAddr;
112+
IPv4Address senderIpAddr;
113+
114+
/// @brief Construct a new Gratuitous Arp Request object
115+
/// @param senderMacAddress The MAC address of the machine sending the gratuitous ARP request.
116+
/// @param senderIPAddress The IP address of the machine sending the gratuitous ARP request.
117+
/// @remarks The target MAC address is set to the broadcast address and the target IP address is set to the
118+
/// sender's.
119+
GratuitousArpRequest(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress)
120+
: senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress) {};
121+
};
122+
123+
/// @brief A struct representing the build data a gratuitous ARP reply
124+
///
125+
/// A gratuitous ARP reply is an ARP reply that is sent by a machine to announce its presence on the network.
126+
/// It is gratuitous in the sense that it is not in response to an ARP request, but sent unsolicited to the network.
127+
struct GratuitousArpReply
128+
{
129+
MacAddress senderMacAddr;
130+
IPv4Address senderIpAddr;
131+
132+
/// @brief Construct a new Gratuitous Arp Reply object
133+
/// @param senderMacAddress The MAC address of the machine sending the gratuitous ARP reply.
134+
/// @param senderIPAddress The IP address of the machine sending the gratuitous ARP reply.
135+
/// @remarks The target MAC address is set to the broadcast address and the target IP address is set to the
136+
/// sender's.
137+
GratuitousArpReply(MacAddress const& senderMacAddress, IPv4Address const& senderIPAddress)
138+
: senderMacAddr(senderMacAddress), senderIpAddr(senderIPAddress) {};
139+
};
140+
48141
/// @class ArpLayer
49142
/// Represents an ARP protocol layer. Currently only IPv4 ARP messages are supported
50143
class ArpLayer : public Layer
@@ -61,15 +154,45 @@ namespace pcpp
61154
m_DataLen = sizeof(arphdr);
62155
}
63156

157+
/// @brief A constructor that creates an ARP header
158+
/// @param[in] opCode ARP message type (ARP request or ARP reply)
159+
/// @param[in] senderMacAddr The sender MAC address (will be put in arphdr#senderMacAddr)
160+
/// @param[in] senderIpAddr The sender IP address (will be put in arphdr#senderIpAddr)
161+
/// @param[in] targetMacAddr The target MAC address (will be put in arphdr#targetMacAddr)
162+
/// @param[in] targetIpAddr The target IP address (will be put in arphdr#targetIpAddr)
163+
/// @remarks No validation is done on the input parameters. The caller must ensure that the input creates a
164+
/// valid header.
165+
ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const IPv4Address& senderIpAddr,
166+
const MacAddress& targetMacAddr, const IPv4Address& targetIpAddr);
167+
64168
/// A constructor that allocates a new ARP header
65169
/// @param[in] opCode ARP message type (ARP request or ARP reply)
66170
/// @param[in] senderMacAddr The sender MAC address (will be put in arphdr#senderMacAddr)
67171
/// @param[in] targetMacAddr The target MAC address (will be put in arphdr#targetMacAddr)
68172
/// @param[in] senderIpAddr The sender IP address (will be put in arphdr#senderIpAddr)
69173
/// @param[in] targetIpAddr The target IP address (will be put in arphdr#targetIpAddr)
174+
/// @deprecated This constructor has been deprecated. Please use one of the other overloads.
175+
/// @remarks This constructor zeroes the target MAC address for ARP requests to keep backward compatibility.
176+
PCPP_DEPRECATED("This constructor has been deprecated. Please use one of the other overloads.")
70177
ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr,
71178
const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr);
72179

180+
/// @brief A constructor that creates an ARP request header.
181+
/// @param arpRequest The ARP request data
182+
explicit ArpLayer(ArpRequest const& arpRequest);
183+
184+
/// @brief A constructor that creates an ARP reply header.
185+
/// @param arpReply The ARP reply data
186+
explicit ArpLayer(ArpReply const& arpReply);
187+
188+
/// @brief A constructor that creates a gratuitous ARP request header.
189+
/// @param gratuitousArpRequest The gratuitous ARP request data
190+
explicit ArpLayer(GratuitousArpRequest const& gratuitousArpRequest);
191+
192+
/// @brief A constructor that creates a gratuitous ARP reply header.
193+
/// @param gratuitousArpReply The gratuitous ARP reply data
194+
explicit ArpLayer(GratuitousArpReply const& gratuitousArpReply);
195+
73196
~ArpLayer() override = default;
74197

75198
/// 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
80203
return reinterpret_cast<arphdr*>(m_Data);
81204
}
82205

206+
/// Get the ARP opcode
207+
/// @return The ARP opcode
208+
/// @remarks The opcode may not be one of the values in @ref ArpOpcode
209+
ArpOpcode getOpcode() const;
210+
83211
/// Get the sender hardware address (SHA) in the form of MacAddress
84212
/// @return A MacAddress containing the sender hardware address (SHA)
85213
inline MacAddress getSenderMacAddress() const
@@ -125,9 +253,12 @@ namespace pcpp
125253
/// - @ref arphdr#hardwareSize = 6
126254
/// - @ref arphdr#protocolType = ETHERTYPE_IP (assume IPv4 over ARP)
127255
/// - @ref arphdr#protocolSize = 4 (assume IPv4 over ARP)
128-
/// - if it's an ARP request: @ref arphdr#targetMacAddr = MacAddress("00:00:00:00:00:00")
129256
void computeCalculateFields() override;
130257

258+
/// @brief Attempts to determine the ARP message type based on the header signature.
259+
/// @return An @ref ArpMessageType representing the ARP message type.
260+
ArpMessageType getMessageType() const;
261+
131262
/// Is this packet an ARP request?
132263
bool isRequest() const;
133264

Packet++/src/ArpLayer.cpp

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,52 @@
66

77
namespace pcpp
88
{
9-
10-
ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr,
11-
const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr)
9+
ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const IPv4Address& senderIpAddr,
10+
const MacAddress& targetMacAddr, const IPv4Address& targetIpAddr)
1211
{
13-
const size_t headerLen = sizeof(arphdr);
12+
constexpr size_t headerLen = sizeof(arphdr);
1413
m_DataLen = headerLen;
15-
m_Data = new uint8_t[headerLen];
16-
memset(m_Data, 0, sizeof(headerLen));
14+
m_Data = new uint8_t[headerLen]{}; // zero-initialized
1715
m_Protocol = ARP;
1816

1917
arphdr* arpHeader = getArpHeader();
2018
arpHeader->opcode = htobe16(static_cast<uint16_t>(opCode));
21-
targetMacAddr.copyTo(arpHeader->targetMacAddr);
2219
senderMacAddr.copyTo(arpHeader->senderMacAddr);
23-
arpHeader->targetIpAddr = targetIpAddr.toInt();
20+
targetMacAddr.copyTo(arpHeader->targetMacAddr);
2421
arpHeader->senderIpAddr = senderIpAddr.toInt();
22+
arpHeader->targetIpAddr = targetIpAddr.toInt();
23+
}
24+
25+
// This constructor zeroes the target MAC address for ARP requests to keep backward compatibility.
26+
ArpLayer::ArpLayer(ArpOpcode opCode, const MacAddress& senderMacAddr, const MacAddress& targetMacAddr,
27+
const IPv4Address& senderIpAddr, const IPv4Address& targetIpAddr)
28+
: ArpLayer(opCode, senderMacAddr, senderIpAddr, opCode == ARP_REQUEST ? MacAddress::Zero : targetMacAddr,
29+
targetIpAddr)
30+
{}
31+
32+
ArpLayer::ArpLayer(ArpRequest const& arpRequest)
33+
: ArpLayer(ARP_REQUEST, arpRequest.senderMacAddr, arpRequest.senderIpAddr, MacAddress::Zero,
34+
arpRequest.targetIpAddr)
35+
{}
36+
37+
ArpLayer::ArpLayer(ArpReply const& arpReply)
38+
: ArpLayer(ARP_REPLY, arpReply.senderMacAddr, arpReply.senderIpAddr, arpReply.targetMacAddr,
39+
arpReply.targetIpAddr)
40+
{}
41+
42+
ArpLayer::ArpLayer(GratuitousArpRequest const& gratuitousArpRequest)
43+
: ArpLayer(ARP_REQUEST, gratuitousArpRequest.senderMacAddr, gratuitousArpRequest.senderIpAddr,
44+
MacAddress::Broadcast, gratuitousArpRequest.senderIpAddr)
45+
{}
46+
47+
ArpLayer::ArpLayer(GratuitousArpReply const& gratuitousArpReply)
48+
: ArpLayer(ARP_REPLY, gratuitousArpReply.senderMacAddr, gratuitousArpReply.senderIpAddr, MacAddress::Broadcast,
49+
gratuitousArpReply.senderIpAddr)
50+
{}
51+
52+
ArpOpcode ArpLayer::getOpcode() const
53+
{
54+
return static_cast<ArpOpcode>(be16toh(getArpHeader()->opcode));
2555
}
2656

2757
void ArpLayer::computeCalculateFields()
@@ -31,31 +61,55 @@ namespace pcpp
3161
arpHeader->hardwareSize = 6;
3262
arpHeader->protocolType = htobe16(PCPP_ETHERTYPE_IP); // assume IPv4 over ARP
3363
arpHeader->protocolSize = 4; // assume IPv4 over ARP
34-
if (arpHeader->opcode == htobe16(ARP_REQUEST))
35-
MacAddress::Zero.copyTo(arpHeader->targetMacAddr);
64+
}
65+
66+
ArpMessageType ArpLayer::getMessageType() const
67+
{
68+
switch (getOpcode())
69+
{
70+
case ArpOpcode::ARP_REQUEST:
71+
{
72+
if (getTargetMacAddress() == MacAddress::Broadcast && getSenderIpAddr() == getTargetIpAddr())
73+
{
74+
return ArpMessageType::GratuitousRequest;
75+
}
76+
return ArpMessageType::Request;
77+
}
78+
case ArpOpcode::ARP_REPLY:
79+
{
80+
if (getTargetMacAddress() == MacAddress::Broadcast && getSenderIpAddr() == getTargetIpAddr())
81+
{
82+
return ArpMessageType::GratuitousReply;
83+
}
84+
return ArpMessageType::Reply;
85+
}
86+
default:
87+
return ArpMessageType::Unknown;
88+
}
3689
}
3790

3891
bool ArpLayer::isRequest() const
3992
{
40-
return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REQUEST;
93+
return getOpcode() == pcpp::ArpOpcode::ARP_REQUEST;
4194
}
4295

4396
bool ArpLayer::isReply() const
4497
{
45-
return be16toh(getArpHeader()->opcode) == pcpp::ArpOpcode::ARP_REPLY;
98+
return getOpcode() == pcpp::ArpOpcode::ARP_REPLY;
4699
}
47100

48101
std::string ArpLayer::toString() const
49102
{
50-
if (be16toh(getArpHeader()->opcode) == ARP_REQUEST)
103+
switch (getOpcode())
51104
{
105+
case ArpOpcode::ARP_REQUEST:
52106
return "ARP Layer, ARP request, who has " + getTargetIpAddr().toString() + " ? Tell " +
53107
getSenderIpAddr().toString();
54-
}
55-
else
56-
{
108+
case ArpOpcode::ARP_REPLY:
57109
return "ARP Layer, ARP reply, " + getSenderIpAddr().toString() + " is at " +
58110
getSenderMacAddress().toString();
111+
default:
112+
return "ARP Layer, unknown opcode (" + std::to_string(getOpcode()) + ")";
59113
}
60114
}
61115

Pcap++/src/NetworkUtils.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,8 @@ namespace pcpp
106106

107107
Packet arpRequest(100);
108108

109-
MacAddress destMac(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
110-
EthLayer ethLayer(sourceMac, destMac);
111-
112-
ArpLayer arpLayer(ARP_REQUEST, sourceMac, destMac, sourceIP, ipAddr);
109+
EthLayer ethLayer(sourceMac, MacAddress::Broadcast);
110+
ArpLayer arpLayer(ArpRequest(sourceMac, sourceIP, ipAddr));
113111

114112
if (!arpRequest.addLayer(&ethLayer))
115113
{

0 commit comments

Comments
 (0)