ibss.c revision 667503ddcb96f3b10211f997fe55907fa7509841
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) 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 bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 28 wdev->ssid, wdev->ssid_len, 29 WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); 30 31 if (WARN_ON(!bss)) 32 return; 33 34 if (wdev->current_bss) { 35 cfg80211_unhold_bss(wdev->current_bss); 36 cfg80211_put_bss(&wdev->current_bss->pub); 37 } 38 39 cfg80211_hold_bss(bss_from_pub(bss)); 40 wdev->current_bss = bss_from_pub(bss); 41 42 nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, 43 GFP_KERNEL); 44#ifdef CONFIG_WIRELESS_EXT 45 memset(&wrqu, 0, sizeof(wrqu)); 46 memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 47 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 48#endif 49} 50 51void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) 52{ 53 struct wireless_dev *wdev = dev->ieee80211_ptr; 54 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 55 struct cfg80211_event *ev; 56 unsigned long flags; 57 58 ev = kzalloc(sizeof(*ev), gfp); 59 if (!ev) 60 return; 61 62 ev->type = EVENT_IBSS_JOINED; 63 memcpy(ev->cr.bssid, bssid, ETH_ALEN); 64 65 spin_lock_irqsave(&wdev->event_lock, flags); 66 list_add_tail(&ev->list, &wdev->event_list); 67 spin_unlock_irqrestore(&wdev->event_lock, flags); 68 schedule_work(&rdev->event_work); 69} 70EXPORT_SYMBOL(cfg80211_ibss_joined); 71 72int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, 73 struct net_device *dev, 74 struct cfg80211_ibss_params *params) 75{ 76 struct wireless_dev *wdev = dev->ieee80211_ptr; 77 int err; 78 79 ASSERT_WDEV_LOCK(wdev); 80 81 if (wdev->ssid_len) 82 return -EALREADY; 83 84#ifdef CONFIG_WIRELESS_EXT 85 wdev->wext.ibss.channel = params->channel; 86#endif 87 err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); 88 89 if (err) 90 return err; 91 92 memcpy(wdev->ssid, params->ssid, params->ssid_len); 93 wdev->ssid_len = params->ssid_len; 94 95 return 0; 96} 97 98int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, 99 struct net_device *dev, 100 struct cfg80211_ibss_params *params) 101{ 102 struct wireless_dev *wdev = dev->ieee80211_ptr; 103 int err; 104 105 wdev_lock(wdev); 106 err = __cfg80211_join_ibss(rdev, dev, params); 107 wdev_unlock(wdev); 108 109 return err; 110} 111 112static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) 113{ 114 struct wireless_dev *wdev = dev->ieee80211_ptr; 115 116 ASSERT_WDEV_LOCK(wdev); 117 118 if (wdev->current_bss) { 119 cfg80211_unhold_bss(wdev->current_bss); 120 cfg80211_put_bss(&wdev->current_bss->pub); 121 } 122 123 wdev->current_bss = NULL; 124 wdev->ssid_len = 0; 125#ifdef CONFIG_WIRELESS_EXT 126 if (!nowext) 127 wdev->wext.ibss.ssid_len = 0; 128#endif 129} 130 131void cfg80211_clear_ibss(struct net_device *dev, bool nowext) 132{ 133 struct wireless_dev *wdev = dev->ieee80211_ptr; 134 135 wdev_lock(wdev); 136 __cfg80211_clear_ibss(dev, nowext); 137 wdev_unlock(wdev); 138} 139 140static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, 141 struct net_device *dev, bool nowext) 142{ 143 struct wireless_dev *wdev = dev->ieee80211_ptr; 144 int err; 145 146 ASSERT_WDEV_LOCK(wdev); 147 148 if (!wdev->ssid_len) 149 return -ENOLINK; 150 151 err = rdev->ops->leave_ibss(&rdev->wiphy, dev); 152 153 if (err) 154 return err; 155 156 __cfg80211_clear_ibss(dev, nowext); 157 158 return 0; 159} 160 161int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, 162 struct net_device *dev, bool nowext) 163{ 164 struct wireless_dev *wdev = dev->ieee80211_ptr; 165 int err; 166 167 wdev_lock(wdev); 168 err = __cfg80211_leave_ibss(rdev, dev, nowext); 169 wdev_unlock(wdev); 170 171 return err; 172} 173 174#ifdef CONFIG_WIRELESS_EXT 175static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, 176 struct wireless_dev *wdev) 177{ 178 enum ieee80211_band band; 179 int i; 180 181 if (!wdev->wext.ibss.beacon_interval) 182 wdev->wext.ibss.beacon_interval = 100; 183 184 /* try to find an IBSS channel if none requested ... */ 185 if (!wdev->wext.ibss.channel) { 186 for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 187 struct ieee80211_supported_band *sband; 188 struct ieee80211_channel *chan; 189 190 sband = rdev->wiphy.bands[band]; 191 if (!sband) 192 continue; 193 194 for (i = 0; i < sband->n_channels; i++) { 195 chan = &sband->channels[i]; 196 if (chan->flags & IEEE80211_CHAN_NO_IBSS) 197 continue; 198 if (chan->flags & IEEE80211_CHAN_DISABLED) 199 continue; 200 wdev->wext.ibss.channel = chan; 201 break; 202 } 203 204 if (wdev->wext.ibss.channel) 205 break; 206 } 207 208 if (!wdev->wext.ibss.channel) 209 return -EINVAL; 210 } 211 212 /* don't join -- SSID is not there */ 213 if (!wdev->wext.ibss.ssid_len) 214 return 0; 215 216 if (!netif_running(wdev->netdev)) 217 return 0; 218 219 return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy), 220 wdev->netdev, &wdev->wext.ibss); 221} 222 223int cfg80211_ibss_wext_siwfreq(struct net_device *dev, 224 struct iw_request_info *info, 225 struct iw_freq *freq, char *extra) 226{ 227 struct wireless_dev *wdev = dev->ieee80211_ptr; 228 struct ieee80211_channel *chan; 229 int err; 230 231 /* call only for ibss! */ 232 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 233 return -EINVAL; 234 235 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 236 return -EOPNOTSUPP; 237 238 chan = cfg80211_wext_freq(wdev->wiphy, freq); 239 if (chan && IS_ERR(chan)) 240 return PTR_ERR(chan); 241 242 if (chan && 243 (chan->flags & IEEE80211_CHAN_NO_IBSS || 244 chan->flags & IEEE80211_CHAN_DISABLED)) 245 return -EINVAL; 246 247 if (wdev->wext.ibss.channel == chan) 248 return 0; 249 250 wdev_lock(wdev); 251 err = 0; 252 if (wdev->ssid_len) 253 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 254 dev, true); 255 wdev_unlock(wdev); 256 257 if (err) 258 return err; 259 260 if (chan) { 261 wdev->wext.ibss.channel = chan; 262 wdev->wext.ibss.channel_fixed = true; 263 } else { 264 /* cfg80211_ibss_wext_join will pick one if needed */ 265 wdev->wext.ibss.channel_fixed = false; 266 } 267 268 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 269} 270/* temporary symbol - mark GPL - in the future the handler won't be */ 271EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq); 272 273int cfg80211_ibss_wext_giwfreq(struct net_device *dev, 274 struct iw_request_info *info, 275 struct iw_freq *freq, char *extra) 276{ 277 struct wireless_dev *wdev = dev->ieee80211_ptr; 278 struct ieee80211_channel *chan = NULL; 279 280 /* call only for ibss! */ 281 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 282 return -EINVAL; 283 284 wdev_lock(wdev); 285 if (wdev->current_bss) 286 chan = wdev->current_bss->pub.channel; 287 else if (wdev->wext.ibss.channel) 288 chan = wdev->wext.ibss.channel; 289 wdev_unlock(wdev); 290 291 if (chan) { 292 freq->m = chan->center_freq; 293 freq->e = 6; 294 return 0; 295 } 296 297 /* no channel if not joining */ 298 return -EINVAL; 299} 300/* temporary symbol - mark GPL - in the future the handler won't be */ 301EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq); 302 303int cfg80211_ibss_wext_siwessid(struct net_device *dev, 304 struct iw_request_info *info, 305 struct iw_point *data, char *ssid) 306{ 307 struct wireless_dev *wdev = dev->ieee80211_ptr; 308 size_t len = data->length; 309 int err; 310 311 /* call only for ibss! */ 312 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 313 return -EINVAL; 314 315 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 316 return -EOPNOTSUPP; 317 318 wdev_lock(wdev); 319 err = 0; 320 if (wdev->ssid_len) 321 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 322 dev, true); 323 wdev_unlock(wdev); 324 325 if (err) 326 return err; 327 328 /* iwconfig uses nul termination in SSID.. */ 329 if (len > 0 && ssid[len - 1] == '\0') 330 len--; 331 332 wdev->wext.ibss.ssid = wdev->ssid; 333 memcpy(wdev->wext.ibss.ssid, ssid, len); 334 wdev->wext.ibss.ssid_len = len; 335 336 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 337} 338/* temporary symbol - mark GPL - in the future the handler won't be */ 339EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid); 340 341int cfg80211_ibss_wext_giwessid(struct net_device *dev, 342 struct iw_request_info *info, 343 struct iw_point *data, char *ssid) 344{ 345 struct wireless_dev *wdev = dev->ieee80211_ptr; 346 347 /* call only for ibss! */ 348 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 349 return -EINVAL; 350 351 data->flags = 0; 352 353 wdev_lock(wdev); 354 if (wdev->ssid_len) { 355 data->flags = 1; 356 data->length = wdev->ssid_len; 357 memcpy(ssid, wdev->ssid, data->length); 358 } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { 359 data->flags = 1; 360 data->length = wdev->wext.ibss.ssid_len; 361 memcpy(ssid, wdev->wext.ibss.ssid, data->length); 362 } 363 wdev_unlock(wdev); 364 365 return 0; 366} 367/* temporary symbol - mark GPL - in the future the handler won't be */ 368EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid); 369 370int cfg80211_ibss_wext_siwap(struct net_device *dev, 371 struct iw_request_info *info, 372 struct sockaddr *ap_addr, char *extra) 373{ 374 struct wireless_dev *wdev = dev->ieee80211_ptr; 375 u8 *bssid = ap_addr->sa_data; 376 int err; 377 378 /* call only for ibss! */ 379 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 380 return -EINVAL; 381 382 if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss) 383 return -EOPNOTSUPP; 384 385 if (ap_addr->sa_family != ARPHRD_ETHER) 386 return -EINVAL; 387 388 /* automatic mode */ 389 if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) 390 bssid = NULL; 391 392 /* both automatic */ 393 if (!bssid && !wdev->wext.ibss.bssid) 394 return 0; 395 396 /* fixed already - and no change */ 397 if (wdev->wext.ibss.bssid && bssid && 398 compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) 399 return 0; 400 401 wdev_lock(wdev); 402 err = 0; 403 if (wdev->ssid_len) 404 err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy), 405 dev, true); 406 wdev_unlock(wdev); 407 408 if (err) 409 return err; 410 411 if (bssid) { 412 memcpy(wdev->wext.bssid, bssid, ETH_ALEN); 413 wdev->wext.ibss.bssid = wdev->wext.bssid; 414 } else 415 wdev->wext.ibss.bssid = NULL; 416 417 return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev); 418} 419/* temporary symbol - mark GPL - in the future the handler won't be */ 420EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap); 421 422int cfg80211_ibss_wext_giwap(struct net_device *dev, 423 struct iw_request_info *info, 424 struct sockaddr *ap_addr, char *extra) 425{ 426 struct wireless_dev *wdev = dev->ieee80211_ptr; 427 428 /* call only for ibss! */ 429 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) 430 return -EINVAL; 431 432 ap_addr->sa_family = ARPHRD_ETHER; 433 434 wdev_lock(wdev); 435 if (wdev->current_bss) 436 memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); 437 else 438 memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); 439 wdev_unlock(wdev); 440 441 return 0; 442} 443/* temporary symbol - mark GPL - in the future the handler won't be */ 444EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap); 445#endif 446