network: implement varlink LinkUp and LinkDown methods
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>
This commit is contained in:
committed by
Yu Watanabe
co-authored by
Yu Watanabe
parent
a48145203c
commit
2291cf0626
@@ -16,6 +16,7 @@
|
||||
#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"
|
||||
@@ -92,7 +93,7 @@ static int vl_method_get_namespace_id(sd_varlink *link, sd_json_variant *paramet
|
||||
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;
|
||||
@@ -104,13 +105,18 @@ static int dispatch_interface(sd_varlink *vlink, sd_json_variant *parameters, Ma
|
||||
{ "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;
|
||||
|
||||
@@ -157,7 +163,7 @@ static int vl_method_get_lldp_neighbors(sd_varlink *vlink, sd_json_variant *para
|
||||
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;
|
||||
|
||||
@@ -278,6 +284,46 @@ static int vl_method_set_persistent_storage(sd_varlink *vlink, sd_json_variant *
|
||||
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 */
|
||||
@@ -312,6 +358,8 @@ int manager_varlink_init(Manager *m, int fd) {
|
||||
"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);
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#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"
|
||||
@@ -1341,6 +1343,103 @@ int link_up_or_down_now(Link *link, bool up) {
|
||||
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;
|
||||
|
||||
@@ -22,6 +22,7 @@ int link_request_to_activate(Link *link);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -194,4 +194,15 @@
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* 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 */
|
||||
@@ -593,6 +594,22 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
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(
|
||||
@@ -603,6 +620,10 @@ 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,
|
||||
|
||||
Reference in New Issue
Block a user