133 lines
3.7 KiB
C++
133 lines
3.7 KiB
C++
#include <linux/if.h>
|
|
#include <linux/if_tun.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "TunDevice.hpp"
|
|
|
|
static const size_t TUN_MTU = 1500; // TODO determine this cleanly
|
|
|
|
TunDevice::TunDevice(const std::string& dev)
|
|
{
|
|
struct ifreq ifr;
|
|
int fd;
|
|
|
|
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) {
|
|
throw TunDevice::InitializationError(
|
|
"Cannot open /dev/net/tun", errno, true);
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
|
|
* IFF_TAP - TAP device
|
|
*
|
|
* IFF_NO_PI - Do not provide packet information
|
|
*/
|
|
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
|
if(!dev.empty()) {
|
|
if(dev.size() >= IFNAMSIZ - 2)
|
|
throw TunDevice::InitializationError("Device name is too long.");
|
|
strncpy(ifr.ifr_name, dev.c_str(), IFNAMSIZ-1);
|
|
}
|
|
|
|
if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0){
|
|
close(fd);
|
|
throw TunDevice::InitializationError(
|
|
"Tunnel interface failed [TUNSETIFF]", errno, true);
|
|
}
|
|
_dev_name = ifr.ifr_name;
|
|
_fd = fd;
|
|
|
|
// Bring interface up
|
|
kdebugf("Bringing interface up...\n");
|
|
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Any socket will do
|
|
if(ioctl(sockfd, SIOCGIFFLAGS, (void*) &ifr) < 0) {
|
|
close(fd);
|
|
close(sockfd);
|
|
throw TunDevice::InitializationError(
|
|
"Could not get tunnel interface flags", errno, true);
|
|
}
|
|
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
|
|
|
|
if(ioctl(sockfd, SIOCSIFFLAGS, (void*) &ifr) < 0) {
|
|
close(fd);
|
|
close(sockfd);
|
|
throw TunDevice::InitializationError(
|
|
"Could not bring tunnel interface up", errno, true);
|
|
}
|
|
close(sockfd);
|
|
|
|
// The device is now fully set up
|
|
_poll_fd.fd = _fd;
|
|
_poll_fd.events = POLLIN;
|
|
}
|
|
|
|
TunDevice::~TunDevice() {
|
|
close(_fd);
|
|
}
|
|
|
|
uint16_t TunDevice::get_mtu() const {
|
|
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Any socket will do
|
|
|
|
struct ifreq ifr;
|
|
ifr.ifr_addr.sa_family = AF_INET6;
|
|
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
|
if (ioctl(sockfd, SIOCGIFMTU, (caddr_t)&ifr) < 0)
|
|
return 0;
|
|
close(sockfd);
|
|
return ifr.ifr_mtu;
|
|
}
|
|
|
|
bool TunDevice::set_mtu(uint16_t mtu) {
|
|
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // Any socket will do
|
|
|
|
struct ifreq ifr;
|
|
ifr.ifr_addr.sa_family = AF_INET6;
|
|
strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)-1);
|
|
ifr.ifr_mtu = mtu;
|
|
if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0)
|
|
return false;
|
|
close(sockfd);
|
|
return true;
|
|
}
|
|
|
|
size_t TunDevice::poll_packet(char* read_buffer, size_t buf_size, int timeout) {
|
|
int poll_rc = poll(&_poll_fd, 1, timeout);
|
|
if(poll_rc < 0) {
|
|
if(errno == EINTR) // Interrupt.
|
|
return 0;
|
|
throw TunDevice::NetError(
|
|
"Error polling from interface", errno, true);
|
|
}
|
|
else if(poll_rc == 0 || (_poll_fd.revents & POLLIN) == 0) {
|
|
// Nothing to read
|
|
return 0;
|
|
}
|
|
|
|
return this->read(read_buffer, buf_size);
|
|
}
|
|
|
|
size_t TunDevice::read(char* read_buffer, size_t buf_size) {
|
|
int nread = ::read(_fd, read_buffer, buf_size);
|
|
if(nread < 0) {
|
|
throw TunDevice::NetError(
|
|
"Error reading from interface", errno, true);
|
|
}
|
|
_last_read_size = nread;
|
|
return _last_read_size;
|
|
}
|
|
|
|
size_t TunDevice::write(const char* data, size_t len) {
|
|
int nwritten = ::write(_fd, data, len);
|
|
if(nwritten < 0) {
|
|
throw TunDevice::NetError(
|
|
"Error writing to interface: ", errno, true);
|
|
}
|
|
return nwritten;
|
|
}
|