]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-json.c
man/systemd.mount: tmpfs automatically gains After=swap.target dep
[thirdparty/systemd.git] / src / network / networkd-json.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <linux/nexthop.h>
4
5 #include "dhcp-server-internal.h"
6 #include "dhcp6-internal.h"
7 #include "dhcp6-lease-internal.h"
8 #include "dns-domain.h"
9 #include "ip-protocol-list.h"
10 #include "netif-util.h"
11 #include "networkd-address.h"
12 #include "networkd-dhcp-common.h"
13 #include "networkd-json.h"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-neighbor.h"
17 #include "networkd-network.h"
18 #include "networkd-nexthop.h"
19 #include "networkd-route-util.h"
20 #include "networkd-route.h"
21 #include "networkd-routing-policy-rule.h"
22 #include "sort-util.h"
23 #include "udev-util.h"
24 #include "user-util.h"
25 #include "wifi-util.h"
26
27 static int address_build_json(Address *address, JsonVariant **ret) {
28 _cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL;
29 int r;
30
31 assert(address);
32 assert(ret);
33
34 r = route_scope_to_string_alloc(address->scope, &scope);
35 if (r < 0)
36 return r;
37
38 r = address_flags_to_string_alloc(address->flags, address->family, &flags);
39 if (r < 0)
40 return r;
41
42 r = network_config_state_to_string_alloc(address->state, &state);
43 if (r < 0)
44 return r;
45
46 return json_build(ret, JSON_BUILD_OBJECT(
47 JSON_BUILD_PAIR_INTEGER("Family", address->family),
48 JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family),
49 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family),
50 JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast),
51 JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen),
52 JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
53 JSON_BUILD_PAIR_STRING("ScopeString", scope),
54 JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
55 JSON_BUILD_PAIR_STRING("FlagsString", flags),
56 JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label),
57 JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", address->lifetime_preferred_usec),
58 JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), /* for backward compat */
59 JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", address->lifetime_valid_usec),
60 JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), /* for backward compat */
61 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)),
62 JSON_BUILD_PAIR_STRING("ConfigState", state),
63 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family)));
64 }
65
66 static int addresses_append_json(Set *addresses, JsonVariant **v) {
67 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
68 Address *address;
69 int r;
70
71 assert(v);
72
73 SET_FOREACH(address, addresses) {
74 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
75
76 r = address_build_json(address, &e);
77 if (r < 0)
78 return r;
79
80 r = json_variant_append_array(&array, e);
81 if (r < 0)
82 return r;
83 }
84
85 return json_variant_set_field_non_null(v, "Addresses", array);
86 }
87
88 static int neighbor_build_json(Neighbor *n, JsonVariant **ret) {
89 _cleanup_free_ char *state = NULL;
90 int r;
91
92 assert(n);
93 assert(ret);
94
95 r = network_config_state_to_string_alloc(n->state, &state);
96 if (r < 0)
97 return r;
98
99 return json_build(ret, JSON_BUILD_OBJECT(
100 JSON_BUILD_PAIR_INTEGER("Family", n->family),
101 JSON_BUILD_PAIR_IN_ADDR("Destination", &n->in_addr, n->family),
102 JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr),
103 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
104 JSON_BUILD_PAIR_STRING("ConfigState", state)));
105 }
106
107 static int neighbors_append_json(Set *neighbors, JsonVariant **v) {
108 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
109 Neighbor *neighbor;
110 int r;
111
112 assert(v);
113
114 SET_FOREACH(neighbor, neighbors) {
115 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
116
117 r = neighbor_build_json(neighbor, &e);
118 if (r < 0)
119 return r;
120
121 r = json_variant_append_array(&array, e);
122 if (r < 0)
123 return r;
124 }
125
126 return json_variant_set_field_non_null(v, "Neighbors", array);
127 }
128
129 static int nexthop_group_build_json(NextHop *nexthop, JsonVariant **ret) {
130 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
131 struct nexthop_grp *g;
132 int r;
133
134 assert(nexthop);
135 assert(ret);
136
137 HASHMAP_FOREACH(g, nexthop->group) {
138 r = json_variant_append_arrayb(
139 &array,
140 JSON_BUILD_OBJECT(
141 JSON_BUILD_PAIR_UNSIGNED("ID", g->id),
142 JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1)));
143 if (r < 0)
144 return r;
145 }
146
147 *ret = TAKE_PTR(array);
148 return 0;
149 }
150
151 static int nexthop_build_json(NextHop *n, JsonVariant **ret) {
152 _cleanup_(json_variant_unrefp) JsonVariant *group = NULL;
153 _cleanup_free_ char *flags = NULL, *protocol = NULL, *state = NULL;
154 int r;
155
156 assert(n);
157 assert(ret);
158
159 r = route_flags_to_string_alloc(n->flags, &flags);
160 if (r < 0)
161 return r;
162
163 r = route_protocol_to_string_alloc(n->protocol, &protocol);
164 if (r < 0)
165 return r;
166
167 r = network_config_state_to_string_alloc(n->state, &state);
168 if (r < 0)
169 return r;
170
171 r = nexthop_group_build_json(n, &group);
172 if (r < 0)
173 return r;
174
175 return json_build(ret, JSON_BUILD_OBJECT(
176 JSON_BUILD_PAIR_UNSIGNED("ID", n->id),
177 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw, n->family),
178 JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags),
179 JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
180 JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol),
181 JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
182 JSON_BUILD_PAIR_BOOLEAN("Blackhole", n->blackhole),
183 JSON_BUILD_PAIR_VARIANT_NON_NULL("Group", group),
184 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)),
185 JSON_BUILD_PAIR_STRING("ConfigState", state)));
186 }
187
188 static int nexthops_append_json(Set *nexthops, JsonVariant **v) {
189 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
190 NextHop *nexthop;
191 int r;
192
193 assert(v);
194
195 SET_FOREACH(nexthop, nexthops) {
196 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
197
198 r = nexthop_build_json(nexthop, &e);
199 if (r < 0)
200 return r;
201
202 r = json_variant_append_array(&array, e);
203 if (r < 0)
204 return r;
205 }
206
207 return json_variant_set_field_non_null(v, "NextHops", array);
208 }
209
210 static int route_build_json(Route *route, JsonVariant **ret) {
211 _cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL;
212 Manager *manager;
213 int r;
214
215 assert(route);
216 assert(ret);
217
218 manager = route->link ? route->link->manager : route->manager;
219
220 assert(manager);
221
222 r = route_scope_to_string_alloc(route->scope, &scope);
223 if (r < 0)
224 return r;
225
226 r = route_protocol_to_string_alloc(route->protocol, &protocol);
227 if (r < 0)
228 return r;
229
230 r = manager_get_route_table_to_string(manager, route->table, /* append_num = */ false, &table);
231 if (r < 0)
232 return r;
233
234 r = route_flags_to_string_alloc(route->flags, &flags);
235 if (r < 0)
236 return r;
237
238 r = network_config_state_to_string_alloc(route->state, &state);
239 if (r < 0)
240 return r;
241
242 return json_build(ret, JSON_BUILD_OBJECT(
243 JSON_BUILD_PAIR_INTEGER("Family", route->family),
244 JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family),
245 JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen),
246 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->gw, route->gw_family),
247 JSON_BUILD_PAIR_CONDITION(route->src_prefixlen > 0,
248 "Source", JSON_BUILD_IN_ADDR(&route->src, route->family)),
249 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("SourcePrefixLength", route->src_prefixlen),
250 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("PreferredSource", &route->prefsrc, route->family),
251 JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope),
252 JSON_BUILD_PAIR_STRING("ScopeString", scope),
253 JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol),
254 JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
255 JSON_BUILD_PAIR_UNSIGNED("Type", route->type),
256 JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)),
257 JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority),
258 JSON_BUILD_PAIR_UNSIGNED("Table", route->table),
259 JSON_BUILD_PAIR_STRING("TableString", table),
260 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route->mtu),
261 JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
262 JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
263 JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
264 JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec),
265 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)),
266 JSON_BUILD_PAIR_STRING("ConfigState", state),
267 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family)));
268 }
269
270 static int routes_append_json(Set *routes, JsonVariant **v) {
271 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
272 Route *route;
273 int r;
274
275 assert(v);
276
277 SET_FOREACH(route, routes) {
278 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
279
280 r = route_build_json(route, &e);
281 if (r < 0)
282 return r;
283
284 r = json_variant_append_array(&array, e);
285 if (r < 0)
286 return r;
287 }
288
289 return json_variant_set_field_non_null(v, "Routes", array);
290 }
291
292 static int routing_policy_rule_build_json(RoutingPolicyRule *rule, JsonVariant **ret) {
293 _cleanup_free_ char *table = NULL, *protocol = NULL, *state = NULL;
294 int r;
295
296 assert(rule);
297 assert(rule->manager);
298 assert(ret);
299
300 r = manager_get_route_table_to_string(rule->manager, rule->table, /* append_num = */ false, &table);
301 if (r < 0 && r != -EINVAL)
302 return r;
303
304 r = route_protocol_to_string_alloc(rule->protocol, &protocol);
305 if (r < 0)
306 return r;
307
308 r = network_config_state_to_string_alloc(rule->state, &state);
309 if (r < 0)
310 return r;
311
312 return json_build(ret, JSON_BUILD_OBJECT(
313 JSON_BUILD_PAIR_INTEGER("Family", rule->family),
314 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("FromPrefix", &rule->from, rule->family),
315 JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->from),
316 "FromPrefixLength", JSON_BUILD_UNSIGNED(rule->from_prefixlen)),
317 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ToPrefix", &rule->to, rule->family),
318 JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->to),
319 "ToPrefixLength", JSON_BUILD_UNSIGNED(rule->to_prefixlen)),
320 JSON_BUILD_PAIR_UNSIGNED("Protocol", rule->protocol),
321 JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
322 JSON_BUILD_PAIR_UNSIGNED("TOS", rule->tos),
323 JSON_BUILD_PAIR_UNSIGNED("Type", rule->type),
324 JSON_BUILD_PAIR_STRING("TypeString", fr_act_type_full_to_string(rule->type)),
325 JSON_BUILD_PAIR_UNSIGNED("IPProtocol", rule->ipproto),
326 JSON_BUILD_PAIR_STRING("IPProtocolString", ip_protocol_to_name(rule->ipproto)),
327 JSON_BUILD_PAIR_UNSIGNED("Priority", rule->priority),
328 JSON_BUILD_PAIR_UNSIGNED("FirewallMark", rule->fwmark),
329 JSON_BUILD_PAIR_UNSIGNED("FirewallMask", rule->fwmask),
330 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Table", rule->table),
331 JSON_BUILD_PAIR_STRING_NON_EMPTY("TableString", table),
332 JSON_BUILD_PAIR_BOOLEAN("Invert", rule->invert_rule),
333 JSON_BUILD_PAIR_CONDITION(rule->suppress_prefixlen >= 0,
334 "SuppressPrefixLength", JSON_BUILD_UNSIGNED(rule->suppress_prefixlen)),
335 JSON_BUILD_PAIR_CONDITION(rule->suppress_ifgroup >= 0,
336 "SuppressInterfaceGroup", JSON_BUILD_UNSIGNED(rule->suppress_ifgroup)),
337 JSON_BUILD_PAIR_CONDITION(rule->sport.start != 0 || rule->sport.end != 0, "SourcePort",
338 JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->sport.start), JSON_BUILD_UNSIGNED(rule->sport.end))),
339 JSON_BUILD_PAIR_CONDITION(rule->dport.start != 0 || rule->dport.end != 0, "DestinationPort",
340 JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->dport.start), JSON_BUILD_UNSIGNED(rule->dport.end))),
341 JSON_BUILD_PAIR_CONDITION(rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID, "User",
342 JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->uid_range.start), JSON_BUILD_UNSIGNED(rule->uid_range.end))),
343 JSON_BUILD_PAIR_STRING_NON_EMPTY("IncomingInterface", rule->iif),
344 JSON_BUILD_PAIR_STRING_NON_EMPTY("OutgoingInterface", rule->oif),
345 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(rule->source)),
346 JSON_BUILD_PAIR_STRING("ConfigState", state)));
347 }
348
349 static int routing_policy_rules_append_json(Set *rules, JsonVariant **v) {
350 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
351 RoutingPolicyRule *rule;
352 int r;
353
354 assert(v);
355
356 SET_FOREACH(rule, rules) {
357 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
358
359 r = routing_policy_rule_build_json(rule, &e);
360 if (r < 0)
361 return r;
362
363 r = json_variant_append_array(&array, e);
364 if (r < 0)
365 return r;
366 }
367
368 return json_variant_set_field_non_null(v, "RoutingPolicyRules", array);
369 }
370
371 static int network_append_json(Network *network, JsonVariant **v) {
372 assert(v);
373
374 if (!network)
375 return 0;
376
377 return json_variant_merge_objectb(
378 v, JSON_BUILD_OBJECT(
379 JSON_BUILD_PAIR_STRING("NetworkFile", network->filename),
380 JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins),
381 JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online),
382 JSON_BUILD_PAIR("RequiredOperationalStateForOnline",
383 JSON_BUILD_ARRAY(JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.min)),
384 JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.max)))),
385 JSON_BUILD_PAIR_STRING("RequiredFamilyForOnline",
386 link_required_address_family_to_string(network->required_family_for_online)),
387 JSON_BUILD_PAIR_STRING("ActivationPolicy",
388 activation_policy_to_string(network->activation_policy))));
389 }
390
391 static int device_append_json(sd_device *device, JsonVariant **v) {
392 _cleanup_strv_free_ char **link_dropins = NULL;
393 const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL, *joined;
394 int r;
395
396 assert(v);
397
398 if (!device)
399 return 0;
400
401 (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link);
402
403 if (sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) {
404 r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE);
405 if (r < 0)
406 return r;
407 }
408
409 (void) sd_device_get_property_value(device, "ID_PATH", &path);
410
411 (void) device_get_vendor_string(device, &vendor);
412 (void) device_get_model_string(device, &model);
413
414 return json_variant_merge_objectb(
415 v,
416 JSON_BUILD_OBJECT(
417 JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link),
418 JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins),
419 JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path),
420 JSON_BUILD_PAIR_STRING_NON_EMPTY("Vendor", vendor),
421 JSON_BUILD_PAIR_STRING_NON_EMPTY("Model", model)));
422 }
423
424 static int dns_append_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
425 assert(link);
426 assert(a);
427 assert(array);
428
429 if (a->ifindex != 0 && a->ifindex != link->ifindex)
430 return 0;
431
432 return json_variant_append_arrayb(
433 array,
434 JSON_BUILD_OBJECT(
435 JSON_BUILD_PAIR_INTEGER("Family", a->family),
436 JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family),
437 JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port),
438 JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)),
439 JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name),
440 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
441 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family)));
442 }
443
444 static int dns_append_json(Link *link, JsonVariant **v) {
445 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
446 int r;
447
448 assert(link);
449 assert(v);
450
451 if (!link->network)
452 return 0;
453
454 if (link->n_dns != UINT_MAX)
455 for (unsigned i = 0; i < link->n_dns; i++) {
456 r = dns_append_json_one(link, link->dns[i], NETWORK_CONFIG_SOURCE_RUNTIME, NULL, &array);
457 if (r < 0)
458 return r;
459 }
460 else {
461 for (unsigned i = 0; i < link->network->n_dns; i++) {
462 r = dns_append_json_one(link, link->network->dns[i], NETWORK_CONFIG_SOURCE_STATIC, NULL, &array);
463 if (r < 0)
464 return r;
465 }
466
467 if (link->dhcp_lease && link->network->dhcp_use_dns) {
468 const struct in_addr *dns;
469 union in_addr_union s;
470 int n_dns;
471
472 r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
473 if (r < 0)
474 return r;
475
476 n_dns = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns);
477 for (int i = 0; i < n_dns; i++) {
478 r = dns_append_json_one(link,
479 &(struct in_addr_full) { .family = AF_INET, .address.in = dns[i], },
480 NETWORK_CONFIG_SOURCE_DHCP4,
481 &s,
482 &array);
483 if (r < 0)
484 return r;
485 }
486 }
487
488 if (link->dhcp6_lease && link->network->dhcp6_use_dns) {
489 const struct in6_addr *dns;
490 union in_addr_union s;
491 int n_dns;
492
493 r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6);
494 if (r < 0)
495 return r;
496
497 n_dns = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &dns);
498 for (int i = 0; i < n_dns; i++) {
499 r = dns_append_json_one(link,
500 &(struct in_addr_full) { .family = AF_INET6, .address.in6 = dns[i], },
501 NETWORK_CONFIG_SOURCE_DHCP6,
502 &s,
503 &array);
504 if (r < 0)
505 return r;
506 }
507 }
508
509 if (link->network->ipv6_accept_ra_use_dns) {
510 NDiscRDNSS *a;
511
512 SET_FOREACH(a, link->ndisc_rdnss) {
513 r = dns_append_json_one(link,
514 &(struct in_addr_full) { .family = AF_INET6, .address.in6 = a->address, },
515 NETWORK_CONFIG_SOURCE_NDISC,
516 &(union in_addr_union) { .in6 = a->router },
517 &array);
518 if (r < 0)
519 return r;
520 }
521 }
522 }
523
524 return json_variant_set_field_non_null(v, "DNS", array);
525 }
526
527 static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
528 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
529 int r;
530
531 assert(IN_SET(family, AF_INET, AF_INET6));
532 assert(a);
533 assert(array);
534
535 r = json_build(&v, JSON_BUILD_OBJECT(
536 JSON_BUILD_PAIR_INTEGER("Family", family),
537 JSON_BUILD_PAIR_IN_ADDR("Address", a, family),
538 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
539 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)));
540 if (r < 0)
541 return r;
542
543 return json_variant_append_array(array, v);
544 }
545
546 static int server_append_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
547 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
548 int r;
549
550 assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6));
551 assert(fqdn);
552 assert(array);
553
554 r = json_build(&v, JSON_BUILD_OBJECT(
555 JSON_BUILD_PAIR_STRING("Server", fqdn),
556 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
557 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)));
558 if (r < 0)
559 return r;
560
561 return json_variant_append_array(array, v);
562 }
563
564 static int server_append_json_one_string(const char *str, NetworkConfigSource s, JsonVariant **array) {
565 union in_addr_union a;
566 int family;
567
568 assert(str);
569
570 if (in_addr_from_string_auto(str, &family, &a) >= 0)
571 return server_append_json_one_addr(family, &a, s, NULL, array);
572
573 return server_append_json_one_fqdn(AF_UNSPEC, str, s, NULL, array);
574 }
575
576 static int ntp_append_json(Link *link, JsonVariant **v) {
577 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
578 int r;
579
580 assert(link);
581 assert(v);
582
583 if (!link->network)
584 return 0;
585
586 STRV_FOREACH(p, link->ntp ?: link->network->ntp) {
587 r = server_append_json_one_string(*p, NETWORK_CONFIG_SOURCE_RUNTIME, &array);
588 if (r < 0)
589 return r;
590 }
591
592 if (!link->ntp) {
593 if (link->dhcp_lease && link->network->dhcp_use_ntp) {
594 const struct in_addr *ntp;
595 union in_addr_union s;
596 int n_ntp;
597
598 r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
599 if (r < 0)
600 return r;
601
602 n_ntp = sd_dhcp_lease_get_ntp(link->dhcp_lease, &ntp);
603 for (int i = 0; i < n_ntp; i++) {
604 r = server_append_json_one_addr(AF_INET,
605 &(union in_addr_union) { .in = ntp[i], },
606 NETWORK_CONFIG_SOURCE_DHCP4,
607 &s,
608 &array);
609 if (r < 0)
610 return r;
611 }
612 }
613
614 if (link->dhcp6_lease && link->network->dhcp6_use_ntp) {
615 const struct in6_addr *ntp_addr;
616 union in_addr_union s;
617 char **ntp_fqdn;
618 int n_ntp;
619
620 r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6);
621 if (r < 0)
622 return r;
623
624 n_ntp = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &ntp_addr);
625 for (int i = 0; i < n_ntp; i++) {
626 r = server_append_json_one_addr(AF_INET6,
627 &(union in_addr_union) { .in6 = ntp_addr[i], },
628 NETWORK_CONFIG_SOURCE_DHCP6,
629 &s,
630 &array);
631 if (r < 0)
632 return r;
633 }
634
635 n_ntp = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &ntp_fqdn);
636 for (int i = 0; i < n_ntp; i++) {
637 r = server_append_json_one_fqdn(AF_INET6,
638 ntp_fqdn[i],
639 NETWORK_CONFIG_SOURCE_DHCP6,
640 &s,
641 &array);
642 if (r < 0)
643 return r;
644 }
645 }
646 }
647
648 return json_variant_set_field_non_null(v, "NTP", array);
649 }
650
651 static int sip_append_json(Link *link, JsonVariant **v) {
652 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
653 const struct in_addr *sip;
654 union in_addr_union s;
655 int n_sip, r;
656
657 assert(link);
658 assert(v);
659
660 if (!link->network || !link->network->dhcp_use_sip || !link->dhcp_lease)
661 return 0;
662
663 n_sip = sd_dhcp_lease_get_sip(link->dhcp_lease, &sip);
664 if (n_sip <= 0)
665 return 0;
666
667 r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
668 if (r < 0)
669 return r;
670
671 for (int i = 0; i < n_sip; i++) {
672 r = server_append_json_one_addr(AF_INET,
673 &(union in_addr_union) { .in = sip[i], },
674 NETWORK_CONFIG_SOURCE_DHCP4,
675 &s,
676 &array);
677 if (r < 0)
678 return r;
679 }
680
681 return json_variant_set_field_non_null(v, "SIP", array);
682 }
683
684 static int domain_append_json(int family, const char *domain, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **array) {
685 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
686 int r;
687
688 assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6));
689 assert(domain);
690 assert(array);
691
692 r = json_build(&v, JSON_BUILD_OBJECT(
693 JSON_BUILD_PAIR_STRING("Domain", domain),
694 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
695 JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family)));
696 if (r < 0)
697 return r;
698
699 return json_variant_append_array(array, v);
700 }
701
702 static int domains_append_json(Link *link, bool is_route, JsonVariant **v) {
703 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
704 OrderedSet *link_domains, *network_domains;
705 DHCPUseDomains use_domains;
706 union in_addr_union s;
707 char **domains;
708 const char *domain;
709 int r;
710
711 assert(link);
712 assert(v);
713
714 if (!link->network)
715 return 0;
716
717 link_domains = is_route ? link->route_domains : link->search_domains;
718 network_domains = is_route ? link->network->route_domains : link->network->search_domains;
719 use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES;
720
721 ORDERED_SET_FOREACH(domain, link_domains ?: network_domains) {
722 r = domain_append_json(AF_UNSPEC, domain,
723 link_domains ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC,
724 NULL, &array);
725 if (r < 0)
726 return r;
727 }
728
729 if (!link_domains) {
730 if (link->dhcp_lease &&
731 link->network->dhcp_use_domains == use_domains) {
732 r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
733 if (r < 0)
734 return r;
735
736 if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domain) >= 0) {
737 r = domain_append_json(AF_INET, domain, NETWORK_CONFIG_SOURCE_DHCP4, &s, &array);
738 if (r < 0)
739 return r;
740 }
741
742 if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0)
743 STRV_FOREACH(p, domains) {
744 r = domain_append_json(AF_INET, *p, NETWORK_CONFIG_SOURCE_DHCP4, &s, &array);
745 if (r < 0)
746 return r;
747 }
748 }
749
750 if (link->dhcp6_lease &&
751 link->network->dhcp6_use_domains == use_domains) {
752 r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6);
753 if (r < 0)
754 return r;
755
756 if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0)
757 STRV_FOREACH(p, domains) {
758 r = domain_append_json(AF_INET6, *p, NETWORK_CONFIG_SOURCE_DHCP6, &s, &array);
759 if (r < 0)
760 return r;
761 }
762 }
763
764 if (link->network->ipv6_accept_ra_use_domains == use_domains) {
765 NDiscDNSSL *a;
766
767 SET_FOREACH(a, link->ndisc_dnssl) {
768 r = domain_append_json(AF_INET6, NDISC_DNSSL_DOMAIN(a), NETWORK_CONFIG_SOURCE_NDISC,
769 &(union in_addr_union) { .in6 = a->router },
770 &array);
771 if (r < 0)
772 return r;
773 }
774 }
775 }
776
777 return json_variant_set_field_non_null(v, is_route ? "RouteDomains" : "SearchDomains", array);
778 }
779
780 static int nta_append_json(const char *nta, NetworkConfigSource s, JsonVariant **array) {
781 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
782 int r;
783
784 assert(nta);
785 assert(array);
786
787 r = json_build(&v, JSON_BUILD_OBJECT(
788 JSON_BUILD_PAIR_STRING("DNSSECNegativeTrustAnchor", nta),
789 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s))));
790 if (r < 0)
791 return r;
792
793 return json_variant_append_array(array, v);
794 }
795
796 static int ntas_append_json(Link *link, JsonVariant **v) {
797 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
798 const char *nta;
799 int r;
800
801 assert(link);
802 assert(v);
803
804 if (!link->network)
805 return 0;
806
807 SET_FOREACH(nta, link->dnssec_negative_trust_anchors ?: link->network->dnssec_negative_trust_anchors) {
808 r = nta_append_json(nta,
809 link->dnssec_negative_trust_anchors ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC,
810 &array);
811 if (r < 0)
812 return r;
813 }
814
815 return json_variant_set_field_non_null(v, "DNSSECNegativeTrustAnchors", array);
816 }
817
818 static int dns_misc_append_json(Link *link, JsonVariant **v) {
819 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
820 ResolveSupport resolve_support;
821 NetworkConfigSource source;
822 DnsOverTlsMode mode;
823 int t, r;
824
825 assert(link);
826 assert(v);
827
828 if (!link->network)
829 return 0;
830
831 resolve_support = link->llmnr >= 0 ? link->llmnr : link->network->llmnr;
832 if (resolve_support >= 0) {
833 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
834
835 source = link->llmnr >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC;
836
837 r = json_build(&e, JSON_BUILD_OBJECT(
838 JSON_BUILD_PAIR_STRING("LLMNR", resolve_support_to_string(resolve_support)),
839 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source))));
840 if (r < 0)
841 return r;
842
843 r = json_variant_append_array(&array, e);
844 if (r < 0)
845 return r;
846 }
847
848 resolve_support = link->mdns >= 0 ? link->mdns : link->network->mdns;
849 if (resolve_support >= 0) {
850 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
851
852 source = link->mdns >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC;
853
854 r = json_build(&e, JSON_BUILD_OBJECT(
855 JSON_BUILD_PAIR_STRING("MDNS", resolve_support_to_string(resolve_support)),
856 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source))));
857 if (r < 0)
858 return r;
859
860 r = json_variant_append_array(&array, e);
861 if (r < 0)
862 return r;
863 }
864
865 t = link->dns_default_route >= 0 ? link->dns_default_route : link->network->dns_default_route;
866 if (t >= 0) {
867 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
868
869 source = link->dns_default_route >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC;
870
871 r = json_build(&e, JSON_BUILD_OBJECT(
872 JSON_BUILD_PAIR_BOOLEAN("DNSDefaultRoute", t),
873 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source))));
874 if (r < 0)
875 return r;
876
877 r = json_variant_append_array(&array, e);
878 if (r < 0)
879 return r;
880 }
881
882 mode = link->dns_over_tls_mode >= 0 ? link->dns_over_tls_mode : link->network->dns_over_tls_mode;
883 if (mode >= 0) {
884 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
885
886 source = link->dns_over_tls_mode >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC;
887
888 r = json_build(&e, JSON_BUILD_OBJECT(
889 JSON_BUILD_PAIR_STRING("DNSOverTLS", dns_over_tls_mode_to_string(mode)),
890 JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source))));
891 if (r < 0)
892 return r;
893
894 r = json_variant_append_array(&array, e);
895 if (r < 0)
896 return r;
897 }
898
899 return json_variant_set_field_non_null(v, "DNSSettings", array);
900 }
901
902 static int captive_portal_append_json(Link *link, JsonVariant **v) {
903 const char *captive_portal;
904 int r;
905
906 assert(link);
907 assert(v);
908
909 r = link_get_captive_portal(link, &captive_portal);
910 if (r <= 0)
911 return r;
912
913 return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
914 }
915
916 static int pref64_append_json(Link *link, JsonVariant **v) {
917 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL, *w = NULL;
918 NDiscPREF64 *i;
919 int r;
920
921 assert(link);
922 assert(v);
923
924 if (!link->network || !link->network->ipv6_accept_ra_use_pref64)
925 return 0;
926
927 SET_FOREACH(i, link->ndisc_pref64) {
928 r = json_variant_append_arrayb(&array,
929 JSON_BUILD_OBJECT(
930 JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("Prefix", &i->prefix),
931 JSON_BUILD_PAIR_UNSIGNED("PrefixLength", i->prefix_len),
932 JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", i->lifetime_usec),
933 JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("ConfigProvider", &i->router)));
934 if (r < 0)
935 return r;
936 }
937
938 r = json_variant_set_field_non_null(&w, "PREF64", array);
939 if (r < 0)
940 return r;
941
942 return json_variant_set_field_non_null(v, "NDisc", w);
943 }
944
945 static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
946 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
947 DHCPLease *lease;
948 int r;
949
950 assert(link);
951 assert(v);
952
953 if (!link->dhcp_server)
954 return 0;
955
956 HASHMAP_FOREACH(lease, link->dhcp_server->bound_leases_by_client_id) {
957 struct in_addr address = { .s_addr = lease->address };
958
959 r = json_variant_append_arrayb(
960 &array,
961 JSON_BUILD_OBJECT(
962 JSON_BUILD_PAIR_BYTE_ARRAY(
963 "ClientId",
964 lease->client_id.data,
965 lease->client_id.length),
966 JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
967 JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
968 JSON_BUILD_PAIR_FINITE_USEC(
969 "ExpirationUSec", lease->expiration)));
970 if (r < 0)
971 return r;
972 }
973
974 return json_variant_set_field_non_null(v, "Leases", array);
975 }
976
977 static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) {
978 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
979 DHCPLease *lease;
980 int r;
981
982 assert(link);
983 assert(v);
984
985 if (!link->dhcp_server)
986 return 0;
987
988 HASHMAP_FOREACH(lease, link->dhcp_server->static_leases_by_client_id) {
989 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
990 struct in_addr address = { .s_addr = lease->address };
991
992 r = json_build(&e,
993 JSON_BUILD_OBJECT(
994 JSON_BUILD_PAIR_BYTE_ARRAY(
995 "ClientId",
996 lease->client_id.data,
997 lease->client_id.length),
998 JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address)));
999 if (r < 0)
1000 return r;
1001
1002 r = json_variant_append_array(&array, e);
1003 if (r < 0)
1004 return r;
1005 }
1006
1007 return json_variant_set_field_non_null(v, "StaticLeases", array);
1008 }
1009
1010 static int dhcp_server_append_json(Link *link, JsonVariant **v) {
1011 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1012 int r;
1013
1014 assert(link);
1015 assert(v);
1016
1017 if (!link->dhcp_server)
1018 return 0;
1019
1020 r = json_build(&w,
1021 JSON_BUILD_OBJECT(
1022 JSON_BUILD_PAIR_UNSIGNED("PoolOffset", link->dhcp_server->pool_offset),
1023 JSON_BUILD_PAIR_UNSIGNED("PoolSize", link->dhcp_server->pool_size)));
1024 if (r < 0)
1025 return r;
1026
1027 r = dhcp_server_offered_leases_append_json(link, &w);
1028 if (r < 0)
1029 return r;
1030
1031 r = dhcp_server_static_leases_append_json(link, &w);
1032 if (r < 0)
1033 return r;
1034
1035 return json_variant_set_field_non_null(v, "DHCPServer", w);
1036 }
1037
1038 static int dhcp6_client_vendor_options_append_json(Link *link, JsonVariant **v) {
1039 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1040 sd_dhcp6_option **options = NULL;
1041 int r, n_vendor_options;
1042
1043 assert(link);
1044 assert(v);
1045
1046 if (!link->dhcp6_lease)
1047 return 0;
1048
1049 n_vendor_options = sd_dhcp6_lease_get_vendor_options(link->dhcp6_lease, &options);
1050
1051 FOREACH_ARRAY(option, options, n_vendor_options) {
1052 r = json_variant_append_arrayb(&array,
1053 JSON_BUILD_OBJECT(
1054 JSON_BUILD_PAIR_UNSIGNED("EnterpriseId", (*option)->enterprise_identifier),
1055 JSON_BUILD_PAIR_UNSIGNED("SubOptionCode", (*option)->option),
1056 JSON_BUILD_PAIR_HEX("SubOptionData", (*option)->data, (*option)->length)));
1057 if (r < 0)
1058 return r;
1059 }
1060
1061 return json_variant_set_field_non_null(v, "VendorSpecificOptions", array);
1062 }
1063
1064 static int dhcp6_client_lease_append_json(Link *link, JsonVariant **v) {
1065 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1066 usec_t ts = USEC_INFINITY, t1 = USEC_INFINITY, t2 = USEC_INFINITY;
1067 int r;
1068
1069 assert(link);
1070 assert(v);
1071
1072 if (!link->dhcp6_lease)
1073 return 0;
1074
1075 r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &ts);
1076 if (r < 0 && r != -ENODATA)
1077 return r;
1078
1079 r = sd_dhcp6_lease_get_t1_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &t1);
1080 if (r < 0 && r != -ENODATA)
1081 return r;
1082
1083 r = sd_dhcp6_lease_get_t2_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, &t2);
1084 if (r < 0 && r != -ENODATA)
1085 return r;
1086
1087 r = json_build(&w, JSON_BUILD_OBJECT(
1088 JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", t1),
1089 JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", t2),
1090 JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", ts)));
1091 if (r < 0)
1092 return r;
1093
1094 return json_variant_set_field_non_null(v, "Lease", w);
1095 }
1096
1097 static int dhcp6_client_pd_append_json(Link *link, JsonVariant **v) {
1098 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1099 int r;
1100
1101 assert(link);
1102 assert(link->network);
1103 assert(v);
1104
1105 if (!link->network->dhcp6_use_pd_prefix ||
1106 !sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
1107 return 0;
1108
1109 FOREACH_DHCP6_PD_PREFIX(link->dhcp6_lease) {
1110 usec_t lifetime_preferred_usec, lifetime_valid_usec;
1111 struct in6_addr prefix;
1112 uint8_t prefix_len;
1113
1114 r = sd_dhcp6_lease_get_pd_prefix(link->dhcp6_lease, &prefix, &prefix_len);
1115 if (r < 0)
1116 return r;
1117
1118 r = sd_dhcp6_lease_get_pd_lifetime_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME,
1119 &lifetime_preferred_usec, &lifetime_valid_usec);
1120 if (r < 0)
1121 return r;
1122
1123 r = json_variant_append_arrayb(&array, JSON_BUILD_OBJECT(
1124 JSON_BUILD_PAIR_IN6_ADDR("Prefix", &prefix),
1125 JSON_BUILD_PAIR_UNSIGNED("PrefixLength", prefix_len),
1126 JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", lifetime_preferred_usec),
1127 JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", lifetime_valid_usec)));
1128 if (r < 0)
1129 return r;
1130 }
1131
1132 return json_variant_set_field_non_null(v, "Prefixes", array);
1133 }
1134
1135 static int dhcp6_client_append_json(Link *link, JsonVariant **v) {
1136 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1137 int r;
1138
1139 assert(link);
1140 assert(v);
1141
1142 if (!link->dhcp6_client)
1143 return 0;
1144
1145 r = dhcp6_client_lease_append_json(link, &w);
1146 if (r < 0)
1147 return r;
1148
1149 r = dhcp6_client_pd_append_json(link, &w);
1150 if (r < 0)
1151 return r;
1152
1153 r = dhcp6_client_vendor_options_append_json(link, &w);
1154 if (r < 0)
1155 return r;
1156
1157 return json_variant_set_field_non_null(v, "DHCPv6Client", w);
1158 }
1159
1160 static int dhcp_client_lease_append_json(Link *link, JsonVariant **v) {
1161 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1162 usec_t lease_timestamp_usec = USEC_INFINITY, t1 = USEC_INFINITY, t2 = USEC_INFINITY;
1163 int r;
1164
1165 assert(link);
1166 assert(v);
1167
1168 if (!link->dhcp_client || !link->dhcp_lease)
1169 return 0;
1170
1171 r = sd_dhcp_lease_get_timestamp(link->dhcp_lease, CLOCK_BOOTTIME, &lease_timestamp_usec);
1172 if (r < 0 && r != -ENODATA)
1173 return r;
1174
1175 r = sd_dhcp_lease_get_t1_timestamp(link->dhcp_lease, CLOCK_BOOTTIME, &t1);
1176 if (r < 0 && r != -ENODATA)
1177 return r;
1178
1179 r = sd_dhcp_lease_get_t2_timestamp(link->dhcp_lease, CLOCK_BOOTTIME, &t2);
1180 if (r < 0 && r != -ENODATA)
1181 return r;
1182
1183 r = json_build(&w, JSON_BUILD_OBJECT(
1184 JSON_BUILD_PAIR_FINITE_USEC("LeaseTimestampUSec", lease_timestamp_usec),
1185 JSON_BUILD_PAIR_FINITE_USEC("Timeout1USec", t1),
1186 JSON_BUILD_PAIR_FINITE_USEC("Timeout2USec", t2)));
1187 if (r < 0)
1188 return r;
1189
1190 return json_variant_set_field_non_null(v, "Lease", w);
1191 }
1192
1193 static int dhcp_client_pd_append_json(Link *link, JsonVariant **v) {
1194 _cleanup_(json_variant_unrefp) JsonVariant *addresses = NULL, *array = NULL;
1195 uint8_t ipv4masklen, sixrd_prefixlen;
1196 struct in6_addr sixrd_prefix;
1197 const struct in_addr *br_addresses;
1198 size_t n_br_addresses = 0;
1199 int r;
1200
1201 assert(link);
1202 assert(link->network);
1203 assert(v);
1204
1205 if (!link->network->dhcp_use_6rd || !sd_dhcp_lease_has_6rd(link->dhcp_lease))
1206 return 0;
1207
1208 r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, &n_br_addresses);
1209 if (r < 0)
1210 return r;
1211
1212 FOREACH_ARRAY(br_address, br_addresses, n_br_addresses) {
1213 r = json_variant_append_arrayb(&addresses, JSON_BUILD_IN4_ADDR(br_address));
1214 if (r < 0)
1215 return r;
1216 }
1217
1218 r = json_build(&array, JSON_BUILD_OBJECT(
1219 JSON_BUILD_PAIR_IN6_ADDR("Prefix", &sixrd_prefix),
1220 JSON_BUILD_PAIR_UNSIGNED("PrefixLength", sixrd_prefixlen),
1221 JSON_BUILD_PAIR_UNSIGNED("IPv4MaskLength", ipv4masklen),
1222 JSON_BUILD_PAIR_VARIANT_NON_NULL("BorderRouters", addresses)));
1223 if (r < 0)
1224 return r;
1225
1226 return json_variant_set_field_non_null(v, "6rdPrefix", array);
1227 }
1228
1229 static int dhcp_client_append_json(Link *link, JsonVariant **v) {
1230 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1231 int r;
1232
1233 assert(link);
1234 assert(v);
1235
1236 if (!link->dhcp_client)
1237 return 0;
1238
1239 r = dhcp_client_lease_append_json(link, &w);
1240 if (r < 0)
1241 return r;
1242
1243 r = dhcp_client_pd_append_json(link, &w);
1244 if (r < 0)
1245 return r;
1246
1247 return json_variant_set_field_non_null(v, "DHCPv4Client", w);
1248 }
1249
1250 int link_build_json(Link *link, JsonVariant **ret) {
1251 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1252 _cleanup_free_ char *type = NULL, *flags = NULL;
1253 int r;
1254
1255 assert(link);
1256 assert(ret);
1257
1258 r = net_get_type_string(link->dev, link->iftype, &type);
1259 if (r == -ENOMEM)
1260 return r;
1261
1262 r = link_flags_to_string_alloc(link->flags, &flags);
1263 if (r < 0)
1264 return r;
1265
1266 r = json_build(&v, JSON_BUILD_OBJECT(
1267 /* basic information */
1268 JSON_BUILD_PAIR_INTEGER("Index", link->ifindex),
1269 JSON_BUILD_PAIR_STRING("Name", link->ifname),
1270 JSON_BUILD_PAIR_STRV_NON_EMPTY("AlternativeNames", link->alternative_names),
1271 JSON_BUILD_PAIR_CONDITION(link->master_ifindex > 0,
1272 "MasterInterfaceIndex", JSON_BUILD_INTEGER(link->master_ifindex)),
1273 JSON_BUILD_PAIR_STRING_NON_EMPTY("Kind", link->kind),
1274 JSON_BUILD_PAIR_STRING("Type", type),
1275 JSON_BUILD_PAIR_STRING_NON_EMPTY("Driver", link->driver),
1276 JSON_BUILD_PAIR_UNSIGNED("Flags", link->flags),
1277 JSON_BUILD_PAIR_STRING("FlagsString", flags),
1278 JSON_BUILD_PAIR_UNSIGNED("KernelOperationalState", link->kernel_operstate),
1279 JSON_BUILD_PAIR_STRING("KernelOperationalStateString", kernel_operstate_to_string(link->kernel_operstate)),
1280 JSON_BUILD_PAIR_UNSIGNED("MTU", link->mtu),
1281 JSON_BUILD_PAIR_UNSIGNED("MinimumMTU", link->min_mtu),
1282 JSON_BUILD_PAIR_UNSIGNED("MaximumMTU", link->max_mtu),
1283 JSON_BUILD_PAIR_HW_ADDR_NON_NULL("HardwareAddress", &link->hw_addr),
1284 JSON_BUILD_PAIR_HW_ADDR_NON_NULL("PermanentHardwareAddress", &link->permanent_hw_addr),
1285 JSON_BUILD_PAIR_HW_ADDR_NON_NULL("BroadcastAddress", &link->bcast_addr),
1286 JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("IPv6LinkLocalAddress", &link->ipv6ll_address),
1287 /* wlan information */
1288 JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceType",
1289 JSON_BUILD_UNSIGNED(link->wlan_iftype)),
1290 JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceTypeString",
1291 JSON_BUILD_STRING(nl80211_iftype_to_string(link->wlan_iftype))),
1292 JSON_BUILD_PAIR_STRING_NON_EMPTY("SSID", link->ssid),
1293 JSON_BUILD_PAIR_ETHER_ADDR_NON_NULL("BSSID", &link->bssid),
1294 /* link state */
1295 JSON_BUILD_PAIR_STRING("AdministrativeState", link_state_to_string(link->state)),
1296 JSON_BUILD_PAIR_STRING("OperationalState", link_operstate_to_string(link->operstate)),
1297 JSON_BUILD_PAIR_STRING("CarrierState", link_carrier_state_to_string(link->carrier_state)),
1298 JSON_BUILD_PAIR_STRING("AddressState", link_address_state_to_string(link->address_state)),
1299 JSON_BUILD_PAIR_STRING("IPv4AddressState", link_address_state_to_string(link->ipv4_address_state)),
1300 JSON_BUILD_PAIR_STRING("IPv6AddressState", link_address_state_to_string(link->ipv6_address_state)),
1301 JSON_BUILD_PAIR_STRING("OnlineState", link_online_state_to_string(link->online_state))));
1302 if (r < 0)
1303 return r;
1304
1305 r = network_append_json(link->network, &v);
1306 if (r < 0)
1307 return r;
1308
1309 r = device_append_json(link->dev, &v);
1310 if (r < 0)
1311 return r;
1312
1313 r = dns_append_json(link, &v);
1314 if (r < 0)
1315 return r;
1316
1317 r = ntp_append_json(link, &v);
1318 if (r < 0)
1319 return r;
1320
1321 r = sip_append_json(link, &v);
1322 if (r < 0)
1323 return r;
1324
1325 r = domains_append_json(link, /* is_route = */ false, &v);
1326 if (r < 0)
1327 return r;
1328
1329 r = domains_append_json(link, /* is_route = */ true, &v);
1330 if (r < 0)
1331 return r;
1332
1333 r = ntas_append_json(link, &v);
1334 if (r < 0)
1335 return r;
1336
1337 r = dns_misc_append_json(link, &v);
1338 if (r < 0)
1339 return r;
1340
1341 r = captive_portal_append_json(link, &v);
1342 if (r < 0)
1343 return r;
1344
1345 r = pref64_append_json(link, &v);
1346 if (r < 0)
1347 return r;
1348
1349 r = addresses_append_json(link->addresses, &v);
1350 if (r < 0)
1351 return r;
1352
1353 r = neighbors_append_json(link->neighbors, &v);
1354 if (r < 0)
1355 return r;
1356
1357 r = nexthops_append_json(link->nexthops, &v);
1358 if (r < 0)
1359 return r;
1360
1361 r = routes_append_json(link->routes, &v);
1362 if (r < 0)
1363 return r;
1364
1365 r = dhcp_server_append_json(link, &v);
1366 if (r < 0)
1367 return r;
1368
1369 r = dhcp_client_append_json(link, &v);
1370 if (r < 0)
1371 return r;
1372
1373 r = dhcp6_client_append_json(link, &v);
1374 if (r < 0)
1375 return r;
1376
1377 *ret = TAKE_PTR(v);
1378 return 0;
1379 }
1380
1381 static int links_append_json(Manager *manager, JsonVariant **v) {
1382 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1383 _cleanup_free_ Link **links = NULL;
1384 size_t n_links = 0;
1385 int r;
1386
1387 assert(manager);
1388 assert(v);
1389
1390 r = hashmap_dump_sorted(manager->links_by_index, (void***) &links, &n_links);
1391 if (r < 0)
1392 return r;
1393
1394 FOREACH_ARRAY(link, links, n_links) {
1395 _cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
1396
1397 r = link_build_json(*link, &e);
1398 if (r < 0)
1399 return r;
1400
1401 r = json_variant_append_array(&array, e);
1402 if (r < 0)
1403 return r;
1404 }
1405
1406 return json_variant_set_field_non_null(v, "Interfaces", array);
1407 }
1408
1409 int manager_build_json(Manager *manager, JsonVariant **ret) {
1410 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1411 int r;
1412
1413 assert(manager);
1414 assert(ret);
1415
1416 r = links_append_json(manager, &v);
1417 if (r < 0)
1418 return r;
1419
1420 r = nexthops_append_json(manager->nexthops, &v);
1421 if (r < 0)
1422 return r;
1423
1424 r = routes_append_json(manager->routes, &v);
1425 if (r < 0)
1426 return r;
1427
1428 r = routing_policy_rules_append_json(manager->rules, &v);
1429 if (r < 0)
1430 return r;
1431
1432 *ret = TAKE_PTR(v);
1433 return 0;
1434 }