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