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