From 556b30daca49c601f4833a77e75cebdd8e496e5d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 22 Oct 2013 19:45:46 +0300 Subject: [PATCH] P2P: Add option to remove channels from GO use The new p2p_no_go_freq frequency range list (comma-separated list of min-max frequency ranges in MHz) can now be used to configure channels on which the local device is not allowed to operate as a GO, but on which that device can be a P2P Client. These channels are left in the P2P Channel List in GO Negotiation to allow the peer device to select one of the channels for the cases where the peer becomes the GO. The local end will remove these channels from consideration if it becomes the GO. Signed-hostap: Jouni Malinen --- src/p2p/p2p.c | 33 +++++++++++++++++++++++ src/p2p/p2p.h | 17 ++++++++++++ src/p2p/p2p_go_neg.c | 3 +++ src/p2p/p2p_i.h | 4 +++ src/p2p/p2p_utils.c | 47 +++++++++++++++++++++++++++++++++ wpa_supplicant/config.c | 22 +++++++++++++++ wpa_supplicant/config.h | 1 + wpa_supplicant/config_file.c | 7 +++++ wpa_supplicant/p2p_supplicant.c | 31 +++++++++++++--------- wpa_supplicant/wpa_cli.c | 1 + 10 files changed, 154 insertions(+), 12 deletions(-) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index aa1f29187..21d3c7ee0 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -1550,8 +1550,15 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) } } + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &intersection); + if (go) { + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); + } freqs = 0; for (i = 0; i < intersection.reg_classes; i++) { struct p2p_reg_class *c = &intersection.reg_class[i]; @@ -2402,6 +2409,7 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->sd_resp); os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p->no_go_freq.range); os_free(p2p); } @@ -3933,6 +3941,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, } +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list) +{ + struct wpa_freq_range *tmp; + + if (list == NULL || list->num == 0) { + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = NULL; + p2p->no_go_freq.num = 0; + return 0; + } + + tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); + if (tmp == NULL) + return -1; + os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = tmp; + p2p->no_go_freq.num = list->num; + p2p_dbg(p2p, "Updated no GO chan list"); + + return 0; +} + + int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 9b6921e55..db7ca9d85 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -1648,6 +1648,14 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels, */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); +/** + * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); + /** * p2p_get_pref_freq - Get channel from preferred channel list * @p2p: P2P module context from p2p_init() @@ -1764,6 +1772,15 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan); +/** + * p2p_set_no_go_freq - Set no GO channel ranges + * @p2p: P2P module context from p2p_init() + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure + */ +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); + /** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 9e9d2eea4..1c0aeb000 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -479,6 +479,9 @@ static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, p2p_channels_dump(p2p, "peer channels", &dev->channels); p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); p2p_channels_dump(p2p, "intersection", &intersection); + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); if (intersection.reg_classes == 0 || intersection.reg_class[0].channels == 0) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 3631abd1e..e5075ae1d 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -316,6 +316,8 @@ struct p2p_data { */ struct p2p_channels channels; + struct wpa_freq_range_list no_go_freq; + enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, @@ -570,6 +572,8 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); void p2p_channels_dump(struct p2p_data *p2p, const char *title, diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 8489b535c..b7bbfcf74 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -204,6 +204,42 @@ void p2p_channels_intersect(const struct p2p_channels *a, } +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list) +{ + size_t o, c; + + if (list == NULL) + return; + + o = 0; + while (o < chan->reg_classes) { + struct p2p_reg_class *op = &chan->reg_class[o]; + + c = 0; + while (c < op->channels) { + int freq = p2p_channel_to_freq(op->reg_class, + op->channel[c]); + if (freq > 0 && freq_range_list_includes(list, freq)) { + op->channels--; + os_memmove(&op->channel[c], + &op->channel[c + 1], + op->channels - c); + } else + c++; + } + + if (op->channels == 0) { + chan->reg_classes--; + os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], + (chan->reg_classes - o) * + sizeof(struct p2p_reg_class)); + } else + o++; + } +} + + /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels @@ -254,6 +290,17 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) } +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) && + !freq_range_list_includes(&p2p->no_go_freq, freq); +} + + unsigned int p2p_get_pref_freq(struct p2p_data *p2p, const struct p2p_channels *channels) { diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 66ee30a3f..d9f2bc268 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1936,6 +1936,7 @@ void wpa_config_free(struct wpa_config *config) os_free(config->p2p_ssid_postfix); os_free(config->pssid); os_free(config->p2p_pref_chan); + os_free(config->p2p_no_go_freq.range); os_free(config->autoscan); os_free(config->freq_list); wpabuf_free(config->wps_nfc_dh_pubkey); @@ -3079,6 +3080,26 @@ fail: wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line); return -1; } + + +static int wpa_config_process_p2p_no_go_freq( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int ret; + + ret = freq_range_list_parse(&config->p2p_no_go_freq, pos); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items", + config->p2p_no_go_freq.num); + + return 0; +} + #endif /* CONFIG_P2P */ @@ -3229,6 +3250,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS }, { INT(p2p_group_idle), 0 }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, + { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN }, { INT(p2p_go_ht40), 0 }, { INT(p2p_disabled), 0 }, { INT(p2p_no_group_iface), 0 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 2e558fdb4..de8ba9e07 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -605,6 +605,7 @@ struct wpa_config { int p2p_intra_bss; unsigned int num_p2p_pref_chan; struct p2p_channel *p2p_pref_chan; + struct wpa_freq_range_list p2p_no_go_freq; int p2p_ignore_shared_freq; struct wpabuf *wps_vendor_ext_m1; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index b8fff70cc..75b302263 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -941,6 +941,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } fprintf(f, "\n"); } + if (config->p2p_no_go_freq.num) { + char *val = freq_range_list_str(&config->p2p_no_go_freq); + if (val) { + fprintf(f, "p2p_no_go_freq=%s\n", val); + os_free(val); + } + } if (config->p2p_go_ht40) fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); if (config->p2p_disabled) diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 817232b30..8e7a45b26 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -3386,6 +3386,8 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) global->p2p, wpa_s->conf->wps_vendor_ext[i]); } + p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq); + return 0; } @@ -4313,8 +4315,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz " "band"); if (wpa_s->best_24_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_24_freq)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_24_freq)) { freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band " "channel: %d MHz", freq); @@ -4330,7 +4332,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz " "band"); if (wpa_s->best_5_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, + p2p_supported_freq_go(wpa_s->global->p2p, wpa_s->best_5_freq)) { freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band " @@ -4338,7 +4340,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } else { os_get_random((u8 *) &r, sizeof(r)); freq = 5180 + (r % 4) * 20; - if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { wpa_printf(MSG_DEBUG, "P2P: Could not select " "5 GHz channel for P2P group"); return -1; @@ -4348,7 +4350,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) } } - if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) { + if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) { wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " "(%u MHz) is not supported for P2P uses", freq); @@ -4401,24 +4403,24 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, "frequency %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_overall_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_overall_freq) && + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_overall_freq) && freq_included(channels, wpa_s->best_overall_freq)) { params->freq = wpa_s->best_overall_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall " "channel %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_24_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_24_freq) && + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_24_freq) && freq_included(channels, wpa_s->best_24_freq)) { params->freq = wpa_s->best_24_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz " "channel %d MHz", params->freq); } else if (wpa_s->conf->p2p_oper_channel == 0 && wpa_s->best_5_freq > 0 && - p2p_supported_freq(wpa_s->global->p2p, - wpa_s->best_5_freq) && + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_5_freq) && freq_included(channels, wpa_s->best_5_freq)) { params->freq = wpa_s->best_5_freq; wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz " @@ -4564,7 +4566,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, NULL)) return -1; if (params.freq && - !p2p_supported_freq(wpa_s->global->p2p, params.freq)) { + !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) { wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO " "(%u MHz) is not supported for P2P uses", params.freq); @@ -5605,6 +5607,11 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) wpa_printf(MSG_ERROR, "P2P: Preferred channel list " "update failed"); } + + if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) { + wpa_printf(MSG_ERROR, "P2P: No GO channel list " + "update failed"); + } } } diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index d79381916..9268446fa 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -615,6 +615,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan", + "p2p_no_go_freq", "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface", "p2p_ignore_shared_freq", "country", "bss_max_count", "bss_expiration_age", "bss_expiration_scan_count",