Skip to content

Instantly share code, notes, and snippets.

@tomberek
Created June 25, 2025 20:19
Show Gist options
  • Save tomberek/ba8c9e5253e6bf9245aff13a0bd01820 to your computer and use it in GitHub Desktop.
Save tomberek/ba8c9e5253e6bf9245aff13a0bd01820 to your computer and use it in GitHub Desktop.
network
#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