ibss.c 11.1 KB
Newer Older
Johannes Berg's avatar
Johannes Berg committed
1 2 3 4 5 6 7 8 9
/*
 * Some IBSS support code for cfg80211.
 *
 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
 */

#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <net/cfg80211.h>
10
#include "wext-compat.h"
Johannes Berg's avatar
Johannes Berg committed
11 12 13
#include "nl80211.h"


Johannes Berg's avatar
Johannes Berg committed
14
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
Johannes Berg's avatar
Johannes Berg committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_bss *bss;
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return;

	if (WARN_ON(!wdev->ssid_len))
		return;

	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
			       wdev->ssid, wdev->ssid_len,
			       WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);

	if (WARN_ON(!bss))
		return;

	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
Johannes Berg's avatar
Johannes Berg committed
37
		cfg80211_put_bss(&wdev->current_bss->pub);
Johannes Berg's avatar
Johannes Berg committed
38 39
	}

Johannes Berg's avatar
Johannes Berg committed
40 41
	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);
Johannes Berg's avatar
Johannes Berg committed
42

Johannes Berg's avatar
Johannes Berg committed
43 44
	cfg80211_upload_connect_keys(wdev);

Johannes Berg's avatar
Johannes Berg committed
45 46
	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
				GFP_KERNEL);
Johannes Berg's avatar
Johannes Berg committed
47 48 49 50 51 52
#ifdef CONFIG_WIRELESS_EXT
	memset(&wrqu, 0, sizeof(wrqu));
	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
#endif
}
Johannes Berg's avatar
Johannes Berg committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_event *ev;
	unsigned long flags;

	ev = kzalloc(sizeof(*ev), gfp);
	if (!ev)
		return;

	ev->type = EVENT_IBSS_JOINED;
	memcpy(ev->cr.bssid, bssid, ETH_ALEN);

	spin_lock_irqsave(&wdev->event_lock, flags);
	list_add_tail(&ev->list, &wdev->event_list);
	spin_unlock_irqrestore(&wdev->event_lock, flags);
	schedule_work(&rdev->event_work);
}
Johannes Berg's avatar
Johannes Berg committed
73 74
EXPORT_SYMBOL(cfg80211_ibss_joined);

Johannes Berg's avatar
Johannes Berg committed
75 76
int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
			 struct net_device *dev,
Johannes Berg's avatar
Johannes Berg committed
77 78
			 struct cfg80211_ibss_params *params,
			 struct cfg80211_cached_keys *connkeys)
Johannes Berg's avatar
Johannes Berg committed
79 80 81 82
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	int err;

Johannes Berg's avatar
Johannes Berg committed
83 84
	ASSERT_WDEV_LOCK(wdev);

Johannes Berg's avatar
Johannes Berg committed
85 86 87
	if (wdev->ssid_len)
		return -EALREADY;

Johannes Berg's avatar
Johannes Berg committed
88 89 90 91
	if (WARN_ON(wdev->connect_keys))
		kfree(wdev->connect_keys);
	wdev->connect_keys = connkeys;

Johannes Berg's avatar
Johannes Berg committed
92
#ifdef CONFIG_WIRELESS_EXT
93
	wdev->wext.ibss.channel = params->channel;
Johannes Berg's avatar
Johannes Berg committed
94 95
#endif
	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
Johannes Berg's avatar
Johannes Berg committed
96 97
	if (err) {
		wdev->connect_keys = NULL;
Johannes Berg's avatar
Johannes Berg committed
98
		return err;
Johannes Berg's avatar
Johannes Berg committed
99
	}
Johannes Berg's avatar
Johannes Berg committed
100 101 102 103 104 105 106

	memcpy(wdev->ssid, params->ssid, params->ssid_len);
	wdev->ssid_len = params->ssid_len;

	return 0;
}

Johannes Berg's avatar
Johannes Berg committed
107 108
int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
		       struct net_device *dev,
Johannes Berg's avatar
Johannes Berg committed
109 110
		       struct cfg80211_ibss_params *params,
		       struct cfg80211_cached_keys *connkeys)
Johannes Berg's avatar
Johannes Berg committed
111 112 113 114 115
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	int err;

	wdev_lock(wdev);
Johannes Berg's avatar
Johannes Berg committed
116
	err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
Johannes Berg's avatar
Johannes Berg committed
117 118 119 120 121 122
	wdev_unlock(wdev);

	return err;
}

static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
Johannes Berg's avatar
Johannes Berg committed
123 124
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg's avatar
Johannes Berg committed
125 126
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	int i;
Johannes Berg's avatar
Johannes Berg committed
127

Johannes Berg's avatar
Johannes Berg committed
128 129
	ASSERT_WDEV_LOCK(wdev);

Johannes Berg's avatar
Johannes Berg committed
130 131 132 133 134 135 136 137 138 139 140
	kfree(wdev->connect_keys);
	wdev->connect_keys = NULL;

	/*
	 * Delete all the keys ... pairwise keys can't really
	 * exist any more anyway, but default keys might.
	 */
	if (rdev->ops->del_key)
		for (i = 0; i < 6; i++)
			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);

Johannes Berg's avatar
Johannes Berg committed
141 142
	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
Johannes Berg's avatar
Johannes Berg committed
143
		cfg80211_put_bss(&wdev->current_bss->pub);
Johannes Berg's avatar
Johannes Berg committed
144 145 146 147
	}

	wdev->current_bss = NULL;
	wdev->ssid_len = 0;
148 149
#ifdef CONFIG_WIRELESS_EXT
	if (!nowext)
150
		wdev->wext.ibss.ssid_len = 0;
151
#endif
Johannes Berg's avatar
Johannes Berg committed
152 153
}

Johannes Berg's avatar
Johannes Berg committed
154 155 156 157 158 159 160 161 162 163 164
void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	wdev_lock(wdev);
	__cfg80211_clear_ibss(dev, nowext);
	wdev_unlock(wdev);
}

static int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
				 struct net_device *dev, bool nowext)
Johannes Berg's avatar
Johannes Berg committed
165
{
166
	struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg's avatar
Johannes Berg committed
167 168
	int err;

Johannes Berg's avatar
Johannes Berg committed
169 170
	ASSERT_WDEV_LOCK(wdev);

171 172 173
	if (!wdev->ssid_len)
		return -ENOLINK;

Johannes Berg's avatar
Johannes Berg committed
174 175 176 177 178
	err = rdev->ops->leave_ibss(&rdev->wiphy, dev);

	if (err)
		return err;

Johannes Berg's avatar
Johannes Berg committed
179
	__cfg80211_clear_ibss(dev, nowext);
Johannes Berg's avatar
Johannes Berg committed
180 181 182 183

	return 0;
}

Johannes Berg's avatar
Johannes Berg committed
184 185 186 187 188 189 190 191 192 193 194 195 196
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
			struct net_device *dev, bool nowext)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	int err;

	wdev_lock(wdev);
	err = __cfg80211_leave_ibss(rdev, dev, nowext);
	wdev_unlock(wdev);

	return err;
}

Johannes Berg's avatar
Johannes Berg committed
197
#ifdef CONFIG_WIRELESS_EXT
Johannes Berg's avatar
Johannes Berg committed
198 199
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
			    struct wireless_dev *wdev)
Johannes Berg's avatar
Johannes Berg committed
200
{
Johannes Berg's avatar
Johannes Berg committed
201
	struct cfg80211_cached_keys *ck = NULL;
Johannes Berg's avatar
Johannes Berg committed
202
	enum ieee80211_band band;
Johannes Berg's avatar
Johannes Berg committed
203 204 205
	int i, err;

	ASSERT_WDEV_LOCK(wdev);
Johannes Berg's avatar
Johannes Berg committed
206

207 208
	if (!wdev->wext.ibss.beacon_interval)
		wdev->wext.ibss.beacon_interval = 100;
209

Johannes Berg's avatar
Johannes Berg committed
210
	/* try to find an IBSS channel if none requested ... */
211
	if (!wdev->wext.ibss.channel) {
Johannes Berg's avatar
Johannes Berg committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225
		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
			struct ieee80211_supported_band *sband;
			struct ieee80211_channel *chan;

			sband = rdev->wiphy.bands[band];
			if (!sband)
				continue;

			for (i = 0; i < sband->n_channels; i++) {
				chan = &sband->channels[i];
				if (chan->flags & IEEE80211_CHAN_NO_IBSS)
					continue;
				if (chan->flags & IEEE80211_CHAN_DISABLED)
					continue;
226
				wdev->wext.ibss.channel = chan;
Johannes Berg's avatar
Johannes Berg committed
227 228 229
				break;
			}

230
			if (wdev->wext.ibss.channel)
Johannes Berg's avatar
Johannes Berg committed
231 232 233
				break;
		}

234
		if (!wdev->wext.ibss.channel)
Johannes Berg's avatar
Johannes Berg committed
235 236 237 238
			return -EINVAL;
	}

	/* don't join -- SSID is not there */
239
	if (!wdev->wext.ibss.ssid_len)
Johannes Berg's avatar
Johannes Berg committed
240 241 242 243 244
		return 0;

	if (!netif_running(wdev->netdev))
		return 0;

Johannes Berg's avatar
Johannes Berg committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
	if (wdev->wext.keys)
		wdev->wext.keys->def = wdev->wext.default_key;

	wdev->wext.ibss.privacy = wdev->wext.default_key != -1;

	if (wdev->wext.keys) {
		ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
		if (!ck)
			return -ENOMEM;
		for (i = 0; i < 6; i++)
			ck->params[i].key = ck->data[i];
	}
	err = __cfg80211_join_ibss(rdev, wdev->netdev,
				   &wdev->wext.ibss, ck);
	if (err)
		kfree(ck);

	return err;
Johannes Berg's avatar
Johannes Berg committed
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
}

int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
			       struct iw_request_info *info,
			       struct iw_freq *freq, char *extra)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct ieee80211_channel *chan;
	int err;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
		return -EOPNOTSUPP;

	chan = cfg80211_wext_freq(wdev->wiphy, freq);
	if (chan && IS_ERR(chan))
		return PTR_ERR(chan);

	if (chan &&
	    (chan->flags & IEEE80211_CHAN_NO_IBSS ||
	     chan->flags & IEEE80211_CHAN_DISABLED))
		return -EINVAL;

289
	if (wdev->wext.ibss.channel == chan)
Johannes Berg's avatar
Johannes Berg committed
290 291
		return 0;

Johannes Berg's avatar
Johannes Berg committed
292 293 294 295 296 297 298 299 300
	wdev_lock(wdev);
	err = 0;
	if (wdev->ssid_len)
		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
					    dev, true);
	wdev_unlock(wdev);

	if (err)
		return err;
Johannes Berg's avatar
Johannes Berg committed
301 302

	if (chan) {
303 304
		wdev->wext.ibss.channel = chan;
		wdev->wext.ibss.channel_fixed = true;
Johannes Berg's avatar
Johannes Berg committed
305 306
	} else {
		/* cfg80211_ibss_wext_join will pick one if needed */
307
		wdev->wext.ibss.channel_fixed = false;
Johannes Berg's avatar
Johannes Berg committed
308 309
	}

Johannes Berg's avatar
Johannes Berg committed
310 311 312 313 314
	wdev_lock(wdev);
	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
	wdev_unlock(wdev);

	return err;
Johannes Berg's avatar
Johannes Berg committed
315 316 317 318 319 320 321 322 323 324 325 326 327
}

int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
			       struct iw_request_info *info,
			       struct iw_freq *freq, char *extra)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct ieee80211_channel *chan = NULL;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
328
	wdev_lock(wdev);
Johannes Berg's avatar
Johannes Berg committed
329
	if (wdev->current_bss)
Johannes Berg's avatar
Johannes Berg committed
330
		chan = wdev->current_bss->pub.channel;
331 332
	else if (wdev->wext.ibss.channel)
		chan = wdev->wext.ibss.channel;
Johannes Berg's avatar
Johannes Berg committed
333
	wdev_unlock(wdev);
Johannes Berg's avatar
Johannes Berg committed
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

	if (chan) {
		freq->m = chan->center_freq;
		freq->e = 6;
		return 0;
	}

	/* no channel if not joining */
	return -EINVAL;
}

int cfg80211_ibss_wext_siwessid(struct net_device *dev,
				struct iw_request_info *info,
				struct iw_point *data, char *ssid)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	size_t len = data->length;
	int err;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
		return -EOPNOTSUPP;

Johannes Berg's avatar
Johannes Berg committed
360 361 362 363 364 365 366 367 368
	wdev_lock(wdev);
	err = 0;
	if (wdev->ssid_len)
		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
					    dev, true);
	wdev_unlock(wdev);

	if (err)
		return err;
Johannes Berg's avatar
Johannes Berg committed
369 370 371 372 373

	/* iwconfig uses nul termination in SSID.. */
	if (len > 0 && ssid[len - 1] == '\0')
		len--;

374 375 376
	wdev->wext.ibss.ssid = wdev->ssid;
	memcpy(wdev->wext.ibss.ssid, ssid, len);
	wdev->wext.ibss.ssid_len = len;
Johannes Berg's avatar
Johannes Berg committed
377

Johannes Berg's avatar
Johannes Berg committed
378 379 380 381 382
	wdev_lock(wdev);
	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
	wdev_unlock(wdev);

	return err;
Johannes Berg's avatar
Johannes Berg committed
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
}
/* temporary symbol - mark GPL - in the future the handler won't be */
EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);

int cfg80211_ibss_wext_giwessid(struct net_device *dev,
				struct iw_request_info *info,
				struct iw_point *data, char *ssid)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

	data->flags = 0;

Johannes Berg's avatar
Johannes Berg committed
399
	wdev_lock(wdev);
Johannes Berg's avatar
Johannes Berg committed
400 401 402 403
	if (wdev->ssid_len) {
		data->flags = 1;
		data->length = wdev->ssid_len;
		memcpy(ssid, wdev->ssid, data->length);
404
	} else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
Johannes Berg's avatar
Johannes Berg committed
405
		data->flags = 1;
406 407
		data->length = wdev->wext.ibss.ssid_len;
		memcpy(ssid, wdev->wext.ibss.ssid, data->length);
Johannes Berg's avatar
Johannes Berg committed
408
	}
Johannes Berg's avatar
Johannes Berg committed
409
	wdev_unlock(wdev);
Johannes Berg's avatar
Johannes Berg committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

	return 0;
}
/* temporary symbol - mark GPL - in the future the handler won't be */
EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);

int cfg80211_ibss_wext_siwap(struct net_device *dev,
			     struct iw_request_info *info,
			     struct sockaddr *ap_addr, char *extra)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	u8 *bssid = ap_addr->sa_data;
	int err;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

	if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
		return -EOPNOTSUPP;

	if (ap_addr->sa_family != ARPHRD_ETHER)
		return -EINVAL;

	/* automatic mode */
	if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
		bssid = NULL;

	/* both automatic */
439
	if (!bssid && !wdev->wext.ibss.bssid)
Johannes Berg's avatar
Johannes Berg committed
440 441 442
		return 0;

	/* fixed already - and no change */
443 444
	if (wdev->wext.ibss.bssid && bssid &&
	    compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0)
Johannes Berg's avatar
Johannes Berg committed
445 446
		return 0;

Johannes Berg's avatar
Johannes Berg committed
447 448 449 450 451 452 453 454 455
	wdev_lock(wdev);
	err = 0;
	if (wdev->ssid_len)
		err = __cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
					    dev, true);
	wdev_unlock(wdev);

	if (err)
		return err;
Johannes Berg's avatar
Johannes Berg committed
456 457

	if (bssid) {
458 459
		memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
		wdev->wext.ibss.bssid = wdev->wext.bssid;
Johannes Berg's avatar
Johannes Berg committed
460
	} else
461
		wdev->wext.ibss.bssid = NULL;
Johannes Berg's avatar
Johannes Berg committed
462

Johannes Berg's avatar
Johannes Berg committed
463 464 465 466 467
	wdev_lock(wdev);
	err = cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
	wdev_unlock(wdev);

	return err;
Johannes Berg's avatar
Johannes Berg committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481
}

int cfg80211_ibss_wext_giwap(struct net_device *dev,
			     struct iw_request_info *info,
			     struct sockaddr *ap_addr, char *extra)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	/* call only for ibss! */
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
		return -EINVAL;

	ap_addr->sa_family = ARPHRD_ETHER;

Johannes Berg's avatar
Johannes Berg committed
482
	wdev_lock(wdev);
483
	if (wdev->current_bss)
Johannes Berg's avatar
Johannes Berg committed
484
		memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
485
	else if (wdev->wext.ibss.bssid)
486
		memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
487 488 489
	else
		memset(ap_addr->sa_data, 0, ETH_ALEN);

Johannes Berg's avatar
Johannes Berg committed
490 491
	wdev_unlock(wdev);

Johannes Berg's avatar
Johannes Berg committed
492 493 494
	return 0;
}
#endif