xref: /haiku/src/kits/network/libnetapi/NetworkDevice.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2010-2015, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <NetworkDevice.h>
8 
9 #include <errno.h>
10 #include <net/if.h>
11 #include <net/if_media.h>
12 #include <stdio.h>
13 #include <sys/sockio.h>
14 
15 #include <Looper.h>
16 #include <Messenger.h>
17 
18 #include <AutoDeleter.h>
19 #include <NetServer.h>
20 #include <NetworkNotifications.h>
21 
22 extern "C" {
23 #	include <compat/sys/cdefs.h>
24 #	include <compat/sys/ioccom.h>
25 #	include <net80211/ieee80211_ioctl.h>
26 }
27 
28 
29 //#define TRACE_DEVICE
30 #ifdef TRACE_DEVICE
31 #	define TRACE(x, ...) printf(x, __VA_ARGS__);
32 #else
33 #	define TRACE(x, ...) ;
34 #endif
35 
36 
37 namespace {
38 
39 
40 struct ie_data {
41 	uint8	type;
42 	uint8	length;
43 	uint8	data[1];
44 };
45 
46 
47 // #pragma mark - private functions (code shared with net_server)
48 
49 
50 static status_t
51 get_80211(const char* name, int32 type, void* data, int32& length)
52 {
53 	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
54 	if (!socket.IsSet())
55 		return errno;
56 
57 	struct ieee80211req ireq;
58 	strlcpy(ireq.i_name, name, IF_NAMESIZE);
59 	ireq.i_type = type;
60 	ireq.i_val = 0;
61 	ireq.i_len = length;
62 	ireq.i_data = data;
63 
64 	if (ioctl(socket.Get(), SIOCG80211, &ireq, sizeof(struct ieee80211req))
65 		< 0)
66 		return errno;
67 
68 	length = ireq.i_len;
69 	return B_OK;
70 }
71 
72 
73 static status_t
74 set_80211(const char* name, int32 type, void* data,
75 	int32 length = 0, int32 value = 0)
76 {
77 	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
78 	if (!socket.IsSet())
79 		return errno;
80 
81 	struct ieee80211req ireq;
82 	strlcpy(ireq.i_name, name, IF_NAMESIZE);
83 	ireq.i_type = type;
84 	ireq.i_val = value;
85 	ireq.i_len = length;
86 	ireq.i_data = data;
87 
88 	if (ioctl(socket.Get(), SIOCS80211, &ireq, sizeof(struct ieee80211req))
89 		< 0)
90 		return errno;
91 
92 	return B_OK;
93 }
94 
95 
96 template<typename T> status_t
97 do_request(T& request, const char* name, int option)
98 {
99 	FileDescriptorCloser socket(::socket(AF_LINK, SOCK_DGRAM, 0));
100 	if (!socket.IsSet())
101 		return errno;
102 
103 	strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE);
104 
105 	if (ioctl(socket.Get(), option, &request, sizeof(T)) < 0)
106 		return errno;
107 
108 	return B_OK;
109 }
110 
111 
112 template<> status_t
113 do_request<ieee80211req>(ieee80211req& request, const char* name, int option)
114 {
115 	FileDescriptorCloser socket(::socket(AF_INET, SOCK_DGRAM, 0));
116 	if (!socket.IsSet())
117 		return errno;
118 
119 	strlcpy(((struct ieee80211req&)request).i_name, name, IFNAMSIZ);
120 
121 	if (ioctl(socket.Get(), option, &request, sizeof(request)) < 0)
122 		return errno;
123 
124 	return B_OK;
125 }
126 
127 
128 //! Read a 16 bit little endian value
129 static uint16
130 read_le16(uint8*& data, int32& length)
131 {
132 	uint16 value = B_LENDIAN_TO_HOST_INT16(*(uint16*)data);
133 	data += 2;
134 	length -= 2;
135 	return value;
136 }
137 
138 
139 //! Read a 32 bit little endian value
140 static uint32
141 read_le32(uint8*& data, int32& length)
142 {
143 	uint32 value = B_LENDIAN_TO_HOST_INT32(*(uint32*)data);
144 	data += 4;
145 	length -= 4;
146 	return value;
147 }
148 
149 
150 static uint32
151 from_rsn_cipher(uint32 cipher)
152 {
153 	if ((cipher & 0xffffff) != RSN_OUI)
154 		return B_NETWORK_CIPHER_CCMP;
155 
156 	switch (cipher >> 24) {
157 		case RSN_CSE_NULL:
158 			return B_NETWORK_CIPHER_NONE;
159 		case RSN_CSE_WEP40:
160 			return B_NETWORK_CIPHER_WEP_40;
161 		case RSN_CSE_WEP104:
162 			return B_NETWORK_CIPHER_WEP_104;
163 		case RSN_CSE_TKIP:
164 			return B_NETWORK_CIPHER_TKIP;
165 		default:
166 		case RSN_CSE_CCMP:
167 			return B_NETWORK_CIPHER_CCMP;
168 		case RSN_CSE_WRAP:
169 			return B_NETWORK_CIPHER_AES_128_CMAC;
170 	}
171 }
172 
173 
174 static uint32
175 from_rsn_key_mode(uint32 mode)
176 {
177 	if ((mode & 0xffffff) != RSN_OUI)
178 		return B_KEY_MODE_IEEE802_1X;
179 
180 	switch (mode >> 24) {
181 		default:
182 		case RSN_ASE_8021X_UNSPEC:
183 			return B_KEY_MODE_IEEE802_1X;
184 		case RSN_ASE_8021X_PSK:
185 			return B_KEY_MODE_PSK;
186 		// the following are currently not defined in net80211
187 		case 3:
188 			return B_KEY_MODE_FT_IEEE802_1X;
189 		case 4:
190 			return B_KEY_MODE_FT_PSK;
191 		case 5:
192 			return B_KEY_MODE_IEEE802_1X_SHA256;
193 		case 6:
194 			return B_KEY_MODE_PSK_SHA256;
195 	}
196 }
197 
198 
199 //! Parse RSN/WPA information elements common data
200 static void
201 parse_ie_rsn_wpa(wireless_network& network, uint8*& data, int32& length)
202 {
203 	if (length >= 4) {
204 		// parse group cipher
205 		network.group_cipher = from_rsn_cipher(read_le32(data, length));
206 	} else if (length > 0)
207 		return;
208 
209 	if (length >= 2) {
210 		// parse unicast cipher
211 		uint16 count = read_le16(data, length);
212 		network.cipher = 0;
213 
214 		for (uint16 i = 0; i < count; i++) {
215 			if (length < 4)
216 				return;
217 			network.cipher |= from_rsn_cipher(read_le32(data, length));
218 		}
219 	} else if (length > 0)
220 		return;
221 
222 	if (length >= 2) {
223 		// parse key management mode
224 		uint16 count = read_le16(data, length);
225 		network.key_mode = 0;
226 
227 		for (uint16 i = 0; i < count; i++) {
228 			if (length < 4)
229 				return;
230 			network.key_mode |= from_rsn_key_mode(read_le32(data, length));
231 		}
232 	} else if (length > 0)
233 		return;
234 
235 	// TODO: capabilities, and PMKID following in case of RSN
236 }
237 
238 
239 //! Parse RSN (Robust Security Network) information element.
240 static void
241 parse_ie_rsn(wireless_network& network, ie_data* ie)
242 {
243 	network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2;
244 	network.cipher = B_NETWORK_CIPHER_CCMP;
245 	network.group_cipher = B_NETWORK_CIPHER_CCMP;
246 	network.key_mode = B_KEY_MODE_IEEE802_1X;
247 
248 	int32 length = ie->length;
249 	if (length < 2)
250 		return;
251 
252 	uint8* data = ie->data;
253 
254 	uint16 version = read_le16(data, length);
255 	if (version != RSN_VERSION)
256 		return;
257 
258 	parse_ie_rsn_wpa(network, data, length);
259 }
260 
261 
262 //! Parse WPA information element.
263 static bool
264 parse_ie_wpa(wireless_network& network, ie_data* ie)
265 {
266 	int32 length = ie->length;
267 	if (length < 6)
268 		return false;
269 
270 	uint8* data = ie->data;
271 
272 	uint32 oui = read_le32(data, length);
273 	TRACE("  oui: %" B_PRIx32 "\n", oui);
274 	if (oui != ((WPA_OUI_TYPE << 24) | WPA_OUI))
275 		return false;
276 
277 	uint16 version = read_le16(data, length);
278 	if (version != WPA_VERSION)
279 		return false;
280 
281 	network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA;
282 	network.cipher = B_NETWORK_CIPHER_TKIP;
283 	network.group_cipher = B_NETWORK_CIPHER_TKIP;
284 	network.key_mode = B_KEY_MODE_IEEE802_1X;
285 
286 	parse_ie_rsn_wpa(network, data, length);
287 	return true;
288 }
289 
290 
291 //! Parse information elements.
292 static void
293 parse_ie(wireless_network& network, uint8* _ie, int32 ieLength)
294 {
295 	struct ie_data* ie = (ie_data*)_ie;
296 	bool hadRSN = false;
297 	bool hadWPA = false;
298 
299 	while (ieLength > 1) {
300 		TRACE("ie type %u\n", ie->type);
301 		switch (ie->type) {
302 			case IEEE80211_ELEMID_SSID:
303 				strlcpy(network.name, (char*)ie->data,
304 					min_c(ie->length + 1, (int)sizeof(network.name)));
305 				break;
306 			case IEEE80211_ELEMID_RSN:
307 				parse_ie_rsn(network, ie);
308 				hadRSN = true;
309 				break;
310 			case IEEE80211_ELEMID_VENDOR:
311 				if (!hadRSN && parse_ie_wpa(network, ie))
312 					hadWPA = true;
313 				break;
314 		}
315 
316 		ieLength -= 2 + ie->length;
317 		ie = (ie_data*)((uint8*)ie + 2 + ie->length);
318 	}
319 
320 	if (hadRSN || hadWPA) {
321 		// Determine authentication mode
322 
323 		if ((network.key_mode & (B_KEY_MODE_IEEE802_1X_SHA256
324 				| B_KEY_MODE_PSK_SHA256)) != 0) {
325 			network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2;
326 		} else if ((network.key_mode & (B_KEY_MODE_IEEE802_1X
327 				| B_KEY_MODE_PSK | B_KEY_MODE_FT_IEEE802_1X
328 				| B_KEY_MODE_FT_PSK)) != 0) {
329 			if (!hadRSN)
330 				network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA;
331 		} else if ((network.key_mode & B_KEY_MODE_NONE) != 0) {
332 			if ((network.cipher & (B_NETWORK_CIPHER_WEP_40
333 					| B_NETWORK_CIPHER_WEP_104)) != 0)
334 				network.authentication_mode = B_NETWORK_AUTHENTICATION_WEP;
335 			else
336 				network.authentication_mode = B_NETWORK_AUTHENTICATION_NONE;
337 		}
338 	}
339 }
340 
341 
342 static void
343 parse_ie(wireless_network& network, struct ieee80211req_sta_info& info)
344 {
345 	parse_ie(network, (uint8*)&info + info.isi_ie_off, info.isi_ie_len);
346 }
347 
348 
349 static void
350 parse_ie(wireless_network& network, struct ieee80211req_scan_result& result)
351 {
352 	parse_ie(network, (uint8*)&result + result.isr_ie_off + result.isr_ssid_len
353 			+ result.isr_meshid_len, result.isr_ie_len);
354 }
355 
356 
357 static bool
358 get_ssid_from_ie(char* name, uint8* _ie, int32 ieLength)
359 {
360 	struct ie_data* ie = (ie_data*)_ie;
361 
362 	while (ieLength > 1) {
363 		switch (ie->type) {
364 			case IEEE80211_ELEMID_SSID:
365 				strlcpy(name, (char*)ie->data, min_c(ie->length + 1, 32));
366 				return true;
367 		}
368 
369 		ieLength -= 2 + ie->length;
370 		ie = (ie_data*)((uint8*)ie + 2 + ie->length);
371 	}
372 	return false;
373 }
374 
375 
376 static bool
377 get_ssid_from_ie(char* name, struct ieee80211req_sta_info& info)
378 {
379 	return get_ssid_from_ie(name, (uint8*)&info + info.isi_ie_off,
380 		info.isi_ie_len);
381 }
382 
383 
384 static void
385 fill_wireless_network(wireless_network& network,
386 	struct ieee80211req_sta_info& info)
387 {
388 	network.name[0] = '\0';
389 	network.address.SetToLinkLevel(info.isi_macaddr,
390 		IEEE80211_ADDR_LEN);
391 	network.signal_strength = info.isi_rssi;
392 	network.noise_level = info.isi_noise;
393 	network.flags |= (info.isi_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0
394 		? B_NETWORK_IS_ENCRYPTED : 0;
395 
396 	network.authentication_mode = 0;
397 	network.cipher = 0;
398 	network.group_cipher = 0;
399 	network.key_mode = 0;
400 
401 	parse_ie(network, info);
402 }
403 
404 
405 static void
406 fill_wireless_network(wireless_network& network, const char* networkName,
407 	struct ieee80211req_scan_result& result)
408 {
409 	strlcpy(network.name, networkName, sizeof(network.name));
410 	network.address.SetToLinkLevel(result.isr_bssid,
411 		IEEE80211_ADDR_LEN);
412 	network.signal_strength = result.isr_rssi;
413 	network.noise_level = result.isr_noise;
414 	network.flags = (result.isr_capinfo & IEEE80211_CAPINFO_PRIVACY)
415 		!= 0 ? B_NETWORK_IS_ENCRYPTED : 0;
416 
417 	network.authentication_mode = 0;
418 	network.cipher = 0;
419 	network.group_cipher = 0;
420 	network.key_mode = 0;
421 
422 	parse_ie(network, result);
423 }
424 
425 
426 static status_t
427 get_scan_results(const char* device, wireless_network*& networks, uint32& count)
428 {
429 	if (networks != NULL)
430 		return B_BAD_VALUE;
431 
432 	// TODO: Find some way to reduce code duplication with the following function!
433 	const size_t kBufferSize = 65535;
434 	uint8* buffer = (uint8*)malloc(kBufferSize);
435 	if (buffer == NULL)
436 		return B_NO_MEMORY;
437 	MemoryDeleter deleter(buffer);
438 
439 	int32 length = kBufferSize;
440 	status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer,
441 		length);
442 	if (status != B_OK)
443 		return status;
444 
445 	BObjectList<wireless_network> networksList(true);
446 
447 	int32 bytesLeft = length;
448 	uint8* entry = buffer;
449 
450 	while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
451 		ieee80211req_scan_result* result
452 			= (ieee80211req_scan_result*)entry;
453 
454 		char networkName[32];
455 		strlcpy(networkName, (char*)(result + 1),
456 			min_c((int)sizeof(networkName), result->isr_ssid_len + 1));
457 
458 		wireless_network* network = new wireless_network;
459 		fill_wireless_network(*network, networkName, *result);
460 		networksList.AddItem(network);
461 
462 		entry += result->isr_len;
463 		bytesLeft -= result->isr_len;
464 	}
465 
466 	count = 0;
467 	if (!networksList.IsEmpty()) {
468 		networks = new wireless_network[networksList.CountItems()];
469 		for (int32 i = 0; i < networksList.CountItems(); i++) {
470 			networks[i] = *networksList.ItemAt(i);
471 			count++;
472 		}
473 	}
474 
475 	return B_OK;
476 }
477 
478 
479 static status_t
480 get_scan_result(const char* device, wireless_network& network, uint32 index,
481 	const BNetworkAddress* address, const char* name)
482 {
483 	if (address != NULL && address->Family() != AF_LINK)
484 		return B_BAD_VALUE;
485 
486 	// TODO: Find some way to reduce code duplication with the preceding function!
487 	const size_t kBufferSize = 65535;
488 	uint8* buffer = (uint8*)malloc(kBufferSize);
489 	if (buffer == NULL)
490 		return B_NO_MEMORY;
491 
492 	MemoryDeleter deleter(buffer);
493 
494 	int32 length = kBufferSize;
495 	status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer,
496 		length);
497 	if (status != B_OK)
498 		return status;
499 
500 	int32 bytesLeft = length;
501 	uint8* entry = buffer;
502 	uint32 count = 0;
503 
504 	while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
505 		ieee80211req_scan_result* result
506 			= (ieee80211req_scan_result*)entry;
507 
508 		char networkName[32];
509 		strlcpy(networkName, (char*)(result + 1),
510 			min_c((int)sizeof(networkName), result->isr_ssid_len + 1));
511 
512 		if (index == count || (address != NULL && !memcmp(
513 				address->LinkLevelAddress(), result->isr_bssid,
514 				IEEE80211_ADDR_LEN))
515 			|| (name != NULL && !strcmp(networkName, name))) {
516 			// Fill wireless_network with scan result data
517 			fill_wireless_network(network, networkName, *result);
518 			return B_OK;
519 		}
520 
521 		entry += result->isr_len;
522 		bytesLeft -= result->isr_len;
523 		count++;
524 	}
525 
526 	return B_ENTRY_NOT_FOUND;
527 }
528 
529 
530 static status_t
531 get_station(const char* device, wireless_network& network, uint32 index,
532 	const BNetworkAddress* address, const char* name)
533 {
534 	if (address != NULL && address->Family() != AF_LINK)
535 		return B_BAD_VALUE;
536 
537 	const size_t kBufferSize = 65535;
538 	uint8* buffer = (uint8*)malloc(kBufferSize);
539 	if (buffer == NULL)
540 		return B_NO_MEMORY;
541 
542 	MemoryDeleter deleter(buffer);
543 
544 	struct ieee80211req_sta_req& request = *(ieee80211req_sta_req*)buffer;
545 	if (address != NULL) {
546 		memcpy(request.is_u.macaddr, address->LinkLevelAddress(),
547 			IEEE80211_ADDR_LEN);
548 	} else
549 		memset(request.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN);
550 
551 	int32 length = kBufferSize;
552 	status_t status = get_80211(device, IEEE80211_IOC_STA_INFO, &request,
553 		length);
554 	if (status != B_OK)
555 		return status;
556 
557 	int32 bytesLeft = length;
558 	uint8* entry = (uint8*)&request.info[0];
559 	uint32 count = 0;
560 
561 	while (bytesLeft > (int32)sizeof(struct ieee80211req_sta_info)) {
562 		ieee80211req_sta_info* info = (ieee80211req_sta_info*)entry;
563 
564 		char networkName[32];
565 		get_ssid_from_ie(networkName, *info);
566 		if (index == count || address != NULL
567 			|| (name != NULL && !strcmp(networkName, name))) {
568 			fill_wireless_network(network, *info);
569 			return B_OK;
570 		}
571 
572 		entry += info->isi_len;
573 		bytesLeft -= info->isi_len;
574 		count++;
575 	}
576 
577 	return B_ENTRY_NOT_FOUND;
578 }
579 
580 
581 static status_t
582 get_network(const char* device, wireless_network& network, uint32 index,
583 	const BNetworkAddress* address, const char* name)
584 {
585 	status_t status = get_station(device, network, index, address, name);
586 	if (status != B_OK)
587 		return get_scan_result(device, network, index, address, name);
588 
589 	return B_OK;
590 }
591 
592 
593 }	// namespace
594 
595 
596 // #pragma mark -
597 
598 
599 BNetworkDevice::BNetworkDevice()
600 {
601 	Unset();
602 }
603 
604 
605 BNetworkDevice::BNetworkDevice(const char* name)
606 {
607 	SetTo(name);
608 }
609 
610 
611 BNetworkDevice::~BNetworkDevice()
612 {
613 }
614 
615 
616 void
617 BNetworkDevice::Unset()
618 {
619 	fName[0] = '\0';
620 }
621 
622 
623 void
624 BNetworkDevice::SetTo(const char* name)
625 {
626 	strlcpy(fName, name, IF_NAMESIZE);
627 }
628 
629 
630 const char*
631 BNetworkDevice::Name() const
632 {
633 	return fName;
634 }
635 
636 
637 bool
638 BNetworkDevice::Exists() const
639 {
640 	ifreq request;
641 	return do_request(request, Name(), SIOCGIFINDEX) == B_OK;
642 }
643 
644 
645 uint32
646 BNetworkDevice::Index() const
647 {
648 	ifreq request;
649 	if (do_request(request, Name(), SIOCGIFINDEX) != B_OK)
650 		return 0;
651 
652 	return request.ifr_index;
653 }
654 
655 
656 uint32
657 BNetworkDevice::Flags() const
658 {
659 	ifreq request;
660 	if (do_request(request, Name(), SIOCGIFFLAGS) != B_OK)
661 		return 0;
662 
663 	return request.ifr_flags;
664 }
665 
666 
667 bool
668 BNetworkDevice::HasLink() const
669 {
670 	return (Flags() & IFF_LINK) != 0;
671 }
672 
673 
674 int32
675 BNetworkDevice::Media() const
676 {
677 	ifreq request;
678 	if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK)
679 		return -1;
680 
681 	return request.ifr_media;
682 }
683 
684 
685 status_t
686 BNetworkDevice::SetMedia(int32 media)
687 {
688 	ifreq request;
689 	request.ifr_media = media;
690 	return do_request(request, Name(), SIOCSIFMEDIA);
691 }
692 
693 
694 status_t
695 BNetworkDevice::GetHardwareAddress(BNetworkAddress& address)
696 {
697 	ifreq request;
698 	status_t status = do_request(request, Name(), SIOCGIFADDR);
699 	if (status != B_OK)
700 		return status;
701 
702 	address.SetTo(request.ifr_addr);
703 	return B_OK;
704 }
705 
706 
707 bool
708 BNetworkDevice::IsEthernet()
709 {
710 	return IFM_TYPE(Media()) == IFM_ETHER;
711 }
712 
713 
714 bool
715 BNetworkDevice::IsWireless()
716 {
717 	return IFM_TYPE(Media()) == IFM_IEEE80211;
718 }
719 
720 
721 status_t
722 BNetworkDevice::Control(int option, void* request)
723 {
724 	switch (IFM_TYPE(Media())) {
725 		case IFM_ETHER:
726 			return do_request(*reinterpret_cast<ifreq*>(request),
727 				&fName[0], option);
728 
729 		case IFM_IEEE80211:
730 			return do_request(*reinterpret_cast<ieee80211req*>(request),
731 				&fName[0], option);
732 
733 		default:
734 			return B_ERROR;
735 	}
736 }
737 
738 
739 status_t
740 BNetworkDevice::Scan(bool wait, bool forceRescan)
741 {
742 	// Network status listener for change notifications
743 	class ScanListener : public BLooper {
744 	public:
745 		ScanListener(BString iface)
746 			:
747 			fInterface(iface)
748 		{
749 			start_watching_network(B_WATCH_NETWORK_WLAN_CHANGES, this);
750 		}
751 		virtual ~ScanListener()
752 		{
753 			stop_watching_network(this);
754 		}
755 
756 	protected:
757 		virtual void MessageReceived(BMessage *message)
758 		{
759 			if (message->what != B_NETWORK_MONITOR) {
760 				BLooper::MessageReceived(message);
761 				return;
762 			}
763 
764 			BString interfaceName;
765 			if (message->FindString("interface", &interfaceName) != B_OK)
766 				return;
767 			// See comment in AutoconfigLooper::_NetworkMonitorNotification
768 			// for the reason as to why we use FindFirst instead of ==.
769 			if (fInterface.FindFirst(interfaceName) < 0)
770 				return;
771 			if (message->FindInt32("opcode") != B_NETWORK_WLAN_SCANNED)
772 				return;
773 
774 			Lock();
775 			Quit();
776 		}
777 
778 	private:
779 		BString fInterface;
780 	};
781 
782 	ScanListener* listener = NULL;
783 	if (wait)
784 		listener = new ScanListener(Name());
785 
786 	// Trigger the scan
787 	struct ieee80211_scan_req request;
788 	memset(&request, 0, sizeof(request));
789 	request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
790 		| IEEE80211_IOC_SCAN_BGSCAN
791 		| IEEE80211_IOC_SCAN_NOPICK
792 		| IEEE80211_IOC_SCAN_ONCE
793 		| (forceRescan ? IEEE80211_IOC_SCAN_FLUSH : 0);
794 	request.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
795 	request.sr_nssid = 0;
796 
797 	status_t status = set_80211(Name(), IEEE80211_IOC_SCAN_REQ, &request,
798 		sizeof(request));
799 
800 	// If there are no VAPs running, the net80211 layer will return ENXIO.
801 	// Try to bring up the interface (which should start a VAP) and try again.
802 	if (status == ENXIO) {
803 		struct ieee80211req dummy;
804 		status = set_80211(Name(), IEEE80211_IOC_HAIKU_COMPAT_WLAN_UP, &dummy,
805 			sizeof(dummy));
806 		if (status != B_OK)
807 			return status;
808 
809 		status = set_80211(Name(), IEEE80211_IOC_SCAN_REQ, &request,
810 			sizeof(request));
811 	}
812 
813 	// If there is already a scan currently running, it's probably an "infinite"
814 	// one, which we of course don't want to wait for. So just return immediately
815 	// if that's the case.
816 	if (status == EINPROGRESS) {
817 		delete listener;
818 		return B_OK;
819 	}
820 
821 	if (!wait || status != B_OK) {
822 		delete listener;
823 		return status;
824 	}
825 
826 	while (wait_for_thread(listener->Run(), NULL) == B_INTERRUPTED)
827 		;
828 	return B_OK;
829 }
830 
831 
832 status_t
833 BNetworkDevice::GetNetworks(wireless_network*& networks, uint32& count)
834 {
835 	return get_scan_results(Name(), networks, count);
836 }
837 
838 
839 status_t
840 BNetworkDevice::GetNetwork(const char* name, wireless_network& network)
841 {
842 	if (name == NULL || name[0] == '\0')
843 		return B_BAD_VALUE;
844 
845 	return get_network(Name(), network, UINT32_MAX, NULL, name);
846 }
847 
848 
849 status_t
850 BNetworkDevice::GetNetwork(const BNetworkAddress& address,
851 	wireless_network& network)
852 {
853 	if (address.Family() != AF_LINK)
854 		return B_BAD_VALUE;
855 
856 	return get_network(Name(), network, UINT32_MAX, &address, NULL);
857 }
858 
859 
860 status_t
861 BNetworkDevice::JoinNetwork(const char* name, const char* password)
862 {
863 	if (name == NULL || name[0] == '\0')
864 		return B_BAD_VALUE;
865 
866 	BMessage message(kMsgJoinNetwork);
867 	status_t status = message.AddString("device", Name());
868 
869 	if (status == B_OK)
870 		status = message.AddString("name", name);
871 	if (status == B_OK && password != NULL)
872 		status = message.AddString("password", password);
873 	if (status != B_OK)
874 		return status;
875 
876 	// Send message to the net_server
877 
878 	BMessenger networkServer(kNetServerSignature);
879 	BMessage reply;
880 	status = networkServer.SendMessage(&message, &reply);
881 	if (status == B_OK)
882 		reply.FindInt32("status", &status);
883 
884 	return status;
885 }
886 
887 
888 status_t
889 BNetworkDevice::JoinNetwork(const wireless_network& network,
890 	const char* password)
891 {
892 	return JoinNetwork(network.address, password);
893 }
894 
895 
896 status_t
897 BNetworkDevice::JoinNetwork(const BNetworkAddress& address,
898 	const char* password)
899 {
900 	if (address.InitCheck() != B_OK)
901 		return B_BAD_VALUE;
902 
903 	BMessage message(kMsgJoinNetwork);
904 	status_t status = message.AddString("device", Name());
905 
906 	if (status == B_OK) {
907 		status = message.AddFlat("address",
908 			const_cast<BNetworkAddress*>(&address));
909 	}
910 	if (status == B_OK && password != NULL)
911 		status = message.AddString("password", password);
912 	if (status != B_OK)
913 		return status;
914 
915 	// Send message to the net_server
916 
917 	BMessenger networkServer(kNetServerSignature);
918 	BMessage reply;
919 	status = networkServer.SendMessage(&message, &reply);
920 	if (status == B_OK)
921 		reply.FindInt32("status", &status);
922 
923 	return status;
924 }
925 
926 
927 status_t
928 BNetworkDevice::LeaveNetwork(const char* name)
929 {
930 	BMessage message(kMsgLeaveNetwork);
931 	status_t status = message.AddString("device", Name());
932 	if (status == B_OK)
933 		status = message.AddString("name", name);
934 	if (status == B_OK)
935 		status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED);
936 	if (status != B_OK)
937 		return status;
938 
939 	BMessenger networkServer(kNetServerSignature);
940 	BMessage reply;
941 	status = networkServer.SendMessage(&message, &reply);
942 	if (status == B_OK)
943 		reply.FindInt32("status", &status);
944 
945 	return status;
946 }
947 
948 
949 status_t
950 BNetworkDevice::LeaveNetwork(const wireless_network& network)
951 {
952 	return LeaveNetwork(network.address);
953 }
954 
955 
956 status_t
957 BNetworkDevice::LeaveNetwork(const BNetworkAddress& address)
958 {
959 	BMessage message(kMsgLeaveNetwork);
960 	status_t status = message.AddString("device", Name());
961 	if (status == B_OK) {
962 		status = message.AddFlat("address",
963 			const_cast<BNetworkAddress*>(&address));
964 	}
965 	if (status == B_OK)
966 		status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED);
967 	if (status != B_OK)
968 		return status;
969 
970 	BMessenger networkServer(kNetServerSignature);
971 	BMessage reply;
972 	status = networkServer.SendMessage(&message, &reply);
973 	if (status == B_OK)
974 		reply.FindInt32("status", &status);
975 
976 	return status;
977 }
978 
979 
980 status_t
981 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie,
982 	wireless_network& network)
983 {
984 	BNetworkAddress address;
985 	status_t status = GetNextAssociatedNetwork(cookie, address);
986 	if (status != B_OK)
987 		return status;
988 
989 	return GetNetwork(address, network);
990 }
991 
992 
993 status_t
994 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie,
995 	BNetworkAddress& address)
996 {
997 	// We currently support only a single associated network
998 	if (cookie != 0)
999 		return B_ENTRY_NOT_FOUND;
1000 
1001 	uint8 mac[IEEE80211_ADDR_LEN];
1002 	int32 length = IEEE80211_ADDR_LEN;
1003 	status_t status = get_80211(Name(), IEEE80211_IOC_BSSID, mac, length);
1004 	if (status != B_OK)
1005 		return status;
1006 
1007 	if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0
1008 			&& mac[5] == 0) {
1009 		return B_ENTRY_NOT_FOUND;
1010 	}
1011 
1012 	address.SetToLinkLevel(mac, IEEE80211_ADDR_LEN);
1013 	cookie++;
1014 	return B_OK;
1015 }
1016