| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <linux/nexthop.h> |
| |
| #include "dns-domain.h" |
| #include "ip-protocol-list.h" |
| #include "netif-util.h" |
| #include "networkd-address.h" |
| #include "networkd-json.h" |
| #include "networkd-link.h" |
| #include "networkd-manager.h" |
| #include "networkd-neighbor.h" |
| #include "networkd-nexthop.h" |
| #include "networkd-network.h" |
| #include "networkd-route-util.h" |
| #include "networkd-route.h" |
| #include "networkd-routing-policy-rule.h" |
| #include "sort-util.h" |
| #include "user-util.h" |
| #include "wifi-util.h" |
| |
| static int address_build_json(Address *address, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
| _cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL; |
| int r; |
| |
| assert(address); |
| assert(ret); |
| |
| r = route_scope_to_string_alloc(address->scope, &scope); |
| if (r < 0) |
| return r; |
| |
| r = address_flags_to_string_alloc(address->flags, address->family, &flags); |
| if (r < 0) |
| return r; |
| |
| r = network_config_state_to_string_alloc(address->state, &state); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", address->family), |
| JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family), |
| JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast), |
| JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen), |
| JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope), |
| JSON_BUILD_PAIR_STRING("ScopeString", scope), |
| JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags), |
| JSON_BUILD_PAIR_STRING("FlagsString", flags), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label), |
| JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), |
| JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)), |
| JSON_BUILD_PAIR_STRING("ConfigState", state), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int addresses_build_json(Set *addresses, JsonVariant **ret) { |
| JsonVariant **elements; |
| Address *address; |
| size_t n = 0; |
| int r; |
| |
| assert(ret); |
| |
| if (set_isempty(addresses)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, set_size(addresses)); |
| if (!elements) |
| return -ENOMEM; |
| |
| SET_FOREACH(address, addresses) { |
| r = address_build_json(address, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Addresses", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int neighbor_build_json(Neighbor *n, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
| _cleanup_free_ char *state = NULL; |
| int r; |
| |
| assert(n); |
| assert(ret); |
| |
| r = network_config_state_to_string_alloc(n->state, &state); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", n->family), |
| JSON_BUILD_PAIR_IN_ADDR("Destination", &n->in_addr, n->family), |
| JSON_BUILD_PAIR_HW_ADDR("LinkLayerAddress", &n->ll_addr), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), |
| JSON_BUILD_PAIR_STRING("ConfigState", state))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int neighbors_build_json(Set *neighbors, JsonVariant **ret) { |
| JsonVariant **elements; |
| Neighbor *neighbor; |
| size_t n = 0; |
| int r; |
| |
| assert(ret); |
| |
| if (set_isempty(neighbors)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, set_size(neighbors)); |
| if (!elements) |
| return -ENOMEM; |
| |
| SET_FOREACH(neighbor, neighbors) { |
| r = neighbor_build_json(neighbor, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Neighbors", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int nexthop_group_build_json(NextHop *nexthop, JsonVariant **ret) { |
| JsonVariant **elements; |
| struct nexthop_grp *g; |
| size_t n = 0; |
| int r; |
| |
| assert(nexthop); |
| assert(ret); |
| |
| if (hashmap_isempty(nexthop->group)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, hashmap_size(nexthop->group)); |
| if (!elements) |
| return -ENOMEM; |
| |
| HASHMAP_FOREACH(g, nexthop->group) { |
| r = json_build(elements + n, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_UNSIGNED("ID", g->id), |
| JSON_BUILD_PAIR_UNSIGNED("Weight", g->weight+1))); |
| if (r < 0) |
| goto failure; |
| |
| n++; |
| } |
| |
| r = json_variant_new_array(ret, elements, n); |
| |
| failure: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int nexthop_build_json(NextHop *n, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *group = NULL; |
| _cleanup_free_ char *flags = NULL, *protocol = NULL, *state = NULL; |
| int r; |
| |
| assert(n); |
| assert(ret); |
| |
| r = route_flags_to_string_alloc(n->flags, &flags); |
| if (r < 0) |
| return r; |
| |
| r = route_protocol_to_string_alloc(n->protocol, &protocol); |
| if (r < 0) |
| return r; |
| |
| r = network_config_state_to_string_alloc(n->state, &state); |
| if (r < 0) |
| return r; |
| |
| r = nexthop_group_build_json(n, &group); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_UNSIGNED("ID", n->id), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &n->gw, n->family), |
| JSON_BUILD_PAIR_UNSIGNED("Flags", n->flags), |
| JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)), |
| JSON_BUILD_PAIR_UNSIGNED("Protocol", n->protocol), |
| JSON_BUILD_PAIR_STRING("ProtocolString", protocol), |
| JSON_BUILD_PAIR_BOOLEAN("Blackhole", n->blackhole), |
| JSON_BUILD_PAIR_VARIANT_NON_NULL("Group", group), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(n->source)), |
| JSON_BUILD_PAIR_STRING("ConfigState", state))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int nexthops_build_json(Set *nexthops, JsonVariant **ret) { |
| JsonVariant **elements; |
| NextHop *nexthop; |
| size_t n = 0; |
| int r; |
| |
| assert(ret); |
| |
| if (set_isempty(nexthops)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, set_size(nexthops)); |
| if (!elements) |
| return -ENOMEM; |
| |
| SET_FOREACH(nexthop, nexthops) { |
| r = nexthop_build_json(nexthop, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("NextHops", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int route_build_json(Route *route, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
| _cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL; |
| Manager *manager; |
| int r; |
| |
| assert(route); |
| assert(ret); |
| |
| manager = route->link ? route->link->manager : route->manager; |
| |
| assert(manager); |
| |
| r = route_scope_to_string_alloc(route->scope, &scope); |
| if (r < 0) |
| return r; |
| |
| r = route_protocol_to_string_alloc(route->protocol, &protocol); |
| if (r < 0) |
| return r; |
| |
| r = manager_get_route_table_to_string(manager, route->table, &table); |
| if (r < 0) |
| return r; |
| |
| r = route_flags_to_string_alloc(route->flags, &flags); |
| if (r < 0) |
| return r; |
| |
| r = network_config_state_to_string_alloc(route->state, &state); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", route->family), |
| JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family), |
| JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Gateway", &route->gw, route->gw_family), |
| JSON_BUILD_PAIR_CONDITION(route->src_prefixlen > 0, |
| "Source", JSON_BUILD_IN_ADDR(&route->src, route->family)), |
| JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("SourcePrefixLength", route->src_prefixlen), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("PreferredSource", &route->prefsrc, route->family), |
| JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope), |
| JSON_BUILD_PAIR_STRING("ScopeString", scope), |
| JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol), |
| JSON_BUILD_PAIR_STRING("ProtocolString", protocol), |
| JSON_BUILD_PAIR_UNSIGNED("Type", route->type), |
| JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)), |
| JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority), |
| JSON_BUILD_PAIR_UNSIGNED("Table", route->table), |
| JSON_BUILD_PAIR_STRING("TableString", table), |
| JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route->mtu), |
| JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref), |
| JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags), |
| JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)), |
| JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)), |
| JSON_BUILD_PAIR_STRING("ConfigState", state), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int routes_build_json(Set *routes, JsonVariant **ret) { |
| JsonVariant **elements; |
| Route *route; |
| size_t n = 0; |
| int r; |
| |
| assert(ret); |
| |
| if (set_isempty(routes)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, set_size(routes)); |
| if (!elements) |
| return -ENOMEM; |
| |
| SET_FOREACH(route, routes) { |
| r = route_build_json(route, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Routes", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int routing_policy_rule_build_json(RoutingPolicyRule *rule, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
| _cleanup_free_ char *table = NULL, *protocol = NULL, *state = NULL; |
| int r; |
| |
| assert(rule); |
| assert(rule->manager); |
| assert(ret); |
| |
| r = manager_get_route_table_to_string(rule->manager, rule->table, &table); |
| if (r < 0 && r != -EINVAL) |
| return r; |
| |
| r = route_protocol_to_string_alloc(rule->protocol, &protocol); |
| if (r < 0) |
| return r; |
| |
| r = network_config_state_to_string_alloc(rule->state, &state); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", rule->family), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("FromPrefix", &rule->from, rule->family), |
| JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->from), |
| "FromPrefixLength", JSON_BUILD_UNSIGNED(rule->from_prefixlen)), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ToPrefix", &rule->to, rule->family), |
| JSON_BUILD_PAIR_CONDITION(in_addr_is_set(rule->family, &rule->to), |
| "ToPrefixLength", JSON_BUILD_UNSIGNED(rule->to_prefixlen)), |
| JSON_BUILD_PAIR_UNSIGNED("Protocol", rule->protocol), |
| JSON_BUILD_PAIR_STRING("ProtocolString", protocol), |
| JSON_BUILD_PAIR_UNSIGNED("TOS", rule->tos), |
| JSON_BUILD_PAIR_UNSIGNED("Type", rule->type), |
| JSON_BUILD_PAIR_STRING("TypeString", fr_act_type_full_to_string(rule->type)), |
| JSON_BUILD_PAIR_UNSIGNED("IPProtocol", rule->ipproto), |
| JSON_BUILD_PAIR_STRING("IPProtocolString", ip_protocol_to_name(rule->ipproto)), |
| JSON_BUILD_PAIR_UNSIGNED("Priority", rule->priority), |
| JSON_BUILD_PAIR_UNSIGNED("FirewallMark", rule->fwmark), |
| JSON_BUILD_PAIR_UNSIGNED("FirewallMask", rule->fwmask), |
| JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Table", rule->table), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("TableString", table), |
| JSON_BUILD_PAIR_BOOLEAN("Invert", rule->invert_rule), |
| JSON_BUILD_PAIR_CONDITION(rule->suppress_prefixlen >= 0, |
| "SuppressPrefixLength", JSON_BUILD_UNSIGNED(rule->suppress_prefixlen)), |
| JSON_BUILD_PAIR_CONDITION(rule->suppress_ifgroup >= 0, |
| "SuppressInterfaceGroup", JSON_BUILD_UNSIGNED(rule->suppress_ifgroup)), |
| JSON_BUILD_PAIR_CONDITION(rule->sport.start != 0 || rule->sport.end != 0, "SourcePort", |
| JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->sport.start), JSON_BUILD_UNSIGNED(rule->sport.end))), |
| JSON_BUILD_PAIR_CONDITION(rule->dport.start != 0 || rule->dport.end != 0, "DestinationPort", |
| JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->dport.start), JSON_BUILD_UNSIGNED(rule->dport.end))), |
| JSON_BUILD_PAIR_CONDITION(rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID, "User", |
| JSON_BUILD_ARRAY(JSON_BUILD_UNSIGNED(rule->uid_range.start), JSON_BUILD_UNSIGNED(rule->uid_range.end))), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("IncomingInterface", rule->iif), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("OutgoingInterface", rule->oif), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(rule->source)), |
| JSON_BUILD_PAIR_STRING("ConfigState", state))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int routing_policy_rules_build_json(Set *rules, JsonVariant **ret) { |
| JsonVariant **elements; |
| RoutingPolicyRule *rule; |
| size_t n = 0; |
| int r; |
| |
| assert(ret); |
| |
| if (set_isempty(rules)) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| elements = new(JsonVariant*, set_size(rules)); |
| if (!elements) |
| return -ENOMEM; |
| |
| SET_FOREACH(rule, rules) { |
| r = routing_policy_rule_build_json(rule, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("RoutingPolicyRules", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int network_build_json(Network *network, JsonVariant **ret) { |
| assert(ret); |
| |
| if (!network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("NetworkFile", network->filename), |
| JSON_BUILD_PAIR_STRV("NetworkFileDropins", network->dropins), |
| JSON_BUILD_PAIR_BOOLEAN("RequiredForOnline", network->required_for_online), |
| JSON_BUILD_PAIR("RequiredOperationalStateForOnline", |
| JSON_BUILD_ARRAY(JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.min)), |
| JSON_BUILD_STRING(link_operstate_to_string(network->required_operstate_for_online.max)))), |
| JSON_BUILD_PAIR_STRING("RequiredFamilyForOnline", |
| link_required_address_family_to_string(network->required_family_for_online)), |
| JSON_BUILD_PAIR_STRING("ActivationPolicy", |
| activation_policy_to_string(network->activation_policy)))); |
| } |
| |
| static int device_build_json(sd_device *device, JsonVariant **ret) { |
| _cleanup_strv_free_ char **link_dropins = NULL; |
| const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL, *joined; |
| int r; |
| |
| assert(ret); |
| |
| if (!device) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link); |
| |
| if (sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &joined) >= 0) { |
| r = strv_split_full(&link_dropins, joined, ":", EXTRACT_CUNESCAPE); |
| if (r < 0) |
| return r; |
| } |
| |
| (void) sd_device_get_property_value(device, "ID_PATH", &path); |
| |
| if (sd_device_get_property_value(device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0) |
| (void) sd_device_get_property_value(device, "ID_VENDOR", &vendor); |
| |
| if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &model) < 0) |
| (void) sd_device_get_property_value(device, "ID_MODEL", &model); |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("LinkFile", link), |
| JSON_BUILD_PAIR_STRV_NON_EMPTY("LinkFileDropins", link_dropins), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Path", path), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Vendor", vendor), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Model", model))); |
| } |
| |
| static int dns_build_json_one(Link *link, const struct in_addr_full *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; |
| int r; |
| |
| assert(link); |
| assert(a); |
| assert(ret); |
| |
| if (a->ifindex != 0 && a->ifindex != link->ifindex) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", a->family), |
| JSON_BUILD_PAIR_IN_ADDR("Address", &a->address, a->family), |
| JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", a->port), |
| JSON_BUILD_PAIR_CONDITION(a->ifindex != 0, "InterfaceIndex", JSON_BUILD_INTEGER(a->ifindex)), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", a->server_name), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, a->family))); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 1; |
| } |
| |
| static int dns_build_json(Link *link, JsonVariant **ret) { |
| JsonVariant **elements = NULL; |
| size_t n = 0; |
| int r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| if (link->n_dns != UINT_MAX) { |
| for (unsigned i = 0; i < link->n_dns; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = dns_build_json_one(link, link->dns[i], NETWORK_CONFIG_SOURCE_RUNTIME, NULL, elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| } else { |
| for (unsigned i = 0; i < link->network->n_dns; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = dns_build_json_one(link, link->network->dns[i], NETWORK_CONFIG_SOURCE_STATIC, NULL, elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| |
| if (link->dhcp_lease && link->network->dhcp_use_dns) { |
| const struct in_addr *dns; |
| union in_addr_union s; |
| int n_dns; |
| |
| r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in); |
| if (r < 0) |
| goto finalize; |
| |
| n_dns = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns); |
| for (int i = 0; i < n_dns; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = dns_build_json_one(link, |
| &(struct in_addr_full) { .family = AF_INET, .address.in = dns[i], }, |
| NETWORK_CONFIG_SOURCE_DHCP4, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| } |
| |
| if (link->dhcp6_lease && link->network->dhcp6_use_dns) { |
| const struct in6_addr *dns; |
| union in_addr_union s; |
| int n_dns; |
| |
| r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6); |
| if (r < 0) |
| goto finalize; |
| |
| n_dns = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &dns); |
| for (int i = 0; i < n_dns; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = dns_build_json_one(link, |
| &(struct in_addr_full) { .family = AF_INET6, .address.in6 = dns[i], }, |
| NETWORK_CONFIG_SOURCE_DHCP6, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| } |
| |
| if (link->network->ipv6_accept_ra_use_dns) { |
| NDiscRDNSS *a; |
| |
| SET_FOREACH(a, link->ndisc_rdnss) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = dns_build_json_one(link, |
| &(struct in_addr_full) { .family = AF_INET6, .address.in6 = a->address, }, |
| NETWORK_CONFIG_SOURCE_NDISC, |
| &(union in_addr_union) { .in6 = a->router }, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| } |
| } |
| |
| if (n == 0) { |
| *ret = NULL; |
| r = 0; |
| goto finalize; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNS", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int server_build_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { |
| assert(IN_SET(family, AF_INET, AF_INET6)); |
| assert(a); |
| assert(ret); |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_INTEGER("Family", family), |
| JSON_BUILD_PAIR_IN_ADDR("Address", a, family), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); |
| } |
| |
| static int server_build_json_one_fqdn(int family, const char *fqdn, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { |
| assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6)); |
| assert(fqdn); |
| assert(ret); |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("Server", fqdn), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); |
| } |
| |
| static int server_build_json_one_string(const char *str, NetworkConfigSource s, JsonVariant **ret) { |
| union in_addr_union a; |
| int family; |
| |
| assert(str); |
| assert(ret); |
| |
| if (in_addr_from_string_auto(str, &family, &a) >= 0) |
| return server_build_json_one_addr(family, &a, s, NULL, ret); |
| |
| return server_build_json_one_fqdn(AF_UNSPEC, str, s, NULL, ret); |
| } |
| |
| static int ntp_build_json(Link *link, JsonVariant **ret) { |
| JsonVariant **elements = NULL; |
| size_t n = 0; |
| int r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| STRV_FOREACH(p, link->ntp ?: link->network->ntp) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = server_build_json_one_string(*p, NETWORK_CONFIG_SOURCE_RUNTIME, elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| if (!link->ntp) { |
| if (link->dhcp_lease && link->network->dhcp_use_ntp) { |
| const struct in_addr *ntp; |
| union in_addr_union s; |
| int n_ntp; |
| |
| r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in); |
| if (r < 0) |
| goto finalize; |
| |
| n_ntp = sd_dhcp_lease_get_ntp(link->dhcp_lease, &ntp); |
| for (int i = 0; i < n_ntp; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = server_build_json_one_addr(AF_INET, |
| &(union in_addr_union) { .in = ntp[i], }, |
| NETWORK_CONFIG_SOURCE_DHCP4, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| } |
| |
| if (link->dhcp6_lease && link->network->dhcp6_use_ntp) { |
| const struct in6_addr *ntp_addr; |
| union in_addr_union s; |
| char **ntp_fqdn; |
| int n_ntp; |
| |
| r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6); |
| if (r < 0) |
| goto finalize; |
| |
| n_ntp = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &ntp_addr); |
| for (int i = 0; i < n_ntp; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = server_build_json_one_addr(AF_INET6, |
| &(union in_addr_union) { .in6 = ntp_addr[i], }, |
| NETWORK_CONFIG_SOURCE_DHCP6, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| n_ntp = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &ntp_fqdn); |
| for (int i = 0; i < n_ntp; i++) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = server_build_json_one_fqdn(AF_INET6, |
| ntp_fqdn[i], |
| NETWORK_CONFIG_SOURCE_DHCP6, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| } |
| } |
| |
| if (n == 0) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("NTP", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int sip_build_json(Link *link, JsonVariant **ret) { |
| const struct in_addr *sip; |
| JsonVariant **elements; |
| union in_addr_union s; |
| size_t n = 0; |
| int n_sip, r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network || !link->network->dhcp_use_sip || !link->dhcp_lease) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| n_sip = sd_dhcp_lease_get_sip(link->dhcp_lease, &sip); |
| if (n_sip <= 0) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in); |
| if (r < 0) |
| return r; |
| |
| elements = new(JsonVariant*, n_sip); |
| if (!elements) |
| return -ENOMEM; |
| |
| for (int i = 0; i < n_sip; i++) { |
| r = server_build_json_one_addr(AF_INET, |
| &(union in_addr_union) { .in = sip[i], }, |
| NETWORK_CONFIG_SOURCE_DHCP4, |
| &s, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| if (r > 0) |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("SIP", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int domain_build_json(int family, const char *domain, NetworkConfigSource s, const union in_addr_union *p, JsonVariant **ret) { |
| assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6)); |
| assert(domain); |
| assert(ret); |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("Domain", domain), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)), |
| JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, family))); |
| } |
| |
| static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) { |
| OrderedSet *link_domains, *network_domains; |
| JsonVariant **elements = NULL; |
| DHCPUseDomains use_domains; |
| union in_addr_union s; |
| char **domains; |
| const char *domain; |
| size_t n = 0; |
| int r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| link_domains = is_route ? link->route_domains : link->search_domains; |
| network_domains = is_route ? link->network->route_domains : link->network->search_domains; |
| use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES; |
| |
| ORDERED_SET_FOREACH(domain, link_domains ?: network_domains) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = domain_build_json(AF_UNSPEC, domain, |
| link_domains ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC, |
| NULL, elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| if (!link_domains) { |
| if (link->dhcp_lease && |
| link->network->dhcp_use_domains == use_domains) { |
| r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in); |
| if (r < 0) |
| goto finalize; |
| |
| if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domain) >= 0) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = domain_build_json(AF_INET, domain, NETWORK_CONFIG_SOURCE_DHCP4, &s, elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0) { |
| STRV_FOREACH(p, domains) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = domain_build_json(AF_INET, *p, NETWORK_CONFIG_SOURCE_DHCP4, &s, elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| } |
| } |
| |
| if (link->dhcp6_lease && |
| link->network->dhcp6_use_domains == use_domains) { |
| r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6); |
| if (r < 0) |
| goto finalize; |
| |
| if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0) { |
| STRV_FOREACH(p, domains) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = domain_build_json(AF_INET6, *p, NETWORK_CONFIG_SOURCE_DHCP6, &s, elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| } |
| } |
| |
| if (link->network->ipv6_accept_ra_use_domains == use_domains) { |
| NDiscDNSSL *a; |
| |
| SET_FOREACH(a, link->ndisc_dnssl) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = domain_build_json(AF_INET6, NDISC_DNSSL_DOMAIN(a), NETWORK_CONFIG_SOURCE_NDISC, |
| &(union in_addr_union) { .in6 = a->router }, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| } |
| } |
| |
| if (n == 0) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(is_route ? "RouteDomains" : "SearchDomains", |
| JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int nta_build_json(const char *nta, NetworkConfigSource s, JsonVariant **ret) { |
| assert(nta); |
| assert(ret); |
| |
| return json_build(ret, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("DNSSECNegativeTrustAnchor", nta), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)))); |
| } |
| |
| static int ntas_build_json(Link *link, JsonVariant **ret) { |
| JsonVariant **elements = NULL; |
| const char *nta; |
| size_t n = 0; |
| int r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| SET_FOREACH(nta, link->dnssec_negative_trust_anchors ?: link->network->dnssec_negative_trust_anchors) { |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = nta_build_json(nta, |
| link->dnssec_negative_trust_anchors ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC, |
| elements + n); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| if (n == 0) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNSSECNegativeTrustAnchors", |
| JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| static int dns_misc_build_json(Link *link, JsonVariant **ret) { |
| JsonVariant **elements = NULL; |
| ResolveSupport resolve_support; |
| NetworkConfigSource source; |
| DnsOverTlsMode mode; |
| size_t n = 0; |
| int t, r; |
| |
| assert(link); |
| assert(ret); |
| |
| if (!link->network) { |
| *ret = NULL; |
| return 0; |
| } |
| |
| resolve_support = link->llmnr >= 0 ? link->llmnr : link->network->llmnr; |
| if (resolve_support >= 0) { |
| source = link->llmnr >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; |
| |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = json_build(elements + n, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("LLMNR", resolve_support_to_string(resolve_support)), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| resolve_support = link->mdns >= 0 ? link->mdns : link->network->mdns; |
| if (resolve_support >= 0) { |
| source = link->mdns >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; |
| |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = json_build(elements + n, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("MDNS", resolve_support_to_string(resolve_support)), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| t = link->dns_default_route >= 0 ? link->dns_default_route : link->network->dns_default_route; |
| if (t >= 0) { |
| source = link->dns_default_route >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; |
| |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = json_build(elements + n, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_BOOLEAN("DNSDefaultRoute", t), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| mode = link->dns_over_tls_mode >= 0 ? link->dns_over_tls_mode : link->network->dns_over_tls_mode; |
| if (mode >= 0) { |
| source = link->dns_over_tls_mode >= 0 ? NETWORK_CONFIG_SOURCE_RUNTIME : NETWORK_CONFIG_SOURCE_STATIC; |
| |
| if (!GREEDY_REALLOC(elements, n + 1)) { |
| r = -ENOMEM; |
| goto finalize; |
| } |
| |
| r = json_build(elements + n, JSON_BUILD_OBJECT( |
| JSON_BUILD_PAIR_STRING("DNSOverTLS", dns_over_tls_mode_to_string(mode)), |
| JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(source)))); |
| if (r < 0) |
| goto finalize; |
| |
| n++; |
| } |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("DNSSettings", |
| JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| int link_build_json(Link *link, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; |
| _cleanup_free_ char *type = NULL, *flags = NULL; |
| int r; |
| |
| assert(link); |
| assert(ret); |
| |
| r = net_get_type_string(link->dev, link->iftype, &type); |
| if (r == -ENOMEM) |
| return r; |
| |
| r = link_flags_to_string_alloc(link->flags, &flags); |
| if (r < 0) |
| return r; |
| |
| r = json_build(&v, JSON_BUILD_OBJECT( |
| /* basic information */ |
| JSON_BUILD_PAIR_INTEGER("Index", link->ifindex), |
| JSON_BUILD_PAIR_STRING("Name", link->ifname), |
| JSON_BUILD_PAIR_STRV_NON_EMPTY("AlternativeNames", link->alternative_names), |
| JSON_BUILD_PAIR_CONDITION(link->master_ifindex > 0, |
| "MasterInterfaceIndex", JSON_BUILD_INTEGER(link->master_ifindex)), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Kind", link->kind), |
| JSON_BUILD_PAIR_STRING("Type", type), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("Driver", link->driver), |
| JSON_BUILD_PAIR_UNSIGNED("Flags", link->flags), |
| JSON_BUILD_PAIR_STRING("FlagsString", flags), |
| JSON_BUILD_PAIR_UNSIGNED("KernelOperationalState", link->kernel_operstate), |
| JSON_BUILD_PAIR_STRING("KernelOperationalStateString", kernel_operstate_to_string(link->kernel_operstate)), |
| JSON_BUILD_PAIR_UNSIGNED("MTU", link->mtu), |
| JSON_BUILD_PAIR_UNSIGNED("MinimumMTU", link->min_mtu), |
| JSON_BUILD_PAIR_UNSIGNED("MaximumMTU", link->max_mtu), |
| JSON_BUILD_PAIR_HW_ADDR_NON_NULL("HardwareAddress", &link->hw_addr), |
| JSON_BUILD_PAIR_HW_ADDR_NON_NULL("PermanentHardwareAddress", &link->permanent_hw_addr), |
| JSON_BUILD_PAIR_HW_ADDR_NON_NULL("BroadcastAddress", &link->bcast_addr), |
| JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("IPv6LinkLocalAddress", &link->ipv6ll_address), |
| /* wlan information */ |
| JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceType", |
| JSON_BUILD_UNSIGNED(link->wlan_iftype)), |
| JSON_BUILD_PAIR_CONDITION(link->wlan_iftype > 0, "WirelessLanInterfaceTypeString", |
| JSON_BUILD_STRING(nl80211_iftype_to_string(link->wlan_iftype))), |
| JSON_BUILD_PAIR_STRING_NON_EMPTY("SSID", link->ssid), |
| JSON_BUILD_PAIR_ETHER_ADDR_NON_NULL("BSSID", &link->bssid), |
| /* link state */ |
| JSON_BUILD_PAIR_STRING("AdministrativeState", link_state_to_string(link->state)), |
| JSON_BUILD_PAIR_STRING("OperationalState", link_operstate_to_string(link->operstate)), |
| JSON_BUILD_PAIR_STRING("CarrierState", link_carrier_state_to_string(link->carrier_state)), |
| JSON_BUILD_PAIR_STRING("AddressState", link_address_state_to_string(link->address_state)), |
| JSON_BUILD_PAIR_STRING("IPv4AddressState", link_address_state_to_string(link->ipv4_address_state)), |
| JSON_BUILD_PAIR_STRING("IPv6AddressState", link_address_state_to_string(link->ipv6_address_state)), |
| JSON_BUILD_PAIR_STRING("OnlineState", link_online_state_to_string(link->online_state)))); |
| if (r < 0) |
| return r; |
| |
| r = network_build_json(link->network, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = device_build_json(link->dev, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = dns_build_json(link, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = ntp_build_json(link, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = sip_build_json(link, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = domains_build_json(link, /* is_route = */ false, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = domains_build_json(link, /* is_route = */ true, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = ntas_build_json(link, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = dns_misc_build_json(link, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = addresses_build_json(link->addresses, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = neighbors_build_json(link->neighbors, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = nexthops_build_json(link->nexthops, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = routes_build_json(link->routes, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |
| |
| static int link_json_compare(JsonVariant * const *a, JsonVariant * const *b) { |
| int64_t index_a, index_b; |
| |
| assert(a && *a); |
| assert(b && *b); |
| |
| index_a = json_variant_integer(json_variant_by_key(*a, "Index")); |
| index_b = json_variant_integer(json_variant_by_key(*b, "Index")); |
| |
| return CMP(index_a, index_b); |
| } |
| |
| static int links_build_json(Manager *manager, JsonVariant **ret) { |
| JsonVariant **elements; |
| Link *link; |
| size_t n = 0; |
| int r; |
| |
| assert(manager); |
| assert(ret); |
| |
| elements = new(JsonVariant*, hashmap_size(manager->links_by_index)); |
| if (!elements) |
| return -ENOMEM; |
| |
| HASHMAP_FOREACH(link, manager->links_by_index) { |
| r = link_build_json(link, elements + n); |
| if (r < 0) |
| goto finalize; |
| n++; |
| } |
| |
| typesafe_qsort(elements, n, link_json_compare); |
| |
| r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Interfaces", JSON_BUILD_VARIANT_ARRAY(elements, n)))); |
| |
| finalize: |
| json_variant_unref_many(elements, n); |
| free(elements); |
| return r; |
| } |
| |
| int manager_build_json(Manager *manager, JsonVariant **ret) { |
| _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; |
| int r; |
| |
| assert(manager); |
| assert(ret); |
| |
| r = links_build_json(manager, &v); |
| if (r < 0) |
| return r; |
| |
| r = nexthops_build_json(manager->nexthops, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = routes_build_json(manager->routes, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| w = json_variant_unref(w); |
| |
| r = routing_policy_rules_build_json(manager->rules, &w); |
| if (r < 0) |
| return r; |
| |
| r = json_variant_merge(&v, w); |
| if (r < 0) |
| return r; |
| |
| *ret = TAKE_PTR(v); |
| return 0; |
| } |