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_REQ: { 181 if (op != SIOCS80211) 182 return B_BAD_VALUE; 183 184 // SIOCS80211SCAN is a no-op, scans cannot actually be initiated. 185 // But we can at least check if one is already in progress. 186 status_t status = EBUSY; 187 188 IFF_LOCKGIANT(ifp); 189 if (ic->ic_state == IEEE80211_S_SCAN || (ic->ic_flags & IEEE80211_F_BGSCAN) != 0) 190 status = EINPROGRESS; 191 IFF_UNLOCKGIANT(ifp); 192 193 return status; 194 } 195 196 case IEEE80211_IOC_SCAN_RESULTS: { 197 if (op != SIOCG80211) 198 return B_BAD_VALUE; 199 200 struct ieee80211_nodereq nodereq; 201 struct ieee80211_nodereq_all nodereq_all = {}; 202 203 // We need a scan_result of maximum possible size to work with. 204 struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*) 205 alloca(sizeof(struct ieee80211req_scan_result) + IEEE80211_NWID_LEN + 257); 206 207 uint16 remaining = ireq.i_len, offset = 0; 208 for (int i = 0; remaining > 0; i++) { 209 nodereq_all.na_node = &nodereq; 210 nodereq_all.na_size = sizeof(struct ieee80211_nodereq); 211 nodereq_all.na_startnode = i; 212 213 IFF_LOCKGIANT(ifp); 214 status_t status = ifp->if_ioctl(ifp, SIOCG80211ALLNODES, (caddr_t)&nodereq_all); 215 IFF_UNLOCKGIANT(ifp); 216 if (status != B_OK) 217 return status; 218 if (nodereq_all.na_nodes == 0) 219 break; 220 221 int32 size = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len; 222 uint16_t ieLen = 0; 223 if (nodereq.nr_rsnie[1] != 0) { 224 ieLen = 2 + nodereq.nr_rsnie[1]; 225 size += ieLen; 226 } 227 const int32 roundedSize = roundup(size, 4); 228 if (remaining < roundedSize) 229 break; 230 231 sr->isr_ie_off = sizeof(struct ieee80211req_scan_result); 232 sr->isr_ie_len = ieLen; 233 sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags); 234 sr->isr_flags = 0; /* not actually used by userland */ 235 sr->isr_noise = 0; /* unknown */ 236 sr->isr_rssi = nodereq.nr_rssi; 237 sr->isr_intval = nodereq.nr_intval; 238 sr->isr_capinfo = fbsd_capinfo_from_obsd(nodereq.nr_capinfo); 239 sr->isr_erp = nodereq.nr_erp; 240 memcpy(sr->isr_bssid, nodereq.nr_bssid, IEEE80211_ADDR_LEN); 241 sr->isr_nrates = nodereq.nr_nrates; 242 memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE); 243 sr->isr_ssid_len = nodereq.nr_nwid_len; 244 sr->isr_meshid_len = 0; 245 memcpy((uint8*)sr + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len); 246 memcpy((uint8*)sr + sr->isr_ie_off + sr->isr_ssid_len, nodereq.nr_rsnie, ieLen); 247 248 sr->isr_len = roundedSize; 249 if (user_memcpy((uint8*)ireq.i_data + offset, sr, size) != B_OK) 250 return B_BAD_ADDRESS; 251 252 offset += sr->isr_len; 253 remaining -= sr->isr_len; 254 } 255 ireq.i_len = offset; 256 257 IFF_LOCKGIANT(ifp); 258 const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */; 259 if (ic->ic_opmode == IEEE80211_M_STA 260 && system_time() >= (ic->ic_last_raise_inact + RAISE_INACT_INTERVAL)) { 261 // net80211 only raises and checks inactivity during active scans, or background 262 // scans performed in S_RUN, so we need to do it here so that stale nodes are 263 // evicted for S_SCAN, too. (This means "inact" will be raised a bit more often 264 // and nodes evicted faster during other modes, but that's acceptable.) 265 ieee80211_iterate_nodes(ic, ieee80211_node_raise_inact, NULL); 266 ieee80211_clean_inactive_nodes(ic, IEEE80211_INACT_SCAN); 267 ic->ic_last_raise_inact = system_time(); 268 } 269 IFF_UNLOCKGIANT(ifp); 270 271 break; 272 } 273 274 case IEEE80211_IOC_BSSID: { 275 if (ireq.i_len != IEEE80211_ADDR_LEN) 276 return EINVAL; 277 278 struct ieee80211_bssid bssid; 279 if (op == SIOCS80211) { 280 if (user_memcpy(bssid.i_bssid, ireq.i_data, ireq.i_len) != B_OK) 281 return B_BAD_ADDRESS; 282 } 283 284 IFF_LOCKGIANT(ifp); 285 status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ? 286 SIOCG80211BSSID : SIOCS80211BSSID, (caddr_t)&bssid); 287 IFF_UNLOCKGIANT(ifp); 288 if (status != B_OK) 289 return status; 290 291 if (op == SIOCG80211) { 292 if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK) 293 return B_BAD_ADDRESS; 294 } 295 296 break; 297 } 298 299 case IEEE80211_IOC_SSID: { 300 struct ifreq ifr; 301 struct ieee80211_nwid nwid; 302 ifr.ifr_data = (uint8_t*)&nwid; 303 304 if (op == SIOCS80211) { 305 nwid.i_len = ireq.i_len; 306 if (user_memcpy(nwid.i_nwid, ireq.i_data, ireq.i_len) != B_OK) 307 return B_BAD_ADDRESS; 308 } 309 310 IFF_LOCKGIANT(ifp); 311 status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ? 312 SIOCG80211NWID : SIOCS80211NWID, (caddr_t)&ifr); 313 ieee80211_state state = ic->ic_state; 314 IFF_UNLOCKGIANT(ifp); 315 if (status != B_OK) 316 return status; 317 318 if (op == SIOCG80211) { 319 if (state == IEEE80211_S_INIT || state == IEEE80211_S_SCAN) { 320 // The returned NWID will be the one we want to join, not connected to. 321 ireq.i_len = 0; 322 } else { 323 if (ireq.i_len < nwid.i_len) 324 return E2BIG; 325 ireq.i_len = nwid.i_len; 326 if (user_memcpy(ireq.i_data, nwid.i_nwid, ireq.i_len) != B_OK) 327 return B_BAD_ADDRESS; 328 } 329 } 330 331 break; 332 } 333 334 case IEEE80211_IOC_MLME: { 335 if (op != SIOCS80211) 336 return B_BAD_VALUE; 337 338 struct ieee80211req_mlme mlme; 339 if (user_memcpy(&mlme, ireq.i_data, min_c(sizeof(mlme), ireq.i_len)) != B_OK) 340 return B_BAD_ADDRESS; 341 342 switch (mlme.im_op) { 343 case IEEE80211_MLME_DISASSOC: 344 case IEEE80211_MLME_DEAUTH: { 345 struct ifreq ifr; 346 struct ieee80211_nwid nwid; 347 ifr.ifr_data = (uint8_t*)&nwid; 348 nwid.i_len = 0; 349 350 IFF_LOCKGIANT(ifp); 351 status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr); 352 IFF_UNLOCKGIANT(ifp); 353 if (status != 0) 354 return status; 355 break; 356 } 357 358 default: 359 TRACE("openbsd wlan_control: unsupported MLME operation %" B_PRIu8 "\n", 360 mlme.im_op); 361 return EOPNOTSUPP; 362 } 363 364 break; 365 } 366 367 case IEEE80211_IOC_HAIKU_JOIN: { 368 if (op != SIOCS80211) 369 return B_BAD_VALUE; 370 371 struct ieee80211_haiku_join_req* haiku_join = 372 (struct ieee80211_haiku_join_req*)calloc(1, ireq.i_len); 373 MemoryDeleter haikuJoinDeleter(haiku_join); 374 if (!haikuJoinDeleter.IsSet()) 375 return B_NO_MEMORY; 376 377 if (user_memcpy(haiku_join, ireq.i_data, ireq.i_len) != B_OK) 378 return B_BAD_ADDRESS; 379 380 struct ifreq ifr; 381 struct ieee80211_nwid nwid; 382 struct ieee80211_wpaparams wpaparams; 383 struct ieee80211_wpapsk wpapsk; 384 memset(&wpaparams, 0, sizeof(wpaparams)); 385 memset(&wpapsk, 0, sizeof(wpapsk)); 386 387 // Convert join information. 388 ifr.ifr_data = (uint8_t*)&nwid; 389 nwid.i_len = haiku_join->i_nwid_len; 390 memcpy(nwid.i_nwid, haiku_join->i_nwid, nwid.i_len); 391 392 switch (haiku_join->i_authentication_mode) { 393 case B_NETWORK_AUTHENTICATION_NONE: 394 break; 395 396 case B_NETWORK_AUTHENTICATION_WPA: 397 case B_NETWORK_AUTHENTICATION_WPA2: 398 wpaparams.i_enabled = 1; 399 wpaparams.i_protos |= 400 (haiku_join->i_authentication_mode == B_NETWORK_AUTHENTICATION_WPA2) ? 401 IEEE80211_WPA_PROTO_WPA2 : IEEE80211_WPA_PROTO_WPA1; 402 403 // NOTE: i_wpaparams.i_groupcipher is not a flags field! 404 wpaparams.i_ciphers = obsd_ciphers_from_haiku(haiku_join->i_ciphers); 405 wpaparams.i_groupcipher = 406 fls(obsd_ciphers_from_haiku(haiku_join->i_group_ciphers)); 407 408 if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X) != 0) 409 wpaparams.i_akms |= IEEE80211_WPA_AKM_8021X; 410 if ((haiku_join->i_key_mode & B_KEY_MODE_PSK) != 0) 411 wpaparams.i_akms |= IEEE80211_WPA_AKM_PSK; 412 if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X_SHA256) != 0) 413 wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_8021X; 414 if ((haiku_join->i_key_mode & B_KEY_MODE_PSK_SHA256) != 0) 415 wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_PSK; 416 417 if (haiku_join->i_key_len != 0) { 418 wpapsk.i_enabled = 1; 419 memcpy(wpapsk.i_psk, haiku_join->i_key, haiku_join->i_key_len); 420 memset(&wpapsk.i_psk[haiku_join->i_key_len], 0, 421 sizeof(wpapsk.i_psk) - haiku_join->i_key_len); 422 } 423 break; 424 425 default: 426 TRACE("openbsd wlan_control: unsupported authentication mode %" B_PRIu32 "\n", 427 haiku_join->i_authentication_mode); 428 return EOPNOTSUPP; 429 } 430 431 IFF_LOCKGIANT(ifp); 432 status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr); 433 if (status != B_OK) { 434 IFF_UNLOCKGIANT(ifp); 435 return status; 436 } 437 if (wpapsk.i_enabled) { 438 status = ifp->if_ioctl(ifp, SIOCS80211WPAPSK, (caddr_t)&wpapsk); 439 if (status != B_OK) { 440 IFF_UNLOCKGIANT(ifp); 441 return status; 442 } 443 } 444 if (wpaparams.i_enabled) { 445 status = ifp->if_ioctl(ifp, SIOCS80211WPAPARMS, (caddr_t)&wpaparams); 446 if (status != B_OK) { 447 IFF_UNLOCKGIANT(ifp); 448 return status; 449 } 450 } 451 IFF_UNLOCKGIANT(ifp); 452 if (status != B_OK) 453 return status; 454 455 break; 456 } 457 458 default: 459 TRACE("openbsd wlan_control: %" B_PRIu32 ", %d (not supported)\n", op, ireq.i_type); 460 return EOPNOTSUPP; 461 } 462 463 if (op == SIOCG80211 && user_memcpy(arg, &ireq, 464 sizeof(struct ieee80211req)) != B_OK) 465 return B_BAD_ADDRESS; 466 return B_OK; 467 } 468 469 470 void 471 ieee80211_rtm_80211info_task(void* arg) 472 { 473 struct ieee80211com *ic = arg; 474 struct ifnet *ifp = &ic->ic_if; 475 476 if (sNotificationModule != NULL) { 477 char messageBuffer[512]; 478 KMessage message; 479 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR); 480 if (ifp->if_link_state == LINK_STATE_UP) 481 message.AddInt32("opcode", B_NETWORK_WLAN_JOINED); 482 else 483 message.AddInt32("opcode", B_NETWORK_WLAN_LEFT); 484 message.AddString("interface", ifp->device_name); 485 // TODO: add data about the node 486 487 sNotificationModule->send_notification(&message); 488 } 489 } 490 491 492 #if 0 493 void 494 ieee80211_notify_scan_done(struct ieee80211vap* vap) 495 { 496 release_sem_etc(vap->iv_ifp->scan_done_sem, 1, 497 B_DO_NOT_RESCHEDULE | B_RELEASE_ALL); 498 499 TRACE("%s\n", __FUNCTION__); 500 501 if (sNotificationModule != NULL) { 502 char messageBuffer[512]; 503 KMessage message; 504 message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR); 505 message.AddInt32("opcode", B_NETWORK_WLAN_SCANNED); 506 message.AddString("interface", vap->iv_ifp->device_name); 507 508 sNotificationModule->send_notification(&message); 509 } 510 } 511 #endif 512