@@ -28,6 +28,7 @@
#include <ell/ell.h>
#include "src/dbus.h"
+#include "src/netconfig.h"
#include "src/agent.h"
#include "src/iwd.h"
#include "src/module.h"
@@ -584,6 +585,60 @@ static struct l_dbus_message *agent_unregister(struct l_dbus *dbus,
return reply;
}
+static struct l_dbus_message *netconfig_agent_register(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+ const char *path;
+ int r;
+
+ l_debug("");
+
+ if (!l_dbus_message_get_arguments(message, "o", &path))
+ return dbus_error_invalid_args(message);
+
+ if (!netconfig_enabled())
+ return dbus_error_not_supported(message);
+
+ r = netconfig_register_agent(l_dbus_message_get_sender(message), path);
+ if (r)
+ return dbus_error_from_errno(r, message);
+
+ l_debug("agent %s path %s",
+ l_dbus_message_get_sender(message), path);
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+ return reply;
+}
+
+static struct l_dbus_message *netconfig_agent_unregister(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct l_dbus_message *reply;
+ const char *path;
+ int r;
+
+ l_debug("");
+
+ if (!l_dbus_message_get_arguments(message, "o", &path))
+ return dbus_error_invalid_args(message);
+
+ if (!netconfig_enabled())
+ return dbus_error_not_supported(message);
+
+ r = netconfig_unregister_agent(l_dbus_message_get_sender(message),
+ path);
+ if (r)
+ return dbus_error_from_errno(r, message);
+
+ reply = l_dbus_message_new_method_return(message);
+ l_dbus_message_set_arguments(reply, "");
+ return reply;
+}
+
static void setup_agent_interface(struct l_dbus_interface *interface)
{
l_dbus_interface_method(interface, "RegisterAgent", 0,
@@ -592,6 +647,13 @@ static void setup_agent_interface(struct l_dbus_interface *interface)
l_dbus_interface_method(interface, "UnregisterAgent", 0,
agent_unregister,
"", "o", "path");
+
+ l_dbus_interface_method(interface,
+ "RegisterNetworkConfigurationAgent", 0,
+ netconfig_agent_register, "", "o", "path");
+ l_dbus_interface_method(interface,
+ "UnregisterNetworkConfigurationAgent", 0,
+ netconfig_agent_unregister, "", "o", "path");
}
static bool release_agent(void *data, void *user_data)
@@ -44,6 +44,8 @@
#define IWD_AP_DIAGNOSTIC_INTERFACE "net.connman.iwd.AccessPointDiagnostic"
#define IWD_STATION_DEBUG_INTERFACE "net.connman.iwd.StationDebug"
#define IWD_DPP_INTERFACE "net.connman.iwd.DeviceProvisioning"
+#define IWD_NETCONFIG_AGENT_INTERFACE \
+ "net.connman.iwd.NetworkConfigurationAgent"
#define IWD_BASE_PATH "/net/connman/iwd"
#define IWD_AGENT_MANAGER_PATH IWD_BASE_PATH
@@ -37,6 +37,7 @@
#include "src/netdev.h"
#include "src/ie.h"
#include "src/resolve.h"
+#include "src/dbus.h"
#include "src/netconfig.h"
struct netconfig_commit_ops {
@@ -110,6 +111,24 @@ void netconfig_commit(struct netconfig *netconfig, uint8_t family,
}
}
+static void netconfig_switch_backend(const struct netconfig_commit_ops *new_ops)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(netconfig_list); entry;
+ entry = entry->next) {
+ struct netconfig *netconfig = entry->data;
+
+ if (commit_ops->free_data)
+ commit_ops->free_data(netconfig, "");
+
+ if (new_ops->init_data)
+ new_ops->init_data(netconfig);
+ }
+
+ commit_ops = new_ops;
+}
+
/*
* Called by all backends when netconfig_commit finishes, synchronously or
* asynchronously.
@@ -278,3 +297,364 @@ static void netconfig_rtnl_commit(struct netconfig *netconfig, uint8_t family,
netconfig_commit_done(netconfig, family, event, true);
}
+
+struct netconfig_agent_data {
+ uint32_t pending_id[2];
+};
+
+struct netconfig_agent_call_data {
+ struct netconfig *netconfig;
+ uint8_t family;
+ enum l_netconfig_event event;
+};
+
+static char *netconfig_agent_name;
+static char *netconfig_agent_path;
+static unsigned int netconfig_agent_watch;
+
+static void netconfig_agent_cancel(struct netconfig *netconfig, uint8_t family,
+ const char *reasonstr)
+{
+ struct netconfig_agent_data *data = netconfig->commit_data;
+ struct l_dbus *dbus = dbus_get_bus();
+ const char *dev_path = netdev_get_path(netconfig->netdev);
+ struct l_dbus_message *message;
+ const char *method;
+
+ if (!data || !data->pending_id[INDEX_FOR_AF(family)])
+ return;
+
+ l_dbus_cancel(dbus, data->pending_id[INDEX_FOR_AF(family)]);
+ data->pending_id[INDEX_FOR_AF(family)] = 0;
+
+ method = (family == AF_INET ? "CancelIPv4" : "CancelIPv6");
+ l_debug("sending a %s(%s, %s) to %s %s", method, dev_path, reasonstr,
+ netconfig_agent_name, netconfig_agent_path);
+
+ message = l_dbus_message_new_method_call(dbus, netconfig_agent_name,
+ netconfig_agent_path,
+ IWD_NETCONFIG_AGENT_INTERFACE,
+ method);
+ l_dbus_message_set_arguments(message, "os", dev_path, reasonstr);
+ l_dbus_message_set_no_reply(message, true);
+ l_dbus_send(dbus, message);
+}
+
+static void netconfig_agent_receive_reply(struct l_dbus_message *reply,
+ void *user_data)
+{
+ struct netconfig_agent_call_data *cd = user_data;
+ struct netconfig_agent_data *data = cd->netconfig->commit_data;
+ const char *error, *text;
+ bool success = true;
+
+ l_debug("agent reply from %s %s", l_dbus_message_get_sender(reply),
+ l_dbus_message_get_path(reply));
+
+ data->pending_id[INDEX_FOR_AF(cd->family)] = 0;
+
+ if (l_dbus_message_get_error(reply, &error, &text)) {
+ success = false;
+ l_error("netconfig agent call returned %s(\"%s\")",
+ error, text);
+ } else if (!l_dbus_message_get_arguments(reply, "")) {
+ success = false;
+ l_error("netconfig agent call reply signature wrong: %s",
+ l_dbus_message_get_signature(reply));
+ }
+
+ netconfig_commit_done(cd->netconfig, cd->family, cd->event, success);
+}
+
+#define IS_IPV6_STR_FAST(str) (strchr(str, ':') != NULL)
+
+typedef void (*netconfig_build_entry_fn)(struct l_dbus_message_builder *builder,
+ const void *data, uint8_t family);
+
+static void netconfig_agent_append_dict_dict_array(
+ struct l_dbus_message_builder *builder,
+ const char *key,
+ const struct l_queue_entry *value,
+ netconfig_build_entry_fn build_entry,
+ uint8_t family)
+{
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's', key);
+ l_dbus_message_builder_enter_variant(builder, "aa{sv}");
+ l_dbus_message_builder_enter_array(builder, "a{sv}");
+
+ for (; value; value = value->next)
+ build_entry(builder, value->data, family);
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+}
+
+static void netconfig_agent_append_dict_strv(
+ struct l_dbus_message_builder *builder,
+ const char *key, char **value,
+ uint8_t family)
+{
+ if (!value)
+ return;
+
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's', key);
+ l_dbus_message_builder_enter_variant(builder, "as");
+ l_dbus_message_builder_enter_array(builder, "s");
+
+ for (; *value; value++) {
+ uint8_t value_family = IS_IPV6_STR_FAST((char *) *value) ?
+ AF_INET6 : AF_INET;
+
+ if (family == AF_UNSPEC || value_family == family)
+ l_dbus_message_builder_append_basic(builder, 's',
+ *value);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+}
+
+static void netconfig_agent_append_address(
+ struct l_dbus_message_builder *builder,
+ const void *data, uint8_t family)
+{
+ const struct l_rtnl_address *addr = data;
+ char addr_str[INET6_ADDRSTRLEN];
+ uint64_t valid_expiry_time;
+ uint64_t preferred_expiry_time;
+ uint64_t now = l_time_now();
+
+ if (l_rtnl_address_get_family(addr) != family)
+ return;
+
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+
+ l_rtnl_address_get_address(addr, addr_str);
+ dbus_append_dict_basic(builder, "Address", 's', addr_str);
+
+ if (family == AF_INET) {
+ uint8_t plen = l_rtnl_address_get_prefix_length(addr);
+ dbus_append_dict_basic(builder, "PrefixLength", 'y', &plen);
+
+ if (l_rtnl_address_get_broadcast(addr, addr_str) &&
+ strcmp(addr_str, "0.0.0.0"))
+ dbus_append_dict_basic(builder, "Broadcast", 's',
+ addr_str);
+ }
+
+ l_rtnl_address_get_expiry(addr, &preferred_expiry_time,
+ &valid_expiry_time);
+
+ if (valid_expiry_time > now) {
+ uint32_t lt = l_time_to_secs(valid_expiry_time - now);
+
+ dbus_append_dict_basic(builder, "ValidLifetime", 'u', <);
+ }
+
+ if (preferred_expiry_time > now) {
+ uint32_t lt = l_time_to_secs(preferred_expiry_time - now);
+
+ dbus_append_dict_basic(builder, "PreferredLifetime", 'u', <);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+}
+
+static void netconfig_agent_append_route(struct l_dbus_message_builder *builder,
+ const void *data,
+ uint8_t family)
+{
+ const struct l_rtnl_route *rt = data;
+ char addr_str[INET6_ADDRSTRLEN];
+ uint8_t prefix_len;
+ uint64_t expiry_time;
+ uint64_t now = l_time_now();
+ uint32_t priority;
+ uint8_t preference;
+ uint32_t mtu;
+
+ if (l_rtnl_route_get_family(rt) != family)
+ return;
+
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+
+ if (l_rtnl_route_get_dst(rt, addr_str, &prefix_len) && prefix_len) {
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's',
+ "Destination");
+ l_dbus_message_builder_enter_variant(builder, "(sy)");
+ l_dbus_message_builder_enter_struct(builder, "sy");
+ l_dbus_message_builder_append_basic(builder, 's', addr_str);
+ l_dbus_message_builder_append_basic(builder, 'y', &prefix_len);
+ l_dbus_message_builder_leave_struct(builder);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+ }
+
+ if (l_rtnl_route_get_gateway(rt, addr_str))
+ dbus_append_dict_basic(builder, "Router", 's', addr_str);
+
+ if (l_rtnl_route_get_prefsrc(rt, addr_str))
+ dbus_append_dict_basic(builder, "PreferredSource", 's',
+ addr_str);
+
+ expiry_time = l_rtnl_route_get_expiry(rt);
+ if (expiry_time > now) {
+ uint32_t lt = l_time_to_secs(expiry_time - now);
+
+ dbus_append_dict_basic(builder, "Lifetime", 'u', <);
+ }
+
+ priority = l_rtnl_route_get_priority(rt);
+ dbus_append_dict_basic(builder, "Priority", 'u', &priority);
+
+ /*
+ * ICMPV6_ROUTER_PREF_MEDIUM is returned by default even for IPv4
+ * routes where this property doesn't make sense so filter those out.
+ */
+ preference = l_rtnl_route_get_preference(rt);
+ if (preference != ICMPV6_ROUTER_PREF_INVALID && family == AF_INET6)
+ dbus_append_dict_basic(builder, "Preference", 'y', &preference);
+
+ mtu = l_rtnl_route_get_mtu(rt);
+ if (mtu)
+ dbus_append_dict_basic(builder, "Priority", 'u', &mtu);
+
+ l_dbus_message_builder_leave_array(builder);
+}
+
+static void netconfig_agent_commit(struct netconfig *netconfig, uint8_t family,
+ enum l_netconfig_event event)
+{
+ struct netconfig_agent_data *data;
+ struct netconfig_agent_call_data *cd;
+ struct l_dbus *dbus = dbus_get_bus();
+ struct l_dbus_message *message;
+ struct l_dbus_message_builder *builder;
+ const char *dev_path = netdev_get_path(netconfig->netdev);
+ const char *dbus_method =
+ (family == AF_INET ? "ConfigureIPv4" : "ConfigureIPv6");
+ const char *cfg_method =
+ netconfig->static_config[INDEX_FOR_AF(family)] ?
+ "static" : "auto";
+ _auto_(l_strv_free) char **dns_list = NULL;
+ _auto_(l_strv_free) char **domains = NULL;
+
+ if (!netconfig->commit_data)
+ netconfig->commit_data = l_new(struct netconfig_agent_data, 1);
+
+ netconfig_agent_cancel(netconfig, family, "superseded");
+
+ l_debug("sending a %s(%s, ...) to %s %s", dbus_method, dev_path,
+ netconfig_agent_name, netconfig_agent_path);
+
+ message = l_dbus_message_new_method_call(dbus, netconfig_agent_name,
+ netconfig_agent_path,
+ IWD_NETCONFIG_AGENT_INTERFACE,
+ dbus_method);
+
+ /*
+ * Build the call arguments: the Device object path and
+ * the complicated config dict.
+ */
+ builder = l_dbus_message_builder_new(message);
+ l_dbus_message_builder_append_basic(builder, 'o', dev_path);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ dbus_append_dict_basic(builder, "Method", 's', cfg_method);
+
+ netconfig_agent_append_dict_dict_array(builder, "Addresses",
+ l_netconfig_get_addresses(netconfig->nc,
+ NULL, NULL, NULL, NULL),
+ netconfig_agent_append_address, family);
+
+ netconfig_agent_append_dict_dict_array(builder, "Routes",
+ l_netconfig_get_routes(netconfig->nc,
+ NULL, NULL, NULL, NULL),
+ netconfig_agent_append_route, family);
+
+ dns_list = l_netconfig_get_dns_list(netconfig->nc);
+ netconfig_agent_append_dict_strv(builder, "DomainNameServers",
+ dns_list, family);
+
+ domains = l_netconfig_get_domain_names(netconfig->nc);
+ netconfig_agent_append_dict_strv(builder, "DomainNames",
+ domains, AF_UNSPEC);
+
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+
+ cd = l_new(struct netconfig_agent_call_data, 1);
+ cd->netconfig = netconfig;
+ cd->family = family;
+ cd->event = event;
+ data = netconfig->commit_data;
+ data->pending_id[INDEX_FOR_AF(family)] =
+ l_dbus_send_with_reply(dbus, message,
+ netconfig_agent_receive_reply,
+ cd, l_free);
+}
+
+static void netconfig_agent_free_data(struct netconfig *netconfig,
+ const char *reasonstr)
+{
+ if (!netconfig->commit_data)
+ return;
+
+ netconfig_agent_cancel(netconfig, AF_INET, reasonstr);
+ netconfig_agent_cancel(netconfig, AF_INET6, reasonstr);
+ l_free(l_steal_ptr(netconfig->commit_data));
+}
+
+static struct netconfig_commit_ops netconfig_agent_ops = {
+ .commit = netconfig_agent_commit,
+ .free_data = netconfig_agent_free_data,
+};
+
+static void netconfig_agent_disconnect_handle(void *user_data)
+{
+ netconfig_unregister_agent(netconfig_agent_name, netconfig_agent_path);
+}
+
+static void netconfig_agent_disconnect_cb(struct l_dbus *dbus, void *user_data)
+{
+ l_debug("");
+ l_idle_oneshot(netconfig_agent_disconnect_handle, NULL, NULL);
+}
+
+int netconfig_register_agent(const char *name, const char *path)
+{
+ if (netconfig_agent_path)
+ return -EEXIST;
+
+ netconfig_agent_name = l_strdup(name);
+ netconfig_agent_path = l_strdup(path);
+ netconfig_agent_watch = l_dbus_add_disconnect_watch(dbus_get_bus(),
+ name,
+ netconfig_agent_disconnect_cb,
+ NULL, NULL);
+
+ netconfig_switch_backend(&netconfig_agent_ops);
+
+ return 0;
+}
+
+int netconfig_unregister_agent(const char *name, const char *path)
+{
+ if (!netconfig_agent_path || strcmp(netconfig_agent_path, path))
+ return -ENOENT;
+
+ if (strcmp(netconfig_agent_name, name))
+ return -EPERM;
+
+ l_free(l_steal_ptr(netconfig_agent_name));
+ l_free(l_steal_ptr(netconfig_agent_path));
+ l_dbus_remove_watch(dbus_get_bus(), netconfig_agent_watch);
+
+ netconfig_switch_backend(&netconfig_rtnl_ops);
+ return 0;
+}
@@ -81,6 +81,8 @@ void netconfig_commit_init(struct netconfig *netconfig);
void netconfig_commit_free(struct netconfig *netconfig, const char *reasonstr);
void netconfig_commit(struct netconfig *netconfig, uint8_t family,
enum l_netconfig_event event);
+int netconfig_register_agent(const char *name, const char *path);
+int netconfig_unregister_agent(const char *name, const char *path);
void netconfig_dhcp_gateway_to_arp(struct netconfig *netconfig);
void netconfig_commit_fils_macs(struct netconfig *netconfig, uint8_t family);