1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "bus-polkit.h"
7 #include "lldp-rx-internal.h"
8 #include "networkd-dhcp-server.h"
9 #include "networkd-manager-varlink.h"
10 #include "stat-util.h"
12 #include "varlink-io.systemd.Network.h"
14 static int vl_method_get_states(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
15 Manager
*m
= ASSERT_PTR(userdata
);
19 if (json_variant_elements(parameters
) > 0)
20 return varlink_error_invalid_parameter(link
, parameters
);
22 return varlink_replyb(link
,
24 JSON_BUILD_PAIR_STRING("AddressState", link_address_state_to_string(m
->address_state
)),
25 JSON_BUILD_PAIR_STRING("IPv4AddressState", link_address_state_to_string(m
->ipv4_address_state
)),
26 JSON_BUILD_PAIR_STRING("IPv6AddressState", link_address_state_to_string(m
->ipv6_address_state
)),
27 JSON_BUILD_PAIR_STRING("CarrierState", link_carrier_state_to_string(m
->carrier_state
)),
28 JSON_BUILD_PAIR_CONDITION(m
->online_state
>= 0, "OnlineState", JSON_BUILD_STRING(link_online_state_to_string(m
->online_state
))),
29 JSON_BUILD_PAIR_STRING("OperationalState", link_operstate_to_string(m
->operational_state
))));
32 static int vl_method_get_namespace_id(Varlink
*link
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
34 uint32_t nsid
= UINT32_MAX
;
39 if (json_variant_elements(parameters
) > 0)
40 return varlink_error_invalid_parameter(link
, parameters
);
42 /* Network namespaces have two identifiers: the inode number (which all namespace types have), and
43 * the "nsid" (aka the "cookie"), which only network namespaces know as a concept, and which is not
44 * assigned by default, but once it is, is fixed. Let's return both, to avoid any confusion which one
48 if (stat("/proc/self/ns/net", &st
) < 0)
49 log_warning_errno(errno
, "Failed to stat network namespace, ignoring: %m");
53 r
= netns_get_nsid(/* netnsfd= */ -EBADF
, &nsid
);
55 log_full_errno(r
== -ENODATA
? LOG_DEBUG
: LOG_WARNING
, r
, "Failed to query network nsid, ignoring: %m");
57 return varlink_replyb(link
,
59 JSON_BUILD_PAIR_UNSIGNED("NamespaceId", inode
),
60 JSON_BUILD_PAIR_CONDITION(nsid
== UINT32_MAX
, "NamespaceNSID", JSON_BUILD_NULL
),
61 JSON_BUILD_PAIR_CONDITION(nsid
!= UINT32_MAX
, "NamespaceNSID", JSON_BUILD_UNSIGNED(nsid
))));
64 typedef struct InterfaceInfo
{
69 static int dispatch_interface(Varlink
*vlink
, JsonVariant
*parameters
, Manager
*manager
, Link
**ret
) {
70 static const JsonDispatch dispatch_table
[] = {
71 { "InterfaceIndex", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_int
, offsetof(InterfaceInfo
, ifindex
), 0 },
72 { "InterfaceName", JSON_VARIANT_STRING
, json_dispatch_const_string
, offsetof(InterfaceInfo
, ifname
), 0 },
76 InterfaceInfo info
= {};
83 r
= varlink_dispatch(vlink
, parameters
, dispatch_table
, &info
);
88 return varlink_error_invalid_parameter(vlink
, JSON_VARIANT_STRING_CONST("InterfaceIndex"));
89 if (info
.ifindex
> 0 && link_get_by_index(manager
, info
.ifindex
, &link
) < 0)
90 return varlink_error_invalid_parameter(vlink
, JSON_VARIANT_STRING_CONST("InterfaceIndex"));
94 if (link_get_by_name(manager
, info
.ifname
, &link_by_name
) < 0)
95 return varlink_error_invalid_parameter(vlink
, JSON_VARIANT_STRING_CONST("InterfaceName"));
97 if (link
&& link_by_name
!= link
)
98 /* If both arguments are specified, then these must be consistent. */
99 return varlink_error_invalid_parameter(vlink
, JSON_VARIANT_STRING_CONST("InterfaceName"));
104 /* If neither InterfaceIndex nor InterfaceName specified, this function returns NULL. */
109 static int link_append_lldp_neighbors(Link
*link
, JsonVariant
*v
, JsonVariant
**array
) {
113 return json_variant_append_arrayb(array
,
115 JSON_BUILD_PAIR_INTEGER("InterfaceIndex", link
->ifindex
),
116 JSON_BUILD_PAIR_STRING("InterfaceName", link
->ifname
),
117 JSON_BUILD_PAIR_STRV_NON_EMPTY("InterfaceAlternativeNames", link
->alternative_names
),
118 JSON_BUILD_PAIR_CONDITION(json_variant_is_blank_array(v
), "Neighbors", JSON_BUILD_EMPTY_ARRAY
),
119 JSON_BUILD_PAIR_CONDITION(!json_variant_is_blank_array(v
), "Neighbors", JSON_BUILD_VARIANT(v
))));
122 static int vl_method_get_lldp_neighbors(Varlink
*vlink
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, Manager
*manager
) {
123 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
130 r
= dispatch_interface(vlink
, parameters
, manager
, &link
);
135 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
138 r
= lldp_rx_build_neighbors_json(link
->lldp_rx
, &v
);
143 r
= link_append_lldp_neighbors(link
, v
, &array
);
147 HASHMAP_FOREACH(link
, manager
->links_by_index
) {
148 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
153 r
= lldp_rx_build_neighbors_json(link
->lldp_rx
, &v
);
157 if (json_variant_is_blank_array(v
))
160 r
= link_append_lldp_neighbors(link
, v
, &array
);
165 return varlink_replyb(vlink
,
167 JSON_BUILD_PAIR_CONDITION(json_variant_is_blank_array(array
), "Neighbors", JSON_BUILD_EMPTY_ARRAY
),
168 JSON_BUILD_PAIR_CONDITION(!json_variant_is_blank_array(array
), "Neighbors", JSON_BUILD_VARIANT(array
))));
171 static int vl_method_set_persistent_storage(Varlink
*vlink
, JsonVariant
*parameters
, VarlinkMethodFlags flags
, void *userdata
) {
172 static const JsonDispatch dispatch_table
[] = {
173 { "Ready", JSON_VARIANT_BOOLEAN
, json_dispatch_boolean
, 0, 0 },
177 Manager
*manager
= ASSERT_PTR(userdata
);
183 r
= varlink_dispatch(vlink
, parameters
, dispatch_table
, &ready
);
188 struct stat st
, st_prev
;
191 fd
= varlink_peek_fd(vlink
, 0);
193 return log_warning_errno(fd
, "Failed to peek file descriptor of the persistent storage: %m");
195 r
= fd_verify_safe_flags_full(fd
, O_DIRECTORY
);
197 return log_warning_errno(r
, "Passed persistent storage fd has unexpected flags, refusing.");
199 return log_warning_errno(r
, "Failed to verify flags of passed persistent storage fd: %m");
201 r
= fd_is_read_only_fs(fd
);
203 return log_warning_errno(r
, "Failed to check if the persistent storage is writable: %m");
205 log_warning("The persistent storage is on read-only filesystem.");
206 return varlink_error(vlink
, "io.systemd.Network.StorageReadOnly", NULL
);
209 if (fstat(fd
, &st
) < 0)
210 return log_warning_errno(errno
, "Failed to stat the passed persistent storage fd: %m");
212 r
= stat_verify_directory(&st
);
214 return log_warning_errno(r
, "The passed persistent storage fd is not a directory, refusing: %m");
216 if (manager
->persistent_storage_fd
>= 0 &&
217 fstat(manager
->persistent_storage_fd
, &st_prev
) >= 0 &&
218 stat_inode_same(&st
, &st_prev
))
219 return varlink_reply(vlink
, NULL
);
222 if (manager
->persistent_storage_fd
< 0)
223 return varlink_reply(vlink
, NULL
);
226 r
= varlink_verify_polkit_async(
229 "org.freedesktop.network1.set-persistent-storage",
231 &manager
->polkit_registry
);
236 _cleanup_close_
int fd
= -EBADF
;
238 fd
= varlink_take_fd(vlink
, 0);
240 return log_warning_errno(fd
, "Failed to take file descriptor of the persistent storage: %m");
242 close_and_replace(manager
->persistent_storage_fd
, fd
);
244 manager
->persistent_storage_fd
= safe_close(manager
->persistent_storage_fd
);
246 manager_toggle_dhcp4_server_state(manager
, ready
);
248 return varlink_reply(vlink
, NULL
);
251 static int on_connect(VarlinkServer
*s
, Varlink
*vlink
, void *userdata
) {
256 r
= varlink_set_allow_fd_passing_input(vlink
, true);
258 return log_warning_errno(r
, "Failed to allow receiving file descriptor through varlink: %m");
263 int manager_connect_varlink(Manager
*m
) {
264 _cleanup_(varlink_server_unrefp
) VarlinkServer
*s
= NULL
;
269 if (m
->varlink_server
)
272 r
= varlink_server_new(&s
, VARLINK_SERVER_ACCOUNT_UID
|VARLINK_SERVER_INHERIT_USERDATA
);
274 return log_error_errno(r
, "Failed to allocate varlink server object: %m");
276 varlink_server_set_userdata(s
, m
);
278 (void) varlink_server_set_description(s
, "varlink-api-network");
280 r
= varlink_server_add_interface(s
, &vl_interface_io_systemd_Network
);
282 return log_error_errno(r
, "Failed to add Network interface to varlink server: %m");
284 r
= varlink_server_bind_method_many(
286 "io.systemd.Network.GetStates", vl_method_get_states
,
287 "io.systemd.Network.GetNamespaceId", vl_method_get_namespace_id
,
288 "io.systemd.Network.GetLLDPNeighbors", vl_method_get_lldp_neighbors
,
289 "io.systemd.Network.SetPersistentStorage", vl_method_set_persistent_storage
);
291 return log_error_errno(r
, "Failed to register varlink methods: %m");
293 r
= varlink_server_listen_address(s
, "/run/systemd/netif/io.systemd.Network", 0666);
295 return log_error_errno(r
, "Failed to bind to varlink socket: %m");
297 r
= varlink_server_attach_event(s
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
299 return log_error_errno(r
, "Failed to attach varlink connection to event loop: %m");
301 r
= varlink_server_bind_connect(s
, on_connect
);
303 return log_error_errno(r
, "Failed to set on-connect callback for varlink: %m");
305 m
->varlink_server
= TAKE_PTR(s
);
309 void manager_varlink_done(Manager
*m
) {
312 m
->varlink_server
= varlink_server_unref(m
->varlink_server
);
313 (void) unlink("/run/systemd/netif/io.systemd.Network");