]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-manager-varlink.c
hwdb: ieee1394-unit-function: add Tascam IF-FW/DM mkII
[thirdparty/systemd.git] / src / network / networkd-manager-varlink.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "bus-polkit.h"
6 #include "fd-util.h"
7 #include "lldp-rx-internal.h"
8 #include "networkd-dhcp-server.h"
9 #include "networkd-manager-varlink.h"
10 #include "stat-util.h"
11 #include "varlink.h"
12 #include "varlink-io.systemd.Network.h"
13
14 static int vl_method_get_states(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
15 Manager *m = ASSERT_PTR(userdata);
16
17 assert(link);
18
19 if (json_variant_elements(parameters) > 0)
20 return varlink_error_invalid_parameter(link, parameters);
21
22 return varlink_replyb(link,
23 JSON_BUILD_OBJECT(
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))));
30 }
31
32 static int vl_method_get_namespace_id(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
33 uint64_t inode = 0;
34 uint32_t nsid = UINT32_MAX;
35 int r;
36
37 assert(link);
38
39 if (json_variant_elements(parameters) > 0)
40 return varlink_error_invalid_parameter(link, parameters);
41
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
45 * this is. */
46
47 struct stat st;
48 if (stat("/proc/self/ns/net", &st) < 0)
49 log_warning_errno(errno, "Failed to stat network namespace, ignoring: %m");
50 else
51 inode = st.st_ino;
52
53 r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid);
54 if (r < 0)
55 log_full_errno(r == -ENODATA ? LOG_DEBUG : LOG_WARNING, r, "Failed to query network nsid, ignoring: %m");
56
57 return varlink_replyb(link,
58 JSON_BUILD_OBJECT(
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))));
62 }
63
64 typedef struct InterfaceInfo {
65 int ifindex;
66 const char *ifname;
67 } InterfaceInfo;
68
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 },
73 {}
74 };
75
76 InterfaceInfo info = {};
77 Link *link = NULL;
78 int r;
79
80 assert(vlink);
81 assert(manager);
82
83 r = varlink_dispatch(vlink, parameters, dispatch_table, &info);
84 if (r != 0)
85 return r;
86
87 if (info.ifindex < 0)
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"));
91 if (info.ifname) {
92 Link *link_by_name;
93
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"));
96
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"));
100
101 link = link_by_name;
102 }
103
104 /* If neither InterfaceIndex nor InterfaceName specified, this function returns NULL. */
105 *ret = link;
106 return 0;
107 }
108
109 static int link_append_lldp_neighbors(Link *link, JsonVariant *v, JsonVariant **array) {
110 assert(link);
111 assert(array);
112
113 return json_variant_append_arrayb(array,
114 JSON_BUILD_OBJECT(
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))));
120 }
121
122 static int vl_method_get_lldp_neighbors(Varlink *vlink, JsonVariant *parameters, VarlinkMethodFlags flags, Manager *manager) {
123 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
124 Link *link = NULL;
125 int r;
126
127 assert(vlink);
128 assert(manager);
129
130 r = dispatch_interface(vlink, parameters, manager, &link);
131 if (r != 0)
132 return r;
133
134 if (link) {
135 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
136
137 if (link->lldp_rx) {
138 r = lldp_rx_build_neighbors_json(link->lldp_rx, &v);
139 if (r < 0)
140 return r;
141 }
142
143 r = link_append_lldp_neighbors(link, v, &array);
144 if (r < 0)
145 return r;
146 } else
147 HASHMAP_FOREACH(link, manager->links_by_index) {
148 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
149
150 if (!link->lldp_rx)
151 continue;
152
153 r = lldp_rx_build_neighbors_json(link->lldp_rx, &v);
154 if (r < 0)
155 return r;
156
157 if (json_variant_is_blank_array(v))
158 continue;
159
160 r = link_append_lldp_neighbors(link, v, &array);
161 if (r < 0)
162 return r;
163 }
164
165 return varlink_replyb(vlink,
166 JSON_BUILD_OBJECT(
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))));
169 }
170
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 },
174 {}
175 };
176
177 Manager *manager = ASSERT_PTR(userdata);
178 bool ready;
179 int r;
180
181 assert(vlink);
182
183 r = varlink_dispatch(vlink, parameters, dispatch_table, &ready);
184 if (r != 0)
185 return r;
186
187 if (ready) {
188 struct stat st, st_prev;
189 int fd;
190
191 fd = varlink_peek_fd(vlink, 0);
192 if (fd < 0)
193 return log_warning_errno(fd, "Failed to peek file descriptor of the persistent storage: %m");
194
195 r = fd_verify_safe_flags_full(fd, O_DIRECTORY);
196 if (r == -EREMOTEIO)
197 return log_warning_errno(r, "Passed persistent storage fd has unexpected flags, refusing.");
198 if (r < 0)
199 return log_warning_errno(r, "Failed to verify flags of passed persistent storage fd: %m");
200
201 r = fd_is_read_only_fs(fd);
202 if (r < 0)
203 return log_warning_errno(r, "Failed to check if the persistent storage is writable: %m");
204 if (r > 0) {
205 log_warning("The persistent storage is on read-only filesystem.");
206 return varlink_error(vlink, "io.systemd.Network.StorageReadOnly", NULL);
207 }
208
209 if (fstat(fd, &st) < 0)
210 return log_warning_errno(errno, "Failed to stat the passed persistent storage fd: %m");
211
212 r = stat_verify_directory(&st);
213 if (r < 0)
214 return log_warning_errno(r, "The passed persistent storage fd is not a directory, refusing: %m");
215
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);
220
221 } else {
222 if (manager->persistent_storage_fd < 0)
223 return varlink_reply(vlink, NULL);
224 }
225
226 r = varlink_verify_polkit_async(
227 vlink,
228 manager->bus,
229 "org.freedesktop.network1.set-persistent-storage",
230 /* details= */ NULL,
231 &manager->polkit_registry);
232 if (r <= 0)
233 return r;
234
235 if (ready) {
236 _cleanup_close_ int fd = -EBADF;
237
238 fd = varlink_take_fd(vlink, 0);
239 if (fd < 0)
240 return log_warning_errno(fd, "Failed to take file descriptor of the persistent storage: %m");
241
242 close_and_replace(manager->persistent_storage_fd, fd);
243 } else
244 manager->persistent_storage_fd = safe_close(manager->persistent_storage_fd);
245
246 manager_toggle_dhcp4_server_state(manager, ready);
247
248 return varlink_reply(vlink, NULL);
249 }
250
251 static int on_connect(VarlinkServer *s, Varlink *vlink, void *userdata) {
252 int r;
253
254 assert(vlink);
255
256 r = varlink_set_allow_fd_passing_input(vlink, true);
257 if (r < 0)
258 return log_warning_errno(r, "Failed to allow receiving file descriptor through varlink: %m");
259
260 return 0;
261 }
262
263 int manager_connect_varlink(Manager *m) {
264 _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
265 int r;
266
267 assert(m);
268
269 if (m->varlink_server)
270 return 0;
271
272 r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID|VARLINK_SERVER_INHERIT_USERDATA);
273 if (r < 0)
274 return log_error_errno(r, "Failed to allocate varlink server object: %m");
275
276 varlink_server_set_userdata(s, m);
277
278 (void) varlink_server_set_description(s, "varlink-api-network");
279
280 r = varlink_server_add_interface(s, &vl_interface_io_systemd_Network);
281 if (r < 0)
282 return log_error_errno(r, "Failed to add Network interface to varlink server: %m");
283
284 r = varlink_server_bind_method_many(
285 s,
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);
290 if (r < 0)
291 return log_error_errno(r, "Failed to register varlink methods: %m");
292
293 r = varlink_server_listen_address(s, "/run/systemd/netif/io.systemd.Network", 0666);
294 if (r < 0)
295 return log_error_errno(r, "Failed to bind to varlink socket: %m");
296
297 r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
298 if (r < 0)
299 return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
300
301 r = varlink_server_bind_connect(s, on_connect);
302 if (r < 0)
303 return log_error_errno(r, "Failed to set on-connect callback for varlink: %m");
304
305 m->varlink_server = TAKE_PTR(s);
306 return 0;
307 }
308
309 void manager_varlink_done(Manager *m) {
310 assert(m);
311
312 m->varlink_server = varlink_server_unref(m->varlink_server);
313 (void) unlink("/run/systemd/netif/io.systemd.Network");
314 }