1 /* 2 * Copyright 2022, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <AutoDeleter.h> 7 #include <util/KMessage.h> 8 9 extern "C" { 10 #include <sys/param.h> 11 #include <sys/systm.h> 12 #include <sys/mbuf.h> 13 #include <sys/kernel.h> 14 #include <sys/bus.h> 15 #include <sys/socket.h> 16 #include <sys/sockio.h> 17 #include <sys/endian.h> 18 #include <sys/errno.h> 19 #include <sys/task.h> 20 21 #include <net/if.h> 22 #include <net/if_dl.h> 23 #include <net/if_media.h> 24 25 #include <netinet/in.h> 26 #include <netinet/if_ether.h> 27 28 #include <net80211/ieee80211_var.h> 29 #include <net80211/ieee80211_node.h> 30 #include <net80211/ieee80211_ioctl.h> 31 32 #undef _NET80211_IEEE80211_IOCTL_H_ 33 #define IEEE80211_IOCTLS_ABBREVIATED 34 #include "../../freebsd_wlan/net80211/ieee80211_ioctl.h" 35 } 36 37 #include <shared.h> 38 39 #include <ether_driver.h> 40 #include <NetworkDevice.h> 41 #include <net_notifications.h> 42 43 44 #define TRACE_WLAN 45 #ifdef TRACE_WLAN 46 # define TRACE(x...) dprintf(x); 47 #else 48 # define TRACE(x...) ; 49 #endif 50 51 52 static net_notifications_module_info* sNotificationModule; 53 54 55 static struct ifnet* 56 get_ifnet(device_t device, int& i) 57 { 58 void* softc = device_get_softc(device); 59 60 for (i = 0; i < MAX_DEVICES; i++) { 61 if (gDevices[i] != NULL && gDevices[i]->if_softc == softc) 62 return gDevices[i]; 63 } 64 65 return NULL; 66 } 67 68 69 status_t 70 init_wlan_stack(void) 71 { 72 get_module(NET_NOTIFICATIONS_MODULE_NAME, 73 (module_info**)&sNotificationModule); 74 75 return B_OK; 76 } 77 78 79 void 80 uninit_wlan_stack(void) 81 { 82 if (sNotificationModule != NULL) 83 put_module(NET_NOTIFICATIONS_MODULE_NAME); 84 } 85 86 87 static void 88 ieee80211_init() 89 { 90 } 91 92 93 status_t 94 start_wlan(device_t device) 95 { 96 int i; 97 struct ifnet* ifp = get_ifnet(device, i); 98 if (ifp == NULL) 99 return B_BAD_VALUE; 100 101 struct ieee80211com* ic = (ieee80211com*)ifp; 102 103 if (ifp->if_init == NULL) 104 ifp->if_init = ieee80211_init; 105 ifp->if_flags |= IFF_NEEDSGIANT; 106 107 if_initname(ifp, device_get_name(device), i); 108 109 TRACE("%s: wlan started.\n", __func__); 110 return B_OK; 111 } 112 113 114 status_t 115 stop_wlan(device_t device) 116 { 117 int i; 118 struct ifnet* ifp = get_ifnet(device, i); 119 if (ifp == NULL) 120 return B_BAD_VALUE; 121 122 struct ieee80211com* ic = (ieee80211com*)ifp; 123 124 return B_OK; 125 } 126 127 128 status_t 129 wlan_close(void* cookie) 130 { 131 TRACE("wlan_close(%p)\n", cookie); 132 struct ifnet* ifp = (struct ifnet*)cookie; 133 134 ifp->if_flags &= ~IFF_UP; 135 ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL); 136 137 return B_OK; 138 } 139 140 141 static uint8_t 142 fbsd_capinfo_from_obsd(uint16_t obsd_capinfo) 143 { 144 // FreeBSD only exposes the first 8 bits of the capinfo, 145 // and these are identical in OpenBSD. Makes things easy. 146 return uint8_t(obsd_capinfo); 147 } 148 149 150 static uint32 151 obsd_ciphers_from_haiku(uint32 ciphers) 152 { 153 uint32 obsd_ciphers = 0; 154 if ((ciphers & B_NETWORK_CIPHER_WEP_40) != 0) 155 obsd_ciphers |= IEEE80211_WPA_CIPHER_WEP40; 156 if ((ciphers & B_NETWORK_CIPHER_WEP_104) != 0) 157 obsd_ciphers |= IEEE80211_WPA_CIPHER_WEP104; 158 if ((ciphers & B_NETWORK_CIPHER_TKIP) != 0) 159 obsd_ciphers |= IEEE80211_WPA_CIPHER_TKIP; 160 if ((ciphers & B_NETWORK_CIPHER_CCMP) != 0) 161 obsd_ciphers |= IEEE80211_WPA_CIPHER_CCMP; 162 return obsd_ciphers; 163 } 164 165 166 status_t 167 wlan_control(void* cookie, uint32 op, void* arg, size_t length) 168 { 169 if (op != SIOCG80211 && op != SIOCS80211) 170 return B_BAD_VALUE; 171 172 struct ifnet* ifp = (struct ifnet*)cookie; 173 struct ieee80211com* ic = (ieee80211com*)ifp; 174 175 struct ieee80211req ireq; 176 if (user_memcpy(&ireq, arg, sizeof(struct ieee80211req)) != B_OK) 177 return B_BAD_ADDRESS; 178 179 switch (ireq.i_type) { 180 case IEEE80211_IOC_SCAN_RESULTS: { 181 if (op != SIOCG80211) 182 return B_BAD_VALUE; 183 184 const int32 count = ireq.i_len / sizeof(struct ieee80211req_scan_result); 185 // ieee80211req_scan_result is variable-length, this will get us the max count 186 187 void* req_scan_result = calloc(1, ireq.i_len); 188 MemoryDeleter resultDeleter(req_scan_result); 189 if (!resultDeleter.IsSet()) 190 return B_NO_MEMORY; 191 192 struct ieee80211_nodereq nodereq; 193 struct ieee80211_nodereq_all nodereq_all = {}; 194 195 uint8* out = (uint8*)req_scan_result; 196 uint16 remaining = ireq.i_len; 197 for (int i = 0; ; i++) { 198 nodereq_all.na_node = &nodereq; 199 nodereq_all.na_size = sizeof(struct ieee80211_nodereq); 200 nodereq_all.na_startnode = i; 201 202 IFF_LOCKGIANT(ifp); 203 status_t status = ifp->if_ioctl(ifp, SIOCG80211ALLNODES, (caddr_t)&nodereq_all); 204 IFF_UNLOCKGIANT(ifp); 205 if (status != B_OK) 206 return status; 207 if (nodereq_all.na_nodes == 0) 208 break; 209 210 int32 required = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len; 211 uint16_t ieLen = 0; 212 if (nodereq.nr_rsnie[1] != 0) { 213 ieLen = 2 + nodereq.nr_rsnie[1]; 214 required += ieLen; 215 } 216 if (remaining < (sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len)) 217 break; 218 219 struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)out; 220 sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); 221 sr->isr_ie_len = ieLen; 222 sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags); 223 sr->isr_flags = 0; /* not actually used by userland */ 224 sr->isr_noise = 0; /* unknown */ 225 sr->isr_rssi = nodereq.nr_rssi; 226 sr->isr_intval = nodereq.nr_intval; 227 sr->isr_capinfo = fbsd_capinfo_from_obsd(nodereq.nr_capinfo); 228 sr->isr_erp = nodereq.nr_erp; 229 memcpy(sr->isr_bssid, nodereq.nr_bssid, IEEE80211_ADDR_LEN); 230 sr->isr_nrates = nodereq.nr_nrates; 231 memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE); 232 sr->isr_ssid_len = nodereq.nr_nwid_len; 233 sr->isr_meshid_len = 0; 234 memcpy(out + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len); 235 memcpy(out + sr->isr_ie_off + sr->isr_ssid_len, 236 nodereq.nr_rsnie, ieLen); 237 238 sr->isr_len = roundup(sr->isr_ie_off + sr->isr_ssid_len + sr->isr_ie_len, 4); 239 out += sr->isr_len; 240 remaining -= sr->isr_len; 241 } 242 ireq.i_len = (out - (uint8*)req_scan_result); 243 244 IFF_LOCKGIANT(ifp); 245 const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */; 246 if (ic->ic_opmode == IEEE80211_M_STA 247 && system_time() >= (ic->ic_last_raise_inact + RAISE_INACT_INTERVAL)) { 248 // net80211 only raises and checks inactivity during active scans, or background 249 // scans performed in S_RUN, so we need to do it here so that stale nodes are 250 // evicted for S_SCAN, too. (This means "inact" will be raised a bit more often 251 // and nodes evicted faster during other modes, but that's acceptable.) 252 ieee80211_iterate_nodes(ic, ieee80211_node_raise_inact, NULL); 253 ieee80211_clean_inactive_nodes(ic, IEEE80211_INACT_SCAN); 254 ic->ic_last_raise_inact = system_time(); 255 } 256 IFF_UNLOCKGIANT(ifp); 257 258 if (user_memcpy(ireq.i_data, req_scan_result, ireq.i_len) != B_OK) 259 return B_BAD_ADDRESS; 260 261 break; 262 } 263 264 case IEEE80211_IOC_BSSID: { 265 if (ireq.i_len != IEEE80211_ADDR_LEN) 266 return EINVAL; 267 268 struct ieee80211_bssid bssid; 269 if (op == SIOCS80211) { 270 if (user_memcpy(bssid.i_bssid, ireq.i_data, ireq.i_len) != B_OK) 271 return B_BAD_ADDRESS; 272 } 273 274 IFF_LOCKGIANT(ifp); 275 status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ? 276 SIOCG80211BSSID : SIOCS80211BSSID, (caddr_t)&bssid); 277 IFF_UNLOCKGIANT(ifp); 278 if (status != B_OK) 279 return status; 280 281 if (op == SIOCG80211) { 282 if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK) 283 return B_BAD_ADDRESS; 284 } 285 286 break; 287 } 288 289 case IEEE80211_IOC_SSID: { 290 struct ifreq ifr; 291 struct ieee80211_nwid nwid; 292 ifr.ifr_data = (uint8_t*)&nwid; 293 294 if (op == SIOCS80211) { 295 nwid.i_len = ireq.i_len; 296 if (user_memcpy(nwid.i_nwid, ireq.i_data, ireq.i_len) != B_OK) 297 return B_BAD_ADDRESS; 298 } 299 300 IFF_LOCKGIANT(ifp); 301 status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ? 302 SIOCG80211NWID : SIOCS80211NWID, (caddr_t)&ifr); 303 ieee80211_state state = ic->ic_state; 304 IFF_UNLOCKGIANT(ifp); 305 if (status != B_OK) 306 return status; 307 308 if (op == SIOCG80211) { 309 if (state == IEEE80211_S_INIT || state == IEEE80211_S_SCAN) { 310 // The returned NWID will be the one we want to join, not connected to. 311 ireq.i_len = 0; 312 } else { 313 if (ireq.i_len < nwid.i_len) 314 return E2BIG; 315 ireq.i_len = nwid.i_len; 316 if (user_memcpy(ireq.i_data, nwid.i_nwid, ireq.i_len) != B_OK) 317 return B_BAD_ADDRESS; 318 } 319 } 320 321 break; 322 } 323 324 case IEEE80211_IOC_MLME: { 325 if (op != SIOCS80211) 326 return B_BAD_VALUE; 327 328 struct ieee80211req_mlme mlme; 329 if (user_memcpy(&mlme, ireq.i_data, min_c(sizeof(mlme), ireq.i_len)) != B_OK) 330 return B_BAD_ADDRESS; 331 332 switch (mlme.im_op) { 333 case IEEE80211_MLME_DISASSOC: 334 case IEEE80211_MLME_DEAUTH: { 335 struct ifreq ifr; 336 struct ieee80211_nwid nwid; 337 ifr.ifr_data = (uint8_t*)&nwid; 338 nwid.i_len = 0; 339 340 IFF_LOCKGIANT(ifp); 341 status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr); 342 IFF_UNLOCKGIANT(ifp); 343 if (status != 0) 344 return status; 345 break; 346 } 347 348 default: 349 TRACE("openbsd wlan_control: unsupported MLME operation %" B_PRIu8 "\n", 350 mlme.im_op); 351 return EOPNOTSUPP; 352 } 353 354 break; 355 } 356 357 case IEEE80211_IOC_HAIKU_JOIN: { 358 if (op != SIOCS80211) 359 return B_BAD_VALUE; 360 361 struct ieee80211_haiku_join_req* haiku_join = 362 (struct ieee80211_haiku_join_req*)calloc(1, ireq.i_len); 363 MemoryDeleter haikuJoinDeleter(haiku_join); 364 if (!haikuJoinDeleter.IsSet()) 365 return B_NO_MEMORY; 366 367 if (user_memcpy(haiku_join, ireq.i_data, ireq.i_len) != B_OK) 368 return B_BAD_ADDRESS; 369 370 struct ifreq ifr; 371 struct ieee80211_nwid nwid; 372 struct ieee80211_wpaparams wpaparams; 373 struct ieee80211_wpapsk wpapsk; 374 memset(&wpaparams, 0, sizeof(wpaparams)); 375 memset(&wpapsk, 0, sizeof(wpapsk)); 376 377 // Convert join information. 378 ifr.ifr_data = (uint8_t*)&nwid; 379 nwid.i_len = haiku_join->i_nwid_len; 380 memcpy(nwid.i_nwid, haiku_join->i_nwid, nwid.i_len); 381 382 switch (haiku_join->i_authentication_mode) { 383 case B_NETWORK_AUTHENTICATION_NONE: 384 break; 385 386 case B_NETWORK_AUTHENTICATION_WPA: 387 case B_NETWORK_AUTHENTICATION_WPA2: 388 wpaparams.i_enabled = 1; 389 wpaparams.i_protos |= 390 (haiku_join->i_authentication_mode == B_NETWORK_AUTHENTICATION_WPA2) ? 391 IEEE80211_WPA_PROTO_WPA2 : IEEE80211_WPA_PROTO_WPA1; 392 393 // NOTE: i_wpaparams.i_groupcipher is not a flags field! 394 wpaparams.i_ciphers = obsd_ciphers_from_haiku(haiku_join->i_ciphers); 395 wpaparams.i_groupcipher = 396 fls(obsd_ciphers_from_haiku(haiku_join->i_group_ciphers)); 397 398 if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X) != 0) 399 wpaparams.i_akms |= IEEE80211_WPA_AKM_8021X; 400 if ((haiku_join->i_key_mode & B_KEY_MODE_PSK) != 0) 401 wpaparams.i_akms |= IEEE80211_WPA_AKM_PSK; 402 if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X_SHA256) != 0) 403 wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_8021X; 404 if ((haiku_join->i_key_mode & B_KEY_MODE_PSK_SHA256) != 0) 405 wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_PSK; 406 407 if (haiku_join->i_key_len != 0) { 408 wpapsk.i_enabled = 1; 409 memcpy(wpapsk.i_psk, haiku_join->i_key, haiku_join->i_key_len); 410 memset(&wpapsk.i_psk[haiku_join->i_key_len], 0, 411 sizeof(wpapsk.i_psk) - haiku_join->i_key_len); 412 } 413 break; 414 415 default: 416 TRACE("openbsd wlan_control: unsupported authentication mode %" B_PRIu32 "\n", 417 haiku_join->i_authentication_mode); 418 return EOPNOTSUPP; 419 } 420 421 IFF_LOCKGIANT(ifp); 422 status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr); 423 if (status != B_OK) { 424 IFF_UNLOCKGIANT(ifp); 425 return status; 426 } 427 if (wpapsk.i_enabled) { 428 status = ifp->if_ioctl(ifp, SIOCS80211WPAPSK, (caddr_t)&wpapsk); 429 if (status != B_OK) { 430 IFF_UNLOCKGIANT(ifp); 431 return status; 432 } 433 } 434 if (wpaparams.i_enabled) { 435 status = ifp->if_ioctl(ifp, SIOCS80211WPAPARMS, (caddr_t)&wpaparams); 436 if (status != B_OK) { 437 IFF_UNLOCKGIANT(ifp); 438 return status; 439 } 440 } 441 IFF_UNLOCKGIANT(ifp); 442 if (status != B_OK) 443 return status; 444 445 break; 446 } 447 448 default: 449 TRACE("openbsd wlan_control: %" B_PRIu32 ", %d (not supported)\n", op, ireq.i_type); 450 return EOPNOTSUPP; 451 } 452 453 if (op == SIOCG80211 && user_memcpy(arg, &ireq, 454 sizeof(struct ieee80211req)) != B_OK) 455 return B_BAD_ADDRESS; 456 return B_OK; 457 } 458 459 460 void 461 ieee80211_rtm_80211info_task(void* arg) 462 { 463 struct ieee80211com *ic = arg; 464 struct ifnet *ifp = &ic->ic_if; 465 466 if (sNotificationModule != NULL) { 467 char messageBuffer[512]; 468 KMessage message; 469 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR); 470 if (ifp->if_link_state == LINK_STATE_UP) 471 message.AddInt32("opcode", B_NETWORK_WLAN_JOINED); 472 else 473 message.AddInt32("opcode", B_NETWORK_WLAN_LEFT); 474 message.AddString("interface", ifp->device_name); 475 // TODO: add data about the node 476 477 sNotificationModule->send_notification(&message); 478 } 479 } 480 481 482 #if 0 483 void 484 ieee80211_notify_scan_done(struct ieee80211vap* vap) 485 { 486 release_sem_etc(vap->iv_ifp->scan_done_sem, 1, 487 B_DO_NOT_RESCHEDULE | B_RELEASE_ALL); 488 489 TRACE("%s\n", __FUNCTION__); 490 491 if (sNotificationModule != NULL) { 492 char messageBuffer[512]; 493 KMessage message; 494 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR); 495 message.AddInt32("opcode", B_NETWORK_WLAN_SCANNED); 496 message.AddString("interface", vap->iv_ifp->device_name); 497 498 sNotificationModule->send_notification(&message); 499 } 500 } 501 #endif 502