From 6356dee0c6dd80f5842b4ca60591f048ea486e98 Mon Sep 17 00:00:00 2001 From: Robert Swiecki Date: Tue, 14 Apr 2026 14:26:28 +0200 Subject: [PATCH] Makefile/all: make libnl3 optional --- Makefile | 4 +- net.cc | 26 +++++++++++ nstun/nstun.cc | 114 ++++++++++++++++++++++-------------------------- tests/nstun.cfg | 2 +- 4 files changed, 83 insertions(+), 63 deletions(-) diff --git a/Makefile b/Makefile index 710d619..da68cc5 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ CXXFLAGS += $(USER_DEFINES) $(COMMON_FLAGS) $(PROTOBUF_CFLAGS) -I. \ LDFLAGS += -pie -Wl,-z,noexecstack -lpthread $(PROTOBUF_LIBS) ifeq ($(NL3_EXISTS), yes) - CXXFLAGS += $(shell pkg-config --cflags libnl-route-3.0) + CXXFLAGS += $(shell pkg-config --cflags libnl-route-3.0) -DHAVE_LIBNL3 LDFLAGS += $(shell pkg-config --libs libnl-route-3.0) endif @@ -157,11 +157,13 @@ test: $(BIN) $(call run_test, ./nsjail --config tests/pasta-nat.cfg -q -t 3 -- /bin/bash -c 'sleep 0.2; ping -W 1 -c 1 8.8.8.8 && exit 77', 77) $(call run_test, ./nsjail --config tests/pasta-port-mappings.cfg -q -t 3 -- /bin/bash -c 'sleep 0.2; { netstat -tan | grep LISTEN; } && exit 77', 77) +ifeq ($(NL3_EXISTS), yes) # --- Traffic rules tests --- $(call run_test, ./nsjail --config tests/traffic-rules.cfg -q -t 1 -- /bin/bash -c 'sleep 10', 137) $(call run_test, ./nsjail --config tests/traffic-drop-tcp4.cfg -q -t 1 -- /bin/bash -c 'sleep 10', 137) $(call run_test, ./nsjail --config tests/traffic-drop-udp6.cfg -q -t 1 -- /bin/bash -c 'sleep 10', 137) $(call run_test, ./nsjail --config tests/traffic-mixed.cfg -q -t 1 -- /bin/bash -c 'sleep 10', 137) +endif # --- IPv4-only NAT tests --- $(call run_test, ./nsjail --config tests/nat-ip4-only.cfg -q -t 3 --cap CAP_NET_RAW -- /bin/bash -c 'ping -4 -W 1 -c 1 8.8.8.8 && exit 77', 77) diff --git a/net.cc b/net.cc index 8425f53..488154b 100644 --- a/net.cc +++ b/net.cc @@ -30,9 +30,11 @@ #include #include #include +#ifdef HAVE_LIBNL3 #include #include #include +#endif #include #include #include @@ -126,10 +128,13 @@ namespace net { #define IFACE_NAME "vs" #include +#ifdef HAVE_LIBNL3 #include #include #include +#endif +#ifdef HAVE_LIBNL3 static bool cloneIface(nsj_t* nsj, struct nl_sock* sk, struct nl_cache* link_cache, int pid) { struct rtnl_link* rmv = rtnl_link_macvlan_alloc(); if (rmv == nullptr) { @@ -177,7 +182,9 @@ static bool cloneIface(nsj_t* nsj, struct nl_sock* sk, struct nl_cache* link_cac rtnl_link_put(rmv); return true; } +#endif +#ifdef HAVE_LIBNL3 static bool moveToNs( const std::string& iface, struct nl_sock* sk, struct nl_cache* link_cache, pid_t pid) { LOG_D("Moving interface '%s' into netns=%d", iface.c_str(), (int)pid); @@ -209,6 +216,7 @@ static bool moveToNs( rtnl_link_put(orig_link); return true; } +#endif static void pastaProcess(nsj_t* nsj, int pid, int err_pipe) { if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) { @@ -431,6 +439,7 @@ bool initParent(nsj_t* nsj, pid_t pid, int pipefd) { if (!nsj->njc.clone_newnet()) { return true; } +#ifdef HAVE_LIBNL3 struct nl_sock* sk = nl_socket_alloc(); if (!sk) { LOG_E("Could not allocate socket with nl_socket_alloc()"); @@ -467,6 +476,14 @@ bool initParent(nsj_t* nsj, pid_t pid, int pipefd) { } return true; +#else + if (!nsj->njc.iface_own().empty() || !nsj->njc.macvlan_iface().empty()) { + LOG_E("Features requiring Netlink (iface_own, macvlan) are requested but nsjail " + "was built without libnl3 support"); + return false; + } + return true; +#endif } static bool isSocket(int fd) { @@ -768,6 +785,7 @@ static bool parseIp6(const std::string& ip_str, struct in6_addr* addr, int* mask return inet_pton(AF_INET6, ip.c_str(), addr) == 1; } +#ifdef HAVE_LIBNL3 static bool applyTrafficRule( struct nl_sock* sk, const nsjail::NsJailConfig_TrafficRule& rule, int family) { struct rtnl_rule* rtnl_rule = rtnl_rule_alloc(); @@ -855,6 +873,7 @@ static bool applyTrafficRule( rtnl_rule_put(rtnl_rule); return true; } +#endif bool initNsFromChild(nsj_t* nsj) { if (!nsj->njc.clone_newnet()) { @@ -869,6 +888,7 @@ bool initNsFromChild(nsj_t* nsj) { return false; } +#ifdef HAVE_LIBNL3 if (nsj->njc.traffic_rule_size() > 0) { struct nl_sock* sk = nl_socket_alloc(); if (!sk) { @@ -891,6 +911,12 @@ bool initNsFromChild(nsj_t* nsj) { } } } +#else + if (nsj->njc.traffic_rule_size() > 0) { + LOG_E("Traffic rules requested but nsjail was built without libnl3 support"); + return false; + } +#endif return true; } diff --git a/nstun/nstun.cc b/nstun/nstun.cc index a6702c1..d880f04 100644 --- a/nstun/nstun.cc +++ b/nstun/nstun.cc @@ -6,8 +6,6 @@ #include #include #include -#include -#include #include #include #include @@ -18,6 +16,7 @@ #include #include +#include #include #include "core.h" @@ -203,12 +202,8 @@ bool nstun_init_parent(int sock, nsj_t* nsj) { ctx->nsj = nsj; auto assign_ip = [](const std::string& str, uint32_t* ip) { - struct nl_addr* addr; - if (nl_addr_parse(str.c_str(), AF_INET, &addr) == 0) { - if (nl_addr_get_len(addr) == 4) { - memcpy(ip, nl_addr_get_binary_addr(addr), 4); - } - nl_addr_put(addr); + if (inet_pton(AF_INET, str.c_str(), ip) != 1) { + LOG_E("Failed to parse IP: %s", str.c_str()); } }; @@ -237,41 +232,53 @@ bool nstun_init_parent(int sock, nsj_t* nsj) { } auto parse_ip = [](const std::string& str, uint32_t* ip, uint32_t* mask) { - struct nl_addr* addr; - if (nl_addr_parse(str.c_str(), AF_INET, &addr) == 0) { - if (nl_addr_get_len(addr) == 4) { - memcpy(ip, nl_addr_get_binary_addr(addr), 4); + std::string ip_str = str; + int bits = 32; + size_t pos = str.find('/'); + if (pos != std::string::npos) { + ip_str = str.substr(0, pos); + const char* p = str.c_str() + pos + 1; + auto [ptr, ec] = std::from_chars(p, str.c_str() + str.length(), bits); + if (ec != std::errc()) { + LOG_E("Failed to parse mask bits: %s", p); + return; } - int bits = nl_addr_get_prefixlen(addr); - *mask = (bits == 0) ? 0 : htonl(~((1ULL << (32 - bits)) - 1)); - nl_addr_put(addr); - } else { - LOG_E("Failed to parse IP/CIDR string: %s", str.c_str()); } + if (inet_pton(AF_INET, ip_str.c_str(), ip) != 1) { + LOG_E("Failed to parse IP string: %s", ip_str.c_str()); + return; + } + *mask = (bits == 0) ? 0 : htonl(~((1ULL << (32 - bits)) - 1)); }; auto parse_ip6 = [](const std::string& str, uint8_t* ip6, uint8_t* mask6) { - struct nl_addr* addr; - if (nl_addr_parse(str.c_str(), AF_INET6, &addr) == 0) { - if (nl_addr_get_len(addr) == nstun::IPV6_ADDR_LEN) { - memcpy(ip6, nl_addr_get_binary_addr(addr), nstun::IPV6_ADDR_LEN); + std::string ip_str = str; + int bits = 128; + size_t pos = str.find('/'); + if (pos != std::string::npos) { + ip_str = str.substr(0, pos); + const char* p = str.c_str() + pos + 1; + auto [ptr, ec] = std::from_chars(p, str.c_str() + str.length(), bits); + if (ec != std::errc()) { + LOG_E("Failed to parse mask bits: %s", p); + return; } - int bits = nl_addr_get_prefixlen(addr); - memset(mask6, 0, nstun::IPV6_ADDR_LEN); - for (int i = 0; i < (int)nstun::IPV6_ADDR_LEN; i++) { - if (bits >= 8) { - mask6[i] = 0xFF; - bits -= 8; - } else if (bits > 0) { - mask6[i] = (uint8_t)(0xFF << (8 - bits)); - bits = 0; - } else { - mask6[i] = 0; - } + } + if (inet_pton(AF_INET6, ip_str.c_str(), ip6) != 1) { + LOG_E("Failed to parse IPv6 string: %s", ip_str.c_str()); + return; + } + memset(mask6, 0, nstun::IPV6_ADDR_LEN); + for (int i = 0; i < (int)nstun::IPV6_ADDR_LEN; i++) { + if (bits >= 8) { + mask6[i] = 0xFF; + bits -= 8; + } else if (bits > 0) { + mask6[i] = (uint8_t)(0xFF << (8 - bits)); + bits = 0; + } else { + mask6[i] = 0; } - nl_addr_put(addr); - } else { - LOG_E("Failed to parse IPv6/CIDR string: %s", str.c_str()); } }; @@ -306,15 +313,8 @@ bool nstun_init_parent(int sock, nsj_t* nsj) { parse_ip(r.dst_ip(), &nr.dst_ip4, &nr.dst_mask4); } - if (r.has_redirect_ip()) { - struct nl_addr* addr; - if (nl_addr_parse(r.redirect_ip().c_str(), AF_INET, &addr) == 0) { - if (nl_addr_get_len(addr) == 4) { - memcpy(&nr.redirect_ip4, nl_addr_get_binary_addr(addr), - sizeof(nr.redirect_ip4)); - } - nl_addr_put(addr); - } + if (inet_pton(AF_INET, r.redirect_ip().c_str(), &nr.redirect_ip4) != 1) { + LOG_E("Failed to parse redirect IP: %s", r.redirect_ip().c_str()); } nr.redirect_port = r.has_redirect_port() ? r.redirect_port() : 0; @@ -406,25 +406,17 @@ bool nstun_init_parent(int sock, nsj_t* nsj) { if (nr.action == NSTUN_ACTION_ENCAP_SOCKS5 || nr.action == NSTUN_ACTION_ENCAP_CONNECT) { /* Proxy is always IPv4 */ - struct nl_addr* addr; - if (nl_addr_parse(r.redirect_ip().c_str(), AF_INET, &addr) == 0) { - if (nl_addr_get_len(addr) == 4) { - memcpy(&nr.redirect_ip4, - nl_addr_get_binary_addr(addr), - sizeof(nr.redirect_ip4)); - } - nl_addr_put(addr); + if (inet_pton(AF_INET, r.redirect_ip().c_str(), &nr.redirect_ip4) != + 1) { + LOG_E("Failed to parse redirect IP: %s", + r.redirect_ip().c_str()); } } else { /* REDIRECT: target is IPv6 */ - struct nl_addr* addr; - if (nl_addr_parse(r.redirect_ip().c_str(), AF_INET6, &addr) == 0) { - if (nl_addr_get_len(addr) == 16) { - memcpy(nr.redirect_ip6, - nl_addr_get_binary_addr(addr), - sizeof(nr.redirect_ip6)); - } - nl_addr_put(addr); + if (inet_pton(AF_INET6, r.redirect_ip().c_str(), nr.redirect_ip6) != + 1) { + LOG_E("Failed to parse redirect IPv6: %s", + r.redirect_ip().c_str()); } } } diff --git a/tests/nstun.cfg b/tests/nstun.cfg index 208e5ee..48c62b3 100644 --- a/tests/nstun.cfg +++ b/tests/nstun.cfg @@ -47,6 +47,6 @@ mount { } exec_bin { - path: "/bin/sh" + path: "/bin/bash" arg: "-i" }