#include "RawSocketDevice.h"
#include "EndianPortable.h"
#ifdef __linux__
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/if_ether.h>
#include <netpacket/packet.h>
#include <ifaddrs.h>
#include <net/if.h>
#endif
#include <string.h>
#include "Logger.h"
#include "IpUtils.h"
#include "SystemUtils.h"
#include "Packet.h"
#include "EthLayer.h"
namespace pcpp
{
#define RAW_SOCKET_BUFFER_LEN 65536
#if defined(_WIN32)
#ifndef SIO_RCVALL
# define SIO_RCVALL 0x98000001
#endif
class WinSockInitializer
{
private:
static bool m_IsInitialized;
public:
static void initialize()
{
if (m_IsInitialized)
return;
WSADATA wsaData;
int res = WSAStartup(MAKEWORD(2,2), &wsaData);
if (res != 0)
{
PCPP_LOG_ERROR("WSAStartup failed with error code: " << res);
m_IsInitialized = false;
}
m_IsInitialized = true;
}
};
bool WinSockInitializer::m_IsInitialized = false;
#endif
struct SocketContainer
{
#if defined(_WIN32)
SOCKET fd;
#elif defined(__linux__)
int fd;
int interfaceIndex;
std::string interfaceName;
#endif
};
RawSocketDevice::RawSocketDevice(const IPAddress& interfaceIP) : IDevice(), m_Socket(nullptr)
{
#if defined(_WIN32)
WinSockInitializer::initialize();
m_InterfaceIP = interfaceIP;
m_SockFamily = (m_InterfaceIP.getType() == IPAddress::IPv4AddressType ? IPv4 : IPv6);
#elif defined(__linux__)
m_InterfaceIP = interfaceIP;
m_SockFamily = Ethernet;
#else
m_SockFamily = Ethernet;
#endif
}
RawSocketDevice::~RawSocketDevice()
{
close();
}
RawSocketDevice::RecvPacketResult RawSocketDevice::receivePacket(RawPacket& rawPacket, bool blocking, int timeout)
{
#if defined(_WIN32)
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return RecvError;
}
SOCKET fd = ((SocketContainer*)m_Socket)->fd;
char* buffer = new char[RAW_SOCKET_BUFFER_LEN];
memset(buffer, 0, RAW_SOCKET_BUFFER_LEN);
if (timeout < 0)
timeout = 0;
u_long blockingMode = (blocking? 0 : 1);
ioctlsocket(fd, FIONBIO, &blockingMode);
DWORD timeoutVal = timeout * 1000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));
int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0);
if (bufferLen < 0)
{
delete [] buffer;
int errorCode = 0;
RecvPacketResult error = getError(errorCode);
if (error == RecvError)
PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode);
return error;
}
if (bufferLen > 0)
{
timeval time;
gettimeofday(&time, NULL);
rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_DLT_RAW1);
return RecvSuccess;
}
PCPP_LOG_ERROR("Buffer length is zero");
delete [] buffer;
return RecvError;
#elif defined(__linux__)
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return RecvError;
}
int fd = ((SocketContainer*)m_Socket)->fd;
char* buffer = new char[RAW_SOCKET_BUFFER_LEN];
memset(buffer, 0, RAW_SOCKET_BUFFER_LEN);
if (timeout < 0)
timeout = 0;
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
{
delete [] buffer;
PCPP_LOG_ERROR("Cannot get socket flags");
return RecvError;
}
flags = (blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK));
if (fcntl(fd, F_SETFL, flags) != 0)
{
delete [] buffer;
PCPP_LOG_ERROR("Cannot set socket non-blocking flag");
return RecvError;
}
struct timeval timeoutVal;
timeoutVal.tv_sec = timeout;
timeoutVal.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeoutVal, sizeof(timeoutVal));
int bufferLen = recv(fd, buffer, RAW_SOCKET_BUFFER_LEN, 0);
if (bufferLen < 0)
{
delete [] buffer;
int errorCode = errno;
RecvPacketResult error = getError(errorCode);
if (error == RecvError)
PCPP_LOG_ERROR("Error reading from recvfrom. Error code is " << errorCode);
return error;
}
if (bufferLen > 0)
{
timeval time;
gettimeofday(&time, NULL);
rawPacket.setRawData((const uint8_t*)buffer, bufferLen, time, LINKTYPE_ETHERNET);
return RecvSuccess;
}
PCPP_LOG_ERROR("Buffer length is zero");
delete [] buffer;
return RecvError;
#else
PCPP_LOG_ERROR("Raw socket are not supported on this platform");
return RecvError;
#endif
}
int RawSocketDevice::receivePackets(RawPacketVector& packetVec, int timeout, int& failedRecv)
{
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return 0;
}
long curSec, curNsec;
clockGetTime(curSec, curNsec);
int packetCount = 0;
failedRecv = 0;
long timeoutSec = curSec + timeout;
while (curSec < timeoutSec)
{
RawPacket* rawPacket = new RawPacket();
if (receivePacket(*rawPacket, true, timeoutSec-curSec) == RecvSuccess)
{
packetVec.pushBack(rawPacket);
packetCount++;
}
else
{
failedRecv++;
delete rawPacket;
}
clockGetTime(curSec, curNsec);
}
return packetCount;
}
bool RawSocketDevice::sendPacket(const RawPacket* rawPacket)
{
#if defined(_WIN32)
PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows");
return 0;
#elif defined(__linux__)
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return false;
}
Packet packet((RawPacket*)rawPacket, OsiModelDataLinkLayer);
if (!packet.isPacketOfType(pcpp::Ethernet))
{
PCPP_LOG_ERROR("Can't send non-Ethernet packets");
return false;
}
int fd = ((SocketContainer*)m_Socket)->fd;
sockaddr_ll addr;
memset(&addr, 0, sizeof(struct sockaddr_ll));
addr.sll_family = htobe16(PF_PACKET);
addr.sll_protocol = htobe16(ETH_P_ALL);
addr.sll_halen = 6;
addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex;
EthLayer* ethLayer = packet.getLayerOfType<EthLayer>();
MacAddress dstMac = ethLayer->getDestMac();
dstMac.copyTo((uint8_t*)&(addr.sll_addr));
if (::sendto(fd, ((RawPacket*)rawPacket)->getRawData(), ((RawPacket*)rawPacket)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
PCPP_LOG_ERROR("Failed to send packet. Error was: '" << strerror(errno) << "'");
return false;
}
return true;
#else
PCPP_LOG_ERROR("Raw socket are not supported on this platform");
return 0;
#endif
}
int RawSocketDevice::sendPackets(const RawPacketVector& packetVec)
{
#if defined(_WIN32)
PCPP_LOG_ERROR("Sending packets with raw socket are not supported on Windows");
return false;
#elif defined(__linux__)
if (!isOpened())
{
PCPP_LOG_ERROR("Device is not open");
return 0;
}
int fd = ((SocketContainer*)m_Socket)->fd;
sockaddr_ll addr;
memset(&addr, 0, sizeof(struct sockaddr_ll));
addr.sll_family = htobe16(PF_PACKET);
addr.sll_protocol = htobe16(ETH_P_ALL);
addr.sll_halen = 6;
addr.sll_ifindex = ((SocketContainer*)m_Socket)->interfaceIndex;
int sendCount = 0;
for (RawPacketVector::ConstVectorIterator iter = packetVec.begin(); iter != packetVec.end(); iter++)
{
Packet packet(*iter, OsiModelDataLinkLayer);
if (!packet.isPacketOfType(pcpp::Ethernet))
{
PCPP_LOG_DEBUG("Can't send non-Ethernet packets");
continue;
}
EthLayer* ethLayer = packet.getLayerOfType<EthLayer>();
MacAddress dstMac = ethLayer->getDestMac();
dstMac.copyTo((uint8_t*)&(addr.sll_addr));
if (::sendto(fd, (*iter)->getRawData(), (*iter)->getRawDataLen(), 0, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
PCPP_LOG_DEBUG("Failed to send packet. Error was: '" << strerror(errno) << "'");
continue;
}
sendCount++;
}
return sendCount;
#else
PCPP_LOG_ERROR("Raw socket are not supported on this platform");
return false;
#endif
}
bool RawSocketDevice::open()
{
#if defined(_WIN32)
if (!m_InterfaceIP.isValid())
{
PCPP_LOG_ERROR("IP address is not valid");
return false;
}
int family = (m_SockFamily == IPv4 ? AF_INET : AF_INET6);
SOCKET fd = socket(family, SOCK_RAW, IPPROTO_IP);
if ((int)fd == SOCKET_ERROR)
{
int error = WSAGetLastError();
std::string additionalMessage = "";
if (error == WSAEACCES)
additionalMessage = ", you may not be running with administrative privileges which is required for opening raw sockets on Windows";
PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << error << " " << additionalMessage);
return false;
}
void* localAddr = NULL;
struct sockaddr_in localAddrIPv4;
struct sockaddr_in6 localAddrIPv6;
size_t localAddrSize = 0;
if (m_SockFamily == IPv4)
{
localAddrIPv4.sin_family = family;
int res = inet_pton(family, m_InterfaceIP.toString().c_str(), &localAddrIPv4.sin_addr.s_addr);
if (res <= 0)
{
PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format");
closesocket(fd);
return false;
}
localAddrIPv4.sin_port = 0; localAddr = &localAddrIPv4;
localAddrSize = sizeof(localAddrIPv4);
}
else
{
localAddrIPv6.sin6_family = family;
int res = inet_pton(AF_INET6, m_InterfaceIP.toString().c_str(), &localAddrIPv6.sin6_addr.s6_addr);
if (res <= 0)
{
PCPP_LOG_ERROR("inet_pton failed, probably IP address provided is in bad format");
closesocket(fd);
return false;
}
localAddrIPv6.sin6_port = 0; localAddrIPv6.sin6_scope_id = 0;
localAddr = &localAddrIPv6;
localAddrSize = sizeof(localAddrIPv6);
}
if (bind(fd, (struct sockaddr *)localAddr, localAddrSize) == SOCKET_ERROR)
{
PCPP_LOG_ERROR("Failed to bind to interface. Error code was '" << WSAGetLastError() << "'");
closesocket(fd);
return false;
}
int n = 1;
DWORD dwBytesRet;
if (WSAIoctl(fd, SIO_RCVALL, &n, sizeof(n), NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR)
{
PCPP_LOG_ERROR("Call to WSAIotcl(" << std::hex << SIO_RCVALL << ") failed with error code " << WSAGetLastError());
closesocket(fd);
return false;
}
m_Socket = new SocketContainer();
((SocketContainer*)m_Socket)->fd = fd;
m_DeviceOpened = true;
return true;
#elif defined(__linux__)
#if defined(__ANDROID_API__) && __ANDROID_API__ < 24
PCPP_LOG_ERROR("Raw sockets aren't supported in Android API < 24");
return false;
#else
if (!m_InterfaceIP.isValid())
{
PCPP_LOG_ERROR("IP address is not valid");
return false;
}
int fd = socket(AF_PACKET, SOCK_RAW, htobe16(ETH_P_ALL));
if (fd < 0)
{
PCPP_LOG_ERROR("Failed to create raw socket. Error code was " << errno);
return false;
}
struct ifaddrs* addrs;
getifaddrs(&addrs);
std::string ifaceName = "";
int ifaceIndex = -1;
for (struct ifaddrs* curAddr = addrs; curAddr != NULL; curAddr = curAddr->ifa_next)
{
if (curAddr->ifa_addr && (curAddr->ifa_flags & IFF_UP))
{
if (curAddr->ifa_addr->sa_family == AF_INET)
{
struct sockaddr_in* sockAddr = (struct sockaddr_in*)(curAddr->ifa_addr);
char addrAsCharArr[32];
inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin_addr), addrAsCharArr, sizeof(addrAsCharArr));
if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr))
{
ifaceName = curAddr->ifa_name;
ifaceIndex = if_nametoindex(curAddr->ifa_name);
}
}
else if (curAddr->ifa_addr->sa_family == AF_INET6)
{
struct sockaddr_in6* sockAddr = (struct sockaddr_in6*)(curAddr->ifa_addr);
char addrAsCharArr[40];
inet_ntop(curAddr->ifa_addr->sa_family, (void *)&(sockAddr->sin6_addr), addrAsCharArr, sizeof(addrAsCharArr));
if (!strcmp(m_InterfaceIP.toString().c_str(), addrAsCharArr))
{
ifaceName = curAddr->ifa_name;
ifaceIndex = if_nametoindex(curAddr->ifa_name);
}
}
}
}
freeifaddrs(addrs);
if (ifaceName == "" || ifaceIndex < 0)
{
PCPP_LOG_ERROR("Cannot detect interface name or index from IP address");
::close(fd);
return false;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifaceName.c_str());
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1)
{
PCPP_LOG_ERROR("Cannot bind raw socket to interface '" << ifaceName << "'");
::close(fd);
return false;
}
m_Socket = new SocketContainer();
((SocketContainer*)m_Socket)->fd = fd;
((SocketContainer*)m_Socket)->interfaceIndex = ifaceIndex;
((SocketContainer*)m_Socket)->interfaceName = ifaceName;
m_DeviceOpened = true;
return true;
#endif
#else
PCPP_LOG_ERROR("Raw socket are not supported on this platform");
return false;
#endif
}
void RawSocketDevice::close()
{
if (m_Socket != nullptr && isOpened())
{
SocketContainer* sockContainer = (SocketContainer*)m_Socket;
#if defined(_WIN32)
closesocket(sockContainer->fd);
#elif defined(__linux__)
::close(sockContainer->fd);
#endif
delete sockContainer;
m_Socket = nullptr;
m_DeviceOpened = false;
}
}
RawSocketDevice::RecvPacketResult RawSocketDevice::getError(int& errorCode) const
{
#if defined(_WIN32)
errorCode = WSAGetLastError();
if (errorCode == WSAEWOULDBLOCK)
return RecvWouldBlock;
if (errorCode == WSAETIMEDOUT)
return RecvTimeout;
return RecvError;
#elif defined(__linux__)
if ((errorCode == EAGAIN) || (errorCode == EWOULDBLOCK))
return RecvWouldBlock;
return RecvError;
#else
return RecvError;
#endif
}
}