#include #include #include #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; if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) { perror("Tun device: cannot open /dev/net/tun: "); exit(1); } 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) { fprintf(stderr, "Tun device: device name is too long.\n"); exit(1); } strncpy(ifr.ifr_name, dev.c_str(), IFNAMSIZ-1); } if(ioctl(fd, TUNSETIFF, (void *) &ifr) < 0){ close(fd); perror("Tun device: tunnel interface failed [TUNSETIFF]: "); exit(1); } _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); perror("Tun device: could not get tunnel interface flags: "); exit(1); } ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if(ioctl(sockfd, SIOCSIFFLAGS, (void*) &ifr) < 0) { close(fd); close(sockfd); perror("Tun device: could not bring tunnel interface up: "); exit(1); } 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; perror("Tun device: error polling from interface: "); exit(1); } 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) { perror("Tun device: error reading from interface: "); exit(1); } _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) { perror("Tun device: error writing to interface: "); exit(1); } return nwritten; }