Users can now supply an AP provisioning file containing an [IPv4]
section and define various DHCP settings:
[IPv4]
Address=<address>
Netmask=<netmask>
Gateway=<gateway>
IPRange=<start_address>,<end_address>
DNSList=<dns1>,<dns2>,...<dnsN>
LeaseTime=<lease_time>
There are a few notes/requirements to keep in mind when using a
provisioning file:
- All settings are optional but [IPv4].Address is required if the
interface does not already have an address set.
- If no [IPv4].Address is defined in the provisioning file and the AP
interface does not already have an address set, StartWithConfig()
will fail with -EINVAL.
- If a provisioning file is provided it will take precedence, and the
AP will not pull from the IP pool.
- A provisioning file containing an IPv4 section assumes DHCP is being
enabled and will override [General].EnableNetworkConfiguration.
- Any address that AP sets on the interface will be deleted when the AP
is stopped.
---
src/ap.c | 225 +++++++++++++++++++++++++++++++++++++++++++++----------
src/ap.h | 1 +
2 files changed, 188 insertions(+), 38 deletions(-)
diff --git a/src/ap.c b/src/ap.c
index 92e8ce62..dab6ef37 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -81,9 +81,12 @@ struct ap_state {
struct l_dhcp_server *server;
uint32_t rtnl_add_cmd;
char *own_ip;
+ unsigned int ip_prefix;
bool started : 1;
bool gtk_set : 1;
+ bool cleanup_ip : 1;
+ bool use_ip_pool : 1;
};
struct sta_state {
@@ -314,7 +317,7 @@ static void ap_reset(struct ap_state *ap)
* Only free a non file based config. Configs which are created from
* provisioning files should be kept around until IWD exits
*/
- if (ap->config->config_file)
+ if (ap->config && !ap->config->config_file)
ap_config_free(ap->config);
ap->config = NULL;
@@ -323,13 +326,18 @@ static void ap_reset(struct ap_state *ap)
ap->started = false;
- if (ap->own_ip) {
+ /* Delete IP if one was set by IWD */
+ if (ap->cleanup_ip)
l_rtnl_ifaddr4_delete(rtnl, netdev_get_ifindex(netdev),
- pool.prefix, ap->own_ip,
+ ap->ip_prefix, ap->own_ip,
broadcast_from_ip(ap->own_ip),
NULL, NULL, NULL);
- ip_pool_put(ap->own_ip);
+ if (ap->own_ip) {
+ /* Release IP from pool if used */
+ if (ap->use_ip_pool)
+ ip_pool_put(ap->own_ip);
+
l_free(ap->own_ip);
}
@@ -2220,6 +2228,162 @@ static void ap_mlme_notify(struct l_genl_msg *msg, void
*user_data)
}
}
+static bool dhcp_load_settings(struct ap_state *ap, struct l_settings *settings)
+{
+ struct l_dhcp_server *server = ap->server;
+ struct in_addr ia;
+
+ L_AUTO_FREE_VAR(char *, netmask) = l_settings_get_string(settings,
+ "IPv4", "Netmask");
+ L_AUTO_FREE_VAR(char *, gateway) = l_settings_get_string(settings,
+ "IPv4", "Gateway");
+ char **dns = l_settings_get_string_list(settings, "IPv4",
+ "DNSList", ',');
+ char **ip_range = l_settings_get_string_list(settings, "IPv4",
+ "IPRange", ',');
+ unsigned int lease_time;
+ bool ret = false;
+
+ if (!l_settings_get_uint(settings, "IPv4", "LeaseTime",
&lease_time))
+ lease_time = 0;
+
+ if (ip_range && l_strv_length(ip_range) != 2)
+ goto parse_error;
+
+ if (netmask && !l_dhcp_server_set_netmask(server, netmask))
+ goto parse_error;
+
+ if (gateway && !l_dhcp_server_set_gateway(server, gateway))
+ goto parse_error;
+
+ if (dns && !l_dhcp_server_set_dns(server, dns))
+ goto parse_error;
+
+ if (ip_range && !l_dhcp_server_set_ip_range(server, ip_range[0],
+ ip_range[1]))
+ goto parse_error;
+
+ if (lease_time && !l_dhcp_server_set_lease_time(server, lease_time))
+ goto parse_error;
+
+ if (netmask && inet_pton(AF_INET, netmask, &ia) > 0)
+ ap->ip_prefix = __builtin_popcountl(ia.s_addr);
+ else
+ ap->ip_prefix = 24;
+
+ ret = true;
+
+parse_error:
+ l_strv_free(dns);
+ l_strv_free(ip_range);
+ return ret;
+}
+
+/*
+ * This will determine the IP being used for DHCP. The IP will be automatically
+ * set to ap->own_ip.
+ *
+ * The address to set (or keep) is determined in this order:
+ * 1. Address defined in provisioning file
+ * 2. Address already set on interface
+ * 3. Address in IP pool.
+ *
+ * Returns: 0 if an IP was successfully selected and needs to be set
+ * -EALREADY if an IP was already set on the interface
+ * -EEXIST if the IP pool ran out of IP's
+ * -EINVAL if there was an error.
+ */
+static int ap_setup_dhcp(struct ap_state *ap)
+{
+ uint32_t ifindex = netdev_get_ifindex(ap->netdev);
+ struct l_settings *settings;
+ struct in_addr ia;
+ uint32_t address = 0;
+ int ret = -EINVAL;
+
+ ap->server = l_dhcp_server_new(ifindex);
+ if (!ap->server) {
+ l_error("Failed to create DHCP server on %u", ifindex);
+ return -EINVAL;;
+ }
+
+ if (getenv("IWD_DHCP_DEBUG"))
+ l_dhcp_server_set_debug(ap->server, do_debug,
+ "[DHCPv4 SERV] ", NULL);
+
+ /* get the current address if there is one */
+ if (l_net_get_address(ifindex, &ia) && ia.s_addr != 0)
+ address = ia.s_addr;
+
+ if (ap->config->config_file) {
+ char *addr;
+
+ settings = l_settings_new();
+
+ if (!l_settings_load_from_file(settings,
+ ap->config->config_file)) {
+ l_settings_free(settings);
+ return ret;
+ }
+
+ addr = l_settings_get_string(settings, "IPv4", "Address");
+ if (addr) {
+ if (inet_pton(AF_INET, addr, &ia) < 0)
+ goto free_settings;
+
+ /* Is a matching address already set on interface? */
+ if (ia.s_addr == address)
+ ret = -EALREADY;
+ else
+ ret = 0;
+ } else if (address) {
+ /* No address in config, but interface has one set */
+ addr = l_strdup(inet_ntoa(ia));
+ ret = -EALREADY;
+ } else
+ goto free_settings;
+
+ /* Set the remaining DHCP options in config file */
+ if (!dhcp_load_settings(ap, settings)) {
+ ret = -EINVAL;
+ goto free_settings;
+ }
+
+ if (!l_dhcp_server_set_ip_address(ap->server, addr)) {
+ ret = -EINVAL;
+ goto free_settings;
+ }
+
+ ap->own_ip = l_strdup(addr);
+
+free_settings:
+ l_free(addr);
+ l_settings_free(settings);
+
+ return ret;
+ } else if (address) {
+ /* No config file and address is already set */
+ ap->own_ip = l_strdup(inet_ntoa(ia));
+
+ return -EALREADY;
+ } else if (pool.used) {
+ /* No config file, no address set. Use IP pool */
+ ap->own_ip = ip_pool_get();
+ if (!ap->own_ip) {
+ l_error("No more IP's in pool, cannot start AP on %u",
+ ifindex);
+ return -EEXIST;
+ }
+
+ ap->use_ip_pool = true;
+ ap->ip_prefix = pool.prefix;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
/*
* Start a simple independent WPA2 AP on given netdev.
*
@@ -2241,7 +2405,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config
*config,
struct l_genl_msg *cmd;
uint64_t wdev_id = netdev_get_wdev_id(netdev);
uint32_t ifindex = netdev_get_ifindex(netdev);
- struct in_addr ia;
int err = -EINVAL;
if (err_out)
@@ -2342,52 +2505,35 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config
*config,
if (!ap->mlme_watch)
l_error("Registering for MLME notification failed");
- /* No IP pool initialized, DHCP is not being used */
- if (!pool.used)
+ /* No IP pool initialized or enabled in config, DHCP not being used */
+ if (!pool.used && !config->dhcp_enabled)
goto done;
- ap->server = l_dhcp_server_new(ifindex);
- if (!ap->server) {
- l_error("Failed to create DHCP server on %u", ifindex);
- goto error;
- }
-
- if (getenv("IWD_DHCP_DEBUG"))
- l_dhcp_server_set_debug(ap->server, do_debug,
- "[DHCPv4 SERV] ", NULL);
-
- /*
- * If there is no IP set on the interface use one from the IP pool
- * defined in main.conf.
- */
- if (!l_net_get_address(ifindex, &ia) || ia.s_addr == 0) {
- ap->own_ip = ip_pool_get();
-
- if (!ap->own_ip) {
- l_error("No more IP's in pool, cannot start AP on %u",
- ifindex);
- err = -EEXIST;
- goto error;
- }
-
+ err = ap_setup_dhcp(ap);
+ if (err == 0) {
+ /* Address change required */
ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
- pool.prefix, ap->own_ip,
+ ap->ip_prefix, ap->own_ip,
broadcast_from_ip(ap->own_ip),
ap_ifaddr4_added_cb, ap, NULL);
if (!ap->rtnl_add_cmd) {
l_error("Failed to add IPv4 address");
+ err = -EIO;
goto error;
}
-
if (err_out)
*err_out = 0;
- /* Finish starting AP in added callback */
+ ap->cleanup_ip = true;
+
return ap;
- }
- /* Else honor the IP set on the interface prior to calling Start() */
+ /* Selected address already set, continue normally */
+ } else if (err == -EALREADY)
+ err = 0;
+ else
+ goto error;
done:
cmd = ap_build_cmd_start_ap(ap);
@@ -2854,6 +3000,9 @@ static struct ap_config *ap_config_from_settings(struct l_settings
*settings,
l_free(passphrase);
}
+ if (l_settings_has_group(settings, "IPv4"))
+ config->dhcp_enabled = true;
+
l_info("Loaded AP configuration %s", config->ssid);
return config;
@@ -2895,10 +3044,10 @@ static int ap_init(void)
if (!ip_pool_create(ip_prefix))
return -EINVAL;
-
- rtnl = iwd_get_rtnl();
}
+ rtnl = iwd_get_rtnl();
+
ap_configs = l_queue_new();
dir = opendir(ap_dir);
diff --git a/src/ap.h b/src/ap.h
index b3fcb882..c1bdd39a 100644
--- a/src/ap.h
+++ b/src/ap.h
@@ -66,6 +66,7 @@ struct ap_config {
char *config_file;
+ bool dhcp_enabled : 1;
bool no_cck_rates : 1;
};
--
2.26.2