#include "test_virtual_tun.h" #include "../lib/debug_config.h" #include #include #include #include #include #include #ifndef DEBUG_CATEGORY_TEST #define DEBUG_CATEGORY_TEST (1 << 30) #endif // Create virtual TUN with bidirectional pipes struct virtual_tun* virtual_tun_create(const char* ifname) { if (!ifname) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "NULL interface name provided"); return NULL; } struct virtual_tun* vtun = calloc(1, sizeof(struct virtual_tun)); if (!vtun) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate virtual TUN"); return NULL; } // Copy interface name strncpy(vtun->ifname, ifname, sizeof(vtun->ifname) - 1); vtun->ifname[sizeof(vtun->ifname) - 1] = '\0'; vtun->enabled = true; // Create pipes for bidirectional communication // read_pipe: [0] = read end, [1] = write end (for packets from network) // write_pipe: [0] = read end, [1] = write end (for packets to network) if (pipe(vtun->read_pipe) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create read pipe: %s", strerror(errno)); free(vtun); return NULL; } if (pipe(vtun->write_pipe) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create write pipe: %s", strerror(errno)); close(vtun->read_pipe[0]); close(vtun->read_pipe[1]); free(vtun); return NULL; } // Set non-blocking mode on pipe ends for (int i = 0; i < 2; i++) { int flags = fcntl(vtun->read_pipe[i], F_GETFL, 0); if (flags == -1 || fcntl(vtun->read_pipe[i], F_SETFL, flags | O_NONBLOCK) < 0) { DEBUG_WARN(DEBUG_CATEGORY_TEST, "Failed to set non-blocking on read pipe[%d]: %s", i, strerror(errno)); } flags = fcntl(vtun->write_pipe[i], F_GETFL, 0); if (flags == -1 || fcntl(vtun->write_pipe[i], F_SETFL, flags | O_NONBLOCK) < 0) { DEBUG_WARN(DEBUG_CATEGORY_TEST, "Failed to set non-blocking on write pipe[%d]: %s", i, strerror(errno)); } } DEBUG_INFO(DEBUG_CATEGORY_TEST, "Virtual TUN created: %s (read_fd=%d, write_fd=%d)", vtun->ifname, vtun->read_pipe[0], vtun->write_pipe[1]); return vtun; } // Cleanup virtual TUN void virtual_tun_destroy(struct virtual_tun* vtun) { if (!vtun) return; DEBUG_INFO(DEBUG_CATEGORY_TEST, "Destroying virtual TUN: %s (stats: sent=%zu packets/%zu bytes, recv=%zu packets/%zu bytes)", vtun->ifname, vtun->stats.packets_sent, vtun->stats.bytes_sent, vtun->stats.packets_received, vtun->stats.bytes_received); // Close all pipe file descriptors if (vtun->read_pipe[0] >= 0) close(vtun->read_pipe[0]); if (vtun->read_pipe[1] >= 0) close(vtun->read_pipe[1]); if (vtun->write_pipe[0] >= 0) close(vtun->write_pipe[0]); if (vtun->write_pipe[1] >= 0) close(vtun->write_pipe[1]); free(vtun); } // Get file descriptor for reading (simulates TUN device read) int virtual_tun_get_read_fd(struct virtual_tun* vtun) { if (!vtun || !vtun->enabled) return -1; return vtun->read_pipe[0]; // Read end of read pipe } // Get file descriptor for writing (simulates TUN device write) int virtual_tun_get_write_fd(struct virtual_tun* vtun) { if (!vtun || !vtun->enabled) return -1; return vtun->write_pipe[1]; // Write end of write pipe } // Inject packet into virtual TUN (simulates packet from network) int virtual_tun_inject_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len) { if (!vtun || !vtun->enabled || !packet || len == 0) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for packet injection"); return -1; } if (len > VIRTUAL_TUN_MAX_PACKET_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Packet too large: %zu > %d", len, VIRTUAL_TUN_MAX_PACKET_SIZE); return -1; } // Write packet to read pipe (simulates packet arriving from network) ssize_t written = write(vtun->read_pipe[1], packet, len); if (written < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to inject packet: %s", strerror(errno)); } return -1; } vtun->stats.packets_received++; vtun->stats.bytes_received += written; DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Injected packet into %s: %zu bytes", vtun->ifname, written); return 0; } // Read packet from virtual TUN (captures packets going to network) ssize_t virtual_tun_read_packet(struct virtual_tun* vtun, uint8_t* buffer, size_t max_len) { if (!vtun || !vtun->enabled || !buffer || max_len == 0) { return -1; } // Read from write pipe (captures packets going to network) ssize_t bytes_read = read(vtun->write_pipe[0], buffer, max_len); if (bytes_read < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to read packet: %s", strerror(errno)); } return -1; } if (bytes_read > 0) { vtun->stats.packets_sent++; vtun->stats.bytes_sent += bytes_read; DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Read packet from %s: %zu bytes", vtun->ifname, bytes_read); } return bytes_read; } // Write packet to virtual TUN (sends packet to network) ssize_t virtual_tun_write_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len) { if (!vtun || !vtun->enabled || !packet || len == 0) { return -1; } if (len > VIRTUAL_TUN_MAX_PACKET_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Packet too large: %zu > %d", len, VIRTUAL_TUN_MAX_PACKET_SIZE); return -1; } // Write packet to write pipe (sends to network) ssize_t written = write(vtun->write_pipe[1], packet, len); if (written < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to write packet: %s", strerror(errno)); } return -1; } DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Wrote packet to %s: %zu bytes", vtun->ifname, written); return written; } // Get virtual TUN statistics void virtual_tun_get_stats(struct virtual_tun* vtun, size_t* packets_sent, size_t* packets_received, size_t* bytes_sent, size_t* bytes_received) { if (!vtun) return; if (packets_sent) *packets_sent = vtun->stats.packets_sent; if (packets_received) *packets_received = vtun->stats.packets_received; if (bytes_sent) *bytes_sent = vtun->stats.bytes_sent; if (bytes_received) *bytes_received = vtun->stats.bytes_received; } // Reset virtual TUN statistics void virtual_tun_reset_stats(struct virtual_tun* vtun) { if (!vtun) return; memset(&vtun->stats, 0, sizeof(vtun->stats)); DEBUG_INFO(DEBUG_CATEGORY_TEST, "Reset statistics for virtual TUN: %s", vtun->ifname); }