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 <net/if_media.h> 12 #include <stdio.h> 13 #include <sys/sockio.h> 14 15 #include <Messenger.h> 16 17 #include <AutoDeleter.h> 18 #include <NetServer.h> 19 20 extern "C" { 21 # include <net80211/ieee80211_ioctl.h> 22 } 23 24 25 //#define TRACE_DEVICE 26 #ifdef TRACE_DEVICE 27 # define TRACE(x, ...) printf(x, __VA_ARGS__); 28 #else 29 # define TRACE(x, ...) ; 30 #endif 31 32 33 namespace { 34 35 36 struct ie_data { 37 uint8 type; 38 uint8 length; 39 uint8 data[1]; 40 }; 41 42 43 static status_t 44 get_80211(const char* name, int32 type, void* data, int32& length) 45 { 46 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 47 if (socket < 0) 48 return errno; 49 50 FileDescriptorCloser closer(socket); 51 52 struct ieee80211req ireq; 53 strlcpy(ireq.i_name, name, IF_NAMESIZE); 54 ireq.i_type = type; 55 ireq.i_val = 0; 56 ireq.i_len = length; 57 ireq.i_data = data; 58 59 if (ioctl(socket, SIOCG80211, &ireq, sizeof(struct ieee80211req)) < 0) 60 return errno; 61 62 length = ireq.i_len; 63 return B_OK; 64 } 65 66 67 template<typename T> status_t 68 do_request(T& request, const char* name, int option) 69 { 70 int socket = ::socket(AF_LINK, SOCK_DGRAM, 0); 71 if (socket < 0) 72 return errno; 73 74 FileDescriptorCloser closer(socket); 75 76 strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE); 77 78 if (ioctl(socket, option, &request, sizeof(T)) < 0) 79 return errno; 80 81 return B_OK; 82 } 83 84 85 //! Read a 16 bit little endian value 86 static uint16 87 read_le16(uint8*& data, int32& length) 88 { 89 uint16 value = B_LENDIAN_TO_HOST_INT16(*(uint16*)data); 90 data += 2; 91 length -= 2; 92 return value; 93 } 94 95 96 //! Read a 32 bit little endian value 97 static uint32 98 read_le32(uint8*& data, int32& length) 99 { 100 uint32 value = B_LENDIAN_TO_HOST_INT32(*(uint32*)data); 101 data += 4; 102 length -= 4; 103 return value; 104 } 105 106 107 static uint32 108 from_rsn_cipher(uint32 cipher) 109 { 110 if ((cipher & 0xffffff) != RSN_OUI) 111 return B_NETWORK_CIPHER_CCMP; 112 113 switch (cipher >> 24) { 114 case RSN_CSE_NULL: 115 return B_NETWORK_CIPHER_NONE; 116 case RSN_CSE_WEP40: 117 return B_NETWORK_CIPHER_WEP_40; 118 case RSN_CSE_WEP104: 119 return B_NETWORK_CIPHER_WEP_104; 120 case RSN_CSE_TKIP: 121 return B_NETWORK_CIPHER_TKIP; 122 default: 123 case RSN_CSE_CCMP: 124 return B_NETWORK_CIPHER_CCMP; 125 case RSN_CSE_WRAP: 126 return B_NETWORK_CIPHER_AES_128_CMAC; 127 } 128 } 129 130 131 static uint32 132 from_rsn_key_mode(uint32 mode) 133 { 134 if ((mode & 0xffffff) != RSN_OUI) 135 return B_KEY_MODE_IEEE802_1X; 136 137 switch (mode >> 24) { 138 default: 139 case RSN_ASE_8021X_UNSPEC: 140 return B_KEY_MODE_IEEE802_1X; 141 case RSN_ASE_8021X_PSK: 142 return B_KEY_MODE_PSK; 143 // the following are currently not defined in net80211 144 case 3: 145 return B_KEY_MODE_FT_IEEE802_1X; 146 case 4: 147 return B_KEY_MODE_FT_PSK; 148 case 5: 149 return B_KEY_MODE_IEEE802_1X_SHA256; 150 case 6: 151 return B_KEY_MODE_PSK_SHA256; 152 } 153 } 154 155 156 //! Parse RSN/WPA information elements common data 157 static void 158 parse_ie_rsn_wpa(wireless_network& network, uint8*& data, int32& length) 159 { 160 if (length >= 4) { 161 // parse group cipher 162 network.group_cipher = from_rsn_cipher(read_le32(data, length)); 163 } else if (length > 0) 164 return; 165 166 if (length >= 2) { 167 // parse unicast cipher 168 uint16 count = read_le16(data, length); 169 network.cipher = 0; 170 171 for (uint16 i = 0; i < count; i++) { 172 if (length < 4) 173 return; 174 network.cipher |= from_rsn_cipher(read_le32(data, length)); 175 } 176 } else if (length > 0) 177 return; 178 179 if (length >= 2) { 180 // parse key management mode 181 uint16 count = read_le16(data, length); 182 network.key_mode = 0; 183 184 for (uint16 i = 0; i < count; i++) { 185 if (length < 4) 186 return; 187 network.key_mode |= from_rsn_key_mode(read_le32(data, length)); 188 } 189 } else if (length > 0) 190 return; 191 192 // TODO: capabilities, and PMKID following in case of RSN 193 } 194 195 196 //! Parse RSN (Robust Security Network) information element. 197 static void 198 parse_ie_rsn(wireless_network& network, ie_data* ie) 199 { 200 network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2; 201 network.cipher = B_NETWORK_CIPHER_CCMP; 202 network.group_cipher = B_NETWORK_CIPHER_CCMP; 203 network.key_mode = B_KEY_MODE_IEEE802_1X; 204 205 int32 length = ie->length; 206 if (length < 2) 207 return; 208 209 uint8* data = ie->data; 210 211 uint16 version = read_le16(data, length); 212 if (version != RSN_VERSION) 213 return; 214 215 parse_ie_rsn_wpa(network, data, length); 216 } 217 218 219 //! Parse WPA information element. 220 static bool 221 parse_ie_wpa(wireless_network& network, ie_data* ie) 222 { 223 int32 length = ie->length; 224 if (length < 6) 225 return false; 226 227 uint8* data = ie->data; 228 229 uint32 oui = read_le32(data, length); 230 TRACE(" oui: %" B_PRIx32 "\n", oui); 231 if (oui != ((WPA_OUI_TYPE << 24) | WPA_OUI)) 232 return false; 233 234 uint16 version = read_le16(data, length); 235 if (version != WPA_VERSION) 236 return false; 237 238 network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA; 239 network.cipher = B_NETWORK_CIPHER_TKIP; 240 network.group_cipher = B_NETWORK_CIPHER_TKIP; 241 network.key_mode = B_KEY_MODE_IEEE802_1X; 242 243 parse_ie_rsn_wpa(network, data, length); 244 return true; 245 } 246 247 248 //! Parse information elements. 249 static void 250 parse_ie(wireless_network& network, uint8* _ie, int32 ieLength) 251 { 252 struct ie_data* ie = (ie_data*)_ie; 253 bool hadRSN = false; 254 bool hadWPA = false; 255 256 while (ieLength > 1) { 257 TRACE("ie type %u\n", ie->type); 258 switch (ie->type) { 259 case IEEE80211_ELEMID_SSID: 260 strlcpy(network.name, (char*)ie->data, 261 min_c(ie->length + 1, (int)sizeof(network.name))); 262 break; 263 case IEEE80211_ELEMID_RSN: 264 parse_ie_rsn(network, ie); 265 hadRSN = true; 266 break; 267 case IEEE80211_ELEMID_VENDOR: 268 if (!hadRSN && parse_ie_wpa(network, ie)) 269 hadWPA = true; 270 break; 271 } 272 273 ieLength -= 2 + ie->length; 274 ie = (ie_data*)((uint8*)ie + 2 + ie->length); 275 } 276 277 if (hadRSN || hadWPA) { 278 // Determine authentication mode 279 280 if ((network.key_mode & (B_KEY_MODE_IEEE802_1X_SHA256 281 | B_KEY_MODE_PSK_SHA256)) != 0) { 282 network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA2; 283 } else if ((network.key_mode & (B_KEY_MODE_IEEE802_1X 284 | B_KEY_MODE_PSK | B_KEY_MODE_FT_IEEE802_1X 285 | B_KEY_MODE_FT_PSK)) != 0) { 286 if (!hadRSN) 287 network.authentication_mode = B_NETWORK_AUTHENTICATION_WPA; 288 } else if ((network.key_mode & B_KEY_MODE_NONE) != 0) { 289 if ((network.cipher & (B_NETWORK_CIPHER_WEP_40 290 | B_NETWORK_CIPHER_WEP_104)) != 0) 291 network.authentication_mode = B_NETWORK_AUTHENTICATION_WEP; 292 else 293 network.authentication_mode = B_NETWORK_AUTHENTICATION_NONE; 294 } 295 } 296 } 297 298 299 static void 300 parse_ie(wireless_network& network, struct ieee80211req_sta_info& info) 301 { 302 parse_ie(network, (uint8*)&info + info.isi_ie_off, info.isi_ie_len); 303 } 304 305 306 static void 307 parse_ie(wireless_network& network, struct ieee80211req_scan_result& result) 308 { 309 parse_ie(network, (uint8*)&result + result.isr_ie_off + result.isr_ssid_len 310 + result.isr_meshid_len, result.isr_ie_len); 311 } 312 313 314 static bool 315 get_ssid_from_ie(char* name, uint8* _ie, int32 ieLength) 316 { 317 struct ie_data* ie = (ie_data*)_ie; 318 319 while (ieLength > 1) { 320 switch (ie->type) { 321 case IEEE80211_ELEMID_SSID: 322 strlcpy(name, (char*)ie->data, min_c(ie->length + 1, 32)); 323 return true; 324 } 325 326 ieLength -= 2 + ie->length; 327 ie = (ie_data*)((uint8*)ie + 2 + ie->length); 328 } 329 return false; 330 } 331 332 333 static bool 334 get_ssid_from_ie(char* name, struct ieee80211req_sta_info& info) 335 { 336 return get_ssid_from_ie(name, (uint8*)&info + info.isi_ie_off, 337 info.isi_ie_len); 338 } 339 340 341 static void 342 fill_wireless_network(wireless_network& network, 343 struct ieee80211req_sta_info& info) 344 { 345 network.name[0] = '\0'; 346 network.address.SetToLinkLevel(info.isi_macaddr, 347 IEEE80211_ADDR_LEN); 348 network.signal_strength = info.isi_rssi; 349 network.noise_level = info.isi_noise; 350 network.flags |= (info.isi_capinfo & IEEE80211_CAPINFO_PRIVACY) != 0 351 ? B_NETWORK_IS_ENCRYPTED : 0; 352 353 network.authentication_mode = 0; 354 network.cipher = 0; 355 network.group_cipher = 0; 356 network.key_mode = 0; 357 358 parse_ie(network, info); 359 } 360 361 362 static void 363 fill_wireless_network(wireless_network& network, const char* networkName, 364 struct ieee80211req_scan_result& result) 365 { 366 strlcpy(network.name, networkName, sizeof(network.name)); 367 network.address.SetToLinkLevel(result.isr_bssid, 368 IEEE80211_ADDR_LEN); 369 network.signal_strength = result.isr_rssi; 370 network.noise_level = result.isr_noise; 371 network.flags = (result.isr_capinfo & IEEE80211_CAPINFO_PRIVACY) 372 != 0 ? B_NETWORK_IS_ENCRYPTED : 0; 373 374 network.authentication_mode = 0; 375 network.cipher = 0; 376 network.group_cipher = 0; 377 network.key_mode = 0; 378 379 parse_ie(network, result); 380 } 381 382 383 static status_t 384 get_scan_result(const char* device, wireless_network& network, uint32 index, 385 const BNetworkAddress* address, const char* name) 386 { 387 if (address != NULL && address->Family() != AF_LINK) 388 return B_BAD_VALUE; 389 390 const size_t kBufferSize = 65535; 391 uint8* buffer = (uint8*)malloc(kBufferSize); 392 if (buffer == NULL) 393 return B_NO_MEMORY; 394 395 MemoryDeleter deleter(buffer); 396 397 int32 length = kBufferSize; 398 status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer, 399 length); 400 if (status != B_OK) 401 return status; 402 403 int32 bytesLeft = length; 404 uint8* entry = buffer; 405 uint32 count = 0; 406 407 while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { 408 ieee80211req_scan_result* result 409 = (ieee80211req_scan_result*)entry; 410 411 char networkName[32]; 412 strlcpy(networkName, (char*)(result + 1), 413 min_c((int)sizeof(networkName), result->isr_ssid_len + 1)); 414 415 if (index == count || (address != NULL && !memcmp( 416 address->LinkLevelAddress(), result->isr_bssid, 417 IEEE80211_ADDR_LEN)) 418 || (name != NULL && !strcmp(networkName, name))) { 419 // Fill wireless_network with scan result data 420 fill_wireless_network(network, networkName, *result); 421 return B_OK; 422 } 423 424 entry += result->isr_len; 425 bytesLeft -= result->isr_len; 426 count++; 427 } 428 429 return B_ENTRY_NOT_FOUND; 430 } 431 432 433 static status_t 434 get_station(const char* device, wireless_network& network, uint32 index, 435 const BNetworkAddress* address, const char* name) 436 { 437 if (address != NULL && address->Family() != AF_LINK) 438 return B_BAD_VALUE; 439 440 const size_t kBufferSize = 65535; 441 uint8* buffer = (uint8*)malloc(kBufferSize); 442 if (buffer == NULL) 443 return B_NO_MEMORY; 444 445 MemoryDeleter deleter(buffer); 446 447 struct ieee80211req_sta_req& request = *(ieee80211req_sta_req*)buffer; 448 if (address != NULL) { 449 memcpy(request.is_u.macaddr, address->LinkLevelAddress(), 450 IEEE80211_ADDR_LEN); 451 } else 452 memset(request.is_u.macaddr, 0xff, IEEE80211_ADDR_LEN); 453 454 int32 length = kBufferSize; 455 status_t status = get_80211(device, IEEE80211_IOC_STA_INFO, &request, 456 length); 457 if (status != B_OK) 458 return status; 459 460 int32 bytesLeft = length; 461 uint8* entry = (uint8*)&request.info[0]; 462 uint32 count = 0; 463 464 while (bytesLeft > (int32)sizeof(struct ieee80211req_sta_info)) { 465 ieee80211req_sta_info* info = (ieee80211req_sta_info*)entry; 466 467 char networkName[32]; 468 get_ssid_from_ie(networkName, *info); 469 if (index == count || address != NULL 470 || (name != NULL && !strcmp(networkName, name))) { 471 fill_wireless_network(network, *info); 472 return B_OK; 473 } 474 475 entry += info->isi_len; 476 bytesLeft -= info->isi_len; 477 count++; 478 } 479 480 return B_ENTRY_NOT_FOUND; 481 } 482 483 484 static status_t 485 get_network(const char* device, wireless_network& network, uint32 index, 486 const BNetworkAddress* address, const char* name) 487 { 488 status_t status = get_station(device, network, index, address, name); 489 if (status != B_OK) 490 return get_scan_result(device, network, index, address, name); 491 492 return B_OK; 493 } 494 495 496 } // namespace 497 498 499 // #pragma mark - 500 501 502 BNetworkDevice::BNetworkDevice() 503 { 504 Unset(); 505 } 506 507 508 BNetworkDevice::BNetworkDevice(const char* name) 509 { 510 SetTo(name); 511 } 512 513 514 BNetworkDevice::~BNetworkDevice() 515 { 516 } 517 518 519 void 520 BNetworkDevice::Unset() 521 { 522 fName[0] = '\0'; 523 } 524 525 526 void 527 BNetworkDevice::SetTo(const char* name) 528 { 529 strlcpy(fName, name, IF_NAMESIZE); 530 } 531 532 533 const char* 534 BNetworkDevice::Name() const 535 { 536 return fName; 537 } 538 539 540 bool 541 BNetworkDevice::Exists() const 542 { 543 ifreq request; 544 return do_request(request, Name(), SIOCGIFINDEX) == B_OK; 545 } 546 547 548 uint32 549 BNetworkDevice::Index() const 550 { 551 ifreq request; 552 if (do_request(request, Name(), SIOCGIFINDEX) != B_OK) 553 return 0; 554 555 return request.ifr_index; 556 } 557 558 559 uint32 560 BNetworkDevice::Flags() const 561 { 562 ifreq request; 563 if (do_request(request, Name(), SIOCGIFFLAGS) != B_OK) 564 return 0; 565 566 return request.ifr_flags; 567 } 568 569 570 bool 571 BNetworkDevice::HasLink() const 572 { 573 return (Flags() & IFF_LINK) != 0; 574 } 575 576 577 int32 578 BNetworkDevice::CountMedia() const 579 { 580 ifmediareq request; 581 request.ifm_count = 0; 582 request.ifm_ulist = NULL; 583 584 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK) 585 return -1; 586 587 return request.ifm_count; 588 } 589 590 591 int32 592 BNetworkDevice::Media() const 593 { 594 ifmediareq request; 595 request.ifm_count = 0; 596 request.ifm_ulist = NULL; 597 598 if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK) 599 return -1; 600 601 return request.ifm_current; 602 } 603 604 605 int32 606 BNetworkDevice::GetMediaAt(int32 index) const 607 { 608 // TODO: this could do some caching 609 return 0; 610 } 611 612 613 status_t 614 BNetworkDevice::SetMedia(int32 media) 615 { 616 ifreq request; 617 request.ifr_media = media; 618 return do_request(request, Name(), SIOCSIFMEDIA); 619 } 620 621 622 status_t 623 BNetworkDevice::GetHardwareAddress(BNetworkAddress& address) 624 { 625 ifreq request; 626 status_t status = do_request(request, Name(), SIOCSIFMEDIA); 627 if (status != B_OK) 628 return status; 629 630 address.SetTo(request.ifr_addr); 631 return B_OK; 632 } 633 634 635 bool 636 BNetworkDevice::IsEthernet() 637 { 638 return IFM_TYPE(Media()) == IFM_ETHER; 639 } 640 641 642 bool 643 BNetworkDevice::IsWireless() 644 { 645 return IFM_TYPE(Media()) == IFM_IEEE80211; 646 } 647 648 649 status_t 650 BNetworkDevice::Scan(bool wait, bool forceRescan) 651 { 652 #if 0 653 if (index == 0) { 654 struct ieee80211_scan_req request; 655 memset(&request, 0, sizeof(request)); 656 request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE 657 | IEEE80211_IOC_SCAN_NOJOIN; 658 request.sr_duration = IEEE80211_IOC_SCAN_FOREVER; 659 set_80211(Name(), IEEE80211_IOC_SCAN_REQ, NULL); 660 } 661 #endif 662 return B_ERROR; 663 } 664 665 666 status_t 667 BNetworkDevice::GetNextNetwork(uint32& cookie, wireless_network& network) 668 { 669 status_t status = get_scan_result(Name(), network, cookie, NULL, NULL); 670 if (status != B_OK) 671 return status; 672 673 cookie++; 674 return B_OK; 675 } 676 677 678 status_t 679 BNetworkDevice::GetNetwork(const char* name, wireless_network& network) 680 { 681 if (name == NULL || name[0] == '\0') 682 return B_BAD_VALUE; 683 684 return get_network(Name(), network, UINT32_MAX, NULL, name); 685 } 686 687 688 status_t 689 BNetworkDevice::GetNetwork(const BNetworkAddress& address, 690 wireless_network& network) 691 { 692 if (address.Family() != AF_LINK) 693 return B_BAD_VALUE; 694 695 return get_network(Name(), network, UINT32_MAX, &address, NULL); 696 } 697 698 699 status_t 700 BNetworkDevice::JoinNetwork(const char* name, const char* password) 701 { 702 if (name == NULL || name[0] == '\0') 703 return B_BAD_VALUE; 704 705 BMessage message(kMsgJoinNetwork); 706 status_t status = message.AddString("device", Name()); 707 708 if (status == B_OK) 709 status = message.AddString("name", name); 710 if (status == B_OK && password != NULL) 711 status = message.AddString("password", password); 712 if (status != B_OK) 713 return status; 714 715 // Send message to the net_server 716 717 BMessenger networkServer(kNetServerSignature); 718 BMessage reply; 719 status = networkServer.SendMessage(&message, &reply); 720 if (status == B_OK) 721 reply.FindInt32("status", &status); 722 723 return status; 724 } 725 726 727 status_t 728 BNetworkDevice::JoinNetwork(const wireless_network& network, 729 const char* password) 730 { 731 return JoinNetwork(network.address, password); 732 } 733 734 735 status_t 736 BNetworkDevice::JoinNetwork(const BNetworkAddress& address, 737 const char* password) 738 { 739 if (address.InitCheck() != B_OK) 740 return B_BAD_VALUE; 741 742 BMessage message(kMsgJoinNetwork); 743 status_t status = message.AddString("device", Name()); 744 745 if (status == B_OK) { 746 status = message.AddFlat("address", 747 const_cast<BNetworkAddress*>(&address)); 748 } 749 if (status == B_OK && password != NULL) 750 status = message.AddString("password", password); 751 if (status != B_OK) 752 return status; 753 754 // Send message to the net_server 755 756 BMessenger networkServer(kNetServerSignature); 757 BMessage reply; 758 status = networkServer.SendMessage(&message, &reply); 759 if (status == B_OK) 760 reply.FindInt32("status", &status); 761 762 return status; 763 } 764 765 766 status_t 767 BNetworkDevice::LeaveNetwork(const char* name) 768 { 769 BMessage message(kMsgLeaveNetwork); 770 status_t status = message.AddString("device", Name()); 771 if (status == B_OK) 772 status = message.AddString("name", name); 773 if (status == B_OK) 774 status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED); 775 if (status != B_OK) 776 return status; 777 778 BMessenger networkServer(kNetServerSignature); 779 BMessage reply; 780 status = networkServer.SendMessage(&message, &reply); 781 if (status == B_OK) 782 reply.FindInt32("status", &status); 783 784 return status; 785 } 786 787 788 status_t 789 BNetworkDevice::LeaveNetwork(const wireless_network& network) 790 { 791 return LeaveNetwork(network.address); 792 } 793 794 795 status_t 796 BNetworkDevice::LeaveNetwork(const BNetworkAddress& address) 797 { 798 BMessage message(kMsgLeaveNetwork); 799 status_t status = message.AddString("device", Name()); 800 if (status == B_OK) { 801 status = message.AddFlat("address", 802 const_cast<BNetworkAddress*>(&address)); 803 } 804 if (status == B_OK) 805 status = message.AddInt32("reason", IEEE80211_REASON_UNSPECIFIED); 806 if (status != B_OK) 807 return status; 808 809 BMessenger networkServer(kNetServerSignature); 810 BMessage reply; 811 status = networkServer.SendMessage(&message, &reply); 812 if (status == B_OK) 813 reply.FindInt32("status", &status); 814 815 return status; 816 } 817 818 819 status_t 820 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie, 821 wireless_network& network) 822 { 823 BNetworkAddress address; 824 status_t status = GetNextAssociatedNetwork(cookie, address); 825 if (status != B_OK) 826 return status; 827 828 return GetNetwork(address, network); 829 } 830 831 832 status_t 833 BNetworkDevice::GetNextAssociatedNetwork(uint32& cookie, 834 BNetworkAddress& address) 835 { 836 // We currently support only a single associated network 837 if (cookie != 0) 838 return B_ENTRY_NOT_FOUND; 839 840 uint8 mac[IEEE80211_ADDR_LEN]; 841 int32 length = IEEE80211_ADDR_LEN; 842 status_t status = get_80211(Name(), IEEE80211_IOC_BSSID, mac, length); 843 if (status != B_OK) 844 return status; 845 846 if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 847 && mac[5] == 0) 848 return B_ENTRY_NOT_FOUND; 849 850 address.SetToLinkLevel(mac, IEEE80211_ADDR_LEN); 851 cookie++; 852 return B_OK; 853 } 854