diff --git a/TunDevice.cpp b/TunDevice.cpp index 4b69a46..f3cfcf3 100644 --- a/TunDevice.cpp +++ b/TunDevice.cpp @@ -4,18 +4,23 @@ #include #include #include +#include #include #include #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, err; + int fd; - if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) - throw TunDevice::InitializationError("Cannot open /dev/net/tun", 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)); @@ -31,16 +36,50 @@ TunDevice::TunDevice(const std::string& dev) strncpy(ifr.ifr_name, dev.c_str(), IFNAMSIZ-1); } - if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){ + if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0){ close(fd); throw TunDevice::InitializationError( - "Tunnel interface failed [TUNSETIFF]", err - ); + "Tunnel interface failed [TUNSETIFF]", errno, true); } _dev_name = ifr.ifr_name; _fd = fd; + + // The device is now fully set up + _poll_fd.fd = _fd; + _poll_fd.events = POLLIN; } TunDevice::~TunDevice() { close(_fd); } + +size_t TunDevice::read_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; + } + + 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_packet(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; +} diff --git a/TunDevice.hpp b/TunDevice.hpp index 9b1fd89..89a0820 100644 --- a/TunDevice.hpp +++ b/TunDevice.hpp @@ -1,14 +1,26 @@ #pragma once #include +#include #include "util.hpp" class TunDevice { public: class InitializationError : public MsgException { public: - InitializationError(const std::string& msg, int code=0) - : MsgException(msg, code) {} + InitializationError( + const std::string& msg, + int code=0, + bool is_perror=false) + : MsgException(msg, code, is_perror) {} + }; + class NetError : public MsgException { + public: + NetError( + const std::string& msg, + int code=0, + bool is_perror=false) + : MsgException(msg, code, is_perror) {} }; TunDevice(const std::string& dev); @@ -16,7 +28,18 @@ class TunDevice { const std::string& get_dev_name() const { return _dev_name; } int get_fd() const { return _fd; } + + /* Reads a packet from the device. + * Timeouts after `timeout` ms, or never if `timeout < 0`. + * Upon timeout, returns 0. + */ + size_t read_packet(char* read_buffer, size_t buf_size, int timeout=-1); + + size_t write_packet(const char* data, size_t len); + private: int _fd; std::string _dev_name; + struct pollfd _poll_fd; + size_t _last_read_size; }; diff --git a/util.cpp b/util.cpp index 41b4258..185cd1c 100644 --- a/util.cpp +++ b/util.cpp @@ -1,13 +1,19 @@ #include +#include #include "util.hpp" -MsgException::MsgException(const std::string& msg, int code) +MsgException::MsgException(const std::string& msg, int code, bool is_perror) : _msg(msg), _code(code) { _what = _msg; if(_code != 0) { + if(is_perror) { + _what += ": "; + _what += strerror(errno); + } + char remainder[20]; sprintf(remainder, " (code %d)", _code); _what += remainder; diff --git a/util.hpp b/util.hpp index 2c97ab3..2adff8a 100644 --- a/util.hpp +++ b/util.hpp @@ -3,10 +3,14 @@ #include #include -// MsgException -- an exception bearing a passed explanation message +/** MsgException -- an exception bearing a passed explanation message + * + * If `is_perror` is true, then the `strerror` corresponding message is appened + * to the message in `what()`. + */ class MsgException : public std::exception { public: - MsgException(const std::string& msg, int code=0); + MsgException(const std::string& msg, int code=0, bool is_perror=false); int errcode() const noexcept { return _code; } const char* what() const noexcept { return _what.c_str(); };