/src/systemd/src/network/networkd-manager-bus.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <sys/stat.h> |
4 | | |
5 | | #include "sd-bus.h" |
6 | | |
7 | | #include "alloc-util.h" |
8 | | #include "bus-common-errors.h" |
9 | | #include "bus-message-util.h" |
10 | | #include "bus-object.h" |
11 | | #include "bus-polkit.h" |
12 | | #include "hashmap.h" |
13 | | #include "networkd-dhcp-server-bus.h" |
14 | | #include "networkd-dhcp4-bus.h" |
15 | | #include "networkd-dhcp6-bus.h" |
16 | | #include "networkd-json.h" |
17 | | #include "networkd-link.h" |
18 | | #include "networkd-link-bus.h" |
19 | | #include "networkd-manager.h" |
20 | | #include "networkd-manager-bus.h" |
21 | | #include "networkd-network-bus.h" |
22 | | #include "path-util.h" |
23 | | |
24 | 0 | static int method_list_links(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
25 | 0 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
26 | 0 | Manager *manager = userdata; |
27 | 0 | Link *link; |
28 | 0 | int r; |
29 | |
|
30 | 0 | r = sd_bus_message_new_method_return(message, &reply); |
31 | 0 | if (r < 0) |
32 | 0 | return r; |
33 | | |
34 | 0 | r = sd_bus_message_open_container(reply, 'a', "(iso)"); |
35 | 0 | if (r < 0) |
36 | 0 | return r; |
37 | | |
38 | 0 | HASHMAP_FOREACH(link, manager->links_by_index) { |
39 | 0 | _cleanup_free_ char *path = NULL; |
40 | |
|
41 | 0 | path = link_bus_path(link); |
42 | 0 | if (!path) |
43 | 0 | return -ENOMEM; |
44 | | |
45 | 0 | r = sd_bus_message_append( |
46 | 0 | reply, "(iso)", |
47 | 0 | link->ifindex, |
48 | 0 | link->ifname, |
49 | 0 | empty_to_root(path)); |
50 | 0 | if (r < 0) |
51 | 0 | return r; |
52 | 0 | } |
53 | | |
54 | 0 | r = sd_bus_message_close_container(reply); |
55 | 0 | if (r < 0) |
56 | 0 | return r; |
57 | | |
58 | 0 | return sd_bus_message_send(reply); |
59 | 0 | } |
60 | | |
61 | 0 | static int method_get_link_by_name(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
62 | 0 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
63 | 0 | _cleanup_free_ char *path = NULL; |
64 | 0 | Manager *manager = userdata; |
65 | 0 | const char *name; |
66 | 0 | Link *link; |
67 | 0 | int r; |
68 | |
|
69 | 0 | r = sd_bus_message_read(message, "s", &name); |
70 | 0 | if (r < 0) |
71 | 0 | return r; |
72 | | |
73 | 0 | if (link_get_by_name(manager, name, &link) < 0) |
74 | 0 | return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %s not known", name); |
75 | | |
76 | 0 | r = sd_bus_message_new_method_return(message, &reply); |
77 | 0 | if (r < 0) |
78 | 0 | return r; |
79 | | |
80 | 0 | path = link_bus_path(link); |
81 | 0 | if (!path) |
82 | 0 | return -ENOMEM; |
83 | | |
84 | 0 | r = sd_bus_message_append(reply, "io", link->ifindex, empty_to_root(path)); |
85 | 0 | if (r < 0) |
86 | 0 | return r; |
87 | | |
88 | 0 | return sd_bus_message_send(reply); |
89 | 0 | } |
90 | | |
91 | 0 | static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
92 | 0 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
93 | 0 | _cleanup_free_ char *path = NULL; |
94 | 0 | Manager *manager = userdata; |
95 | 0 | int ifindex, r; |
96 | 0 | Link *link; |
97 | |
|
98 | 0 | r = bus_message_read_ifindex(message, error, &ifindex); |
99 | 0 | if (r < 0) |
100 | 0 | return r; |
101 | | |
102 | 0 | r = link_get_by_index(manager, ifindex, &link); |
103 | 0 | if (r < 0) |
104 | 0 | return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); |
105 | | |
106 | 0 | r = sd_bus_message_new_method_return(message, &reply); |
107 | 0 | if (r < 0) |
108 | 0 | return r; |
109 | | |
110 | 0 | path = link_bus_path(link); |
111 | 0 | if (!path) |
112 | 0 | return -ENOMEM; |
113 | | |
114 | 0 | r = sd_bus_message_append(reply, "so", link->ifname, empty_to_root(path)); |
115 | 0 | if (r < 0) |
116 | 0 | return r; |
117 | | |
118 | 0 | return sd_bus_message_send(reply); |
119 | 0 | } |
120 | | |
121 | 0 | static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) { |
122 | 0 | int ifindex, r; |
123 | 0 | Link *l; |
124 | |
|
125 | 0 | assert(m); |
126 | 0 | assert(message); |
127 | 0 | assert(handler); |
128 | |
|
129 | 0 | r = bus_message_read_ifindex(message, error, &ifindex); |
130 | 0 | if (r < 0) |
131 | 0 | return r; |
132 | | |
133 | 0 | r = link_get_by_index(m, ifindex, &l); |
134 | 0 | if (r < 0) |
135 | 0 | return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex); |
136 | | |
137 | 0 | return handler(message, l, error); |
138 | 0 | } |
139 | | |
140 | 0 | static int bus_method_set_link_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
141 | 0 | return call_link_method(userdata, message, bus_link_method_set_ntp_servers, error); |
142 | 0 | } |
143 | | |
144 | 0 | static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
145 | 0 | return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); |
146 | 0 | } |
147 | | |
148 | 0 | static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
149 | 0 | return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error); |
150 | 0 | } |
151 | | |
152 | 0 | static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
153 | 0 | return call_link_method(userdata, message, bus_link_method_set_domains, error); |
154 | 0 | } |
155 | | |
156 | 0 | static int bus_method_set_link_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
157 | 0 | return call_link_method(userdata, message, bus_link_method_set_default_route, error); |
158 | 0 | } |
159 | | |
160 | 0 | static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
161 | 0 | return call_link_method(userdata, message, bus_link_method_set_llmnr, error); |
162 | 0 | } |
163 | | |
164 | 0 | static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
165 | 0 | return call_link_method(userdata, message, bus_link_method_set_mdns, error); |
166 | 0 | } |
167 | | |
168 | 0 | static int bus_method_set_link_dns_over_tls(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
169 | 0 | return call_link_method(userdata, message, bus_link_method_set_dns_over_tls, error); |
170 | 0 | } |
171 | | |
172 | 0 | static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
173 | 0 | return call_link_method(userdata, message, bus_link_method_set_dnssec, error); |
174 | 0 | } |
175 | | |
176 | 0 | static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
177 | 0 | return call_link_method(userdata, message, bus_link_method_set_dnssec_negative_trust_anchors, error); |
178 | 0 | } |
179 | | |
180 | 0 | static int bus_method_revert_link_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
181 | 0 | return call_link_method(userdata, message, bus_link_method_revert_ntp, error); |
182 | 0 | } |
183 | | |
184 | 0 | static int bus_method_revert_link_dns(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
185 | 0 | return call_link_method(userdata, message, bus_link_method_revert_dns, error); |
186 | 0 | } |
187 | | |
188 | 0 | static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
189 | 0 | return call_link_method(userdata, message, bus_link_method_renew, error); |
190 | 0 | } |
191 | | |
192 | 0 | static int bus_method_force_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
193 | 0 | return call_link_method(userdata, message, bus_link_method_force_renew, error); |
194 | 0 | } |
195 | | |
196 | 0 | static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
197 | 0 | return call_link_method(userdata, message, bus_link_method_reconfigure, error); |
198 | 0 | } |
199 | | |
200 | 0 | static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
201 | 0 | Manager *manager = ASSERT_PTR(userdata); |
202 | 0 | int r; |
203 | |
|
204 | 0 | if (manager->reloading > 0) |
205 | 0 | return sd_bus_error_set(error, BUS_ERROR_NETWORK_ALREADY_RELOADING, "Already reloading."); |
206 | | |
207 | 0 | r = bus_verify_polkit_async( |
208 | 0 | message, |
209 | 0 | "org.freedesktop.network1.reload", |
210 | 0 | /* details= */ NULL, |
211 | 0 | &manager->polkit_registry, |
212 | 0 | error); |
213 | 0 | if (r < 0) |
214 | 0 | return r; |
215 | 0 | if (r == 0) |
216 | 0 | return 1; /* Polkit will call us back */ |
217 | | |
218 | 0 | r = manager_reload(manager, message, /* varlink= */ NULL, /* reconfigure_links= */ true); |
219 | 0 | if (r < 0) |
220 | 0 | return r; |
221 | | |
222 | 0 | if (manager->reloading > 0) |
223 | 0 | return 1; /* Will reply later. */ |
224 | | |
225 | 0 | return sd_bus_reply_method_return(message, NULL); |
226 | 0 | } |
227 | | |
228 | 0 | static int bus_method_describe_link(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
229 | 0 | return call_link_method(userdata, message, bus_link_method_describe, error); |
230 | 0 | } |
231 | | |
232 | 0 | static int bus_method_describe(sd_bus_message *message, void *userdata, sd_bus_error *error) { |
233 | 0 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
234 | 0 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; |
235 | 0 | _cleanup_free_ char *text = NULL; |
236 | 0 | Manager *manager = ASSERT_PTR(userdata); |
237 | 0 | int r; |
238 | |
|
239 | 0 | assert(message); |
240 | |
|
241 | 0 | r = manager_build_json(manager, &v); |
242 | 0 | if (r < 0) |
243 | 0 | return log_error_errno(r, "Failed to build JSON data: %m"); |
244 | | |
245 | 0 | r = sd_json_variant_format(v, 0, &text); |
246 | 0 | if (r < 0) |
247 | 0 | return log_error_errno(r, "Failed to format JSON data: %m"); |
248 | | |
249 | 0 | r = sd_bus_message_new_method_return(message, &reply); |
250 | 0 | if (r < 0) |
251 | 0 | return r; |
252 | | |
253 | 0 | r = sd_bus_message_append(reply, "s", text); |
254 | 0 | if (r < 0) |
255 | 0 | return r; |
256 | | |
257 | 0 | return sd_bus_message_send(reply); |
258 | 0 | } |
259 | | |
260 | | static int property_get_namespace_id( |
261 | | sd_bus *bus, |
262 | | const char *path, |
263 | | const char *interface, |
264 | | const char *property, |
265 | | sd_bus_message *reply, |
266 | | void *userdata, |
267 | 0 | sd_bus_error *error) { |
268 | |
|
269 | 0 | uint64_t id = 0; |
270 | 0 | struct stat st; |
271 | |
|
272 | 0 | assert(bus); |
273 | 0 | assert(reply); |
274 | | |
275 | | /* Returns our own network namespace ID, i.e. the inode number of /proc/self/ns/net. This allows |
276 | | * unprivileged clients to determine whether they are in the same network namespace as us (note that |
277 | | * access to that path is restricted, thus they can't check directly unless privileged). */ |
278 | |
|
279 | 0 | if (stat("/proc/self/ns/net", &st) < 0) { |
280 | 0 | log_warning_errno(errno, "Failed to stat network namespace, ignoring: %m"); |
281 | 0 | id = 0; |
282 | 0 | } else |
283 | 0 | id = st.st_ino; |
284 | |
|
285 | 0 | return sd_bus_message_append(reply, "t", id); |
286 | 0 | } |
287 | | |
288 | | static int property_get_namespace_nsid( |
289 | | sd_bus *bus, |
290 | | const char *path, |
291 | | const char *interface, |
292 | | const char *property, |
293 | | sd_bus_message *reply, |
294 | | void *userdata, |
295 | 0 | sd_bus_error *error) { |
296 | |
|
297 | 0 | uint32_t nsid = UINT32_MAX; |
298 | 0 | int r; |
299 | |
|
300 | 0 | assert(bus); |
301 | 0 | assert(reply); |
302 | | |
303 | | /* Returns our own "nsid", which is another ID for the network namespace, different from the inode |
304 | | * number. */ |
305 | |
|
306 | 0 | r = netns_get_nsid(/* netnsfd= */ -EBADF, &nsid); |
307 | 0 | if (r < 0 && r != -ENODATA) |
308 | 0 | log_warning_errno(r, "Failed to query network nsid, ignoring: %m"); |
309 | |
|
310 | 0 | return sd_bus_message_append(reply, "u", nsid); |
311 | 0 | } |
312 | | |
313 | | static const sd_bus_vtable manager_vtable[] = { |
314 | | SD_BUS_VTABLE_START(0), |
315 | | |
316 | | SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
317 | | SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
318 | | SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
319 | | SD_BUS_PROPERTY("IPv4AddressState", "s", property_get_address_state, offsetof(Manager, ipv4_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
320 | | SD_BUS_PROPERTY("IPv6AddressState", "s", property_get_address_state, offsetof(Manager, ipv6_address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
321 | | SD_BUS_PROPERTY("OnlineState", "s", property_get_online_state, offsetof(Manager, online_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), |
322 | | SD_BUS_PROPERTY("NamespaceId", "t", property_get_namespace_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), |
323 | | SD_BUS_PROPERTY("NamespaceNSID", "u", property_get_namespace_nsid, 0, 0), |
324 | | |
325 | | SD_BUS_METHOD_WITH_ARGS("ListLinks", |
326 | | SD_BUS_NO_ARGS, |
327 | | SD_BUS_RESULT("a(iso)", links), |
328 | | method_list_links, |
329 | | SD_BUS_VTABLE_UNPRIVILEGED), |
330 | | SD_BUS_METHOD_WITH_ARGS("GetLinkByName", |
331 | | SD_BUS_ARGS("s", name), |
332 | | SD_BUS_RESULT("i", ifindex, "o", path), |
333 | | method_get_link_by_name, |
334 | | SD_BUS_VTABLE_UNPRIVILEGED), |
335 | | SD_BUS_METHOD_WITH_ARGS("GetLinkByIndex", |
336 | | SD_BUS_ARGS("i", ifindex), |
337 | | SD_BUS_RESULT("s", name, "o", path), |
338 | | method_get_link_by_index, |
339 | | SD_BUS_VTABLE_UNPRIVILEGED), |
340 | | SD_BUS_METHOD_WITH_ARGS("SetLinkNTP", |
341 | | SD_BUS_ARGS("i", ifindex, "as", servers), |
342 | | SD_BUS_NO_RESULT, |
343 | | bus_method_set_link_ntp_servers, |
344 | | SD_BUS_VTABLE_UNPRIVILEGED), |
345 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDNS", |
346 | | SD_BUS_ARGS("i", ifindex, "a(iay)", addresses), |
347 | | SD_BUS_NO_RESULT, |
348 | | bus_method_set_link_dns_servers, |
349 | | SD_BUS_VTABLE_UNPRIVILEGED), |
350 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDNSEx", |
351 | | SD_BUS_ARGS("i", ifindex, "a(iayqs)", addresses), |
352 | | SD_BUS_NO_RESULT, |
353 | | bus_method_set_link_dns_servers_ex, |
354 | | SD_BUS_VTABLE_UNPRIVILEGED), |
355 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDomains", |
356 | | SD_BUS_ARGS("i", ifindex, "a(sb)", domains), |
357 | | SD_BUS_NO_RESULT, |
358 | | bus_method_set_link_domains, |
359 | | SD_BUS_VTABLE_UNPRIVILEGED), |
360 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDefaultRoute", |
361 | | SD_BUS_ARGS("i", ifindex, "b", enable), |
362 | | SD_BUS_NO_RESULT, |
363 | | bus_method_set_link_default_route, |
364 | | SD_BUS_VTABLE_UNPRIVILEGED), |
365 | | SD_BUS_METHOD_WITH_ARGS("SetLinkLLMNR", |
366 | | SD_BUS_ARGS("i", ifindex, "s", mode), |
367 | | SD_BUS_NO_RESULT, |
368 | | bus_method_set_link_llmnr, |
369 | | SD_BUS_VTABLE_UNPRIVILEGED), |
370 | | SD_BUS_METHOD_WITH_ARGS("SetLinkMulticastDNS", |
371 | | SD_BUS_ARGS("i", ifindex, "s", mode), |
372 | | SD_BUS_NO_RESULT, |
373 | | bus_method_set_link_mdns, |
374 | | SD_BUS_VTABLE_UNPRIVILEGED), |
375 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDNSOverTLS", |
376 | | SD_BUS_ARGS("i", ifindex, "s", mode), |
377 | | SD_BUS_NO_RESULT, |
378 | | bus_method_set_link_dns_over_tls, |
379 | | SD_BUS_VTABLE_UNPRIVILEGED), |
380 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSEC", |
381 | | SD_BUS_ARGS("i", ifindex, "s", mode), |
382 | | SD_BUS_NO_RESULT, |
383 | | bus_method_set_link_dnssec, |
384 | | SD_BUS_VTABLE_UNPRIVILEGED), |
385 | | SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSECNegativeTrustAnchors", |
386 | | SD_BUS_ARGS("i", ifindex, "as", names), |
387 | | SD_BUS_NO_RESULT, |
388 | | bus_method_set_link_dnssec_negative_trust_anchors, |
389 | | SD_BUS_VTABLE_UNPRIVILEGED), |
390 | | SD_BUS_METHOD_WITH_ARGS("RevertLinkNTP", |
391 | | SD_BUS_ARGS("i", ifindex), |
392 | | SD_BUS_NO_RESULT, |
393 | | bus_method_revert_link_ntp, |
394 | | SD_BUS_VTABLE_UNPRIVILEGED), |
395 | | SD_BUS_METHOD_WITH_ARGS("RevertLinkDNS", |
396 | | SD_BUS_ARGS("i", ifindex), |
397 | | SD_BUS_NO_RESULT, |
398 | | bus_method_revert_link_dns, |
399 | | SD_BUS_VTABLE_UNPRIVILEGED), |
400 | | SD_BUS_METHOD_WITH_ARGS("RenewLink", |
401 | | SD_BUS_ARGS("i", ifindex), |
402 | | SD_BUS_NO_RESULT, |
403 | | bus_method_renew_link, |
404 | | SD_BUS_VTABLE_UNPRIVILEGED), |
405 | | SD_BUS_METHOD_WITH_ARGS("ForceRenewLink", |
406 | | SD_BUS_ARGS("i", ifindex), |
407 | | SD_BUS_NO_RESULT, |
408 | | bus_method_force_renew_link, |
409 | | SD_BUS_VTABLE_UNPRIVILEGED), |
410 | | SD_BUS_METHOD_WITH_ARGS("ReconfigureLink", |
411 | | SD_BUS_ARGS("i", ifindex), |
412 | | SD_BUS_NO_RESULT, |
413 | | bus_method_reconfigure_link, |
414 | | SD_BUS_VTABLE_UNPRIVILEGED), |
415 | | SD_BUS_METHOD_WITH_ARGS("Reload", |
416 | | SD_BUS_NO_ARGS, |
417 | | SD_BUS_NO_RESULT, |
418 | | bus_method_reload, |
419 | | SD_BUS_VTABLE_UNPRIVILEGED), |
420 | | SD_BUS_METHOD_WITH_ARGS("DescribeLink", |
421 | | SD_BUS_ARGS("i", ifindex), |
422 | | SD_BUS_RESULT("s", json), |
423 | | bus_method_describe_link, |
424 | | SD_BUS_VTABLE_UNPRIVILEGED), |
425 | | SD_BUS_METHOD_WITH_ARGS("Describe", |
426 | | SD_BUS_NO_ARGS, |
427 | | SD_BUS_RESULT("s", json), |
428 | | bus_method_describe, |
429 | | SD_BUS_VTABLE_UNPRIVILEGED), |
430 | | |
431 | | SD_BUS_VTABLE_END |
432 | | }; |
433 | | |
434 | 0 | int manager_send_changed_strv(Manager *manager, char **properties) { |
435 | 0 | assert(manager); |
436 | 0 | assert(properties); |
437 | |
|
438 | 0 | if (sd_bus_is_ready(manager->bus) <= 0) |
439 | 0 | return 0; |
440 | | |
441 | 0 | return sd_bus_emit_properties_changed_strv( |
442 | 0 | manager->bus, |
443 | 0 | "/org/freedesktop/network1", |
444 | 0 | "org.freedesktop.network1.Manager", |
445 | 0 | properties); |
446 | 0 | } |
447 | | |
448 | | const BusObjectImplementation manager_object = { |
449 | | "/org/freedesktop/network1", |
450 | | "org.freedesktop.network1.Manager", |
451 | | .vtables = BUS_VTABLES(manager_vtable), |
452 | | .children = BUS_IMPLEMENTATIONS( |
453 | | &link_object, /* This is the main implementation for /org/freedesktop/network1/link, |
454 | | * and must be earlier than the dhcp objects below. */ |
455 | | &dhcp_server_object, |
456 | | &dhcp_client_object, |
457 | | &dhcp6_client_object, |
458 | | &network_object), |
459 | | }; |