Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / kits / network / libnetapi / NetworkDevice.cpp
blob29fade4e40a2d4708eba21fb64fc6a5b8f59048d
1 /*
2 * Copyright 2010-2015, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <NetworkDevice.h>
9 #include <errno.h>
10 #include <net/if.h>
11 #include <net/if_media.h>
12 #include <stdio.h>
13 #include <sys/sockio.h>
15 #include <Messenger.h>
17 #include <AutoDeleter.h>
18 #include <NetServer.h>
20 extern "C" {
21 # include <net80211/ieee80211_ioctl.h>
25 //#define TRACE_DEVICE
26 #ifdef TRACE_DEVICE
27 # define TRACE(x, ...) printf(x, __VA_ARGS__);
28 #else
29 # define TRACE(x, ...) ;
30 #endif
33 namespace {
36 struct ie_data {
37 uint8 type;
38 uint8 length;
39 uint8 data[1];
43 static status_t
44 get_80211(const char* name, int32 type, void* data, int32& length)
46 int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
47 if (socket < 0)
48 return errno;
50 FileDescriptorCloser closer(socket);
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;
59 if (ioctl(socket, SIOCG80211, &ireq, sizeof(struct ieee80211req)) < 0)
60 return errno;
62 length = ireq.i_len;
63 return B_OK;
67 template<typename T> status_t
68 do_request(T& request, const char* name, int option)
70 int socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
71 if (socket < 0)
72 return errno;
74 FileDescriptorCloser closer(socket);
76 strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE);
78 if (ioctl(socket, option, &request, sizeof(T)) < 0)
79 return errno;
81 return B_OK;
85 //! Read a 16 bit little endian value
86 static uint16
87 read_le16(uint8*& data, int32& length)
89 uint16 value = B_LENDIAN_TO_HOST_INT16(*(uint16*)data);
90 data += 2;
91 length -= 2;
92 return value;
96 //! Read a 32 bit little endian value
97 static uint32
98 read_le32(uint8*& data, int32& length)
100 uint32 value = B_LENDIAN_TO_HOST_INT32(*(uint32*)data);
101 data += 4;
102 length -= 4;
103 return value;
107 static uint32
108 from_rsn_cipher(uint32 cipher)
110 if ((cipher & 0xffffff) != RSN_OUI)
111 return B_NETWORK_CIPHER_CCMP;
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;
131 static uint32
132 from_rsn_key_mode(uint32 mode)
134 if ((mode & 0xffffff) != RSN_OUI)
135 return B_KEY_MODE_IEEE802_1X;
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;
156 //! Parse RSN/WPA information elements common data
157 static void
158 parse_ie_rsn_wpa(wireless_network& network, uint8*& data, int32& length)
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;
166 if (length >= 2) {
167 // parse unicast cipher
168 uint16 count = read_le16(data, length);
169 network.cipher = 0;
171 for (uint16 i = 0; i < count; i++) {
172 if (length < 4)
173 return;
174 network.cipher |= from_rsn_cipher(read_le32(data, length));
176 } else if (length > 0)
177 return;
179 if (length >= 2) {
180 // parse key management mode
181 uint16 count = read_le16(data, length);
182 network.key_mode = 0;
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));
189 } else if (length > 0)
190 return;
192 // TODO: capabilities, and PMKID following in case of RSN
196 //! Parse RSN (Robust Security Network) information element.
197 static void
198 parse_ie_rsn(wireless_network& network, ie_data* ie)
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;
205 int32 length = ie->length;
206 if (length < 2)
207 return;
209 uint8* data = ie->data;
211 uint16 version = read_le16(data, length);
212 if (version != RSN_VERSION)
213 return;
215 parse_ie_rsn_wpa(network, data, length);
219 //! Parse WPA information element.
220 static bool
221 parse_ie_wpa(wireless_network& network, ie_data* ie)
223 int32 length = ie->length;
224 if (length < 6)
225 return false;
227 uint8* data = ie->data;
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;
234 uint16 version = read_le16(data, length);
235 if (version != WPA_VERSION)
236 return false;
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;
243 parse_ie_rsn_wpa(network, data, length);
244 return true;
248 //! Parse information elements.
249 static void
250 parse_ie(wireless_network& network, uint8* _ie, int32 ieLength)
252 struct ie_data* ie = (ie_data*)_ie;
253 bool hadRSN = false;
254 bool hadWPA = false;
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;
273 ieLength -= 2 + ie->length;
274 ie = (ie_data*)((uint8*)ie + 2 + ie->length);
277 if (hadRSN || hadWPA) {
278 // Determine authentication mode
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;
299 static void
300 parse_ie(wireless_network& network, struct ieee80211req_sta_info& info)
302 parse_ie(network, (uint8*)&info + info.isi_ie_off, info.isi_ie_len);
306 static void
307 parse_ie(wireless_network& network, struct ieee80211req_scan_result& result)
309 parse_ie(network, (uint8*)&result + result.isr_ie_off + result.isr_ssid_len
310 + result.isr_meshid_len, result.isr_ie_len);
314 static bool
315 get_ssid_from_ie(char* name, uint8* _ie, int32 ieLength)
317 struct ie_data* ie = (ie_data*)_ie;
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;
326 ieLength -= 2 + ie->length;
327 ie = (ie_data*)((uint8*)ie + 2 + ie->length);
329 return false;
333 static bool
334 get_ssid_from_ie(char* name, struct ieee80211req_sta_info& info)
336 return get_ssid_from_ie(name, (uint8*)&info + info.isi_ie_off,
337 info.isi_ie_len);
341 static void
342 fill_wireless_network(wireless_network& network,
343 struct ieee80211req_sta_info& info)
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;
353 network.authentication_mode = 0;
354 network.cipher = 0;
355 network.group_cipher = 0;
356 network.key_mode = 0;
358 parse_ie(network, info);
362 static void
363 fill_wireless_network(wireless_network& network, const char* networkName,
364 struct ieee80211req_scan_result& result)
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;
374 network.authentication_mode = 0;
375 network.cipher = 0;
376 network.group_cipher = 0;
377 network.key_mode = 0;
379 parse_ie(network, result);
383 static status_t
384 get_scan_result(const char* device, wireless_network& network, uint32 index,
385 const BNetworkAddress* address, const char* name)
387 if (address != NULL && address->Family() != AF_LINK)
388 return B_BAD_VALUE;
390 const size_t kBufferSize = 65535;
391 uint8* buffer = (uint8*)malloc(kBufferSize);
392 if (buffer == NULL)
393 return B_NO_MEMORY;
395 MemoryDeleter deleter(buffer);
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;
403 int32 bytesLeft = length;
404 uint8* entry = buffer;
405 uint32 count = 0;
407 while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
408 ieee80211req_scan_result* result
409 = (ieee80211req_scan_result*)entry;
411 char networkName[32];
412 strlcpy(networkName, (char*)(result + 1),
413 min_c((int)sizeof(networkName), result->isr_ssid_len + 1));
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;
424 entry += result->isr_len;
425 bytesLeft -= result->isr_len;
426 count++;
429 return B_ENTRY_NOT_FOUND;
433 static status_t
434 get_station(const char* device, wireless_network& network, uint32 index,
435 const BNetworkAddress* address, const char* name)
437 if (address != NULL && address->Family() != AF_LINK)
438 return B_BAD_VALUE;
440 const size_t kBufferSize = 65535;
441 uint8* buffer = (uint8*)malloc(kBufferSize);
442 if (buffer == NULL)
443 return B_NO_MEMORY;
445 MemoryDeleter deleter(buffer);
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);
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;
460 int32 bytesLeft = length;
461 uint8* entry = (uint8*)&request.info[0];
462 uint32 count = 0;
464 while (bytesLeft > (int32)sizeof(struct ieee80211req_sta_info)) {
465 ieee80211req_sta_info* info = (ieee80211req_sta_info*)entry;
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;
475 entry += info->isi_len;
476 bytesLeft -= info->isi_len;
477 count++;
480 return B_ENTRY_NOT_FOUND;
484 static status_t
485 get_network(const char* device, wireless_network& network, uint32 index,
486 const BNetworkAddress* address, const char* name)
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);
492 return B_OK;
496 } // namespace
499 // #pragma mark -
502 BNetworkDevice::BNetworkDevice()
504 Unset();
508 BNetworkDevice::BNetworkDevice(const char* name)
510 SetTo(name);
514 BNetworkDevice::~BNetworkDevice()
519 void
520 BNetworkDevice::Unset()
522 fName[0] = '\0';
526 void
527 BNetworkDevice::SetTo(const char* name)
529 strlcpy(fName, name, IF_NAMESIZE);
533 const char*
534 BNetworkDevice::Name() const
536 return fName;
540 bool
541 BNetworkDevice::Exists() const
543 ifreq request;
544 return do_request(request, Name(), SIOCGIFINDEX) == B_OK;
548 uint32
549 BNetworkDevice::Index() const
551 ifreq request;
552 if (do_request(request, Name(), SIOCGIFINDEX) != B_OK)
553 return 0;
555 return request.ifr_index;
559 uint32
560 BNetworkDevice::Flags() const
562 ifreq request;
563 if (do_request(request, Name(), SIOCGIFFLAGS) != B_OK)
564 return 0;
566 return request.ifr_flags;
570 bool
571 BNetworkDevice::HasLink() const
573 return (Flags() & IFF_LINK) != 0;
577 int32
578 BNetworkDevice::CountMedia() const
580 ifmediareq request;
581 request.ifm_count = 0;
582 request.ifm_ulist = NULL;
584 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK)
585 return -1;
587 return request.ifm_count;
591 int32
592 BNetworkDevice::Media() const
594 ifmediareq request;
595 request.ifm_count = 0;
596 request.ifm_ulist = NULL;
598 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK)
599 return -1;
601 return request.ifm_current;
605 int32
606 BNetworkDevice::GetMediaAt(int32 index) const
608 // TODO: this could do some caching
609 return 0;
613 status_t
614 BNetworkDevice::SetMedia(int32 media)
616 ifreq request;
617 request.ifr_media = media;
618 return do_request(request, Name(), SIOCSIFMEDIA);
622 status_t
623 BNetworkDevice::GetHardwareAddress(BNetworkAddress& address)
625 ifreq request;
626 status_t status = do_request(request, Name(), SIOCGIFADDR);
627 if (status != B_OK)
628 return status;
630 address.SetTo(request.ifr_addr);
631 return B_OK;
635 bool
636 BNetworkDevice::IsEthernet()
638 return IFM_TYPE(Media()) == IFM_ETHER;
642 bool
643 BNetworkDevice::IsWireless()
645 return IFM_TYPE(Media()) == IFM_IEEE80211;
649 status_t
650 BNetworkDevice::Scan(bool wait, bool forceRescan)
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);
661 #endif
662 return B_ERROR;
666 status_t
667 BNetworkDevice::GetNextNetwork(uint32& cookie, wireless_network& network)
669 status_t status = get_scan_result(Name(), network, cookie, NULL, NULL);
670 if (status != B_OK)
671 return status;
673 cookie++;
674 return B_OK;
678 status_t
679 BNetworkDevice::GetNetwork(const char* name, wireless_network& network)
681 if (name == NULL || name[0] == '\0')
682 return B_BAD_VALUE;
684 return get_network(Name(), network, UINT32_MAX, NULL, name);
688 status_t
689 BNetworkDevice::GetNetwork(const BNetworkAddress& address,
690 wireless_network& network)
692 if (address.Family() != AF_LINK)
693 return B_BAD_VALUE;
695 return get_network(Name(), network, UINT32_MAX, &address, NULL);
699 status_t
700 BNetworkDevice::JoinNetwork(const char* name, const char* password)
702 if (name == NULL || name[0] == '\0')
703 return B_BAD_VALUE;
705 BMessage message(kMsgJoinNetwork);
706 status_t status = message.AddString("device", Name());
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;
715 // Send message to the net_server
717 BMessenger networkServer(kNetServerSignature);
718 BMessage reply;
719 status = networkServer.SendMessage(&message, &reply);
720 if (status == B_OK)
721 reply.FindInt32("status", &status);
723 return status;
727 status_t
728 BNetworkDevice::JoinNetwork(const wireless_network& network,
729 const char* password)
731 return JoinNetwork(network.address, password);
735 status_t
736 BNetworkDevice::JoinNetwork(const BNetworkAddress& address,
737 const char* password)
739 if (address.InitCheck() != B_OK)
740 return B_BAD_VALUE;
742 BMessage message(kMsgJoinNetwork);
743 status_t status = message.AddString("device", Name());
745 if (status == B_OK) {
746 status = message.AddFlat("address",
747 const_cast<BNetworkAddress*>(&address));
749 if (status == B_OK && password != NULL)
750 status = message.AddString("password", password);
751 if (status != B_OK)
752 return status;
754 // Send message to the net_server
756 BMessenger networkServer(kNetServerSignature);
757 BMessage reply;
758 status = networkServer.SendMessage(&message, &reply);
759 if (status == B_OK)
760 reply.FindInt32("status", &status);
762 return status;
766 status_t
767 BNetworkDevice::LeaveNetwork(const char* name)
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;
778 BMessenger networkServer(kNetServerSignature);
779 BMessage reply;
780 status = networkServer.SendMessage(&message, &reply);
781 if (status == B_OK)
782 reply.FindInt32("status", &status);
784 return status;
788 status_t
789 BNetworkDevice::LeaveNetwork(const wireless_network& network)
791 return LeaveNetwork(network.address);
795 status_t
796 BNetworkDevice::LeaveNetwork(const BNetworkAddress& address)
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));
804 if (status == B_OK)
805 status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED);
806 if (status != B_OK)
807 return status;
809 BMessenger networkServer(kNetServerSignature);
810 BMessage reply;
811 status = networkServer.SendMessage(&message, &reply);
812 if (status == B_OK)
813 reply.FindInt32("status", &status);
815 return status;
819 status_t
820 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie,
821 wireless_network& network)
823 BNetworkAddress address;
824 status_t status = GetNextAssociatedNetwork(cookie, address);
825 if (status != B_OK)
826 return status;
828 return GetNetwork(address, network);
832 status_t
833 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie,
834 BNetworkAddress& address)
836 // We currently support only a single associated network
837 if (cookie != 0)
838 return B_ENTRY_NOT_FOUND;
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;
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;
851 address.SetToLinkLevel(mac, IEEE80211_ADDR_LEN);
852 cookie++;
853 return B_OK;