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