#include "protoCap.h"
#include "protoSocket.h"
#include "protoDebug.h"
#include "protoPktETH.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/dlil.h>
#include <net/ndrv.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
class BsdCap : public ProtoCap
{
public:
BsdCap();
~BsdCap();
bool Open(const char* interfaceName = NULL);
void Close();
bool Send(const char* buffer, unsigned int& numBytes);
bool Recv(char* buffer, unsigned int& numBytes, Direction* direction = NULL);
};
ProtoCap* ProtoCap::Create()
{
return (static_cast<ProtoCap*>(new BsdCap));
}
BsdCap::BsdCap()
{
StartInputNotification(); }
BsdCap::~BsdCap()
{
Close();
}
bool BsdCap::Open(const char* interfaceName)
{
Close(); char buffer[256];
if (NULL == interfaceName)
{
ProtoAddress localAddress;
if (!localAddress.ResolveLocalAddress())
{
PLOG(PL_ERROR, "BsdCap::Open() error: couldn't auto determine local interface\n");
return false;
}
if (!ProtoSocket::GetInterfaceName(localAddress, buffer, 256))
{
PLOG(PL_ERROR, "BsdCap::Open() error: couldn't determine local interface name\n");
return false;
}
interfaceName = buffer;
}
ProtoAddress macAddr;
if (!ProtoSocket::GetInterfaceAddress(interfaceName, ProtoAddress::ETH, if_addr))
PLOG(PL_ERROR, "BsdCap::Open() warning: unable to get MAC address for interface \"%s\"\n", interfaceName);
int ifIndex = ProtoSocket::GetInterfaceIndex(interfaceName);
descriptor = socket(PF_NDRV, SOCK_RAW, 0);
if (descriptor < 0)
{
PLOG(PL_ERROR, "BsdCap::Open() error: socket(PF_NDRV) failure: %s\n", GetErrorString());
return false;
}
struct sockaddr_ndrv sa;
sa.snd_len = sizeof(sa);
sa.snd_family = PF_NDRV;
strncpy((char*)sa.snd_name, interfaceName, IFNAMSIZ);
if (bind(descriptor, (struct sockaddr*)&sa, sa.snd_len) < 0)
{
PLOG(PL_ERROR, "BsdCap::Open() error: bind(%s) failure: %s\n", interfaceName, GetErrorString());
Close();
return false;
}
struct ndrv_demux_desc desc[3];
memset(desc, 0, 3*sizeof(desc));
desc[0].type = NDRV_DEMUXTYPE_ETHERTYPE;
desc[0].length = sizeof(unsigned short);
desc[0].data.ether_type = htons(ProtoPktETH::IP);
desc[1].type = NDRV_DEMUXTYPE_ETHERTYPE;
desc[1].length = sizeof(unsigned short);
desc[1].data.ether_type = htons(ProtoPktETH::IPv6);
desc[2].type = NDRV_DEMUXTYPE_ETHERTYPE;
desc[2].length = sizeof(unsigned short);
desc[2].data.ether_type = htons(ProtoPktETH::ARP);
struct ndrv_protocol_desc ndrvDesc;
ndrvDesc.version = NDRV_PROTOCOL_DESC_VERS;
ndrvDesc.protocol_family = PF_INET;
ndrvDesc.demux_count = 3;
ndrvDesc.demux_list = desc;
if (setsockopt(descriptor, SOL_NDRVPROTO, NDRV_SETDMXSPEC,
&ndrvDesc, sizeof(ndrvDesc)) < 0)
{
PLOG(PL_ERROR, "BsdCap::Open() error: setsockopt(NDRV_SETDMXSPEC) failure: %s\n",
GetErrorString());
Close();
return false;
}
if (!ProtoCap::Open(interfaceName))
{
PLOG(PL_ERROR, "BsdCap::Open() ProtoCap::Open() error\n");
Close();
return false;
}
if_index = ifIndex;
if_type = ProtoNet::ETH; return true;
}
void BsdCap::Close()
{
if (descriptor >= 0)
{
ProtoCap::Close();
close(descriptor);
descriptor = -1;
if_index = 0;
if_type = ProtoNet::IFACE_TYPE_INVALID;
}
}
bool BsdCap::Recv(char* buffer, unsigned int& numBytes, Direction* direction)
{
if (direction) *direction = UNSPECIFIED;
for (;;)
{
ssize_t result = read(descriptor, buffer, numBytes);
if (result < 0)
{
switch (errno)
{
case EINTR:
continue; case EWOULDBLOCK:
numBytes = 0;
return true;
default:
PLOG(PL_ERROR, "BsdCap::Recv() read() error: %s\n",
GetErrorString());
numBytes = 0;
return false;
}
}
else
{
numBytes = result;
break;
}
}
return true;
}
bool BsdCap::Send(const char* buffer, unsigned int& numBytes)
{
UINT16 type;
memcpy(&type, buffer+12, 2);
type = ntohs(type);
if (type <= 0x05dc) {
PLOG(PL_DEBUG, "BsdCap::Send() unsupported 802.3 frame (len = %04x)\n", type);
return false;
}
for (;;)
{
struct sockaddr sockAddr; ssize_t result = sendto(descriptor, buffer+put, buflen-put, 0, &sockAddr, sizeof(sockAddr));
if (result < 0)
{
switch (errno)
{
case EINTR:
continue; case EWOULDBLOCK:
numBytes = 0;
case ENOBUFS:
default:
PLOG(PL_ERROR, "BsdCap::Send() error: %s", GetErrorString());
break;
}
return false;
}
else
{
ASSERT(result == numBytes);
break;
}
}
return true;
}