Makefile/all: make libnl3 optional

This commit is contained in:
Robert Swiecki
2026-04-14 14:26:28 +02:00
parent bcf9eca738
commit 6356dee0c6
4 changed files with 83 additions and 63 deletions

View File

@@ -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)

26
net.cc
View File

@@ -30,9 +30,11 @@
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#ifdef HAVE_LIBNL3
#include <netlink/route/nexthop.h>
#include <netlink/route/route.h>
#include <netlink/route/rule.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -126,10 +128,13 @@ namespace net {
#define IFACE_NAME "vs"
#include <linux/if_ether.h>
#ifdef HAVE_LIBNL3
#include <linux/rtnetlink.h>
#include <netlink/route/link.h>
#include <netlink/route/link/macvlan.h>
#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;
}

View File

@@ -6,8 +6,6 @@
#include <linux/if_tun.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netlink/addr.h>
#include <netlink/netlink.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
@@ -18,6 +16,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include <charconv>
#include <thread>
#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());
}
}
}

View File

@@ -47,6 +47,6 @@ mount {
}
exec_bin {
path: "/bin/sh"
path: "/bin/bash"
arg: "-i"
}