The new varlink methods are basically equivalent to 'ip link set INTERFACE up/down',
but they support polkit authentication. Also, on LinkDown, it gracefully
stops dynamic engines like DHCP client/server before the interface is
bring down. Hence, e.g. an empty RA on stop should be sent.
Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-manager-varlink.h"
+#include "networkd-setlink.h"
#include "stat-util.h"
#include "varlink-io.systemd.Network.h"
#include "varlink-io.systemd.service.h"
SD_JSON_BUILD_PAIR_CONDITION(nsid != UINT32_MAX, "NamespaceNSID", SD_JSON_BUILD_UNSIGNED(nsid)));
}
-static int dispatch_interface(sd_varlink *vlink, sd_json_variant *parameters, Manager *manager, Link **ret) {
+static int dispatch_interface(sd_varlink *vlink, sd_json_variant *parameters, Manager *manager, bool polkit, Link **ret) {
struct {
int ifindex;
const char *ifname;
{ "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, voffsetof(info, ifindex), SD_JSON_RELAX },
{ "InterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(info, ifname), 0 },
{}
+ }, dispatch_polkit_table[] = {
+ { "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, voffsetof(info, ifindex), SD_JSON_RELAX },
+ { "InterfaceName", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(info, ifname), 0 },
+ VARLINK_DISPATCH_POLKIT_FIELD,
+ {}
};
assert(vlink);
assert(manager);
assert(ret);
- r = sd_varlink_dispatch(vlink, parameters, dispatch_table, &info);
+ r = sd_varlink_dispatch(vlink, parameters, polkit ? dispatch_polkit_table : dispatch_table, &info);
if (r != 0)
return r;
assert(vlink);
assert(manager);
- r = dispatch_interface(vlink, parameters, manager, &link);
+ r = dispatch_interface(vlink, parameters, manager, /* polkit= */ false, &link);
if (r != 0)
return r;
return sd_varlink_reply(vlink, NULL);
}
+static int vl_method_link_up_or_down(sd_varlink *vlink, sd_json_variant *parameters, Manager *manager, bool up) {
+ Link *link;
+ int r;
+
+ assert(vlink);
+ assert(manager);
+
+ r = dispatch_interface(vlink, parameters, manager, /* polkit= */ true, &link);
+ if (r != 0)
+ return r;
+
+ /* Require a specific link to be specified. */
+ if (!link)
+ return sd_varlink_error_invalid_parameter(vlink, JSON_VARIANT_STRING_CONST("InterfaceIndex"));
+
+ r = varlink_verify_polkit_async(
+ vlink,
+ manager->bus,
+ "org.freedesktop.network1.manage-links",
+ /* details= */ NULL,
+ &manager->polkit_registry);
+ if (r <= 0)
+ return r;
+
+ if (!up)
+ /* Stop all network engines while interface is still up to allow proper cleanup,
+ * e.g. sending IPv6 shutdown RA messages before the interface is brought down. */
+ (void) link_stop_engines(link, /* may_keep_dynamic = */ false);
+
+ return link_up_or_down_now_by_varlink(link, up, vlink);
+}
+
+static int vl_method_link_up(sd_varlink *vlink, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ return vl_method_link_up_or_down(vlink, parameters, userdata, /* up= */ true);
+}
+
+static int vl_method_link_down(sd_varlink *vlink, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ return vl_method_link_up_or_down(vlink, parameters, userdata, /* up= */ false);
+}
+
int manager_varlink_init(Manager *m, int fd) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
_unused_ _cleanup_close_ int fd_close = fd; /* take possession */
"io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id,
"io.systemd.Network.GetLLDPNeighbors", vl_method_get_lldp_neighbors,
"io.systemd.Network.SetPersistentStorage", vl_method_set_persistent_storage,
+ "io.systemd.Network.LinkUp", vl_method_link_up,
+ "io.systemd.Network.LinkDown", vl_method_link_down,
"io.systemd.service.Ping", varlink_method_ping,
"io.systemd.service.SetLogLevel", varlink_method_set_log_level,
"io.systemd.service.GetEnvironment", varlink_method_get_environment);
#include <netinet/in.h>
#include "sd-netlink.h"
+#include "sd-varlink.h"
+#include "alloc-util.h"
#include "device-private.h"
#include "missing-network.h"
#include "netif-util.h"
return 0;
}
+typedef struct SetLinkVarlinkContext {
+ Link *link;
+ sd_varlink *vlink;
+ bool up;
+} SetLinkVarlinkContext;
+
+static SetLinkVarlinkContext* set_link_varlink_context_free(SetLinkVarlinkContext *ctx) {
+ if (!ctx)
+ return NULL;
+
+ if (ctx->vlink)
+ sd_varlink_unref(ctx->vlink);
+ if (ctx->link)
+ link_unref(ctx->link);
+ return mfree(ctx);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SetLinkVarlinkContext*, set_link_varlink_context_free);
+
+static void set_link_varlink_context_destroy(SetLinkVarlinkContext *ctx) {
+ set_link_varlink_context_free(ctx);
+}
+
+static int link_up_or_down_now_varlink_handler(sd_netlink *rtnl, sd_netlink_message *m, SetLinkVarlinkContext *ctx) {
+ int r;
+
+ assert(m);
+ assert(ctx);
+
+ Link *link = ASSERT_PTR(ctx->link);
+ sd_varlink *vlink = ASSERT_PTR(ctx->vlink);
+ bool up = ctx->up;
+
+ assert(link->set_flags_messages > 0);
+
+ link->set_flags_messages--;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0) {
+ (void) sd_varlink_error_errno(vlink, r);
+ log_link_message_warning_errno(link, m, r, "Could not bring %s interface", up_or_down(up));
+ } else
+ (void) sd_varlink_reply(vlink, NULL);
+
+ if (link->state == LINK_STATE_LINGER)
+ return 0;
+
+ r = link_call_getlink(link, get_link_update_flag_handler);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 0;
+ }
+
+ link->set_flags_messages++;
+ return 0;
+}
+
+int link_up_or_down_now_by_varlink(Link *link, bool up, sd_varlink *vlink) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ log_link_debug(link, "Bringing link %s (varlink)", up_or_down(up));
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_rtnl_message_link_set_flags(req, up ? IFF_UP : 0, IFF_UP);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set link flags: %m");
+
+ _cleanup_(set_link_varlink_context_freep) SetLinkVarlinkContext *ctx = new(SetLinkVarlinkContext, 1);
+ if (!ctx)
+ return log_oom();
+
+ *ctx = (SetLinkVarlinkContext) {
+ .link = link_ref(link),
+ .vlink = sd_varlink_ref(vlink),
+ .up = up,
+ };
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ link_up_or_down_now_varlink_handler,
+ set_link_varlink_context_destroy,
+ ctx);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m");
+
+ TAKE_PTR(ctx);
+ link->set_flags_messages++;
+ return 0;
+}
+
int link_down_slave_links(Link *link) {
Link *slave;
int r;
int link_request_to_bring_up_or_down(Link *link, bool up);
int link_up_or_down_now(Link *link, bool up);
+int link_up_or_down_now_by_varlink(Link *link, bool up, sd_varlink *vlink);
static inline int link_up_now(Link *link) {
return link_up_or_down_now(link, true);
}
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
</action>
+ <action id="org.freedesktop.network1.manage-links">
+ <description gettext-domain="systemd">Manage network links</description>
+ <message gettext-domain="systemd">Authentication is required to manage network links.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+ </action>
+
</policyconfig>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "bus-polkit.h"
#include "varlink-io.systemd.Network.h"
/* Helper macro to define address fields with both binary and string representation */
SD_VARLINK_FIELD_COMMENT("Whether persistent storage is ready and writable"),
SD_VARLINK_DEFINE_INPUT(Ready, SD_VARLINK_BOOL, 0));
+static SD_VARLINK_DEFINE_METHOD(
+ LinkUp,
+ SD_VARLINK_FIELD_COMMENT("Index of the interface. If specified together with InterfaceName, both must reference the same link."),
+ SD_VARLINK_DEFINE_INPUT(InterfaceIndex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Name of the interface. If specified together with InterfaceIndex, both must reference the same link."),
+ SD_VARLINK_DEFINE_INPUT(InterfaceName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_METHOD(
+ LinkDown,
+ SD_VARLINK_FIELD_COMMENT("Index of the interface. If specified together with InterfaceName, both must reference the same link."),
+ SD_VARLINK_DEFINE_INPUT(InterfaceIndex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Name of the interface. If specified together with InterfaceIndex, both must reference the same link."),
+ SD_VARLINK_DEFINE_INPUT(InterfaceName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ VARLINK_DEFINE_POLKIT_INPUT);
+
static SD_VARLINK_DEFINE_ERROR(StorageReadOnly);
SD_VARLINK_DEFINE_INTERFACE(
&vl_method_GetNamespaceId,
&vl_method_GetLLDPNeighbors,
&vl_method_SetPersistentStorage,
+ SD_VARLINK_SYMBOL_COMMENT("Bring the specified link up."),
+ &vl_method_LinkUp,
+ SD_VARLINK_SYMBOL_COMMENT("Bring the specified link down."),
+ &vl_method_LinkDown,
&vl_type_Address,
&vl_type_DHCPLease,
&vl_type_DHCPServer,