[PATCH] nl80211: Allow configuring allowable legacy rate mask.
greearb at candelatech.com
greearb
Tue Sep 15 17:04:45 PDT 2015
From: Ben Greear <greearb at candelatech.com>
With this, we should be able to disable un-wanted rates
early in the association process, before 'iw' could
have a chance to make any settings.
Signed-off-by: Ben Greear <greearb at candelatech.com>
---
src/drivers/driver.h | 10 ++
src/drivers/driver_nl80211.c | 216 +++++++++++++++++++++++++----------
src/drivers/driver_nl80211.h | 5 +-
src/drivers/driver_nl80211_monitor.c | 4 +-
wpa_supplicant/driver_i.h | 7 ++
wpa_supplicant/wpa_supplicant.c | 8 ++
wpa_supplicant/wpa_supplicant.conf | 5 +
7 files changed, 192 insertions(+), 63 deletions(-)
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 3cdab5a..d5fbf2c 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1425,6 +1425,9 @@ struct wpa_init_params {
char **bridge;
size_t num_bridge;
+ /* legacy rate mask, 12 bits for legacy rates, (1<<31) to enable use of this mask */
+ unsigned int legacy_rates;
+
u8 *own_addr; /* buffer for writing own MAC address */
};
@@ -3012,6 +3015,13 @@ struct wpa_driver_ops {
int (*stop_sched_scan)(void *priv);
/**
+ * Set the legacy rates. User would have previously configured the
+ * ratemask through other means. This just tells the kernel to apply
+ * it.
+ */
+ int (*set_legacy_rates)(void *priv);
+
+ /**
* poll_client - Probe (null data or such) the given station
* @priv: Private driver interface data
* @own_addr: MAC address of sending interface
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 075a20e..3fbf0f4 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -186,8 +186,10 @@ static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan);
-static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
- int ifindex, int disabled);
+static int nl80211_set_legacy_rates(struct i802_bss *bss,
+ struct wpa_driver_nl80211_data *drv,
+ int ifindex, int b_disabled,
+ unsigned int cfg_legacy_mask);
static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
int reset_mode);
@@ -2295,8 +2297,8 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
- bss->ifname, drv->disabled_11b_rates);
+ wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s",
+ bss->ifname);
bss->in_deinit = 1;
if (drv->data_tx_status)
@@ -2340,8 +2342,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
if (drv->if_indices != drv->default_if_indices)
os_free(drv->if_indices);
- if (drv->disabled_11b_rates)
- nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 0, 0xFFF);
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
IF_OPER_UP);
@@ -2864,25 +2865,6 @@ static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
}
-static void nl80211_unmask_11b_rates(struct i802_bss *bss)
-{
- struct wpa_driver_nl80211_data *drv = bss->drv;
-
- if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
- return;
-
- /*
- * Looks like we failed to unmask 11b rates previously. This could
- * happen, e.g., if the interface was down at the point in time when a
- * P2P group was terminated.
- */
- wpa_printf(MSG_DEBUG,
- "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
- bss->ifname);
- nl80211_disable_11b_rates(drv, drv->ifindex, 0);
-}
-
-
static int wpa_driver_nl80211_authenticate(
struct i802_bss *bss, struct wpa_driver_auth_params *params)
{
@@ -2894,7 +2876,7 @@ static int wpa_driver_nl80211_authenticate(
int count = 0;
int is_retry;
- nl80211_unmask_11b_rates(bss);
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 0, bss->legacy_rates);
is_retry = drv->retry_auth;
drv->retry_auth = 0;
@@ -4076,7 +4058,7 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
}
-int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+int nl80211_create_iface(struct i802_bss *bss, struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
@@ -4118,7 +4100,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s created for P2P - disable 11b rates",
ifname);
- nl80211_disable_11b_rates(drv, ret, 1);
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 1, bss->legacy_rates);
}
return ret;
@@ -4149,7 +4131,7 @@ static int nl80211_setup_ap(struct i802_bss *bss)
return -1;
if (!drv->device_ap_sme && drv->use_monitor &&
- nl80211_create_monitor_interface(drv) &&
+ nl80211_create_monitor_interface(bss, drv) &&
!drv->device_ap_sme)
return -1;
@@ -4810,7 +4792,7 @@ static int wpa_driver_nl80211_associate(
int ret = -1;
struct nl_msg *msg;
- nl80211_unmask_11b_rates(bss);
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 0, bss->legacy_rates);
if (params->mode == IEEE80211_MODE_AP)
return wpa_driver_nl80211_ap(drv, params);
@@ -4989,12 +4971,12 @@ done:
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s mode change to P2P - disable 11b rates",
bss->ifname);
- nl80211_disable_11b_rates(drv, drv->ifindex, 1);
- } else if (drv->disabled_11b_rates) {
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 1, bss->legacy_rates);
+ } else {
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
bss->ifname);
- nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+ nl80211_set_legacy_rates(bss, drv, drv->ifindex, 0, bss->legacy_rates);
}
if (is_ap_interface(nlmode)) {
@@ -5595,7 +5577,7 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
" aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
if (val) {
if (!if_nametoindex(name)) {
- if (nl80211_create_iface(drv, name,
+ if (nl80211_create_iface(bss, drv, name,
NL80211_IFTYPE_AP_VLAN,
bss->addr, 1, NULL, NULL, 0) <
0)
@@ -5917,7 +5899,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
struct wdev_info p2pdev_info;
os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
- ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ ifidx = nl80211_create_iface(bss, drv, ifname, nlmode, addr,
0, nl80211_wdev_handler,
&p2pdev_info, use_existing);
if (!p2pdev_info.wdev_id_set || ifidx != 0) {
@@ -5934,7 +5916,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
ifname,
(long long unsigned int) p2pdev_info.wdev_id);
} else {
- ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+ ifidx = nl80211_create_iface(bss, drv, ifname, nlmode, addr,
0, NULL, NULL, use_existing);
if (use_existing && ifidx == -ENFILE) {
added = 0;
@@ -6373,17 +6355,19 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
}
-static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
- int ifindex, int disabled)
+static int nl80211_set_legacy_rates(struct i802_bss *bss,
+ struct wpa_driver_nl80211_data *drv,
+ int ifindex, int b_disabled,
+ unsigned int legacy_rates)
{
struct nl_msg *msg;
struct nlattr *bands, *band;
int ret;
wpa_printf(MSG_DEBUG,
- "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
- ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
- "no NL80211_TXRATE_LEGACY constraint");
+ "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s cfg-legacy-mask: 0x%x)",
+ ifindex, b_disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+ "no NL80211_TXRATE_LEGACY constraint", legacy_rates);
msg = nl80211_ifindex_msg(drv, ifindex, 0,
NL80211_CMD_SET_TX_BITRATE_MASK);
@@ -6400,20 +6384,130 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
* rates. All 5 GHz rates are left enabled.
*/
band = nla_nest_start(msg, NL80211_BAND_2GHZ);
- if (!band ||
- (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
- "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+ if (!band)
goto fail;
+
+ if (legacy_rates & (1<<31) || b_disabled) {
+ unsigned char rates[12];
+ int i = 0;
+ if (legacy_rates & (1<<31)) {
+ /* Pay attention to the legacy mask */
+ if (!(b_disabled || ((legacy_rates & 0x1) == 0))) {
+ rates[i] = 2;
+ i++;
+ }
+ if (!(b_disabled || ((legacy_rates & 0x2) == 0))) {
+ rates[i] = 4;
+ i++;
+ }
+ if (!(b_disabled || ((legacy_rates & 0x4) == 0))) {
+ rates[i] = 11;
+ i++;
+ }
+ if (!(b_disabled || ((legacy_rates & 0x8) == 0))) {
+ rates[i] = 22;
+ i++;
+ }
+ if (legacy_rates & 0x10) { // 6Mbps
+ rates[i] = 12;
+ i++;
+ }
+ if (legacy_rates & 0x20) { // 9Mbps
+ rates[i] = 18;
+ i++;
+ }
+ if (legacy_rates & 0x40) { // 12Mbps
+ rates[i] = 24;
+ i++;
+ }
+ if (legacy_rates & 0x80) { // 18Mbps
+ rates[i] = 36;
+ i++;
+ }
+ if (legacy_rates & 0x100) { // 24Mbps
+ rates[i] = 48;
+ i++;
+ }
+ if (legacy_rates & 0x200) { // 36Mbps
+ rates[i] = 72;
+ i++;
+ }
+ if (legacy_rates & 0x400) { // 48Mbps
+ rates[i] = 96;
+ i++;
+ }
+ if (legacy_rates & 0x800) { // 54Mbps
+ rates[i] = 108;
+ i++;
+ }
+ }/* if user configured a legacy mask */
+ else if (b_disabled) {
+ i = 8;
+ /* Enable all /g rates */
+ memcpy(rates, "\x0c\x12\x18\x24\x30\x48\x60\x6c", i);
+ }
+ if (legacy_rates & (1<<31) || i) {
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, i, rates))
+ goto fail;
+ }
+ }/* if we might need to disable some rates */
+
nla_nest_end(msg, band);
+ if (legacy_rates & (1<<31)) {
+ unsigned char rates[8];
+ int i = 0;
+
+ band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+ if (!band)
+ goto fail;
+
+ if (legacy_rates & 0x10) { // 6Mbps
+ rates[i] = 12;
+ i++;
+ }
+ if (legacy_rates & 0x20) { // 9Mbps
+ rates[i] = 18;
+ i++;
+ }
+ if (legacy_rates & 0x40) { // 12Mbps
+ rates[i] = 24;
+ i++;
+ }
+ if (legacy_rates & 0x80) { // 18Mbps
+ rates[i] = 36;
+ i++;
+ }
+ if (legacy_rates & 0x100) { // 24Mbps
+ rates[i] = 48;
+ i++;
+ }
+ if (legacy_rates & 0x200) { // 36Mbps
+ rates[i] = 72;
+ i++;
+ }
+ if (legacy_rates & 0x400) { // 48Mbps
+ rates[i] = 96;
+ i++;
+ }
+ if (legacy_rates & 0x800) { // 54Mbps
+ rates[i] = 108;
+ i++;
+ }
+
+ if (nla_put(msg, NL80211_TXRATE_LEGACY, i, rates))
+ goto fail;
+
+ nla_nest_end(msg, band);
+ }
+
nla_nest_end(msg, bands);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
- wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+ wpa_printf(MSG_DEBUG, "nl80211: Set Legacy TX rates failed: ret=%d "
"(%s)", ret, strerror(-ret));
- } else
- drv->disabled_11b_rates = disabled;
+ }
return ret;
@@ -6422,6 +6516,11 @@ fail:
return -1;
}
+int wpa_driver_nl80211_set_legacy_rates(void *priv)
+{
+ struct i802_bss *bss = priv;
+ return nl80211_set_legacy_rates(bss, bss->drv, bss->drv->ifindex, 0, bss->legacy_rates);
+}
static int wpa_driver_nl80211_deinit_ap(void *priv)
{
@@ -6573,15 +6672,16 @@ static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
static int nl80211_set_param(void *priv, const char *param)
{
+ char* tmp;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
if (param == NULL)
return 0;
#ifdef CONFIG_P2P
if (os_strstr(param, "use_p2p_group_interface=1")) {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
-
wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
"interface");
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
@@ -6590,25 +6690,24 @@ static int nl80211_set_param(void *priv, const char *param)
#endif /* CONFIG_P2P */
if (os_strstr(param, "use_monitor=1")) {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
drv->use_monitor = 1;
}
if (os_strstr(param, "force_connect_cmd=1")) {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
drv->force_connect_cmd = 1;
}
if (os_strstr(param, "no_offchannel_tx=1")) {
- struct i802_bss *bss = priv;
- struct wpa_driver_nl80211_data *drv = bss->drv;
drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
drv->test_use_roc_tx = 1;
}
+ if ((tmp = os_strstr(param, "legacy_rates="))) {
+ bss->legacy_rates = atoi(tmp + strlen("legacy_rates="));
+ bss->legacy_rates |= (1<<31); /* so we know user actually configured something */
+ }
+
return 0;
}
@@ -7456,7 +7555,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"monitor_refcount=%d\n"
"last_mgmt_freq=%u\n"
"eapol_tx_sock=%d\n"
- "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s",
drv->phyname,
MAC2STR(drv->perm_addr),
drv->ifindex,
@@ -7477,8 +7576,6 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"ignore_if_down_event=1\n" : "",
drv->scan_complete_events ?
"scan_complete_events=1\n" : "",
- drv->disabled_11b_rates ?
- "disabled_11b_rates=1\n" : "",
drv->pending_remain_on_chan ?
"pending_remain_on_chan=1\n" : "",
drv->in_interface_list ? "in_interface_list=1\n" : "",
@@ -8731,6 +8828,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.scan2 = driver_nl80211_scan2,
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
+ .set_legacy_rates = wpa_driver_nl80211_set_legacy_rates,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 5c21e0f..bc64e5c 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -70,6 +70,7 @@ struct i802_bss {
int freq;
int bandwidth;
int if_dynamic;
+ unsigned int legacy_rates; /* 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54. (1<<31) enables this mask */
void *ctx;
struct nl_handle *nl_preq, *nl_mgmt;
@@ -195,7 +196,7 @@ struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data);
-int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+int nl80211_create_iface(struct i802_bss *bss, struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
@@ -220,7 +221,7 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code,
int local_state_change);
-int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_create_monitor_interface(struct i802_bss *bss, struct wpa_driver_nl80211_data *drv);
void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
const void *data, size_t len,
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
index 45385da..8e0b482 100644
--- a/src/drivers/driver_nl80211_monitor.c
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -338,7 +338,7 @@ void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
}
-int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+int nl80211_create_monitor_interface(struct i802_bss *bss, struct wpa_driver_nl80211_data *drv)
{
char buf[IFNAMSIZ];
struct sockaddr_ll ll;
@@ -368,7 +368,7 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx =
- nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+ nl80211_create_iface(bss, drv, buf, NL80211_IFTYPE_MONITOR, NULL,
0, NULL, NULL, 0);
if (drv->monitor_ifidx == -EOPNOTSUPP) {
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 73768c7..a1853e5 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -116,6 +116,13 @@ static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
return -1;
}
+static inline int wpa_drv_set_legacy_rates(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->set_legacy_rates)
+ return wpa_s->driver->set_legacy_rates(wpa_s->drv_priv);
+ return -1;
+}
+
static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
struct wpa_supplicant *wpa_s)
{
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 92b1137..09c9371 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -723,6 +723,12 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
wpa_s->normal_scans = 0;
}
+ /* If we are moving from DISCONNECTED to Scanning, then (re)set
+ * the legacy ratemask, as it could have just been cleared as
+ * the network device just bounced.
+ */
+ wpa_drv_set_legacy_rates(wpa_s);
+
if (state == WPA_COMPLETED) {
wpas_connect_work_done(wpa_s);
/* Reinitialize normal_scan counter */
@@ -3375,6 +3381,8 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
wpa_drv_flush_pmkid(wpa_s);
+ wpa_drv_set_legacy_rates(wpa_s);
+
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_s->prev_scan_wildcard = 0;
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index c738b3f..41c55ef 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -182,6 +182,11 @@ fast_reauth=1
# format is specific to the selected driver interface. This field is not used
# in most cases.
#driver_param="field=value"
+#
+# In particular, nl80211 supports: use_monitor=1, force_connect_cmd=1,
+# no_offchannel_tx=1, legacy_rates=[number]
+# For legacy_rates, the number is a bitfield where rate 1Mbps is first bit,
+# and 54Mbps is 12th bit.
# Country code
# The ISO/IEC alpha2 country code for the country in which this device is
--
1.9.3
More information about the Hostap
mailing list