#include "protoVif.h"
#include "protoNet.h"
#include "protoDebug.h"
#ifdef UNIX
#include <unistd.h>
#include <net/if.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#endif
#ifdef LINUX
extern "C"
{
#include <linux/if_tun.h>
}
#endif
class UnixVif : public ProtoVif
{
public:
UnixVif();
~UnixVif();
bool Open(const char* vifName, const ProtoAddress& ipAddr, unsigned int maskLen);
void Close();
bool SetHardwareAddress(const ProtoAddress& ethAddr);
bool SetARP(bool status);
bool Write(const char* buffer, unsigned int numBytes);
bool Read(char* buffer, unsigned int& numBytes);
};
ProtoVif* ProtoVif::Create()
{
return static_cast<ProtoVif*>(new UnixVif);
}
UnixVif::UnixVif()
{
}
UnixVif::~UnixVif()
{
}
bool UnixVif::Open(const char* vifName, const ProtoAddress& ipAddr, unsigned int maskLen)
{
Close(); #ifdef LINUX
#ifdef __ANDROID__
const char* devName = "/dev//tun";
#else
const char* devName = "/dev/net/tun_flowctl";
#endif descriptor = open(devName, O_RDWR);
if (descriptor < 0)
{
const char* devName = "/dev/net/tun";
if ((descriptor = open(devName, O_RDWR)) < 0)
{
PLOG(PL_ERROR,"UnixVif::Open(%s) error: open(\"%s\") failed: %s\n", vifName, devName, GetErrorString());
return false;
}
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, vifName, IFNAMSIZ);
if (ioctl(descriptor, TUNSETIFF, &ifr) < 0)
{
PLOG(PL_ERROR, "UnixVif::Open(%s) error: ioctl(TUNSETIFF) failed: %s\n", vifName, GetErrorString());
Close();
return false;
}
strncpy(vif_name, vifName, VIF_NAME_MAX);
#endif
#ifdef MACOSX
char devName[PATH_MAX];
for (int i = 0; i < 256; i++)
{
snprintf(devName, PATH_MAX, "/dev/tap%d", i);
if ((descriptor = open(devName, O_RDWR)) < 0)
{
PLOG(PL_ERROR,"UnixVif::Open() error: open(\"%s\") failed: %s\n", devName, GetErrorString());
continue;
}
snprintf(vif_name, VIF_NAME_MAX, "tap%d", i);
break;
}
if (INVALID_HANDLE == descriptor)
{
PLOG(PL_ERROR,"UnixVif::Open() error: no TAP device available!\n");
return false;
}
#endif
char cmd[1024];
#ifdef __ANDROID__
snprintf(cmd, 1024, "ip link set %s up", vif_name);
#else
if (ipAddr.IsValid()) snprintf(cmd, 1024, "/sbin/ifconfig %s %s/%d up", vif_name, ipAddr.GetHostString(), maskLen);
else snprintf(cmd, 1024, "/sbin/ifconfig %s up", vif_name);
#endif if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::Open(%s) error: \"%s\n\" failed: %s\n", vifName, cmd, GetErrorString());
Close();
return false;
}
#ifdef __ANDROID__
if (ipAddr.IsValid())
{
if (!ProtoNet::AddInterfaceAddress(vif_name, ipAddr, maskLen))
{
PLOG(PL_ERROR, "UnixVif::Open(%s) error: unable to assign IP address!\n", vifName);
Close();
return false;
}
}
#endif if (!ProtoNet::GetInterfaceAddress(vif_name, ProtoAddress::ETH, hw_addr))
PLOG(PL_ERROR, "UnixVif::Open(%s) error: unable to get ETH address!\n", vif_name);
if (!ProtoChannel::Open())
{
PLOG(PL_ERROR, "UnixVif::Open(%s) error: couldn't install ProtoChannel\n", vif_name);
Close();
return false;
}
else
{
return true;
}
}
void UnixVif::Close()
{
ProtoChannel::Close();
close(descriptor);
descriptor = INVALID_HANDLE;
}
bool UnixVif::SetARP(bool status)
{
char cmd[1024];
snprintf(cmd, 1024, "/sbin/ifconfig %s %s", vif_name, status ? "arp" : "-arp");
if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::SetARP() error: \"%s\n\" failed: %s\n", cmd, GetErrorString());
return false;
}
return true;
}
bool UnixVif::SetHardwareAddress(const ProtoAddress& ethAddr)
{
if (ProtoAddress::ETH != ethAddr.GetType())
{
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress() error: invalid address type!\n");
return false;
}
const UINT8* addr = (const UINT8*)ethAddr.GetRawHostAddress();
char cmd[1024];
#ifdef LINUX
snprintf(cmd, 1024, "/sbin/ifconfig %s down", vif_name);
if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress(%s) error: \"%s\n\" failed: %s\n", cmd, GetErrorString());
return false;
}
snprintf(cmd, 1024, "/sbin/ifconfig %s hw ether %02x:%02x:%02x:%02x:%02x:%02x",
vif_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress(%s) error: \"%s\n\" failed: %s\n", cmd, GetErrorString());
return false;
}
snprintf(cmd, 1024, "/sbin/ifconfig %s up", vif_name);
if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress(%s) error: \"%s\n\" failed: %s\n", cmd, GetErrorString());
return false;
}
#endif
#ifdef MACOSX
snprintf(cmd, 1024, "/sbin/ifconfig %s lladdr %02x:%02x:%02x:%02x:%02x:%02x",
vif_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
if (system(cmd) < 0)
{
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress(%s) error: \"%s\n\" failed: %s\n", cmd, GetErrorString());
return false;
}
#endif
if (!ProtoNet::GetInterfaceAddress(vif_name, ProtoAddress::ETH, hw_addr))
PLOG(PL_ERROR, "UnixVif::SetHardwareAddress() error: unable to get ETH address for virtual interface \"%s\"!\n", vif_name);
return true;
}
bool UnixVif::Write(const char* buffer, unsigned int numBytes)
{
int nWritten = write(descriptor, buffer, numBytes);
if (nWritten != (int)numBytes)
{
PLOG(PL_ERROR,"UnixVif::Write() error: write() failure:%s\n", GetErrorString());
return false;
}
return true;
}
bool UnixVif::Read(char* buffer, unsigned int& numBytes)
{
int result = read(descriptor, buffer, numBytes);
if (result < 0)
{
if (EAGAIN != errno)
PLOG(PL_ERROR, "UnixVif::Read() error read() failure: %s\n", GetErrorString());
numBytes = 0;
return false;
}
numBytes = result;
return true;
}