ibss.c revision 8e30bc55de98c000b0b836cb42525c82f605f191
1/* 2 * Some IBSS support code for cfg80211. 3 * 4 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 5 */ 6 7#include <linux/etherdevice.h> 8#include <linux/if_arp.h> 9#include <net/cfg80211.h> 10#include "nl80211.h" 11 12 13void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) 14{ 15 struct wireless_dev *wdev = dev->ieee80211_ptr; 16 struct cfg80211_bss *bss; 17#ifdef CONFIG_WIRELESS_EXT 18 union iwreq_data wrqu; 19#endif 20 21 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 22 return; 23 24 if (WARN_ON(!wdev->ssid_len)) 25 return; 26 27 if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0) 28 return; 29 30 bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 31 wdev->ssid, wdev->ssid_len, 32 WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); 33 34 if (WARN_ON(!bss)) 35 return; 36 37 if (wdev->current_bss) { 38 cfg80211_unhold_bss(wdev->current_bss); 39 cfg80211_put_bss(wdev->current_bss); 40 } 41 42 cfg80211_hold_bss(bss); 43 wdev->current_bss = bss; 44 memcpy(wdev->bssid, bssid, ETH_ALEN); 45 46 nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp); 47#ifdef CONFIG_WIRELESS_EXT 48 memset(&wrqu, 0, sizeof(wrqu)); 49 memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 50 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 51#endif 52} 53EXPORT_SYMBOL(cfg80211_ibss_joined); 54 55int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, 56 struct net_device *dev, 57 struct cfg80211_ibss_params *params) 58{ 59 struct wireless_dev *wdev = dev->ieee80211_ptr; 60 int err; 61 62 if (wdev->ssid_len) 63 return -EALREADY; 64 65#ifdef CONFIG_WIRELESS_EXT 66 wdev->wext.channel = params->channel; 67#endif 68 err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); 69 70 if (err) 71 return err; 72 73 memcpy(wdev->ssid, params->ssid, params->ssid_len); 74 wdev->ssid_len = params->ssid_len; 75 76 return 0; 77} 78 79void cfg80211_clear_ibss(struct net_device *dev, bool nowext) 80{ 81 struct wireless_dev *wdev = dev->ieee80211_ptr; 82 83 if (wdev->current_bss) { 84 cfg80211_unhold_bss(wdev->current_bss); 85 cfg80211_put_bss(wdev->current_bss); 86 } 87 88 wdev->current_bss = NULL; 89 wdev->ssid_len = 0; 90 memset(wdev->bssid, 0, ETH_ALEN); 91#ifdef CONFIG_WIRELESS_EXT 92 if (!nowext) 93 wdev->wext.ssid_len = 0; 94#endif 95} 96 97int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, 98 struct net_device *dev, bool nowext) 99{ 100 int err; 101 102 err = rdev->ops->leave_ibss(&rdev->wiphy, dev); 103 104 if (err) 105 return err; 106 107 cfg80211_clear_ibss(dev, nowext); 108 109 return 0; 110} 111 112#ifdef CONFIG_WIRELESS_EXT 113static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, 114 struct wireless_dev *wdev) 115{ 116 enum ieee80211_band band; 117 int i; 118 119 if (!wdev->wext.beacon_interval) 120 wdev->wext.beacon_interval = 100; 121 122 /* try to find an IBSS channel if none requested ... */ 123 if (!wdev->wext.channel) { 124 for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 125 struct ieee80211_supported_band *sband; 126 struct ieee80211_channel *chan; 127 128 sband = rdev->wiphy.bands[band]; 129 if (!sband) 130 continue; 131 132 for (i = 0; i < sband->n_channels; i++) { 133 chan = &sband->channels[i]; 134 if (chan->flags & IEEE80211_CHAN_NO_IBSS) 135 continue; 136 if (chan->flags & IEEE80211_CHAN_DISABLED) 137 continue; 138 wdev->wext.channel = chan; 139 break; 140 } 141 142 if (wdev->wext.channel) 143 break; 144 } 145 146 if (!wdev->wext.channel) 147 return -EINVAL; 148 } 149 150 /* don't join -- SSID is not there */ 151 if (!wdev->wext.ssid_len) 152 return 0; 153 154 if (!netif_running(wdev->netdev)) 155 return 0; 156 157 return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy), 158 wdev->netdev, &wdev->wext); 159} 160 161int cfg80211_ibss_wext_siwfreq(struct net_device *dev, 162 struct iw_request_info *info, 163 struct iw_freq *freq, char *extra) 164{ 165 struct wireless_dev *wdev = dev->ieee80211_ptr; 166 struct ieee80211_channel *chan; 167 int err; 168 169 /* call only for ibss! */ 170 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 171 return -EINVAL; 172 173 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 174 return -EOPNOTSUPP; 175 176 chan = cfg80211_wext_freq(wdev->wiphy, freq); 177 if (chan && IS_ERR(chan)) 178 return PTR_ERR(chan); 179 180 if (chan && 181 (chan->flags & IEEE80211_CHAN_NO_IBSS || 182 chan->flags & IEEE80211_CHAN_DISABLED)) 183 return -EINVAL; 184 185 if (wdev->wext.channel == chan) 186 return 0; 187 188 if (wdev->ssid_len) { 189 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 190 dev, true); 191 if (err) 192 return err; 193 } 194 195 if (chan) { 196 wdev->wext.channel = chan; 197 wdev->wext.channel_fixed = true; 198 } else { 199 /* cfg80211_ibss_wext_join will pick one if needed */ 200 wdev->wext.channel_fixed = false; 201 } 202 203 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 204} 205/* temporary symbol - mark GPL - in the future the handler won't be */ 206EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); 207 208int cfg80211_ibss_wext_giwfreq(struct net_device *dev, 209 struct iw_request_info *info, 210 struct iw_freq *freq, char *extra) 211{ 212 struct wireless_dev *wdev = dev->ieee80211_ptr; 213 struct ieee80211_channel *chan = NULL; 214 215 /* call only for ibss! */ 216 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 217 return -EINVAL; 218 219 if (wdev->current_bss) 220 chan = wdev->current_bss->channel; 221 else if (wdev->wext.channel) 222 chan = wdev->wext.channel; 223 224 if (chan) { 225 freq->m = chan->center_freq; 226 freq->e = 6; 227 return 0; 228 } 229 230 /* no channel if not joining */ 231 return -EINVAL; 232} 233/* temporary symbol - mark GPL - in the future the handler won't be */ 234EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq); 235 236int cfg80211_ibss_wext_siwessid(struct net_device *dev, 237 struct iw_request_info *info, 238 struct iw_point *data, char *ssid) 239{ 240 struct wireless_dev *wdev = dev->ieee80211_ptr; 241 size_t len = data->length; 242 int err; 243 244 /* call only for ibss! */ 245 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 246 return -EINVAL; 247 248 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 249 return -EOPNOTSUPP; 250 251 if (wdev->ssid_len) { 252 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 253 dev, true); 254 if (err) 255 return err; 256 } 257 258 /* iwconfig uses nul termination in SSID.. */ 259 if (len > 0 && ssid[len - 1] == '\0') 260 len--; 261 262 wdev->wext.ssid = wdev->ssid; 263 memcpy(wdev->wext.ssid, ssid, len); 264 wdev->wext.ssid_len = len; 265 266 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 267} 268/* temporary symbol - mark GPL - in the future the handler won't be */ 269EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); 270 271int cfg80211_ibss_wext_giwessid(struct net_device *dev, 272 struct iw_request_info *info, 273 struct iw_point *data, char *ssid) 274{ 275 struct wireless_dev *wdev = dev->ieee80211_ptr; 276 277 /* call only for ibss! */ 278 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 279 return -EINVAL; 280 281 data->flags = 0; 282 283 if (wdev->ssid_len) { 284 data->flags = 1; 285 data->length = wdev->ssid_len; 286 memcpy(ssid, wdev->ssid, data->length); 287 } else if (wdev->wext.ssid && wdev->wext.ssid_len) { 288 data->flags = 1; 289 data->length = wdev->wext.ssid_len; 290 memcpy(ssid, wdev->wext.ssid, data->length); 291 } 292 293 return 0; 294} 295/* temporary symbol - mark GPL - in the future the handler won't be */ 296EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid); 297 298int cfg80211_ibss_wext_siwap(struct net_device *dev, 299 struct iw_request_info *info, 300 struct sockaddr *ap_addr, char *extra) 301{ 302 struct wireless_dev *wdev = dev->ieee80211_ptr; 303 u8 *bssid = ap_addr->sa_data; 304 int err; 305 306 /* call only for ibss! */ 307 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 308 return -EINVAL; 309 310 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 311 return -EOPNOTSUPP; 312 313 if (ap_addr->sa_family != ARPHRD_ETHER) 314 return -EINVAL; 315 316 /* automatic mode */ 317 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 318 bssid = NULL; 319 320 /* both automatic */ 321 if (!bssid && !wdev->wext.bssid) 322 return 0; 323 324 /* fixed already - and no change */ 325 if (wdev->wext.bssid && bssid && 326 compare_ether_addr(bssid, wdev->wext.bssid) == 0) 327 return 0; 328 329 if (wdev->ssid_len) { 330 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 331 dev, true); 332 if (err) 333 return err; 334 } 335 336 if (bssid) { 337 memcpy(wdev->wext_bssid, bssid, ETH_ALEN); 338 wdev->wext.bssid = wdev->wext_bssid; 339 } else 340 wdev->wext.bssid = NULL; 341 342 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 343} 344/* temporary symbol - mark GPL - in the future the handler won't be */ 345EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); 346 347int cfg80211_ibss_wext_giwap(struct net_device *dev, 348 struct iw_request_info *info, 349 struct sockaddr *ap_addr, char *extra) 350{ 351 struct wireless_dev *wdev = dev->ieee80211_ptr; 352 353 /* call only for ibss! */ 354 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 355 return -EINVAL; 356 357 ap_addr->sa_family = ARPHRD_ETHER; 358 359 if (wdev->wext.bssid) { 360 memcpy(ap_addr->sa_data, wdev->wext.bssid, ETH_ALEN); 361 return 0; 362 } 363 364 memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN); 365 return 0; 366} 367/* temporary symbol - mark GPL - in the future the handler won't be */ 368EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap); 369#endif 370