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