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 <sys/sockio.h> 12 13 #include <AutoDeleter.h> 14 15 extern "C" { 16 #include <net80211/ieee80211_ioctl.h> 17 } 18 19 20 status_t 21 get_80211(const char* name, int32 type, void* data, int32& length) 22 { 23 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 24 if (socket < 0) 25 return errno; 26 27 FileDescriptorCloser closer(socket); 28 29 struct ieee80211req ireq; 30 strlcpy(ireq.i_name, name, IF_NAMESIZE); 31 ireq.i_type = type; 32 ireq.i_val = 0; 33 ireq.i_len = length; 34 ireq.i_data = data; 35 36 if (ioctl(socket, SIOCG80211, &ireq, sizeof(struct ieee80211req)) < 0) 37 return errno; 38 39 length = ireq.i_len; 40 return B_OK; 41 } 42 43 #if 0 44 static status_t 45 set_80211(const char* name, int32 type, void* data, 46 int32 length = 0, int32 value = 0) 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 = value; 58 ireq.i_len = length; 59 ireq.i_data = data; 60 61 if (ioctl(socket, SIOCS80211, &ireq, sizeof(struct ieee80211req)) < 0) 62 return errno; 63 64 return B_OK; 65 } 66 67 68 template<typename T> status_t 69 set_80211(const char* name, int32 type, T& data, int32 length = 0, 70 int32 value = 0) 71 { 72 if (length == 0) 73 length = sizeof(T); 74 75 return set_80211(name, &data, length, value); 76 } 77 #endif 78 79 template<typename T> status_t 80 do_request(T& request, const char* name, int option) 81 { 82 int socket = ::socket(AF_LINK, SOCK_DGRAM, 0); 83 if (socket < 0) 84 return errno; 85 86 FileDescriptorCloser closer(socket); 87 88 strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE); 89 90 if (ioctl(socket, option, &request, sizeof(T)) < 0) 91 return errno; 92 93 return B_OK; 94 } 95 96 97 // #pragma mark - 98 99 100 BNetworkDevice::BNetworkDevice() 101 { 102 Unset(); 103 } 104 105 106 BNetworkDevice::BNetworkDevice(const char* name) 107 { 108 SetTo(name); 109 } 110 111 112 BNetworkDevice::~BNetworkDevice() 113 { 114 } 115 116 117 void 118 BNetworkDevice::Unset() 119 { 120 fName[0] = '\0'; 121 } 122 123 124 void 125 BNetworkDevice::SetTo(const char* name) 126 { 127 strlcpy(fName, name, IF_NAMESIZE); 128 } 129 130 131 const char* 132 BNetworkDevice::Name() const 133 { 134 return fName; 135 } 136 137 138 uint32 139 BNetworkDevice::Flags() const 140 { 141 ifreq request; 142 if (do_request(request, Name(), SIOCGIFFLAGS) != B_OK) 143 return 0; 144 145 return request.ifr_flags; 146 } 147 148 149 bool 150 BNetworkDevice::HasLink() const 151 { 152 return (Flags() & IFF_LINK) != 0; 153 } 154 155 156 int32 157 BNetworkDevice::CountMedia() const 158 { 159 ifmediareq request; 160 request.ifm_count = 0; 161 request.ifm_ulist = NULL; 162 163 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK) 164 return -1; 165 166 return request.ifm_count; 167 } 168 169 170 int32 171 BNetworkDevice::Media() const 172 { 173 ifmediareq request; 174 request.ifm_count = 0; 175 request.ifm_ulist = NULL; 176 177 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK) 178 return -1; 179 180 return request.ifm_current; 181 } 182 183 184 int32 185 BNetworkDevice::GetMediaAt(int32 index) const 186 { 187 // TODO: this could do some caching 188 return 0; 189 } 190 191 192 status_t 193 BNetworkDevice::SetMedia(int32 media) 194 { 195 ifreq request; 196 request.ifr_media = media; 197 return do_request(request, Name(), SIOCSIFMEDIA); 198 } 199 200 201 status_t 202 BNetworkDevice::GetHardwareAddress(BNetworkAddress& address) 203 { 204 ifreq request; 205 status_t status = do_request(request, Name(), SIOCSIFMEDIA); 206 if (status != B_OK) 207 return status; 208 209 address.SetTo(request.ifr_addr); 210 return B_OK; 211 } 212 213 214 ssize_t 215 BNetworkDevice::CountScanResults() 216 { 217 // TODO: this needs some caching! 218 const size_t kBufferSize = 32768; 219 uint8* buffer = (uint8*)malloc(kBufferSize); 220 if (buffer == NULL) 221 return B_NO_MEMORY; 222 223 MemoryDeleter deleter(buffer); 224 225 int32 length = kBufferSize; 226 status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS, 227 buffer, length); 228 if (status < B_OK) 229 return status; 230 231 int32 bytesLeft = length; 232 uint8* entry = buffer; 233 int32 count = 0; 234 235 while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { 236 ieee80211req_scan_result* result 237 = (ieee80211req_scan_result*)entry; 238 239 entry += result->isr_len; 240 bytesLeft -= result->isr_len; 241 count++; 242 } 243 244 return count; 245 } 246 247 248 status_t 249 BNetworkDevice::GetScanResultAt(int32 index, wireless_network& network) 250 { 251 #if 0 252 if (index == 0) { 253 struct ieee80211_scan_req request; 254 memset(&request, 0, sizeof(request)); 255 request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE 256 | IEEE80211_IOC_SCAN_NOJOIN; 257 request.sr_duration = IEEE80211_IOC_SCAN_FOREVER; 258 set_80211(Name(), IEEE80211_IOC_SCAN_REQ, NULL); 259 } 260 #endif 261 262 const size_t kBufferSize = 32768; 263 uint8* buffer = (uint8*)malloc(kBufferSize); 264 if (buffer == NULL) 265 return B_NO_MEMORY; 266 267 MemoryDeleter deleter(buffer); 268 269 int32 length = kBufferSize; 270 status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS, 271 buffer, length); 272 if (status != B_OK) 273 return status; 274 275 int32 bytesLeft = length; 276 uint8* entry = buffer; 277 int32 count = 0; 278 279 while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { 280 ieee80211req_scan_result* result 281 = (ieee80211req_scan_result*)entry; 282 283 if (count == index) { 284 // Fill wireless_network with scan result data 285 // TODO: add more fields 286 strlcpy(network.name, (char*)(result + 1), 287 min_c((int)sizeof(network.name), result->isr_ssid_len + 1)); 288 network.address.SetToLinkLevel(result->isr_bssid, 289 IEEE80211_ADDR_LEN); 290 network.signal_strength = result->isr_rssi; 291 network.noise_level = result->isr_noise; 292 293 return B_OK; 294 } 295 296 entry += result->isr_len; 297 bytesLeft -= result->isr_len; 298 count++; 299 } 300 301 return B_BAD_INDEX; 302 } 303