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