ap: Refactor global address pool loading

Deprecate the global [General].APRanges setting in favour of
[IPv4].APAddressPool with an extended (but backwards-compatible) syntax.
Drop the existing address pool creation code.

The new APAddressPool setting has the same syntax as the profile-local
[IPv4].Address setting and the subnet selection code will fall back
to the global setting if it's missing, this way we use common code to
handle both settings.
This commit is contained in:
Andrew Zaborowski 2021-05-28 02:47:33 +02:00 committed by Denis Kenzior
parent c5d1a5c31f
commit ff2840f95f
1 changed files with 41 additions and 142 deletions

183
src/ap.c
View File

@ -97,7 +97,6 @@ struct ap_state {
bool started : 1;
bool gtk_set : 1;
bool netconfig_set_addr4 : 1;
bool netconfig_use_ip_pool : 1;
};
struct sta_state {
@ -128,99 +127,11 @@ struct ap_wsc_pbc_probe_record {
uint64_t timestamp;
};
struct ap_ip_pool {
uint32_t start;
uint32_t end;
uint8_t prefix;
/* Fist/last valid subnet */
uint8_t sub_start;
uint8_t sub_end;
struct l_uintset *used;
};
struct ap_ip_pool pool;
static bool netconfig_enabled;
static char **global_addr4_strs;
static uint32_t netdev_watch;
static struct l_netlink *rtnl;
/*
* Creates pool of IPs which AP intefaces can use. Each call to ip_pool_get
* will advance the subnet +1 so there are no IP conflicts between AP
* interfaces
*/
static bool ip_pool_create(const char *ip_prefix)
{
if (!util_ip_prefix_tohl(ip_prefix, &pool.prefix, &pool.start,
&pool.end, NULL))
return false;
if (pool.prefix > 24) {
l_error("APRanges prefix must 24 or less (%u used)",
pool.prefix);
memset(&pool, 0, sizeof(pool));
return false;
}
/*
* Find the number of subnets we can use, this will dictate the number
* of AP interfaces that can be created (when using DHCP)
*/
pool.sub_start = (pool.start & 0x0000ff00) >> 8;
pool.sub_end = (pool.end & 0x0000ff00) >> 8;
pool.used = l_uintset_new_from_range(pool.sub_start, pool.sub_end);
return true;
}
static char *ip_pool_get()
{
uint32_t ip;
struct in_addr ia;
uint8_t next_subnet = (uint8_t)l_uintset_find_unused_min(pool.used);
/* This shouldn't happen */
if (next_subnet < pool.sub_start || next_subnet > pool.sub_end)
return 0;
l_uintset_put(pool.used, next_subnet);
ip = pool.start;
ip &= 0xffff00ff;
ip |= (next_subnet << 8);
ia.s_addr = htonl(ip);
return l_strdup(inet_ntoa(ia));
}
static bool ip_pool_put(const char *address)
{
struct in_addr ia;
uint32_t ip;
uint8_t subnet;
if (inet_aton(address, &ia) < 0)
return false;
ip = ntohl(ia.s_addr);
subnet = (ip & 0x0000ff00) >> 8;
if (subnet < pool.sub_start || subnet > pool.sub_end)
return false;
return l_uintset_take(pool.used, subnet);
}
static void ip_pool_destroy()
{
if (pool.used)
l_uintset_free(pool.used);
memset(&pool, 0, sizeof(pool));
}
static const char *broadcast_from_ip(const char *ip)
{
struct in_addr ia;
@ -331,12 +242,6 @@ static void ap_reset(struct ap_state *ap)
ap->netconfig_set_addr4 = false;
}
if (ap->netconfig_use_ip_pool) {
/* Release IP from pool if used */
ip_pool_put(ap->netconfig_addr4_str);
ap->netconfig_use_ip_pool = false;
}
l_free(ap->netconfig_addr4_str);
ap->netconfig_addr4_str = NULL;
@ -2511,7 +2416,6 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
int ret;
struct in_addr ia;
struct l_dhcp_server *dhcp = NULL;
bool use_ip_pool = false;
bool r;
char addr_str_buf[INET_ADDRSTRLEN];
@ -2552,27 +2456,11 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
new_addr = l_rtnl_address_new(addr_str_buf, prefix_len);
ret = 0;
} else {
L_AUTO_FREE_VAR(char *, addr_str) = NULL;
if (!prefix_len)
prefix_len = AP_DEFAULT_IPV4_PREFIX_LEN;
if (prefix_len) {
l_error("[IPv4].Netmask can't be used without an "
"address");
ret = -EINVAL;
goto cleanup;
}
/* No config, no address set. Use IP pool */
addr_str = ip_pool_get();
if (addr_str) {
use_ip_pool = true;
prefix_len = pool.prefix;
new_addr = l_rtnl_address_new(addr_str, prefix_len);
ret = 0;
} else {
l_error("No more IP's in pool, cannot start AP on %u",
ifindex);
ret = -EEXIST;
}
ret = ip_pool_select_addr4((const char **) global_addr4_strs,
prefix_len, &new_addr);
}
if (ret)
@ -2631,7 +2519,6 @@ static int ap_setup_netconfig4(struct ap_state *ap, const char **addr_str_list,
ap->netconfig_addr4_str = l_strdup(addr_str_buf);
ap->netconfig_prefix_len4 = prefix_len;
ap->netconfig_set_addr4 = true;
ap->netconfig_use_ip_pool = use_ip_pool;
ap->netconfig_dhcp = l_steal_ptr(dhcp);
ret = 0;
@ -2663,7 +2550,7 @@ static int ap_load_ipv4(struct ap_state *ap, const struct l_settings *config)
unsigned int lease_time = 0;
struct in_addr ia;
if (!l_settings_has_group(config, "IPv4") || !pool.used)
if (!l_settings_has_group(config, "IPv4") || !netconfig_enabled)
return 0;
if (l_settings_has_key(config, "IPv4", "Address")) {
@ -3652,7 +3539,6 @@ static void ap_netdev_watch(struct netdev *netdev,
static int ap_init(void)
{
const struct l_settings *settings = iwd_get_config();
bool dhcp_enable;
netdev_watch = netdev_watch_add(ap_netdev_watch, NULL, NULL);
@ -3663,31 +3549,44 @@ static int ap_init(void)
ap_diagnostic_interface_destroy, false);
/*
* Reusing [General].EnableNetworkConfiguration as a switch to enable
* DHCP server. If no value is found or it is false do not create a
* DHCP server.
* Enable network configuration and DHCP only if
* [General].EnableNetworkConfiguration is true.
*/
if (!l_settings_get_bool(settings, "General",
"EnableNetworkConfiguration", &dhcp_enable))
dhcp_enable = false;
"EnableNetworkConfiguration",
&netconfig_enabled))
netconfig_enabled = false;
if (dhcp_enable) {
L_AUTO_FREE_VAR(char *, ip_prefix);
ip_prefix = l_settings_get_string(settings, "General",
"APRanges");
/*
* In this case its assumed the user only cares about station
* netconfig so we let ap_init pass but DHCP will not be
* enabled.
*/
if (!ip_prefix) {
l_warn("[General].APRanges must be set for DHCP");
return 0;
if (netconfig_enabled) {
if (l_settings_get_value(settings, "IPv4", "APAddressPool")) {
global_addr4_strs = l_settings_get_string_list(settings,
"IPv4",
"APAddressPool",
',');
if (!global_addr4_strs || !*global_addr4_strs) {
l_error("Can't parse the [IPv4].APAddressPool "
"setting as a string list");
l_strv_free(global_addr4_strs);
global_addr4_strs = NULL;
}
} else if (l_settings_get_value(settings,
"General", "APRanges")) {
global_addr4_strs = l_settings_get_string_list(settings,
"General",
"APRanges",
',');
if (!global_addr4_strs || !*global_addr4_strs) {
l_error("Can't parse the [General].APRanges "
"setting as a string list");
l_strv_free(global_addr4_strs);
global_addr4_strs = NULL;
}
}
if (!ip_pool_create(ip_prefix))
return -EINVAL;
/* Fall back to 192.168.0.0/16 */
if (!global_addr4_strs)
global_addr4_strs =
l_strv_append(NULL, "192.168.0.0/16");
}
rtnl = iwd_get_rtnl();
@ -3700,7 +3599,7 @@ static void ap_exit(void)
netdev_watch_remove(netdev_watch);
l_dbus_unregister_interface(dbus_get_bus(), IWD_AP_INTERFACE);
ip_pool_destroy();
l_strv_free(global_addr4_strs);
}
IWD_MODULE(ap, ap_init, ap_exit)