1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#ifndef PCAPPP_RAW_SOCKET_DEVICE
#define PCAPPP_RAW_SOCKET_DEVICE
/// @file
#include "IpAddress.h"
#include "Device.h"
/**
* \namespace pcpp
* \brief The main namespace for the PcapPlusPlus lib
*/
namespace pcpp
{
/**
* @class RawSocketDevice
* A class that wraps the raw socket functionality. A raw socket is a network socket that allows direct sending and receiving
* of IP packets without any protocol-specific transport layer formatting
* (taken from Wikipedia: https://en.wikipedia.org/wiki/Network_socket#Raw_socket).
* This wrapper class enables creation of a raw socket, binding it to a network interface, and then receiving and sending
* packets on it. Current implementation supports only Windows and Linux because other platforms provide poor support for raw
* sockets making them practically unusable. There are also major differences between Linux and Windows in raw socket
* implementation, let's mention some of the:
* - On Windows administrative privileges are required for raw sockets creation, meaning the process running the code
* has to have these privileges. In Linux 'sudo' is required
* - On Windows raw sockets are implemented in L3, meaning the L2 (Ethernet) layer is omitted by the socket and only L3 and
* up are visible to the user. On Linux raw sockets are implemented on L2, meaning all layers (including the Ethernet
* data) are visible to the user.
* - On Windows sending packets is not supported, a raw socket can only receive packets. On Linux both send and receive are
* supported
* - Linux doesn't require binding to a specific network interface for receiving packets, but it does require binding
* for sending packets. Windows requires binding for receiving packets. For the sake of keeping a unified and simple cross-platform interface
* this class requires binding for both Linux and Windows, on both send and receive
*
* More details about opening the raw socket, receiving and sending packets are explained in the corresponding class methods.
* Raw sockets are supported for both IPv4 and IPv6, so you can create and bind raw sockets to each of the two.
* Also, there is no limit on the number of sockets opened for a specific IP address or network interface, so you can
* create multiple instances of this class and bind all of them to the same interface and IP address.
*/
class RawSocketDevice : public IDevice
{
public:
/**
* An enum for reporting packet receive results
*/
enum RecvPacketResult
{
/** Receive success */
RecvSuccess = 0,
/** Receive timeout - timeout expired without any packets being captured */
RecvTimeout = 1,
/** Receive would block - in non-blocking mode if there are no packets in the rx queue the receive method will return immediately with this return value */
RecvWouldBlock = 2,
/** Receive error, usually will be followed by an error log */
RecvError = 3
};
/*
* A c'tor for this class. This c'tor doesn't create the raw socket, but rather initializes internal structures. The actual
* raw socket creation is done in the open() method. Each raw socket is bound to a network interface which means
* packets will be received and sent from only from this network interface only
* @param[in] interfaceIP The network interface IP to bind the raw socket to. It can be either an IPv4 or IPv6 address
* (both are supported in raw sockets)
*/
explicit RawSocketDevice(const IPAddress& interfaceIP);
/**
* A d'tor for this class. It closes the raw socket if not previously closed by calling close()
*/
~RawSocketDevice();
/**
* Receive a packet on the raw socket. This method has several modes of operation:
* - Blocking/non-blocking - in blocking mode the method will not return until a packet is received on the socket
* or until the timeout expires. In non-blocking mode it will return immediately and in case no packets are on the
* receive queue RawSocketDevice#RecvWouldBlock will be returned. Unless specified otherwise, the default value is
* blocking mode
* - Receive timeout - in blocking mode, the user can set a timeout to wait until a packet is received. If the timeout
* expires and no packets were received, the method will return RawSocketDevice#RecvTimeout. The default value is a
* negative value which means no timeout
*
* There is a slight difference on this method's behavior between Windows and Linux around how packets are received.
* On Linux the received packet contains all layers starting from the L2 (Ethernet). However on Windows raw socket are
* integrated in L3 level so the received packet contains only L3 (IP) layer and up.
* @param[out] rawPacket An empty packet instance where the received packet data will be written to
* @param[in] blocking Indicates whether to run in blocking or non-blocking mode. Default value is blocking
* @param[in] timeout When in blocking mode, specifies the timeout [in seconds] to wait for a packet. If timeout expired
* and no packets were captured the method will return RawSocketDevice#RecvTimeout. Zero or negative values mean no
* timeout. The default value is no timeout
* @return The method returns one on the following values:
* - RawSocketDevice#RecvSuccess is returned if a packet was received successfully
* - RawSocketDevice#RecvTimeout is returned if in blocking mode and timeout expired
* - RawSocketDevice#RecvWouldBlock is returned if in non-blocking mode and no packets were captured
* - RawSocketDevice#RecvError is returned if an error occurred such as device is not opened or the recv operation
* returned some error. A log message will be followed specifying the error and error code
*/
RecvPacketResult receivePacket(RawPacket& rawPacket, bool blocking = true, int timeout = -1);
/**
* Receive packets into a packet vector for a certain amount of time. This method starts a timer and invokes the
* receivePacket() method in blocking mode repeatedly until the timeout expires. All packets received successfully are
* put into a packet vector
* @param[out] packetVec The packet vector to add the received packet to
* @param[in] timeout Timeout in seconds to receive packets on the raw socket
* @param[out] failedRecv Number of receive attempts that failed
* @return The number of packets received successfully
*/
int receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv);
/**
* Send an Ethernet packet to the network. L2 protocols other than Ethernet are not supported in raw sockets.
* The entire packet is sent as is, including the original Ethernet and IP data.
* This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using
* it from other platforms will also return "false" with a corresponding error log message
* @param[in] rawPacket The packet to send
* @return True if packet was sent successfully or false if the socket is not open, if the packet is not Ethernet or
* if there was a failure sending the packet
*/
bool sendPacket(const RawPacket* rawPacket);
/**
* Send a set of Ethernet packets to the network. L2 protocols other than Ethernet are not supported by raw sockets.
* The entire packet is sent as is, including the original Ethernet and IP data.
* This method is only supported in Linux as Windows doesn't allow sending packets from raw sockets. Using it from
* other platforms will return "false" with an appropriate error log message
* @param[in] packetVec The set of packets to send
* @return The number of packets sent successfully. For packets that weren't sent successfully there will be a
* corresponding error message printed to log
*/
int sendPackets(const RawPacketVector& packetVec);
// overridden methods
/**
* Open the device by creating a raw socket and binding it to the network interface specified in the c'tor
* @return True if device was opened successfully, false otherwise with a corresponding error log message
*/
virtual bool open();
/**
* Close the raw socket
*/
virtual void close();
private:
enum SocketFamily
{
Ethernet = 0,
IPv4 = 1,
IPv6 = 2
};
SocketFamily m_SockFamily;
void* m_Socket;
IPAddress m_InterfaceIP;
RecvPacketResult getError(int& errorCode) const;
};
}
#endif // PCAPPP_RAW_SOCKET_DEVICE