mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-05-12 16:19:44 -06:00
Use emulated Ethernet identity for NP MAC
Derive a stable locally administered Ethernet address from Console PSID and PS3 user id, with a hidden network config override. Expose that identity through cellNetCtl instead of host MAC discovery and add focused NP helper tests.
This commit is contained in:
parent
4e1cedcb0f
commit
97adb673e8
@ -27,7 +27,6 @@
|
||||
<Link>
|
||||
<AdditionalDependencies>
|
||||
ws2_32.lib;
|
||||
Iphlpapi.lib;
|
||||
Bcrypt.lib;
|
||||
avcodec.lib;
|
||||
avformat.lib;
|
||||
|
||||
@ -88,7 +88,7 @@ if (NOT ANDROID)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3_lib PRIVATE ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi)
|
||||
target_link_libraries(rpcs3_lib PRIVATE ws2_32 Winmm Psapi gdi32 setupapi)
|
||||
else()
|
||||
target_link_libraries(rpcs3_lib PRIVATE ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
@ -188,6 +188,7 @@ if(BUILD_RPCS3_TESTS)
|
||||
tests/test_simple_array.cpp
|
||||
tests/test_address_range.cpp
|
||||
tests/test_sys_fs.cpp
|
||||
tests/test_np_helpers.cpp
|
||||
tests/test_rsx_cfg.cpp
|
||||
tests/test_rsx_fp_asm.cpp
|
||||
tests/test_dmux_pamf.cpp
|
||||
|
||||
@ -12,12 +12,6 @@
|
||||
#include "Emu/NP/np_handler.h"
|
||||
#include "Emu/NP/np_helpers.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(cellNetCtl);
|
||||
|
||||
template <>
|
||||
@ -254,39 +248,7 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
|
||||
if (code == CELL_NET_CTL_INFO_ETHER_ADDR)
|
||||
{
|
||||
const auto& ether = nph.get_ether_addr();
|
||||
|
||||
// Check if MAC address is valid (non-zero)
|
||||
if (ether[0] == 0 && ether[1] == 0 && ether[2] == 0 &&
|
||||
ether[3] == 0 && ether[4] == 0 && ether[5] == 0)
|
||||
{
|
||||
cellNetCtl.error("MAC address is all zeros - generating fallback");
|
||||
|
||||
// Generate a fallback locally-administered MAC based on a simple hash
|
||||
char hostname[256] = {};
|
||||
if (gethostname(hostname, sizeof(hostname) - 1) != 0)
|
||||
{
|
||||
std::strcpy(hostname, "rpcs3");
|
||||
}
|
||||
|
||||
u64 hash = 0;
|
||||
for (const char* p = hostname; *p; ++p)
|
||||
hash = hash * 31 + static_cast<u8>(*p);
|
||||
|
||||
info->ether_addr.data[0] = 0x02; // Locally administered, unicast
|
||||
info->ether_addr.data[1] = static_cast<u8>((hash >> 0) & 0xff);
|
||||
info->ether_addr.data[2] = static_cast<u8>((hash >> 8) & 0xff);
|
||||
info->ether_addr.data[3] = static_cast<u8>((hash >> 16) & 0xff);
|
||||
info->ether_addr.data[4] = static_cast<u8>((hash >> 24) & 0xff);
|
||||
info->ether_addr.data[5] = static_cast<u8>((hash >> 32) & 0xff);
|
||||
|
||||
cellNetCtl.notice("Generated fallback MAC: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
info->ether_addr.data[0], info->ether_addr.data[1], info->ether_addr.data[2],
|
||||
info->ether_addr.data[3], info->ether_addr.data[4], info->ether_addr.data[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(info->ether_addr.data, ether.data(), 6);
|
||||
}
|
||||
std::memcpy(info->ether_addr.data, ether.data(), 6);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -20,16 +20,13 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
@ -38,11 +35,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include <span>
|
||||
@ -446,9 +438,9 @@ namespace np
|
||||
|
||||
g_fxo->need<named_thread<signaling_handler>>();
|
||||
|
||||
// Always discover MAC address - games may query it regardless of network status
|
||||
// A real PS3 always has a hardware MAC address
|
||||
discover_ether_address();
|
||||
// Always initialize a stable emulated MAC address - games may query it regardless of network status.
|
||||
// A real PS3 always has a hardware MAC address.
|
||||
reset_ether_address();
|
||||
|
||||
is_connected = (g_cfg.net.net_active == np_internet_status::enabled);
|
||||
is_psn_active = (g_cfg.net.psn_status >= np_psn_status::psn_fake) && is_connected;
|
||||
@ -501,6 +493,18 @@ namespace np
|
||||
|
||||
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
|
||||
|
||||
const auto resolved_ether_address = resolve_ether_address();
|
||||
if (!is_valid_ether_addr(ether_address))
|
||||
{
|
||||
nph_log.warning("Savestate contained an invalid Ethernet address %s; using %s instead.", ether_to_string(ether_address), ether_to_string(resolved_ether_address));
|
||||
ether_address = resolved_ether_address;
|
||||
}
|
||||
else if (ether_address != resolved_ether_address)
|
||||
{
|
||||
nph_log.notice("Savestate Ethernet address %s differs from the current emulated identity; using %s instead.", ether_to_string(ether_address), ether_to_string(resolved_ether_address));
|
||||
ether_address = resolved_ether_address;
|
||||
}
|
||||
|
||||
// Call init func if needed (np_memory is unaffected when an empty pool is provided)
|
||||
init_NP(0, vm::null);
|
||||
|
||||
@ -613,158 +617,29 @@ namespace np
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper to check if MAC address is valid (non-zero, non-broadcast)
|
||||
static bool is_valid_mac(const u8* mac)
|
||||
std::array<u8, 6> np_handler::resolve_ether_address() const
|
||||
{
|
||||
// Check for all-zero MAC
|
||||
if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 && mac[4] == 0 && mac[5] == 0)
|
||||
return false;
|
||||
const std::string override_value = g_cfg.net.ethernet_address.to_string();
|
||||
if (!override_value.empty())
|
||||
{
|
||||
if (const auto parsed = string_to_ether_addr(override_value))
|
||||
{
|
||||
nph_log.notice("Using configured emulated Ethernet address %s.", ether_to_string(*parsed));
|
||||
return *parsed;
|
||||
}
|
||||
|
||||
// Check for broadcast MAC (ff:ff:ff:ff:ff:ff)
|
||||
if (mac[0] == 0xff && mac[1] == 0xff && mac[2] == 0xff && mac[3] == 0xff && mac[4] == 0xff && mac[5] == 0xff)
|
||||
return false;
|
||||
nph_log.error("Configured Ethernet address '%s' is invalid; deriving an emulated address instead.", override_value);
|
||||
}
|
||||
|
||||
return true;
|
||||
auto resolved = generate_emulated_ether_addr(g_cfg.sys.console_psid.get(), Emu.GetUsrId());
|
||||
ensure(is_valid_ether_addr(resolved));
|
||||
nph_log.notice("Using derived emulated Ethernet address %s for user %08u.", ether_to_string(resolved), Emu.GetUsrId());
|
||||
return resolved;
|
||||
}
|
||||
|
||||
bool np_handler::discover_ether_address()
|
||||
void np_handler::reset_ether_address()
|
||||
{
|
||||
bool discovered = false;
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
ifaddrs* ifap;
|
||||
|
||||
if (getifaddrs(&ifap) == 0)
|
||||
{
|
||||
for (ifaddrs* p = ifap; p; p = p->ifa_next)
|
||||
{
|
||||
// Skip interfaces without addresses
|
||||
if (!p->ifa_addr)
|
||||
continue;
|
||||
|
||||
// Skip loopback interfaces
|
||||
if (p->ifa_flags & IFF_LOOPBACK)
|
||||
continue;
|
||||
|
||||
if (p->ifa_addr->sa_family == AF_LINK)
|
||||
{
|
||||
sockaddr_dl* sdp = reinterpret_cast<sockaddr_dl*>(p->ifa_addr);
|
||||
|
||||
// Validate hardware address length
|
||||
if (sdp->sdl_alen < 6)
|
||||
continue;
|
||||
|
||||
const u8* mac = reinterpret_cast<const u8*>(sdp->sdl_data + sdp->sdl_nlen);
|
||||
if (!is_valid_mac(mac))
|
||||
continue;
|
||||
|
||||
memcpy(ether_address.data(), mac, 6);
|
||||
nph_log.notice("Discovered Ethernet address %02x:%02x:%02x:%02x:%02x:%02x from interface %s",
|
||||
ether_address[0], ether_address[1], ether_address[2],
|
||||
ether_address[3], ether_address[4], ether_address[5], p->ifa_name);
|
||||
discovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
std::vector<u8> adapter_infos(sizeof(IP_ADAPTER_INFO));
|
||||
ULONG size_infos = sizeof(IP_ADAPTER_INFO);
|
||||
|
||||
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == ERROR_BUFFER_OVERFLOW)
|
||||
adapter_infos.resize(size_infos);
|
||||
|
||||
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == NO_ERROR && size_infos)
|
||||
{
|
||||
PIP_ADAPTER_INFO adapter = reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data());
|
||||
while (adapter)
|
||||
{
|
||||
if (adapter->AddressLength >= 6 && is_valid_mac(adapter->Address))
|
||||
{
|
||||
memcpy(ether_address.data(), adapter->Address, 6);
|
||||
nph_log.notice("Discovered Ethernet address %02x:%02x:%02x:%02x:%02x:%02x from adapter %s",
|
||||
ether_address[0], ether_address[1], ether_address[2],
|
||||
ether_address[3], ether_address[4], ether_address[5], adapter->AdapterName);
|
||||
discovered = true;
|
||||
break;
|
||||
}
|
||||
adapter = adapter->Next;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ifreq ifr;
|
||||
ifconf ifc;
|
||||
char buf[1024];
|
||||
|
||||
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (sock != -1)
|
||||
{
|
||||
ifc.ifc_len = sizeof(buf);
|
||||
ifc.ifc_buf = buf;
|
||||
if (ioctl(sock, SIOCGIFCONF, &ifc) != -1)
|
||||
{
|
||||
ifreq* it = ifc.ifc_req;
|
||||
const ifreq* const end = it + (ifc.ifc_len / sizeof(ifreq));
|
||||
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
strcpy_trunc(ifr.ifr_name, it->ifr_name);
|
||||
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0)
|
||||
{
|
||||
if (!(ifr.ifr_flags & IFF_LOOPBACK))
|
||||
{
|
||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0)
|
||||
{
|
||||
const u8* mac = reinterpret_cast<const u8*>(ifr.ifr_hwaddr.sa_data);
|
||||
if (is_valid_mac(mac))
|
||||
{
|
||||
memcpy(ether_address.data(), mac, 6);
|
||||
nph_log.notice("Discovered Ethernet address %02x:%02x:%02x:%02x:%02x:%02x from interface %s",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], ifr.ifr_name);
|
||||
discovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!discovered)
|
||||
{
|
||||
// Generate a fallback MAC address if discovery failed
|
||||
// Use a locally administered, unicast MAC (02:xx:xx:xx:xx:xx)
|
||||
// Based on a hash of the hostname to keep it consistent across runs
|
||||
nph_log.warning("Failed to discover MAC address from network interfaces, generating fallback");
|
||||
|
||||
char host_buf[256] = {};
|
||||
if (gethostname(host_buf, sizeof(host_buf) - 1) != 0)
|
||||
{
|
||||
strcpy_trunc(host_buf, "rpcs3-fallback");
|
||||
}
|
||||
|
||||
u64 hash = 0;
|
||||
for (const char* p = host_buf; *p; ++p)
|
||||
hash = hash * 31 + static_cast<u8>(*p);
|
||||
|
||||
// Set locally administered bit (bit 1 of first octet) and clear multicast bit (bit 0)
|
||||
ether_address[0] = 0x02; // Locally administered, unicast
|
||||
ether_address[1] = static_cast<u8>((hash >> 0) & 0xff);
|
||||
ether_address[2] = static_cast<u8>((hash >> 8) & 0xff);
|
||||
ether_address[3] = static_cast<u8>((hash >> 16) & 0xff);
|
||||
ether_address[4] = static_cast<u8>((hash >> 24) & 0xff);
|
||||
ether_address[5] = static_cast<u8>((hash >> 32) & 0xff);
|
||||
|
||||
nph_log.notice("Generated fallback Ethernet address %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
ether_address[0], ether_address[1], ether_address[2],
|
||||
ether_address[3], ether_address[4], ether_address[5]);
|
||||
}
|
||||
|
||||
return true; // Always succeed - we have a fallback
|
||||
ether_address = resolve_ether_address();
|
||||
}
|
||||
|
||||
const std::array<u8, 6>& np_handler::get_ether_addr() const
|
||||
|
||||
@ -296,7 +296,8 @@ namespace np
|
||||
private:
|
||||
// Various generic helpers
|
||||
bool discover_ip_address();
|
||||
bool discover_ether_address();
|
||||
std::array<u8, 6> resolve_ether_address() const;
|
||||
void reset_ether_address();
|
||||
bool error_and_disconnect(const std::string& error_msg);
|
||||
|
||||
// Notification handlers
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#include "Emu/Cell/Modules/sceNp.h"
|
||||
#include "stdafx.h"
|
||||
#include "util/types.hpp"
|
||||
#include "np_helpers.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "rpcn_client.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
@ -25,6 +23,133 @@ namespace np
|
||||
return fmt::format("%02X:%02X:%02X:%02X:%02X:%02X", ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]);
|
||||
}
|
||||
|
||||
bool is_valid_ether_addr(const std::array<u8, 6>& ether)
|
||||
{
|
||||
return std::any_of(ether.begin(), ether.end(), [](u8 value) { return value != 0; }) &&
|
||||
std::any_of(ether.begin(), ether.end(), [](u8 value) { return value != 0xff; }) &&
|
||||
(ether[0] & 0x01) == 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::optional<u8> hex_pair_to_byte(char high, char low)
|
||||
{
|
||||
const auto hex_to_nibble = [](char ch) -> std::optional<u8>
|
||||
{
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
return static_cast<u8>(ch - '0');
|
||||
}
|
||||
|
||||
if (ch >= 'A' && ch <= 'F')
|
||||
{
|
||||
return static_cast<u8>(ch - 'A' + 10);
|
||||
}
|
||||
|
||||
if (ch >= 'a' && ch <= 'f')
|
||||
{
|
||||
return static_cast<u8>(ch - 'a' + 10);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
const auto high_nibble = hex_to_nibble(high);
|
||||
const auto low_nibble = hex_to_nibble(low);
|
||||
|
||||
if (!high_nibble || !low_nibble)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<u8>((*high_nibble << 4) | *low_nibble);
|
||||
}
|
||||
|
||||
u64 fnv1a64_append(u64 hash, u64 value)
|
||||
{
|
||||
constexpr u64 fnv_prime = 1099511628211ull;
|
||||
|
||||
for (usz i = 0; i < sizeof(value); i++)
|
||||
{
|
||||
hash ^= static_cast<u8>(value >> (i * 8));
|
||||
hash *= fnv_prime;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
u64 fnv1a64_append(u64 hash, std::string_view value)
|
||||
{
|
||||
constexpr u64 fnv_prime = 1099511628211ull;
|
||||
|
||||
for (char ch : value)
|
||||
{
|
||||
hash ^= static_cast<u8>(ch);
|
||||
hash *= fnv_prime;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::array<u8, 6>> string_to_ether_addr(std::string_view str)
|
||||
{
|
||||
if (str.size() != 17)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::array<u8, 6> ether{};
|
||||
|
||||
for (usz i = 0; i < ether.size(); i++)
|
||||
{
|
||||
const usz pos = i * 3;
|
||||
|
||||
if (i != 5 && str[pos + 2] != ':')
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto byte = hex_pair_to_byte(str[pos], str[pos + 1]);
|
||||
|
||||
if (!byte)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ether[i] = *byte;
|
||||
}
|
||||
|
||||
if (!is_valid_ether_addr(ether))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ether;
|
||||
}
|
||||
|
||||
std::array<u8, 6> generate_emulated_ether_addr(u128 console_psid, u32 user_id)
|
||||
{
|
||||
constexpr u64 fnv_offset_basis = 14695981039346656037ull;
|
||||
|
||||
u64 hash = fnv_offset_basis;
|
||||
hash = fnv1a64_append(hash, "rpcs3-emulated-ethernet-v1");
|
||||
hash = fnv1a64_append(hash, static_cast<u64>(console_psid));
|
||||
hash = fnv1a64_append(hash, static_cast<u64>(console_psid >> 64));
|
||||
hash = fnv1a64_append(hash, user_id);
|
||||
|
||||
std::array<u8, 6> ether{};
|
||||
for (usz i = 0; i < ether.size(); i++)
|
||||
{
|
||||
ether[i] = static_cast<u8>(hash >> (i * 8));
|
||||
}
|
||||
|
||||
ether[0] &= 0xfe; // Unicast.
|
||||
ether[0] |= 0x02; // Locally administered.
|
||||
|
||||
return ether;
|
||||
}
|
||||
|
||||
bool validate_communication_id(const SceNpCommunicationId& com_id)
|
||||
{
|
||||
return std::all_of(com_id.data, com_id.data + 9, [](char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); }) && com_id.num <= 99;
|
||||
|
||||
@ -2,12 +2,20 @@
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Cell/Modules/sceNp.h"
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "rpcn_client.h"
|
||||
|
||||
namespace np
|
||||
{
|
||||
std::string ip_to_string(u32 addr);
|
||||
std::string ether_to_string(const std::array<u8, 6>& ether);
|
||||
bool is_valid_ether_addr(const std::array<u8, 6>& ether);
|
||||
std::optional<std::array<u8, 6>> string_to_ether_addr(std::string_view str);
|
||||
std::array<u8, 6> generate_emulated_ether_addr(u128 console_psid, u32 user_id);
|
||||
bool validate_communication_id(const SceNpCommunicationId& com_id);
|
||||
std::string communication_id_to_string(const SceNpCommunicationId& communicationId);
|
||||
std::optional<SceNpCommunicationId> string_to_communication_id(std::string_view str);
|
||||
|
||||
@ -320,6 +320,7 @@ struct cfg_root : cfg::node
|
||||
cfg::_enum<np_internet_status> net_active{this, "Internet enabled", np_internet_status::disabled};
|
||||
cfg::string ip_address{this, "IP address", "0.0.0.0"};
|
||||
cfg::string bind_address{this, "Bind address", "0.0.0.0"};
|
||||
cfg::string ethernet_address{this, "Ethernet address", "", false};
|
||||
cfg::string dns{this, "DNS address", "8.8.8.8"};
|
||||
cfg::string swap_list{this, "IP swap list", ""};
|
||||
cfg::_bool upnp_enabled{this, "UPNP Enabled", false};
|
||||
|
||||
@ -101,6 +101,7 @@
|
||||
<ClCompile Include="test_simple_array.cpp" />
|
||||
<ClCompile Include="test_address_range.cpp" />
|
||||
<ClCompile Include="test_sys_fs.cpp" />
|
||||
<ClCompile Include="test_np_helpers.cpp" />
|
||||
<ClCompile Include="test_tuple.cpp" />
|
||||
<ClCompile Include="test_pair.cpp" />
|
||||
</ItemGroup>
|
||||
@ -115,4 +116,4 @@
|
||||
</PropertyGroup>
|
||||
<Warning Condition="!Exists('$(GTestPath)')" Text="$([System.String]::Format('$(ErrorText)', '$(GTestPath)'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
51
rpcs3/tests/test_np_helpers.cpp
Normal file
51
rpcs3/tests/test_np_helpers.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "Emu/NP/np_helpers.h"
|
||||
|
||||
namespace np
|
||||
{
|
||||
TEST(NpHelpers, ParsesCanonicalEtherAddress)
|
||||
{
|
||||
const auto ether = string_to_ether_addr("02:00:00:00:00:01");
|
||||
|
||||
ASSERT_TRUE(ether);
|
||||
EXPECT_EQ((std::array<u8, 6>{0x02, 0x00, 0x00, 0x00, 0x00, 0x01}), *ether);
|
||||
EXPECT_TRUE(is_valid_ether_addr(*ether));
|
||||
EXPECT_EQ("02:00:00:00:00:01", ether_to_string(*ether));
|
||||
}
|
||||
|
||||
TEST(NpHelpers, RejectsInvalidEtherAddresses)
|
||||
{
|
||||
EXPECT_FALSE(string_to_ether_addr("00:00:00:00:00:00"));
|
||||
EXPECT_FALSE(string_to_ether_addr("ff:ff:ff:ff:ff:ff"));
|
||||
EXPECT_FALSE(string_to_ether_addr("01:00:00:00:00:00"));
|
||||
EXPECT_FALSE(string_to_ether_addr("03:00:00:00:00:00"));
|
||||
EXPECT_FALSE(string_to_ether_addr("02:00:00:00:00"));
|
||||
EXPECT_FALSE(string_to_ether_addr("02:00:00:00:00:001"));
|
||||
EXPECT_FALSE(string_to_ether_addr("2:00:00:00:00:01"));
|
||||
EXPECT_FALSE(string_to_ether_addr("02-00-00-00-00-01"));
|
||||
EXPECT_FALSE(string_to_ether_addr("02:00:00:00:00:0g"));
|
||||
EXPECT_FALSE(string_to_ether_addr(" 02:00:00:00:00:01"));
|
||||
}
|
||||
|
||||
TEST(NpHelpers, GeneratedEtherAddressIsValidDeterministicAndLocallyAdministered)
|
||||
{
|
||||
const u128 psid = (u128{0x0123456789abcdefull} << 64) | u128{0xfedcba9876543210ull};
|
||||
|
||||
const auto ether1 = generate_emulated_ether_addr(psid, 1);
|
||||
const auto ether2 = generate_emulated_ether_addr(psid, 1);
|
||||
|
||||
EXPECT_EQ(ether1, ether2);
|
||||
EXPECT_TRUE(is_valid_ether_addr(ether1));
|
||||
EXPECT_EQ(0x00, ether1[0] & 0x01); // Unicast.
|
||||
EXPECT_EQ(0x02, ether1[0] & 0x02); // Locally administered.
|
||||
}
|
||||
|
||||
TEST(NpHelpers, GeneratedEtherAddressChangesWithSeedInputs)
|
||||
{
|
||||
const u128 psid1 = (u128{0x0123456789abcdefull} << 64) | u128{0xfedcba9876543210ull};
|
||||
const u128 psid2 = (u128{0x1123456789abcdefull} << 64) | u128{0xfedcba9876543210ull};
|
||||
|
||||
EXPECT_NE(generate_emulated_ether_addr(psid1, 1), generate_emulated_ether_addr(psid1, 2));
|
||||
EXPECT_NE(generate_emulated_ether_addr(psid1, 1), generate_emulated_ether_addr(psid2, 1));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user