diff --git a/man/org.freedesktop.network1.xml b/man/org.freedesktop.network1.xml
index c6cadee177..1d8ce0de81 100644
--- a/man/org.freedesktop.network1.xml
+++ b/man/org.freedesktop.network1.xml
@@ -87,6 +87,8 @@ node /org/freedesktop/network1 {
readonly s OnlineState = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t NamespaceId = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly u NamespaceNSID = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
@@ -148,8 +150,6 @@ node /org/freedesktop/network1 {
-
-
@@ -212,11 +212,24 @@ node /org/freedesktop/network1 {
+
+
Provides information about the manager.
+
+
+ Properties
+
+ NamespaceId contains the inode number of the network namespace that the
+ network service runs in. A client may compare this with the inode number of its own network namespace
+ to verify whether the service manages the same network namespace.
+
+ NamespaceNSID contains the "nsid" identifier the kernel maintains for the
+ network namespace, if there's one assigned.
+
@@ -584,5 +597,9 @@ $ gdbus introspect --system \
DHCPv6 Client Object
State was added in version 255.
+
+ Manager Object
+ NamespaceNSID was added in version 256.
+
diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
index f84097e16d..fb11c7e02b 100644
--- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
@@ -56,6 +56,10 @@ static bool rtnl_message_type_is_mdb(uint16_t type) {
return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
}
+static bool rtnl_message_type_is_nsid(uint16_t type) {
+ return IN_SET(type, RTM_NEWNSID, RTM_DELNSID, RTM_GETNSID);
+}
+
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
struct rtmsg *rtm;
@@ -1216,3 +1220,24 @@ int sd_rtnl_message_new_mdb(
return 0;
}
+
+int sd_rtnl_message_new_nsid(
+ sd_netlink *rtnl,
+ sd_netlink_message **ret,
+ uint16_t nlmsg_type) {
+
+ struct rtgenmsg *rt;
+ int r;
+
+ assert_return(rtnl_message_type_is_nsid(nlmsg_type), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(rtnl, ret, nlmsg_type);
+ if (r < 0)
+ return r;
+
+ rt = NLMSG_DATA((*ret)->hdr);
+ rt->rtgen_family = AF_UNSPEC;
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
index 0153456d9b..681b3086d0 100644
--- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -1185,6 +1186,13 @@ static const NLAPolicy rtnl_mdb_policies[] = {
DEFINE_POLICY_SET(rtnl_mdb);
+static const NLAPolicy rtnl_nsid_policies[] = {
+ [NETNSA_FD] = BUILD_POLICY(S32),
+ [NETNSA_NSID] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_nsid);
+
static const NLAPolicy rtnl_policies[] = {
[RTM_NEWLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
[RTM_DELLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
@@ -1220,6 +1228,9 @@ static const NLAPolicy rtnl_policies[] = {
[RTM_NEWMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
[RTM_DELMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
[RTM_GETMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+ [RTM_NEWNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+ [RTM_DELNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+ [RTM_GETNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
};
DEFINE_POLICY_SET(rtnl);
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index a8906f81c1..035537c869 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -279,6 +279,31 @@ static int property_get_namespace_id(
return sd_bus_message_append(reply, "t", id);
}
+static int property_get_namespace_nsid(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint32_t nsid = UINT32_MAX;
+ int r;
+
+ assert(bus);
+ assert(reply);
+
+ /* Returns our own "nsid", which is another ID for the network namespace, different from the inode
+ * number. */
+
+ r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to query network nsid, ignoring: %m");
+
+ return sd_bus_message_append(reply, "u", nsid);
+}
+
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -289,6 +314,7 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("OnlineState", "s", property_get_online_state, offsetof(Manager, online_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NamespaceId", "t", property_get_namespace_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NamespaceNSID", "u", property_get_namespace_nsid, 0, 0),
SD_BUS_METHOD_WITH_ARGS("ListLinks",
SD_BUS_NO_ARGS,
diff --git a/src/network/networkd-manager-varlink.c b/src/network/networkd-manager-varlink.c
index d9750d9b64..30aebb5d9e 100644
--- a/src/network/networkd-manager-varlink.c
+++ b/src/network/networkd-manager-varlink.c
@@ -25,23 +25,35 @@ static int vl_method_get_states(Varlink *link, JsonVariant *parameters, VarlinkM
}
static int vl_method_get_namespace_id(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
- uint64_t id;
+ uint64_t inode = 0;
+ uint32_t nsid = UINT32_MAX;
+ int r;
assert(link);
if (json_variant_elements(parameters) > 0)
return varlink_error_invalid_parameter(link, parameters);
+ /* Network namespaces have two identifiers: the inode number (which all namespace types have), and
+ * the "nsid" (aka the "cookie"), which only network namespaces know as a concept, and which is not
+ * assigned by default, but once it is, is fixed. Let's return both, to avoid any confusion which one
+ * this is. */
+
struct stat st;
- if (stat("/proc/self/ns/net", &st) < 0) {
+ if (stat("/proc/self/ns/net", &st) < 0)
log_warning_errno(errno, "Failed to stat network namespace, ignoring: %m");
- id = 0;
- } else
- id = st.st_ino;
+ else
+ inode = st.st_ino;
+
+ r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to query network nsid, ignoring: %m");
return varlink_replyb(link,
JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_UNSIGNED("NamespaceId", id)));
+ JSON_BUILD_PAIR_UNSIGNED("NamespaceId", inode),
+ JSON_BUILD_PAIR_CONDITION(nsid == UINT32_MAX, "NamespaceNSID", JSON_BUILD_NULL),
+ JSON_BUILD_PAIR_CONDITION(nsid != UINT32_MAX, "NamespaceNSID", JSON_BUILD_UNSIGNED(nsid))));
}
int manager_connect_varlink(Manager *m) {
diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c
index 0ba5762761..94b699a5ee 100644
--- a/src/shared/socket-netlink.c
+++ b/src/shared/socket-netlink.c
@@ -2,14 +2,17 @@
#include
#include
+#include
#include
#include
#include "alloc-util.h"
#include "errno-util.h"
#include "extract-word.h"
+#include "fd-util.h"
#include "log.h"
#include "memory-util.h"
+#include "namespace-util.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "socket-netlink.h"
@@ -407,3 +410,69 @@ const char *in_addr_full_to_string(struct in_addr_full *a) {
return a->cached_server_string;
}
+
+int netns_get_nsid(int netnsfd, uint32_t *ret) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_close_ int _netns_fd = -EBADF;
+ int r;
+
+ if (netnsfd < 0) {
+ r = namespace_open(
+ 0,
+ /* pidns_fd= */ NULL,
+ /* mntns_fd= */ NULL,
+ &_netns_fd,
+ /* userns_fd= */ NULL,
+ /* root_fd= */ NULL);
+ if (r < 0)
+ return r;
+
+ netnsfd = _netns_fd;
+ }
+
+ r = sd_netlink_open(&rtnl);
+ if (r < 0)
+ return r;
+
+ r = sd_rtnl_message_new_nsid(rtnl, &req, RTM_GETNSID);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_s32(req, NETNSA_FD, netnsfd);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(rtnl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
+ uint16_t type;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_get_type(m, &type);
+ if (r < 0)
+ return r;
+ if (type != RTM_NEWNSID)
+ continue;
+
+ uint32_t u;
+ r = sd_netlink_message_read_u32(m, NETNSA_NSID, &u);
+ if (r < 0)
+ return r;
+
+ if (u == UINT32_MAX) /* no NSID assigned yet */
+ return -ENODATA;
+
+ if (ret)
+ *ret = u;
+
+ return 0;
+ }
+
+ return -ENXIO;
+}
diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h
index 6256a831ba..2c06fbe3a7 100644
--- a/src/shared/socket-netlink.h
+++ b/src/shared/socket-netlink.h
@@ -42,3 +42,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
int in_addr_full_new(int family, const union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
const char *in_addr_full_to_string(struct in_addr_full *a);
+
+int netns_get_nsid(int netnsfd, uint32_t *ret);
diff --git a/src/shared/varlink-io.systemd.Network.c b/src/shared/varlink-io.systemd.Network.c
index ec25e26e29..e2da5810e3 100644
--- a/src/shared/varlink-io.systemd.Network.c
+++ b/src/shared/varlink-io.systemd.Network.c
@@ -11,7 +11,8 @@ static VARLINK_DEFINE_METHOD(GetStates,
VARLINK_DEFINE_OUTPUT(OperationalState, VARLINK_STRING, 0));
static VARLINK_DEFINE_METHOD(GetNamespaceId,
- VARLINK_DEFINE_OUTPUT(NamespaceId, VARLINK_INT, 0));
+ VARLINK_DEFINE_OUTPUT(NamespaceId, VARLINK_INT, 0),
+ VARLINK_DEFINE_OUTPUT(NamespaceNSID, VARLINK_INT, VARLINK_NULLABLE));
VARLINK_DEFINE_INTERFACE(
io_systemd_Network,
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index 240d21f9ca..34db2bbf1c 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -216,6 +216,8 @@ int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *
int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex);
+int sd_rtnl_message_new_nsid(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type);
+
/* genl */
int sd_genl_socket_open(sd_netlink **ret);
int sd_genl_message_new(sd_netlink *genl, const char *family_name, uint8_t cmd, sd_netlink_message **ret);
diff --git a/src/test/test-socket-netlink.c b/src/test/test-socket-netlink.c
index 6dbd50f4a1..ad642ca4b1 100644
--- a/src/test/test-socket-netlink.c
+++ b/src/test/test-socket-netlink.c
@@ -369,4 +369,16 @@ TEST(in_addr_port_ifindex_name_from_string_auto) {
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo#hoge.com", AF_INET6, 53, 1, "hoge.com", "[fe80::18]:53%1#hoge.com");
}
+TEST(netns_get_nsid) {
+ uint32_t u;
+ int r;
+
+ r = netns_get_nsid(-EBADF, &u);
+ assert_se(r == -ENODATA || r >= 0);
+ if (r == -ENODATA)
+ log_info("Our network namespace has no NSID assigned.");
+ else
+ log_info("Our NSID is %" PRIu32, u);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);