Created
June 25, 2025 20:19
-
-
Save tomberek/ba8c9e5253e6bf9245aff13a0bd01820 to your computer and use it in GitHub Desktop.
network
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <vector> | |
#include <cstring> | |
#include <unistd.h> | |
#include <sys/wait.h> | |
#include <sys/mount.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <sched.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <net/if.h> | |
#include <linux/if_tun.h> | |
#include <sys/ioctl.h> | |
#include <cstdlib> | |
#include <fstream> | |
#include <filesystem> | |
#define STACK_SIZE (1024 * 1024) | |
namespace fs = std::filesystem; | |
// Error handling macro | |
#define CHECK(call) \ | |
if ((call) == -1) { \ | |
perror(#call); \ | |
exit(EXIT_FAILURE); \ | |
} | |
// Execute command and check return status | |
int execute_command(const std::string& cmd, bool silent = false) { | |
if (!silent) std::cout << "Executing: " << cmd << std::endl; | |
int status = system(cmd.c_str()); | |
if (WIFEXITED(status)) { | |
int exit_status = WEXITSTATUS(status); | |
if (exit_status != 0) { | |
if (!silent) std::cerr << "Command failed with exit code: " << exit_status << " - " << cmd << std::endl; | |
return -1; | |
} | |
} else if (WIFSIGNALED(status)) { | |
if (!silent) std::cerr << "Command killed by signal: " << WTERMSIG(status) << " - " << cmd << std::endl; | |
return -1; | |
} | |
return 0; | |
} | |
// Get default gateway interface - more reliable method | |
std::string get_default_interface() { | |
// Try reading route information | |
std::ifstream route_file("/proc/net/route"); | |
if (!route_file) { | |
std::cerr << "Failed to open /proc/net/route" << std::endl; | |
return "eth0"; | |
} | |
std::string line; | |
// Skip header | |
std::getline(route_file, line); | |
while (std::getline(route_file, line)) { | |
std::istringstream iss(line); | |
std::string iface, destination, gateway, flags; | |
iss >> iface >> destination >> gateway >> flags; | |
// Check for default route (destination 00000000) and non-zero gateway | |
if (destination == "00000000" && gateway != "00000000") { | |
// Check if interface exists | |
if (fs::exists("/sys/class/net/" + iface)) { | |
return iface; | |
} | |
} | |
} | |
// Fallback: try common interface names | |
const std::vector<std::string> common_interfaces = {"eth0", "enp0s3", "wlan0", "en0"}; | |
for (const auto& iface : common_interfaces) { | |
if (fs::exists("/sys/class/net/" + iface)) { | |
return iface; | |
} | |
} | |
return "eth0"; // Final fallback | |
} | |
// Clean up network resources | |
void cleanup_resources() { | |
std::cout << "Cleaning up resources..." << std::endl; | |
execute_command("ip link delete veth-host 2>/dev/null", true); | |
execute_command("ip netns delete netns-exec 2>/dev/null", true); | |
execute_command("rm -rf /etc/netns/netns-exec 2>/dev/null", true); | |
} | |
// Create veth pair | |
void create_veth_pair() { | |
// Remove existing veth pair if it exists | |
execute_command("ip link delete veth-host 2>/dev/null", true); | |
if (execute_command("ip link add veth-host type veth peer name veth-ns") != 0) { | |
std::cerr << "Failed to create veth pair" << std::endl; | |
exit(EXIT_FAILURE); | |
} | |
execute_command("ip link set veth-host up", true); | |
} | |
// Move interface to namespace | |
void move_interface_to_ns(pid_t pid) { | |
std::string cmd = "ip link set veth-ns netns " + std::to_string(pid); | |
if (execute_command(cmd) != 0) { | |
std::cerr << "Failed to move interface to namespace" << std::endl; | |
exit(EXIT_FAILURE); | |
} | |
} | |
// Configure host network | |
void configure_host_network() { | |
if (execute_command("ip addr add 10.0.0.1/24 dev veth-host") != 0) { | |
std::cerr << "Failed to configure host IP" << std::endl; | |
} | |
if (execute_command("ip link set veth-host up") != 0) { | |
std::cerr << "Failed to bring up host interface" << std::endl; | |
} | |
} | |
// Enable NAT with better configuration | |
void enable_nat(const std::string& external_if) { | |
// Enable IP forwarding | |
if (execute_command("sysctl -w net.ipv4.ip_forward=1") != 0) { | |
std::cerr << "Failed to enable IP forwarding" << std::endl; | |
} | |
// Remove existing rules if they exist | |
execute_command("iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -j MASQUERADE 2>/dev/null", true); | |
execute_command("iptables -D FORWARD -i veth-host -o " + external_if + " -j ACCEPT 2>/dev/null", true); | |
execute_command("iptables -D FORWARD -i " + external_if + " -o veth-host -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null", true); | |
// Add new rules - more specific | |
if (execute_command("iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE") != 0) { | |
std::cerr << "Failed to set up NAT" << std::endl; | |
} | |
if (execute_command("iptables -A FORWARD -i veth-host -o " + external_if + " -j ACCEPT") != 0) { | |
std::cerr << "Failed to set up forwarding rule" << std::endl; | |
} | |
if (execute_command("iptables -A FORWARD -i " + external_if + " -o veth-host -m state --state RELATED,ESTABLISHED -j ACCEPT") != 0) { | |
std::cerr << "Failed to set up reverse forwarding rule" << std::endl; | |
} | |
// Add rule to allow traffic from namespace | |
if (execute_command("iptables -A FORWARD -s 10.0.0.0/24 -j ACCEPT") != 0) { | |
std::cerr << "Failed to set up general forwarding rule" << std::endl; | |
} | |
} | |
// Configure network in namespace | |
void configure_namespace_network(pid_t pid) { | |
// Create network namespace entry | |
execute_command("mkdir -p /var/run/netns", true); | |
std::string ns_cmd = "ln -sf /proc/" + std::to_string(pid) + "/ns/net /var/run/netns/netns-exec"; | |
execute_command(ns_cmd, true); | |
// Configure networking inside namespace | |
execute_command("ip netns exec netns-exec ip link set lo up", true); | |
execute_command("ip netns exec netns-exec ip addr add 10.0.0.2/24 dev veth-ns", true); | |
execute_command("ip netns exec netns-exec ip link set veth-ns up", true); | |
execute_command("ip netns exec netns-exec ip route add default via 10.0.0.1", true); | |
// Add DNS configuration | |
execute_command("mkdir -p /etc/netns/netns-exec", true); | |
execute_command("echo 'nameserver 1.1.1.1' > /etc/netns/netns-exec/resolv.conf", true); | |
} | |
// Child process function | |
int child_func(void* arg) { | |
char** argv = static_cast<char**>(arg); | |
// Create new mount namespace | |
CHECK(unshare(CLONE_NEWNS)); | |
// Mount proc and sys | |
if (mount("proc", "/proc", "proc", MS_NOEXEC|MS_NOSUID|MS_NODEV, NULL) < 0) { | |
perror("mount proc"); | |
} | |
if (mount("sysfs", "/sys", "sysfs", MS_NOEXEC|MS_NOSUID|MS_NODEV, NULL) < 0) { | |
perror("mount sys"); | |
} | |
// Execute the command | |
execvp(argv[0], argv); | |
perror("execvp"); | |
return EXIT_FAILURE; | |
} | |
int main(int argc, char* argv[]) { | |
if (argc < 2) { | |
std::cerr << "Usage: " << argv[0] << " <command> [args...]" << std::endl; | |
return EXIT_FAILURE; | |
} | |
// Register cleanup handler | |
atexit(cleanup_resources); | |
// Detect external interface | |
std::string external_if = get_default_interface(); | |
std::cout << "Using external interface: " << external_if << std::endl; | |
// Allocate stack for child | |
char* stack = new char[STACK_SIZE]; | |
if (!stack) { | |
perror("new"); | |
return EXIT_FAILURE; | |
} | |
// Prepare arguments | |
char** child_argv = &argv[1]; | |
std::cout << "Creating veth pair..." << std::endl; | |
create_veth_pair(); | |
std::cout << "Cloning child process..." << std::endl; | |
pid_t pid = clone(child_func, stack + STACK_SIZE, | |
CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | SIGCHLD, | |
child_argv); | |
CHECK(pid); | |
std::cout << "Moving interface to child namespace (PID: " << pid << ")..." << std::endl; | |
move_interface_to_ns(pid); | |
std::cout << "Configuring namespace network..." << std::endl; | |
configure_namespace_network(pid); | |
std::cout << "Configuring host network..." << std::endl; | |
configure_host_network(); | |
std::cout << "Enabling NAT..." << std::endl; | |
enable_nat(external_if); | |
std::cout << "\n===== Host Network Configuration =====" << std::endl; | |
execute_command("ip addr show"); | |
execute_command("ip route show"); | |
execute_command("iptables -t nat -L -n -v"); | |
execute_command("iptables -L -n -v"); | |
std::cout << "\n===== Namespace Network Configuration =====" << std::endl; | |
execute_command("ip netns exec netns-exec ip addr show"); | |
execute_command("ip netns exec netns-exec ip route show"); | |
execute_command("ip netns exec netns-exec cat /etc/resolv.conf"); | |
std::cout << "\nProcess " << pid << " running in network namespace with external access" << std::endl; | |
std::cout << "To test connectivity, run in namespace: ping 1.1.1.1" << std::endl; | |
// Wait for child | |
waitpid(pid, nullptr, 0); | |
// Clean up | |
delete[] stack; | |
std::cout << "Child process exited" << std::endl; | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment