From 440d0e61e812dd49268aa75e9fee369e4df4db0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 8 Dec 2010 22:55:57 +0000 Subject: [PATCH] * Pretty much completed BNetworkDevice. The only parts missing are GetMediaAt(), and parsing the extra station data to retrieve the authentication details. Comments welcome. * NetworkStatus should now mark the currently associated network (if any). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39774 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/net/NetworkDevice.h | 60 ++- src/apps/networkstatus/NetworkStatusView.cpp | 22 +- src/kits/network/libnetapi/NetworkDevice.cpp | 454 ++++++++++++++----- 3 files changed, 409 insertions(+), 127 deletions(-) diff --git a/headers/os/net/NetworkDevice.h b/headers/os/net/NetworkDevice.h index 5218a54802..b630d4b33f 100644 --- a/headers/os/net/NetworkDevice.h +++ b/headers/os/net/NetworkDevice.h @@ -19,8 +19,43 @@ struct wireless_network { BNetworkAddress address; uint8 noise_level; uint8 signal_strength; + uint32 flags; + uint32 authentication_mode; + uint32 cipher; + uint32 key_mode; }; +// flags +#define B_NETWORK_IS_ENCRYPTED 0x01 + +// authentication modes +enum { + B_NETWORK_AUTHENTICATION_NONE = 0, + B_NETWORK_AUTHENTICATION_WEP = 1, + B_NETWORK_AUTHENTICATION_WPA = 2, + B_NETWORK_AUTHENTICATION_WPA2 = 3 +}; + +// cipher algorithms +enum { + B_NETWORK_CIPHER_NONE = 0x01, + B_NETWORK_CIPHER_WEP_40 = 0x02, + B_NETWORK_CIPHER_WEP_104 = 0x04, + B_NETWORK_CIPHER_WEP_TKIP = 0x08, + B_NETWORK_CIPHER_WEP_CCMP = 0x10, + B_NETWORK_CIPHER_WEP_AES_128_CMAC = 0x20 +}; + +// key modes +enum { + B_KEY_MODE_IEEE802_1X = 0x0001, + B_KEY_MODE_PSK = 0x0002, + B_KEY_MODE_IEEE802_1X_SHA256 = 0x0080, + B_KEY_MODE_PSK_SHA256 = 0x0100, + B_KEY_MODE_WPS = 0x0200 +}; + + class BNetworkDevice { public: BNetworkDevice(); @@ -31,6 +66,7 @@ public: void SetTo(const char* name); const char* Name() const; + bool Exists() const; uint32 Index() const; uint32 Flags() const; @@ -49,17 +85,29 @@ public: status_t Scan(bool wait = true, bool forceRescan = true); - ssize_t CountScanResults(); - status_t GetScanResultAt(int32 index, + + status_t GetNextNetwork(uint32& cookie, + wireless_network& network); + status_t GetNetwork(const char* name, + wireless_network& network); + status_t GetNetwork(const BNetworkAddress& address, wireless_network& network); - status_t JoinNetwork(wireless_network& network, + status_t JoinNetwork(const char* name, + const char* password = NULL); + status_t JoinNetwork(const wireless_network& network, const char* password = NULL); status_t JoinNetwork(const BNetworkAddress& address, const char* password = NULL); - status_t LeaveNetwork(); - status_t GetCurrentNetwork(wireless_network& network); - status_t GetCurrentNetwork(BNetworkAddress& address); + + status_t LeaveNetwork(const char* name); + status_t LeaveNetwork(const wireless_network& network); + status_t LeaveNetwork(const BNetworkAddress& address); + + status_t GetNextAssociatedNetwork(uint32& cookie, + wireless_network& network); + status_t GetNextAssociatedNetwork(uint32& cookie, + BNetworkAddress& address); private: char fName[IF_NAMESIZE]; diff --git a/src/apps/networkstatus/NetworkStatusView.cpp b/src/apps/networkstatus/NetworkStatusView.cpp index 1b5c0faa43..6f37dc7459 100644 --- a/src/apps/networkstatus/NetworkStatusView.cpp +++ b/src/apps/networkstatus/NetworkStatusView.cpp @@ -11,6 +11,8 @@ #include "NetworkStatusView.h" +#include + #include #include #include @@ -503,13 +505,23 @@ NetworkStatusView::MouseDown(BPoint point) if (!device.IsWireless()) continue; + std::set associated; + BNetworkAddress address; + uint32 cookie = 0; + while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) + associated.insert(address); + wireless_network network; int32 count = 0; - for (int32 i = 0; device.GetScanResultAt(i, network) == B_OK; i++) { - printf("%s: noise %u : rssi %u\n", network.name, - network.noise_level, network.signal_strength); - menu->AddItem(new WirelessNetworkMenuItem(network.name, - network.signal_strength, false, NULL)); + cookie = 0; + while (device.GetNextNetwork(cookie, network) == B_OK) { + BMenuItem* item = new WirelessNetworkMenuItem(network.name, + network.signal_strength, + (network.flags & B_NETWORK_IS_ENCRYPTED) != 0, NULL); + menu->AddItem(item); + if (associated.find(network.address) != associated.end()) + item->SetMarked(true); + count++; } if (count == 0) { diff --git a/src/kits/network/libnetapi/NetworkDevice.cpp b/src/kits/network/libnetapi/NetworkDevice.cpp index 00e0a9cdad..c2a786ad94 100644 --- a/src/kits/network/libnetapi/NetworkDevice.cpp +++ b/src/kits/network/libnetapi/NetworkDevice.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include @@ -23,6 +25,13 @@ extern "C" { namespace { +struct ie_data { + uint8 type; + uint8 length; + uint8 data[1]; +}; + + static status_t get_80211(const char* name, int32 type, void* data, int32& length) { @@ -102,17 +111,11 @@ do_request(T& request, const char* name, int option) } +//! Parse information elements. static void -fill_wireless_network(wireless_network& network, - struct ieee80211req_sta_req& request) +parse_ie(wireless_network& network, uint8* _ie, int32 ieLength) { - // Parse IE data - int32 ieLength = request.info[0].isi_ie_len; - struct ie_data { - uint8 type; - uint8 length; - uint8 data[1]; - } *ie = (ie_data*)((uint8*)&request.info[0] + request.info[0].isi_ie_off); + struct ie_data* ie = (ie_data*)_ie; while (ieLength > 1) { switch (ie->type) { @@ -120,16 +123,206 @@ fill_wireless_network(wireless_network& network, strlcpy(network.name, (char*)ie->data, min_c(ie->length + 1, (int)sizeof(network.name))); break; + case IEEE80211_ELEMID_RSN: + // TODO: we might need to parse those in order to find out + // the authentication mode (WPA info in vendor?) + break; } ieLength -= 2 + ie->length; ie = (ie_data*)((uint8*)ie + 2 + ie->length); } +} - network.address.SetToLinkLevel(request.info[0].isi_macaddr, + +static void +parse_ie(wireless_network& network, struct ieee80211req_sta_info& info) +{ + parse_ie(network, (uint8*)&info + info.isi_ie_off, info.isi_ie_len); +} + + +static void +parse_ie(wireless_network& network, struct ieee80211req_scan_result& result) +{ + parse_ie(network, (uint8*)&result + result.isr_ie_off + result.isr_ssid_len + + result.isr_meshid_len, result.isr_ie_len); +} + + +static bool +get_ssid_from_ie(char* name, uint8* _ie, int32 ieLength) +{ + struct ie_data* ie = (ie_data*)_ie; + + while (ieLength > 1) { + switch (ie->type) { + case IEEE80211_ELEMID_SSID: + strlcpy(name, (char*)ie->data, min_c(ie->length + 1, 32)); + return true; + } + + ieLength -= 2 + ie->length; + ie = (ie_data*)((uint8*)ie + 2 + ie->length); + } + return false; +} + + +static bool +get_ssid_from_ie(char* name, struct ieee80211req_sta_info& info) +{ + return get_ssid_from_ie(name, (uint8*)&info + info.isi_ie_off, + info.isi_ie_len); +} + + +static void +fill_wireless_network(wireless_network& network, + struct ieee80211req_sta_info& info) +{ + network.name[0] = '\0'; + network.address.SetToLinkLevel(info.isi_macaddr, IEEE80211_ADDR_LEN); - network.signal_strength = request.info[0].isi_rssi; - network.noise_level = request.info[0].isi_noise; + network.signal_strength = info.isi_rssi; + network.noise_level = info.isi_noise; + network.flags |= (info.isi_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0 + ? B_NETWORK_IS_ENCRYPTED : 0; + network.authentication_mode = 0; + // TODO: build from IE if possible + + parse_ie(network, info); +} + + +static void +fill_wireless_network(wireless_network& network, const char* networkName, + struct ieee80211req_scan_result& result) +{ + strlcpy(network.name, networkName, sizeof(network.name)); + network.address.SetToLinkLevel(result.isr_bssid, + IEEE80211_ADDR_LEN); + network.signal_strength = result.isr_rssi; + network.noise_level = result.isr_noise; + network.flags = (result.isr_capinfo & IEEE80211_CAPINFO_PRIVACY) + != 0 ? B_NETWORK_IS_ENCRYPTED : 0; + network.authentication_mode = 0; + // TODO: build from IE if possible + + parse_ie(network, result); +} + + +static status_t +get_scan_result(const char* device, wireless_network& network, uint32 index, + const BNetworkAddress* address, const char* name) +{ + if (address != NULL && address->Family() != AF_LINK) + return B_BAD_VALUE; + + const size_t kBufferSize = 65535; + uint8* buffer = (uint8*)malloc(kBufferSize); + if (buffer == NULL) + return B_NO_MEMORY; + + MemoryDeleter deleter(buffer); + + int32 length = kBufferSize; + status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer, + length); + if (status != B_OK) + return status; + + int32 bytesLeft = length; + uint8* entry = buffer; + uint32 count = 0; + + while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { + ieee80211req_scan_result* result + = (ieee80211req_scan_result*)entry; + + char networkName[32]; + strlcpy(networkName, (char*)(result + 1), + min_c((int)sizeof(networkName), result->isr_ssid_len + 1)); + + if (index == count || (address != NULL && !memcmp( + address->LinkLevelAddress(), result->isr_bssid, + IEEE80211_ADDR_LEN)) + || (name != NULL && !strcmp(networkName, name))) { + // Fill wireless_network with scan result data + fill_wireless_network(network, networkName, *result); + return B_OK; + } + + entry += result->isr_len; + bytesLeft -= result->isr_len; + count++; + } + + return B_ENTRY_NOT_FOUND; +} + + +static status_t +get_station(const char* device, wireless_network& network, uint32 index, + const BNetworkAddress* address, const char* name) +{ + if (address != NULL && address->Family() != AF_LINK) + return B_BAD_VALUE; + + const size_t kBufferSize = 65535; + uint8* buffer = (uint8*)malloc(kBufferSize); + if (buffer == NULL) + return B_NO_MEMORY; + + MemoryDeleter deleter(buffer); + + struct ieee80211req_sta_req& request = *(ieee80211req_sta_req*)buffer; + if (address != NULL) { + memcpy(request.is_u.macaddr, address->LinkLevelAddress(), + IEEE80211_ADDR_LEN); + } else + memset(request.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); + + int32 length = kBufferSize; + status_t status = get_80211(device, IEEE80211_IOC_STA_INFO, &request, + length); + if (status != B_OK) + return status; + + int32 bytesLeft = length; + uint8* entry = (uint8*)&request.info[0]; + uint32 count = 0; + + while (bytesLeft > (int32)sizeof(struct ieee80211req_sta_info)) { + ieee80211req_sta_info* info = (ieee80211req_sta_info*)entry; + + char networkName[32]; + get_ssid_from_ie(networkName, *info); + if (index == count || address != NULL + || (name != NULL && !strcmp(networkName, name))) { + fill_wireless_network(network, *info); + return B_OK; + } + + entry += info->isi_len; + bytesLeft -= info->isi_len; + count++; + } + + return B_ENTRY_NOT_FOUND; +} + + +static status_t +get_network(const char* device, wireless_network& network, uint32 index, + const BNetworkAddress* address, const char* name) +{ + status_t status = get_station(device, network, index, address, name); + if (status != B_OK) + return get_scan_result(device, network, index, address, name); + + return B_OK; } @@ -177,6 +370,14 @@ BNetworkDevice::Name() const } +bool +BNetworkDevice::Exists() const +{ + ifreq request; + return do_request(request, Name(), SIOCGIFINDEX) == B_OK; +} + + uint32 BNetworkDevice::Index() const { @@ -267,54 +468,19 @@ BNetworkDevice::GetHardwareAddress(BNetworkAddress& address) bool BNetworkDevice::IsEthernet() { - return !IsWireless(); + return IFM_TYPE(Media()) == IFM_ETHER; } bool BNetworkDevice::IsWireless() { - // TODO: fix me! - return strstr(Name(), "wifi") != NULL; -} - - -ssize_t -BNetworkDevice::CountScanResults() -{ - // TODO: this needs some caching! - const size_t kBufferSize = 32768; - uint8* buffer = (uint8*)malloc(kBufferSize); - if (buffer == NULL) - return B_NO_MEMORY; - - MemoryDeleter deleter(buffer); - - int32 length = kBufferSize; - status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS, - buffer, length); - if (status < B_OK) - return status; - - int32 bytesLeft = length; - uint8* entry = buffer; - int32 count = 0; - - while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { - ieee80211req_scan_result* result - = (ieee80211req_scan_result*)entry; - - entry += result->isr_len; - bytesLeft -= result->isr_len; - count++; - } - - return count; + return IFM_TYPE(Media()) == IFM_IEEE80211; } status_t -BNetworkDevice::GetScanResultAt(int32 index, wireless_network& network) +BNetworkDevice::Scan(bool wait, bool forceRescan) { #if 0 if (index == 0) { @@ -326,52 +492,74 @@ BNetworkDevice::GetScanResultAt(int32 index, wireless_network& network) set_80211(Name(), IEEE80211_IOC_SCAN_REQ, NULL); } #endif - - const size_t kBufferSize = 32768; - uint8* buffer = (uint8*)malloc(kBufferSize); - if (buffer == NULL) - return B_NO_MEMORY; - - MemoryDeleter deleter(buffer); - - int32 length = kBufferSize; - status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS, - buffer, length); - if (status != B_OK) - return status; - - int32 bytesLeft = length; - uint8* entry = buffer; - int32 count = 0; - - while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { - ieee80211req_scan_result* result - = (ieee80211req_scan_result*)entry; - - if (count == index) { - // Fill wireless_network with scan result data - // TODO: add more fields - strlcpy(network.name, (char*)(result + 1), - min_c((int)sizeof(network.name), result->isr_ssid_len + 1)); - network.address.SetToLinkLevel(result->isr_bssid, - IEEE80211_ADDR_LEN); - network.signal_strength = result->isr_rssi; - network.noise_level = result->isr_noise; - - return B_OK; - } - - entry += result->isr_len; - bytesLeft -= result->isr_len; - count++; - } - - return B_BAD_INDEX; + return B_ERROR; } status_t -BNetworkDevice::JoinNetwork(wireless_network& network, const char* password) +BNetworkDevice::GetNextNetwork(uint32& cookie, wireless_network& network) +{ + status_t status = get_scan_result(Name(), network, cookie, NULL, NULL); + if (status != B_OK) + return status; + + cookie++; + return B_OK; +} + + +status_t +BNetworkDevice::GetNetwork(const char* name, wireless_network& network) +{ + if (name == NULL || name[0] == '\0') + return B_BAD_VALUE; + + return get_network(Name(), network, UINT32_MAX, NULL, name); +} + + +status_t +BNetworkDevice::GetNetwork(const BNetworkAddress& address, + wireless_network& network) +{ + if (address.Family() != AF_LINK) + return B_BAD_VALUE; + + return get_network(Name(), network, UINT32_MAX, &address, NULL); +} + + +status_t +BNetworkDevice::JoinNetwork(const char* name, const char* password) +{ + if (name == NULL || name[0] == '\0') + return B_BAD_VALUE; + + BMessage message(kMsgJoinNetwork); + status_t status = message.AddString("device", Name()); + + if (status == B_OK) + status = message.AddString("name", name); + if (status == B_OK && password != NULL) + status = message.AddString("password", password); + if (status != B_OK) + return status; + + // Send message to the net_server + + BMessenger networkServer(kNetServerSignature); + BMessage reply; + status = networkServer.SendMessage(&message, &reply); + if (status == B_OK) + reply.FindInt32("status", &status); + + return status; +} + + +status_t +BNetworkDevice::JoinNetwork(const wireless_network& network, + const char* password) { return JoinNetwork(network.address, password); } @@ -409,10 +597,14 @@ BNetworkDevice::JoinNetwork(const BNetworkAddress& address, status_t -BNetworkDevice::LeaveNetwork() +BNetworkDevice::LeaveNetwork(const char* name) { BMessage message(kMsgLeaveNetwork); status_t status = message.AddString("device", Name()); + if (status == B_OK) + status = message.AddString("name", name); + if (status == B_OK) + status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED); if (status != B_OK) return status; @@ -427,38 +619,68 @@ BNetworkDevice::LeaveNetwork() status_t -BNetworkDevice::GetCurrentNetwork(wireless_network& network) +BNetworkDevice::LeaveNetwork(const wireless_network& network) { - BNetworkAddress address; - status_t status = GetCurrentNetwork(address); - if (status != B_OK) - return status; - - uint8 buffer[10240]; - struct ieee80211req_sta_req& request = *(ieee80211req_sta_req*)buffer; - memcpy(request.is_u.macaddr, address.LinkLevelAddress(), - IEEE80211_ADDR_LEN); - - int32 length = sizeof(buffer); - status = get_80211(Name(), IEEE80211_IOC_STA_INFO, &request, length); - if (status != B_OK) - return status; - - fill_wireless_network(network, request); - return B_OK; + return LeaveNetwork(network.address); } status_t -BNetworkDevice::GetCurrentNetwork(BNetworkAddress& address) +BNetworkDevice::LeaveNetwork(const BNetworkAddress& address) { - uint8 mac[IEEE80211_ADDR_LEN]; - int32 length = IEEE80211_ADDR_LEN; - status_t status = get_80211(Name(), IEEE80211_IOC_BSSID, - mac, length); + BMessage message(kMsgLeaveNetwork); + status_t status = message.AddString("device", Name()); + if (status == B_OK) { + status = message.AddFlat("address", + const_cast(&address)); + } + if (status == B_OK) + status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED); if (status != B_OK) return status; + BMessenger networkServer(kNetServerSignature); + BMessage reply; + status = networkServer.SendMessage(&message, &reply); + if (status == B_OK) + reply.FindInt32("status", &status); + + return status; +} + + +status_t +BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie, + wireless_network& network) +{ + BNetworkAddress address; + status_t status = GetNextAssociatedNetwork(cookie, address); + if (status != B_OK) + return status; + + return GetNetwork(address, network); +} + + +status_t +BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie, + BNetworkAddress& address) +{ + // We currently support only a single associated network + if (cookie != 0) + return B_ENTRY_NOT_FOUND; + + uint8 mac[IEEE80211_ADDR_LEN]; + int32 length = IEEE80211_ADDR_LEN; + status_t status = get_80211(Name(), IEEE80211_IOC_BSSID, mac, length); + if (status != B_OK) + return status; + + if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 + && mac[5] == 0) + return B_ENTRY_NOT_FOUND; + address.SetToLinkLevel(mac, IEEE80211_ADDR_LEN); + cookie++; return B_OK; }