You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.8 KiB
190 lines
6.8 KiB
#include "test_virtual_tun.h" |
|
#include "../lib/debug_config.h" |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
|
|
#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); |
|
} |