On Friday 16 February 2007 12:15, Jiri Benc wrote: > On Sun, 11 Feb 2007 03:26:17 -0500, Michael Wu wrote: > > [...] > > --- a/net/d80211/ieee80211_ioctl.c > > +++ b/net/d80211/ieee80211_ioctl.c > > [...] > > @@ -1887,7 +1903,12 @@ static int ieee80211_ioctl_siwessid(stru > > sdata->u.sta.ssid_len = len; > > return 0; > > } > > - return ieee80211_sta_set_ssid(dev, ssid, len); > > + sdata->u.sta.auto_ssid_sel = !data->flags; > > Shouldn't be just some bit tested here? > iwconfig sets it to 1 when the essid is set and 0 when the user set it to any. > > + if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) { > > + sdata->u.sta.auto_bssid_sel = 1; > > + sdata->u.sta.auto_channel_sel = 1; > > + } else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) > > + sdata->u.sta.auto_bssid_sel = 1; > > + else > > + sdata->u.sta.auto_bssid_sel = 0; > > + ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data); > > u.sta.bssid_set is set in ieee80211_sta_set_bssid only and solely based > on sa_data address containing all zeros. Shouldn't it be set on > broadcast address too? > If you mean set to zero, yes, that would make more sense. Don't think it'll have any effect in practice though, but worth fixing anyway. > > + if (ret) > > + return ret; > > + ieee80211_sta_req_auth(dev, &sdata->u.sta); > > + return 0; > > } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { > > if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, > > ETH_ALEN) == 0) > > diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c > > index 0b56135..78d5cf5 100644 > > --- a/net/d80211/ieee80211_sta.c > > +++ b/net/d80211/ieee80211_sta.c > > [...] > > @@ -1924,8 +1925,9 @@ void ieee80211_sta_work(struct work_stru > > } > > > > if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) { > > - ifsta->state = IEEE80211_AUTHENTICATE; > > - ieee80211_sta_reset_auth(dev, ifsta); > > + if (ieee80211_sta_config_auth(dev, ifsta)) > > + return; > > + clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); > > Shouldn't we return immediatelly or something when a scan was requested > by ieee80211_sta_config_auth? > We do. ieee80211_sta_config returns -1. > Seems like you forget to update top_rssi. > Yup, thanks. > if (selected) > atomic_inc(...); > spin_unlock(...); > if (selected) { > ... > > I'd consider less error prone. > Sure, looks better to me too. -- d80211: Support automatic channel/BSSID/SSID configuration This patch implements auto channel/BSSID/SSID selection for backwards compatibility with anyone not using wpa_supplicant to associate. Signed-off-by: Michael Wu --- net/d80211/ieee80211_i.h | 5 ++ net/d80211/ieee80211_iface.c | 2 + net/d80211/ieee80211_ioctl.c | 47 +++++++++++++-- net/d80211/ieee80211_sta.c | 128 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 156 insertions(+), 26 deletions(-) diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h index fa42fb5..3c59a1f 100644 --- a/net/d80211/ieee80211_i.h +++ b/net/d80211/ieee80211_i.h @@ -268,6 +268,9 @@ struct ieee80211_if_sta { unsigned int create_ibss:1; unsigned int mixed_cell:1; unsigned int wmm_enabled:1; + unsigned int auto_ssid_sel:1; + unsigned int auto_bssid_sel:1; + unsigned int auto_channel_sel:1; #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_AUTH 1 #define IEEE80211_STA_REQ_RUN 2 @@ -671,6 +674,8 @@ int ieee80211_sta_set_ssid(struct net_de int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len); int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); +void ieee80211_sta_req_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta); int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c index 939e289..ec9cdc2 100644 --- a/net/d80211/ieee80211_iface.c +++ b/net/d80211/ieee80211_iface.c @@ -195,6 +195,8 @@ void ieee80211_if_set_type(struct net_de IEEE80211_AUTH_ALG_SHARED_KEY; ifsta->create_ibss = 1; ifsta->wmm_enabled = 1; + ifsta->auto_channel_sel = 1; + ifsta->auto_bssid_sel = 1; msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev); sdata->bss = &msdata->u.ap; diff --git a/net/d80211/ieee80211_ioctl.c b/net/d80211/ieee80211_ioctl.c index 0a1a5eb..fa85fb0 100644 --- a/net/d80211/ieee80211_ioctl.c +++ b/net/d80211/ieee80211_ioctl.c @@ -1247,8 +1247,14 @@ static int ieee80211_set_gen_ie(struct n sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type == IEEE80211_IF_TYPE_STA || - sdata->type == IEEE80211_IF_TYPE_IBSS) - return ieee80211_sta_set_extra_ie(dev, ie, len); + sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret = ieee80211_sta_set_extra_ie(dev, ie, len); + if (ret) + return ret; + sdata->u.sta.auto_bssid_sel = 0; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; + } if (sdata->type == IEEE80211_IF_TYPE_AP) { kfree(sdata->u.ap.generic_elem); @@ -1833,11 +1839,20 @@ static int ieee80211_ioctl_siwfreq(struc struct iw_freq *freq, char *extra) { struct ieee80211_local *local = dev->ieee80211_ptr; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->type == IEEE80211_IF_TYPE_STA) + sdata->u.sta.auto_channel_sel = 0; /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ - if (freq->e == 0) - return ieee80211_set_channel(local, freq->m, -1); - else { + if (freq->e == 0) { + if (freq->m < 0) { + if (sdata->type == IEEE80211_IF_TYPE_STA) + sdata->u.sta.auto_channel_sel = 1; + return 0; + } else + return ieee80211_set_channel(local, freq->m, -1); + } else { int i, div = 1000000; for (i = 0; i < freq->e; i++) div /= 10; @@ -1880,6 +1895,7 @@ static int ieee80211_ioctl_siwessid(stru sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type == IEEE80211_IF_TYPE_STA || sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret; if (local->user_space_mlme) { if (len > IEEE80211_MAX_SSID_LEN) return -EINVAL; @@ -1887,7 +1903,12 @@ static int ieee80211_ioctl_siwessid(stru sdata->u.sta.ssid_len = len; return 0; } - return ieee80211_sta_set_ssid(dev, ssid, len); + sdata->u.sta.auto_ssid_sel = !data->flags; + ret = ieee80211_sta_set_ssid(dev, ssid, len); + if (ret) + return ret; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; } if (sdata->type == IEEE80211_IF_TYPE_AP) { @@ -1943,12 +1964,24 @@ static int ieee80211_ioctl_siwap(struct sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type == IEEE80211_IF_TYPE_STA || sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret; if (local->user_space_mlme) { memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data, ETH_ALEN); return 0; } - return ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data); + if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) { + sdata->u.sta.auto_bssid_sel = 1; + sdata->u.sta.auto_channel_sel = 1; + } else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) + sdata->u.sta.auto_bssid_sel = 1; + else + sdata->u.sta.auto_bssid_sel = 0; + ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data); + if (ret) + return ret; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, ETH_ALEN) == 0) diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c index 0b56135..eabb0b7 100644 --- a/net/d80211/ieee80211_sta.c +++ b/net/d80211/ieee80211_sta.c @@ -3,6 +3,7 @@ * Copyright 2003, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. + * Copyright 2007, Michael Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +68,7 @@ static int ieee80211_sta_find_ibss(struc static int ieee80211_sta_wep_configured(struct net_device *dev); static int ieee80211_sta_start_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); -static void ieee80211_sta_reset_auth(struct net_device *dev, +static int ieee80211_sta_config_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta); @@ -1924,8 +1926,9 @@ void ieee80211_sta_work(struct work_stru } if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) { - ifsta->state = IEEE80211_AUTHENTICATE; - ieee80211_sta_reset_auth(dev, ifsta); + if (ieee80211_sta_config_auth(dev, ifsta)) + return; + clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request)) return; @@ -1991,18 +1994,115 @@ static void ieee80211_sta_reset_auth(str } -static void ieee80211_sta_new_auth(struct net_device *dev, - struct ieee80211_if_sta *ifsta) +void ieee80211_sta_req_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); if (sdata->type != IEEE80211_IF_TYPE_STA) return; - set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); - schedule_work(&ifsta->work); + if ((ifsta->bssid_set || ifsta->auto_bssid_sel) && + (ifsta->ssid_set || ifsta->auto_ssid_sel)) { + set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); + schedule_work(&ifsta->work); + } } +static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta, + const char *ssid, int ssid_len) +{ + int tmp, hidden_ssid; + + if (!memcmp(ifsta->ssid, ssid, ssid_len)) + return 1; + + if (ifsta->auto_bssid_sel) + return 0; + + hidden_ssid = 1; + tmp = ssid_len; + while (tmp--) { + if (ssid[tmp] != '\0') { + hidden_ssid = 0; + break; + } + } + + if (hidden_ssid && ifsta->ssid_len == ssid_len) + return 1; + + if (ssid_len == 1 && ssid[0] == ' ') + return 1; + + return 0; +} + +static int ieee80211_sta_config_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = dev->ieee80211_ptr; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sta_bss *bss, *selected = NULL; + int top_rssi = 0, freq; + + if (!ifsta->auto_channel_sel && !ifsta->auto_bssid_sel && + !ifsta->auto_ssid_sel) { + ifsta->state = IEEE80211_AUTHENTICATE; + ieee80211_sta_reset_auth(dev, ifsta); + return 0; + } + + spin_lock_bh(&local->sta_bss_lock); + freq = local->oper_channel->freq; + list_for_each_entry(bss, &local->sta_bss_list, list) { + if (!(bss->capability & WLAN_CAPABILITY_ESS)) + continue; + + if (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^ + !!sdata->default_key) + continue; + + if (!ifsta->auto_channel_sel && bss->freq != freq) + continue; + + if (!ifsta->auto_bssid_sel && + memcmp(bss->bssid, ifsta->bssid, ETH_ALEN)) + continue; + + if (!ifsta->auto_ssid_sel && + !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len)) + continue; + + if (top_rssi < bss->rssi) + selected = bss; + + top_rssi = bss->rssi; + } + if (selected) + atomic_inc(&selected->users); + spin_unlock_bh(&local->sta_bss_lock); + + if (selected) { + ieee80211_set_channel(local, -1, selected->freq); + if (!ifsta->ssid_set) + ieee80211_sta_set_ssid(dev, selected->ssid, + selected->ssid_len); + ieee80211_sta_set_bssid(dev, selected->bssid); + ieee80211_rx_bss_put(dev, selected); + ifsta->state = IEEE80211_AUTHENTICATE; + ieee80211_sta_reset_auth(dev, ifsta); + return 0; + } else { + if (ifsta->state != IEEE80211_AUTHENTICATE) { + ieee80211_sta_start_scan(dev, NULL, 0);; + ifsta->state = IEEE80211_AUTHENTICATE; + set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); + } else + ifsta->state = IEEE80211_DISABLED; + } + return -1; +} static int ieee80211_sta_join_ibss(struct net_device *dev, struct ieee80211_if_sta *ifsta, @@ -2353,16 +2453,12 @@ int ieee80211_sta_set_ssid(struct net_de memset(ifsta->ssid + len, 0, IEEE80211_MAX_SSID_LEN - len); ifsta->ssid_len = len; - ifsta->ssid_set = 1; + ifsta->ssid_set = len ? 1 : 0; if (sdata->type == IEEE80211_IF_TYPE_IBSS && !ifsta->bssid_set) { ifsta->ibss_join_req = jiffies; ifsta->state = IEEE80211_IBSS_SEARCH; return ieee80211_sta_find_ibss(dev, ifsta); } - - if (ifsta->bssid_set && ifsta->state != IEEE80211_AUTHENTICATE) - ieee80211_sta_new_auth(dev, ifsta); - return 0; } @@ -2396,13 +2492,10 @@ int ieee80211_sta_set_bssid(struct net_d } } - if (memcmp(bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) + if (!is_valid_ether_addr(bssid)) ifsta->bssid_set = 0; else ifsta->bssid_set = 1; - if (ifsta->ssid_set) - ieee80211_sta_new_auth(dev, ifsta); - return 0; } @@ -2812,9 +2905,6 @@ int ieee80211_sta_set_extra_ie(struct ne } memcpy(ifsta->extra_ie, ie, len); ifsta->extra_ie_len = len; - if (ifsta->bssid_set && ifsta->ssid_set && - ifsta->state != IEEE80211_AUTHENTICATE) - ieee80211_sta_new_auth(dev, ifsta); return 0; }