3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2025-01-03 10:32:33 +01:00

ap: Drop struct ap_config in favor of l_settings

Change ap_start to load all of the AP configuration from a struct
l_settings, moving the 6 or so parameters from struct ap_config members
to the l_settings groups and keys.  This extends the ap profile concept
used for the DHCP settings.  ap_start callers create the l_settings
object and fill the values in it or read the settings in from a file.

Since ap_setup_dhcp and ap_load_profile_and_dhcp no longer do the
settings file loading, they needed to be refactored and some issues were
fixed in their logic, e.g. l_dhcp_server_set_ip_address() was never
called when the "IP pool" was used.  Also the IP pool was previously only
used if the ap->config->profile was NULL and this didn't match what the
docs said:
"If [IPv4].Address is not provided and no IP address is set on the
interface prior to calling StartProfile the IP pool will be used."
This commit is contained in:
Andrew Zaborowski 2021-04-27 01:34:06 +02:00 committed by Denis Kenzior
parent 11914431bc
commit ab6cd7e465
3 changed files with 357 additions and 258 deletions

545
src/ap.c
View File

@ -61,7 +61,15 @@ struct ap_state {
const struct ap_ops *ops; const struct ap_ops *ops;
ap_stopped_func_t stopped_func; ap_stopped_func_t stopped_func;
void *user_data; void *user_data;
struct ap_config *config;
char ssid[33];
char passphrase[64];
uint8_t psk[32];
uint8_t channel;
uint8_t *authorized_macs;
unsigned int authorized_macs_num;
char wsc_name[33];
struct wsc_primary_device_type wsc_primary_device_type;
unsigned int ciphers; unsigned int ciphers;
enum ie_rsn_cipher_suite group_cipher; enum ie_rsn_cipher_suite group_cipher;
@ -228,24 +236,6 @@ static const char *broadcast_from_ip(const char *ip)
return inet_ntoa(ia); return inet_ntoa(ia);
} }
void ap_config_free(struct ap_config *config)
{
if (unlikely(!config))
return;
l_free(config->ssid);
explicit_bzero(config->passphrase, sizeof(config->passphrase));
explicit_bzero(config->psk, sizeof(config->psk));
l_free(config->authorized_macs);
l_free(config->wsc_name);
if (config->profile)
l_free(config->profile);
l_free(config);
}
static void ap_stop_handshake(struct sta_state *sta) static void ap_stop_handshake(struct sta_state *sta)
{ {
if (sta->sm) { if (sta->sm) {
@ -301,6 +291,14 @@ static void ap_reset(struct ap_state *ap)
{ {
struct netdev *netdev = ap->netdev; struct netdev *netdev = ap->netdev;
explicit_bzero(ap->passphrase, sizeof(ap->passphrase));
explicit_bzero(ap->psk, sizeof(ap->psk));
if (ap->authorized_macs_num) {
l_free(ap->authorized_macs);
ap->authorized_macs_num = 0;
}
if (ap->mlme_watch) if (ap->mlme_watch)
l_genl_family_unregister(ap->nl80211, ap->mlme_watch); l_genl_family_unregister(ap->nl80211, ap->mlme_watch);
@ -317,9 +315,6 @@ static void ap_reset(struct ap_state *ap)
if (ap->rates) if (ap->rates)
l_uintset_free(ap->rates); l_uintset_free(ap->rates);
ap_config_free(ap->config);
ap->config = NULL;
l_queue_destroy(ap->wsc_pbc_probes, l_free); l_queue_destroy(ap->wsc_pbc_probes, l_free);
ap->started = false; ap->started = false;
@ -666,25 +661,23 @@ static size_t ap_write_wsc_ie(struct ap_state *ap,
wsc_pr.response_type = WSC_RESPONSE_TYPE_AP; wsc_pr.response_type = WSC_RESPONSE_TYPE_AP;
memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e)); memcpy(wsc_pr.uuid_e, ap->wsc_uuid_r, sizeof(wsc_pr.uuid_e));
wsc_pr.primary_device_type = wsc_pr.primary_device_type = ap->wsc_primary_device_type;
ap->config->wsc_primary_device_type;
if (ap->config->wsc_name) if (ap->wsc_name[0] != '\0')
l_strlcpy(wsc_pr.device_name, ap->config->wsc_name, l_strlcpy(wsc_pr.device_name, ap->wsc_name,
sizeof(wsc_pr.device_name)); sizeof(wsc_pr.device_name));
wsc_pr.config_methods = wsc_pr.config_methods =
WSC_CONFIGURATION_METHOD_PUSH_BUTTON; WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
if (ap->config->authorized_macs_num) { if (ap->authorized_macs_num) {
size_t len; size_t len = ap->authorized_macs_num * 6;
len = ap->config->authorized_macs_num * 6;
if (len > sizeof(wsc_pr.authorized_macs)) if (len > sizeof(wsc_pr.authorized_macs))
len = sizeof(wsc_pr.authorized_macs); len = sizeof(wsc_pr.authorized_macs);
memcpy(wsc_pr.authorized_macs, memcpy(wsc_pr.authorized_macs,
ap->config->authorized_macs, len); ap->authorized_macs, len);
} }
wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size); wsc_data = wsc_build_probe_response(&wsc_pr, &wsc_data_size);
@ -701,15 +694,14 @@ static size_t ap_write_wsc_ie(struct ap_state *ap,
WSC_CONFIGURATION_METHOD_PUSH_BUTTON; WSC_CONFIGURATION_METHOD_PUSH_BUTTON;
} }
if (ap->config->authorized_macs_num) { if (ap->authorized_macs_num) {
size_t len; size_t len = ap->authorized_macs_num * 6;
len = ap->config->authorized_macs_num * 6;
if (len > sizeof(wsc_beacon.authorized_macs)) if (len > sizeof(wsc_beacon.authorized_macs))
len = sizeof(wsc_beacon.authorized_macs); len = sizeof(wsc_beacon.authorized_macs);
memcpy(wsc_beacon.authorized_macs, memcpy(wsc_beacon.authorized_macs,
ap->config->authorized_macs, len); ap->authorized_macs, len);
} }
wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size); wsc_data = wsc_build_beacon(&wsc_beacon, &wsc_data_size);
@ -831,8 +823,7 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
/* SSID IE */ /* SSID IE */
ie_tlv_builder_next(&builder, IE_TYPE_SSID); ie_tlv_builder_next(&builder, IE_TYPE_SSID);
ie_tlv_builder_set_data(&builder, ap->config->ssid, ie_tlv_builder_set_data(&builder, ap->ssid, strlen(ap->ssid));
strlen(ap->config->ssid));
/* Supported Rates IE */ /* Supported Rates IE */
ie_tlv_builder_next(&builder, IE_TYPE_SUPPORTED_RATES); ie_tlv_builder_next(&builder, IE_TYPE_SUPPORTED_RATES);
@ -857,7 +848,7 @@ static size_t ap_build_beacon_pr_head(struct ap_state *ap,
/* DSSS Parameter Set IE for DSSS, HR, ERP and HT PHY rates */ /* DSSS Parameter Set IE for DSSS, HR, ERP and HT PHY rates */
ie_tlv_builder_next(&builder, IE_TYPE_DSSS_PARAMETER_SET); ie_tlv_builder_next(&builder, IE_TYPE_DSSS_PARAMETER_SET);
ie_tlv_builder_set_data(&builder, &ap->config->channel, 1); ie_tlv_builder_set_data(&builder, &ap->channel, 1);
ie_tlv_builder_finalize(&builder, &len); ie_tlv_builder_finalize(&builder, &len);
return 36 + len; return 36 + len;
@ -938,8 +929,7 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
frame_xchg_cb_t callback, frame_xchg_cb_t callback,
void *user_data) void *user_data)
{ {
uint32_t ch_freq = scan_channel_to_freq(ap->config->channel, uint32_t ch_freq = scan_channel_to_freq(ap->channel, SCAN_BAND_2_4_GHZ);
SCAN_BAND_2_4_GHZ);
uint64_t wdev_id = netdev_get_wdev_id(ap->netdev); uint64_t wdev_id = netdev_get_wdev_id(ap->netdev);
struct iovec iov[2]; struct iovec iov[2];
@ -958,8 +948,7 @@ static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start,
struct ie_rsn_info rsn; struct ie_rsn_info rsn;
uint8_t bss_rsne[64]; uint8_t bss_rsne[64];
handshake_state_set_ssid(sta->hs, (void *) ap->config->ssid, handshake_state_set_ssid(sta->hs, (void *) ap->ssid, strlen(ap->ssid));
strlen(ap->config->ssid));
handshake_state_set_authenticator_address(sta->hs, own_addr); handshake_state_set_authenticator_address(sta->hs, own_addr);
handshake_state_set_supplicant_address(sta->hs, sta->addr); handshake_state_set_supplicant_address(sta->hs, sta->addr);
@ -1026,7 +1015,7 @@ static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
handshake_state_set_authenticator(sta->hs, true); handshake_state_set_authenticator(sta->hs, true);
handshake_state_set_event_func(sta->hs, ap_handshake_event, sta); handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne); handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
handshake_state_set_pmk(sta->hs, sta->ap->config->psk, 32); handshake_state_set_pmk(sta->hs, sta->ap->psk, 32);
ap_start_handshake(sta, false, gtk_rsc); ap_start_handshake(sta, false, gtk_rsc);
} }
@ -1144,16 +1133,15 @@ static void ap_start_eap_wsc(struct sta_state *sta)
WSC_RF_BAND_2_4_GHZ); WSC_RF_BAND_2_4_GHZ);
l_settings_set_uint(sta->wsc_settings, "WSC", "ConfigurationMethods", l_settings_set_uint(sta->wsc_settings, "WSC", "ConfigurationMethods",
WSC_CONFIGURATION_METHOD_PUSH_BUTTON); WSC_CONFIGURATION_METHOD_PUSH_BUTTON);
l_settings_set_string(sta->wsc_settings, "WSC", "WPA2-SSID", l_settings_set_string(sta->wsc_settings, "WSC", "WPA2-SSID", ap->ssid);
ap->config->ssid);
if (ap->config->passphrase[0]) if (ap->passphrase[0])
l_settings_set_string(sta->wsc_settings, l_settings_set_string(sta->wsc_settings,
"WSC", "WPA2-Passphrase", "WSC", "WPA2-Passphrase",
ap->config->passphrase); ap->passphrase);
else else
l_settings_set_bytes(sta->wsc_settings, l_settings_set_bytes(sta->wsc_settings,
"WSC", "WPA2-PSK", ap->config->psk, 32); "WSC", "WPA2-PSK", ap->psk, 32);
sta->hs = netdev_handshake_state_new(ap->netdev); sta->hs = netdev_handshake_state_new(ap->netdev);
handshake_state_set_authenticator(sta->hs, true); handshake_state_set_authenticator(sta->hs, true);
@ -1611,8 +1599,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
} }
if (!rates || !ssid || (!wsc_data && !rsn) || if (!rates || !ssid || (!wsc_data && !rsn) ||
ssid_len != strlen(ap->config->ssid) || ssid_len != strlen(ap->ssid) ||
memcmp(ssid, ap->config->ssid, ssid_len)) { memcmp(ssid, ap->ssid, ssid_len)) {
err = MMPDU_REASON_CODE_INVALID_IE; err = MMPDU_REASON_CODE_INVALID_IE;
goto bad_frame; goto bad_frame;
} }
@ -1922,11 +1910,11 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
if (!ssid || ssid_len == 0) /* Wildcard SSID */ if (!ssid || ssid_len == 0) /* Wildcard SSID */
match = true; match = true;
else if (ssid && ssid_len == strlen(ap->config->ssid) && /* One SSID */ else if (ssid && ssid_len == strlen(ap->ssid) && /* One SSID */
!memcmp(ssid, ap->config->ssid, ssid_len)) !memcmp(ssid, ap->ssid, ssid_len))
match = true; match = true;
else if (ssid && ssid_len == 7 && !memcmp(ssid, "DIRECT-", 7) && else if (ssid && ssid_len == 7 && !memcmp(ssid, "DIRECT-", 7) &&
!memcmp(ssid, ap->config->ssid, 7)) /* P2P wildcard */ !memcmp(ssid, ap->ssid, 7)) /* P2P wildcard */
match = true; match = true;
else if (ssid_list) { /* SSID List */ else if (ssid_list) { /* SSID List */
ie_tlv_iter_init(&iter, ssid_list, ssid_list_len); ie_tlv_iter_init(&iter, ssid_list, ssid_list_len);
@ -1938,16 +1926,15 @@ static void ap_probe_req_cb(const struct mmpdu_header *hdr, const void *body,
ssid = (const char *) ie_tlv_iter_get_data(&iter); ssid = (const char *) ie_tlv_iter_get_data(&iter);
ssid_len = ie_tlv_iter_get_length(&iter); ssid_len = ie_tlv_iter_get_length(&iter);
if (ssid_len == strlen(ap->config->ssid) && if (ssid_len == strlen(ap->ssid) &&
!memcmp(ssid, ap->config->ssid, !memcmp(ssid, ap->ssid, ssid_len)) {
ssid_len)) {
match = true; match = true;
break; break;
} }
} }
} }
if (dsss_channel != 0 && dsss_channel != ap->config->channel) if (dsss_channel != 0 && dsss_channel != ap->channel)
match = false; match = false;
if (!match) if (!match)
@ -2053,15 +2040,15 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
memcmp(hdr->address_3, bssid, 6)) memcmp(hdr->address_3, bssid, 6))
return; return;
if (ap->config->authorized_macs_num) { if (ap->authorized_macs_num) {
unsigned int i; unsigned int i;
for (i = 0; i < ap->config->authorized_macs_num; i++) for (i = 0; i < ap->authorized_macs_num; i++)
if (!memcmp(from, ap->config->authorized_macs + i * 6, if (!memcmp(from, ap->authorized_macs + i * 6,
6)) 6))
break; break;
if (i == ap->config->authorized_macs_num) { if (i == ap->authorized_macs_num) {
ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED); ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
return; return;
} }
@ -2210,8 +2197,7 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
uint32_t nl_akm = CRYPTO_AKM_PSK; uint32_t nl_akm = CRYPTO_AKM_PSK;
uint32_t wpa_version = NL80211_WPA_VERSION_2; uint32_t wpa_version = NL80211_WPA_VERSION_2;
uint32_t auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; uint32_t auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM;
uint32_t ch_freq = scan_channel_to_freq(ap->config->channel, uint32_t ch_freq = scan_channel_to_freq(ap->channel, SCAN_BAND_2_4_GHZ);
SCAN_BAND_2_4_GHZ);
uint32_t ch_width = NL80211_CHAN_WIDTH_20; uint32_t ch_width = NL80211_CHAN_WIDTH_20;
unsigned int i; unsigned int i;
@ -2233,7 +2219,7 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
return NULL; return NULL;
cmd = l_genl_msg_new_sized(NL80211_CMD_START_AP, 256 + head_len + cmd = l_genl_msg_new_sized(NL80211_CMD_START_AP, 256 + head_len +
tail_len + strlen(ap->config->ssid)); tail_len + strlen(ap->ssid));
/* SET_BEACON attrs */ /* SET_BEACON attrs */
l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head); l_genl_msg_append_attr(cmd, NL80211_ATTR_BEACON_HEAD, head_len, head);
@ -2247,8 +2233,8 @@ static struct l_genl_msg *ap_build_cmd_start_ap(struct ap_state *ap)
&ap->beacon_interval); &ap->beacon_interval);
l_genl_msg_append_attr(cmd, NL80211_ATTR_DTIM_PERIOD, 4, &dtim_period); l_genl_msg_append_attr(cmd, NL80211_ATTR_DTIM_PERIOD, 4, &dtim_period);
l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_append_attr(cmd, NL80211_ATTR_IFINDEX, 4, &ifindex);
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ap->config->ssid), l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, strlen(ap->ssid),
ap->config->ssid); ap->ssid);
l_genl_msg_append_attr(cmd, NL80211_ATTR_HIDDEN_SSID, 4, l_genl_msg_append_attr(cmd, NL80211_ATTR_HIDDEN_SSID, 4,
&hidden_ssid); &hidden_ssid);
l_genl_msg_append_attr(cmd, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, l_genl_msg_append_attr(cmd, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
@ -2493,7 +2479,8 @@ 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) static bool dhcp_load_settings(struct ap_state *ap,
const struct l_settings *settings)
{ {
struct l_dhcp_server *server = ap->server; struct l_dhcp_server *server = ap->server;
struct in_addr ia; struct in_addr ia;
@ -2568,77 +2555,41 @@ error:
* 3. Address in IP pool. * 3. Address in IP pool.
* *
* Returns: 0 if an IP was successfully selected and needs to be set * Returns: 0 if an IP was successfully selected and needs to be set
* -EALREADY if an IP was already set on the interface * -EALREADY if an IP was already set on the interface or
* IP configuration was not enabled,
* -EEXIST if the IP pool ran out of IP's * -EEXIST if the IP pool ran out of IP's
* -EINVAL if there was an error. * -EINVAL if there was an error.
*/ */
static int ap_setup_dhcp(struct ap_state *ap, struct l_settings *settings) static int ap_setup_dhcp(struct ap_state *ap, const struct l_settings *settings)
{ {
uint32_t ifindex = netdev_get_ifindex(ap->netdev); uint32_t ifindex = netdev_get_ifindex(ap->netdev);
struct in_addr ia; struct in_addr ia;
uint32_t address = 0; uint32_t address = 0;
L_AUTO_FREE_VAR(char *, addr) = NULL;
int ret = -EINVAL; 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 */ /* get the current address if there is one */
if (l_net_get_address(ifindex, &ia) && ia.s_addr != 0) if (l_net_get_address(ifindex, &ia) && ia.s_addr != 0)
address = ia.s_addr; address = ia.s_addr;
if (ap->config->profile) { addr = l_settings_get_string(settings, "IPv4", "Address");
char *addr; if (addr) {
if (inet_pton(AF_INET, addr, &ia) < 0)
return -EINVAL;
addr = l_settings_get_string(settings, "IPv4", "Address"); /* Is a matching address already set on interface? */
if (addr) { if (ia.s_addr == address)
if (inet_pton(AF_INET, addr, &ia) < 0)
goto free_addr;
/* 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; ret = -EALREADY;
} else else
goto free_addr; ret = 0;
/* Set the remaining DHCP options in config file */
if (!dhcp_load_settings(ap, settings)) {
ret = -EINVAL;
goto free_addr;
}
if (!l_dhcp_server_set_ip_address(ap->server, addr)) {
ret = -EINVAL;
goto free_addr;
}
ap->own_ip = l_strdup(addr);
free_addr:
l_free(addr);
return ret;
} else if (address) { } else if (address) {
/* No config file and address is already set */ /* No address in config, but interface has one set */
ap->own_ip = l_strdup(inet_ntoa(ia)); addr = l_strdup(inet_ntoa(ia));
ret = -EALREADY;
return -EALREADY;
} else if (pool.used) { } else if (pool.used) {
/* No config file, no address set. Use IP pool */ /* No config file, no address set. Use IP pool */
ap->own_ip = ip_pool_get(); addr = ip_pool_get();
if (!ap->own_ip) { if (!addr) {
l_error("No more IP's in pool, cannot start AP on %u", l_error("No more IP's in pool, cannot start AP on %u",
ifindex); ifindex);
return -EEXIST; return -EEXIST;
@ -2646,52 +2597,41 @@ free_addr:
ap->use_ip_pool = true; ap->use_ip_pool = true;
ap->ip_prefix = pool.prefix; ap->ip_prefix = pool.prefix;
ret = 0;
} else
return -EALREADY;
return 0; ap->server = l_dhcp_server_new(ifindex);
if (!ap->server) {
l_error("Failed to create DHCP server on %u", ifindex);
return -EINVAL;
} }
return -EINVAL; if (getenv("IWD_DHCP_DEBUG"))
l_dhcp_server_set_debug(ap->server, do_debug,
"[DHCPv4 SERV] ", NULL);
/* Set the remaining DHCP options in config file */
if (!dhcp_load_settings(ap, settings))
return -EINVAL;
if (!l_dhcp_server_set_ip_address(ap->server, addr))
return -EINVAL;
ap->own_ip = l_steal_ptr(addr);
return ret;
} }
static int ap_load_profile_and_dhcp(struct ap_state *ap, bool *wait_dhcp) static int ap_load_dhcp(struct ap_state *ap, const struct l_settings *config,
bool *wait_dhcp)
{ {
uint32_t ifindex = netdev_get_ifindex(ap->netdev); uint32_t ifindex = netdev_get_ifindex(ap->netdev);
char *passphrase;
struct l_settings *settings = NULL;
int err = -EINVAL; int err = -EINVAL;
/* No profile or DHCP settings */ if (!l_settings_has_group(config, "IPv4"))
if (!ap->config->profile && !pool.used)
return 0; return 0;
if (ap->config->profile) { err = ap_setup_dhcp(ap, config);
settings = l_settings_new();
if (!l_settings_load_from_file(settings, ap->config->profile))
goto cleanup;
passphrase = l_settings_get_string(settings, "Security",
"Passphrase");
if (passphrase) {
if (strlen(passphrase) > 63) {
l_error("[Security].Passphrase must not exceed "
"63 characters");
l_free(passphrase);
goto cleanup;
}
strcpy(ap->config->passphrase, passphrase);
l_free(passphrase);
}
if (!l_settings_has_group(settings, "IPv4")) {
*wait_dhcp = false;
err = 0;
goto cleanup;
}
}
err = ap_setup_dhcp(ap, settings);
if (err == 0) { if (err == 0) {
/* Address change required */ /* Address change required */
ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex, ap->rtnl_add_cmd = l_rtnl_ifaddr4_add(rtnl, ifindex,
@ -2701,8 +2641,7 @@ static int ap_load_profile_and_dhcp(struct ap_state *ap, bool *wait_dhcp)
if (!ap->rtnl_add_cmd) { if (!ap->rtnl_add_cmd) {
l_error("Failed to add IPv4 address"); l_error("Failed to add IPv4 address");
err = -EIO; return -EIO;
goto cleanup;
} }
ap->cleanup_ip = true; ap->cleanup_ip = true;
@ -2715,24 +2654,201 @@ static int ap_load_profile_and_dhcp(struct ap_state *ap, bool *wait_dhcp)
err = 0; err = 0;
} }
cleanup:
l_settings_free(settings);
return err; return err;
} }
static bool ap_load_psk(struct ap_state *ap, const struct l_settings *config)
{
L_AUTO_FREE_VAR(char *, passphrase) =
l_settings_get_string(config, "Security", "Passphrase");
int err;
if (passphrase) {
if (strlen(passphrase) > 63) {
l_error("AP [Security].Passphrase must not exceed "
"63 characters");
return false;
}
strcpy(ap->passphrase, passphrase);
}
if (l_settings_has_key(config, "Security", "PreSharedKey")) {
size_t psk_len;
L_AUTO_FREE_VAR(uint8_t *, psk) = l_settings_get_bytes(config,
"Security",
"PreSharedKey",
&psk_len);
if (!psk || psk_len != 32) {
l_error("AP [Security].PreSharedKey must be a 32-byte "
"hexstring");
return false;
}
memcpy(ap->psk, psk, 32);
return true;
}
if (!passphrase) {
l_error("AP requires at least one of [Security].PreSharedKey, "
"[Security].Passphrase to be present");
return false;
}
err = crypto_psk_from_passphrase(passphrase, (uint8_t *) ap->ssid,
strlen(ap->ssid), ap->psk);
if (err < 0) {
l_error("AP couldn't generate the PSK from given "
"[Security].Passphrase value: %s (%i)",
strerror(-err), -err);
return false;
}
return true;
}
static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
bool *out_wait_dhcp, bool *out_cck_rates)
{
size_t len;
L_AUTO_FREE_VAR(char *, strval) = NULL;
int err;
strval = l_settings_get_string(config, "General", "SSID");
if (L_WARN_ON(!strval))
return -ENOMSG;
len = strlen(strval);
if (len < 1 || len > 32) {
l_error("AP SSID length outside the [1, 32] range");
return -EINVAL;
}
if (L_WARN_ON(!l_utf8_validate(strval, len, NULL)))
return -EINVAL;
strcpy(ap->ssid, strval);
l_free(l_steal_ptr(strval));
if (!ap_load_psk(ap, config))
return -EINVAL;
/*
* This loads DHCP settings either from the l_settings object or uses
* the defaults. wait_on_address will be set true if an address change
* is required.
*/
err = ap_load_dhcp(ap, config, out_wait_dhcp);
if (err)
return err;
if (l_settings_has_key(config, "General", "Channel")) {
unsigned int uintval;
if (!l_settings_get_uint(config, "General", "Channel",
&uintval) ||
!scan_channel_to_freq(uintval,
SCAN_BAND_2_4_GHZ)) {
l_error("AP Channel value unsupported");
return -EINVAL;
}
ap->channel = uintval;
} else
/* TODO: Start a Get Survey to decide the channel */
ap->channel = 6;
strval = l_settings_get_string(config, "WSC", "DeviceName");
if (strval) {
len = strlen(strval);
if (len > 32) {
l_error("AP WSC name length outside the [1, 32] range");
return -EINVAL;
}
if (!l_utf8_validate(strval, len, NULL)) {
l_error("AP WSC name doesn't validate as UTF-8");
return -EINVAL;
}
strcpy(ap->wsc_name, strval);
l_free(l_steal_ptr(strval));
} else
memcpy(ap->wsc_name, ap->ssid, 33);
strval = l_settings_get_string(config, "WSC", "PrimaryDeviceType");
if (strval) {
bool ok = wsc_device_type_from_setting_str(strval,
&ap->wsc_primary_device_type);
if (!ok) {
l_error("AP [WSC].PrimaryDeviceType format unknown");
return -EINVAL;
}
} else {
/* Make ourselves a WFA standard PC by default */
ap->wsc_primary_device_type.category = 1;
memcpy(ap->wsc_primary_device_type.oui, wsc_wfa_oui, 3);
ap->wsc_primary_device_type.oui_type = 0x04;
ap->wsc_primary_device_type.subcategory = 1;
}
if (l_settings_get_value(config, "WSC", "AuthorizedMACs")) {
char **strvval;
unsigned int i;
strvval = l_settings_get_string_list(config, "WSC",
"AuthorizedMACs", ',');
if (!strvval) {
l_error("AP Authorized MACs list format wrong");
return -EINVAL;
}
ap->authorized_macs_num = l_strv_length(strvval);
ap->authorized_macs = l_malloc(ap->authorized_macs_num * 6);
for (i = 0; strvval[i]; i++)
if (!util_string_to_address(strvval[i],
ap->authorized_macs + i * 6)) {
l_error("Bad authorized MAC format: %s",
strvval[i]);
l_strfreev(strvval);
return -EINVAL;
}
l_strfreev(strvval);
}
if (l_settings_get_value(config, "General", "NoCCKRates")) {
bool boolval;
if (!l_settings_get_bool(config, "General", "NoCCKRates",
&boolval)) {
l_error("AP [General].NoCCKRates not a valid "
"boolean");
return -EINVAL;
}
*out_cck_rates = !boolval;
} else
*out_cck_rates = true;
return 0;
}
/* /*
* Start a simple independent WPA2 AP on given netdev. * Start a simple independent WPA2 AP on given netdev.
* *
* @ops.handle_event is required and must react to AP_EVENT_START_FAILED * @ops.handle_event is required and must react to AP_EVENT_START_FAILED
* and AP_EVENT_STOPPING by forgetting the ap_state struct, which is * and AP_EVENT_STOPPING by forgetting the ap_state struct, which is
* going to be freed automatically. * going to be freed automatically.
* In the @config struct the .ssid field is required and one of * In the @config struct the [General].SSID key is required and one of
* .passphrase and .psk must be filled in. All other fields are optional. * [Security].Passphrase and [Security].PreSharedKey must be filled in.
* If @ap_start succeeds, the returned ap_state takes ownership of * All other fields are optional.
* @config and the caller shouldn't free it or any of the memory pointed
* to by its members (which also can't be static).
*/ */
struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config, struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
const struct ap_ops *ops, int *err_out, const struct ap_ops *ops, int *err_out,
void *user_data) void *user_data)
{ {
@ -2742,57 +2858,37 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
uint64_t wdev_id = netdev_get_wdev_id(netdev); uint64_t wdev_id = netdev_get_wdev_id(netdev);
int err = -EINVAL; int err = -EINVAL;
bool wait_on_address = false; bool wait_on_address = false;
bool cck_rates = true;
if (err_out) if (err_out)
*err_out = err; *err_out = err;
if (L_WARN_ON(!config->ssid)) if (L_WARN_ON(!config))
return NULL;
if (L_WARN_ON(!config->profile && !config->passphrase[0] &&
l_memeqzero(config->psk, sizeof(config->psk))))
return NULL; return NULL;
ap = l_new(struct ap_state, 1); ap = l_new(struct ap_state, 1);
ap->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME); ap->nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME);
ap->config = config;
ap->netdev = netdev; ap->netdev = netdev;
ap->ops = ops; ap->ops = ops;
ap->user_data = user_data; ap->user_data = user_data;
/* err = ap_load_config(ap, config, &wait_on_address, &cck_rates);
* This both loads a profile if required and loads DHCP settings either if (err)
* by the profile itself or the IP pool (or does nothing in the case
* of a profile-less configuration). wait_on_address will be set true
* if an address change is required.
*/
err = ap_load_profile_and_dhcp(ap, &wait_on_address);
if (err < 0)
goto error; goto error;
if (!config->channel) err = -EINVAL;
/* TODO: Start a Get Survey to decide the channel */
config->channel = 6;
if (!config->wsc_name)
config->wsc_name = l_strdup(config->ssid);
if (!config->wsc_primary_device_type.category) {
/* Make ourselves a WFA standard PC by default */
config->wsc_primary_device_type.category = 1;
memcpy(config->wsc_primary_device_type.oui, wsc_wfa_oui, 3);
config->wsc_primary_device_type.oui_type = 0x04;
config->wsc_primary_device_type.subcategory = 1;
}
/* TODO: Add all ciphers supported by wiphy */ /* TODO: Add all ciphers supported by wiphy */
ap->ciphers = wiphy_select_cipher(wiphy, 0xffff); ap->ciphers = wiphy_select_cipher(wiphy, 0xffff);
ap->group_cipher = wiphy_select_cipher(wiphy, 0xffff); ap->group_cipher = wiphy_select_cipher(wiphy, 0xffff);
ap->beacon_interval = 100; ap->beacon_interval = 100;
wsc_uuid_from_addr(netdev_get_address(netdev), ap->wsc_uuid_r);
ap->rates = l_uintset_new(200); ap->rates = l_uintset_new(200);
/* TODO: Pick from actual supported rates */ /* TODO: Pick from actual supported rates */
if (config->no_cck_rates) { if (!cck_rates) {
l_uintset_put(ap->rates, 12); /* 6 Mbps*/ l_uintset_put(ap->rates, 12); /* 6 Mbps*/
l_uintset_put(ap->rates, 18); /* 9 Mbps*/ l_uintset_put(ap->rates, 18); /* 9 Mbps*/
l_uintset_put(ap->rates, 24); /* 12 Mbps*/ l_uintset_put(ap->rates, 24); /* 12 Mbps*/
@ -2807,15 +2903,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
l_uintset_put(ap->rates, 22); /* 11 Mbps*/ l_uintset_put(ap->rates, 22); /* 11 Mbps*/
} }
wsc_uuid_from_addr(netdev_get_address(netdev), ap->wsc_uuid_r);
if (config->passphrase[0] &&
crypto_psk_from_passphrase(config->passphrase,
(uint8_t *) config->ssid,
strlen(config->ssid),
config->psk) < 0)
goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4), (MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4),
NULL, 0, ap_assoc_req_cb, ap, NULL)) NULL, 0, ap_assoc_req_cb, ap, NULL))
@ -2878,7 +2965,6 @@ error:
if (err_out) if (err_out)
*err_out = err; *err_out = err;
ap->config = NULL;
ap_reset(ap); ap_reset(ap);
l_genl_family_free(ap->nl80211); l_genl_family_free(ap->nl80211);
l_free(ap); l_free(ap);
@ -3128,7 +3214,7 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
{ {
struct ap_if_data *ap_if = user_data; struct ap_if_data *ap_if = user_data;
const char *ssid, *wpa2_passphrase; const char *ssid, *wpa2_passphrase;
struct ap_config *config; struct l_settings *config;
int err; int err;
if (ap_if->ap && ap_if->ap->started) if (ap_if->ap && ap_if->ap->started)
@ -3141,16 +3227,17 @@ static struct l_dbus_message *ap_dbus_start(struct l_dbus *dbus,
&ssid, &wpa2_passphrase)) &ssid, &wpa2_passphrase))
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
config = l_new(struct ap_config, 1); config = l_settings_new();
config->ssid = l_strdup(ssid); l_settings_set_string(config, "General", "SSID", ssid);
l_strlcpy(config->passphrase, wpa2_passphrase, l_settings_set_string(config, "Security", "Passphrase",
sizeof(config->passphrase)); wpa2_passphrase);
l_settings_add_group(config, "IPv4");
ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if); ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if);
if (!ap_if->ap) { l_settings_free(config);
ap_config_free(config);
if (!ap_if->ap)
return dbus_error_from_errno(err, message); return dbus_error_from_errno(err, message);
}
ap_if->pending = l_dbus_message_ref(message); ap_if->pending = l_dbus_message_ref(message);
return NULL; return NULL;
@ -3200,7 +3287,8 @@ static struct l_dbus_message *ap_dbus_start_profile(struct l_dbus *dbus,
{ {
struct ap_if_data *ap_if = user_data; struct ap_if_data *ap_if = user_data;
const char *ssid; const char *ssid;
struct ap_config *config; struct l_settings *config;
char *config_path;
int err; int err;
if (ap_if->ap && ap_if->ap->started) if (ap_if->ap && ap_if->ap->started)
@ -3212,19 +3300,32 @@ static struct l_dbus_message *ap_dbus_start_profile(struct l_dbus *dbus,
if (!l_dbus_message_get_arguments(message, "s", &ssid)) if (!l_dbus_message_get_arguments(message, "s", &ssid))
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
config = l_new(struct ap_config, 1); config = l_settings_new();
config->ssid = l_strdup(ssid); config_path = storage_get_path("ap/%s.ap", ssid);
/* This tells ap_start to pull settings from a profile on disk */ err = l_settings_load_from_file(config, config_path) ? 0 : -EIO;
config->profile = storage_get_path("ap/%s.ap", ssid); l_free(config_path);
if (err)
goto error;
/*
* Since [General].SSID is not an allowed setting for a profile on
* disk, we're free to potentially overwrite it with the SSID that
* the DBus user asked for.
*/
l_settings_set_string(config, "General", "SSID", ssid);
ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if); ap_if->ap = ap_start(ap_if->netdev, config, &ap_dbus_ops, &err, ap_if);
if (!ap_if->ap) { l_settings_free(config);
ap_config_free(config);
return dbus_error_from_errno(err, message); if (!ap_if->ap)
} goto error;
ap_if->pending = l_dbus_message_ref(message); ap_if->pending = l_dbus_message_ref(message);
return NULL; return NULL;
error:
return dbus_error_from_errno(err, message);
} }
static bool ap_dbus_property_get_started(struct l_dbus *dbus, static bool ap_dbus_property_get_started(struct l_dbus *dbus,
@ -3247,11 +3348,11 @@ static bool ap_dbus_property_get_name(struct l_dbus *dbus,
{ {
struct ap_if_data *ap_if = user_data; struct ap_if_data *ap_if = user_data;
if (!ap_if->ap || !ap_if->ap->config || !ap_if->ap->started) if (!ap_if->ap || !ap_if->ap->started)
return false; return false;
l_dbus_message_builder_append_basic(builder, 's', l_dbus_message_builder_append_basic(builder, 's',
ap_if->ap->config->ssid); ap_if->ap->ssid);
return true; return true;
} }

View File

@ -58,21 +58,6 @@ struct ap_event_registration_success_data {
typedef void (*ap_stopped_func_t)(void *user_data); typedef void (*ap_stopped_func_t)(void *user_data);
struct ap_config {
char *ssid;
char passphrase[64];
uint8_t psk[32];
uint8_t channel;
uint8_t *authorized_macs;
unsigned int authorized_macs_num;
char *wsc_name;
struct wsc_primary_device_type wsc_primary_device_type;
char *profile;
bool no_cck_rates : 1;
};
struct ap_ops { struct ap_ops {
void (*handle_event)(enum ap_event_type type, const void *event_data, void (*handle_event)(enum ap_event_type type, const void *event_data,
void *user_data); void *user_data);
@ -100,9 +85,7 @@ struct ap_ops {
uint8_t *out_buf, void *user_data); uint8_t *out_buf, void *user_data);
}; };
void ap_config_free(struct ap_config *config); struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
struct ap_state *ap_start(struct netdev *netdev, struct ap_config *config,
const struct ap_ops *ops, int *err, const struct ap_ops *ops, int *err,
void *user_data); void *user_data);
void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func, void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,

View File

@ -1187,13 +1187,32 @@ static const struct ap_ops p2p_go_ops = {
static void p2p_group_start(struct p2p_device *dev) static void p2p_group_start(struct p2p_device *dev)
{ {
struct ap_config *config = l_new(struct ap_config, 1); struct l_settings *config = l_settings_new();
uint8_t psk[32];
char *macs[2] = {};
const struct wsc_primary_device_type *pdt =
&dev->device_info.primary_device_type;
uint64_t pdt_uint =
((uint64_t) pdt->category << 48) |
((uint64_t) pdt->oui[0] << 40) |
((uint64_t) pdt->oui[1] << 32) |
((uint64_t) pdt->oui[2] << 24) |
((uint64_t) pdt->oui_type << 16) |
pdt->subcategory;
config->ssid = l_strdup(dev->go_group_id.ssid); l_settings_set_string(config, "General", "SSID", dev->go_group_id.ssid);
config->channel = dev->listen_channel; l_settings_set_uint(config, "General", "Channel", dev->listen_channel);
config->wsc_name = l_strdup(dev->device_info.device_name); l_settings_set_bool(config, "General", "NoCCKRates", true);
config->wsc_primary_device_type = dev->device_info.primary_device_type; l_settings_set_string(config, "WSC", "DeviceName",
config->no_cck_rates = true; dev->device_info.device_name);
l_settings_set_uint64(config, "WSC", "PrimaryDeviceType", pdt_uint);
/*
* Section 3.1.4.4: "It shall only allow association by the
* P2P Device that it is currently in Group Formation with."
*/
macs[0] = (char *) util_address_to_string(
dev->conn_peer_interface_addr);
l_settings_set_string_list(config, "WSC", "AuthorizedMACs", macs, ',');
/* /*
* Section 3.2.1: "The Credentials for a P2P Group issued to a * Section 3.2.1: "The Credentials for a P2P Group issued to a
@ -1207,29 +1226,25 @@ static void p2p_group_start(struct p2p_device *dev)
* it's a little costlier to generate for the same cryptographic * it's a little costlier to generate for the same cryptographic
* strength as the PSK. * strength as the PSK.
*/ */
if (!l_getrandom(config->psk, 32)) { if (!l_getrandom(psk, 32)) {
l_error("l_getrandom() failed"); l_error("l_getrandom() failed");
ap_config_free(config); l_settings_free(config);
p2p_connect_failed(dev); p2p_connect_failed(dev);
return; return;
} }
/* l_settings_set_bytes(config, "Security", "PreSharedKey", psk, 32);
* Section 3.1.4.4: "It shall only allow association by the
* P2P Device that it is currently in Group Formation with." l_settings_add_group(config, "IPv4");
*/
config->authorized_macs = l_memdup(dev->conn_peer_interface_addr, 6);
config->authorized_macs_num = 1;
dev->capability.group_caps |= P2P_GROUP_CAP_GO; dev->capability.group_caps |= P2P_GROUP_CAP_GO;
dev->capability.group_caps |= P2P_GROUP_CAP_GROUP_FORMATION; dev->capability.group_caps |= P2P_GROUP_CAP_GROUP_FORMATION;
dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, NULL, dev); dev->group = ap_start(dev->conn_netdev, config, &p2p_go_ops, NULL, dev);
if (!dev->group) { l_settings_free(config);
ap_config_free(config);
if (!dev->group)
p2p_connect_failed(dev); p2p_connect_failed(dev);
return;
}
} }
static void p2p_netconfig_event_handler(enum netconfig_event event, static void p2p_netconfig_event_handler(enum netconfig_event event,