// Copyright 2014 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Common/Network.h" #include #include #include #ifndef _WIN32 #include #include #include #else #include #endif #include #include "Common/Random.h" #include "Common/StringUtil.h" namespace Common { MACAddress GenerateMacAddress(const MACConsumer type) { constexpr std::array oui_bba{{0x00, 0x09, 0xbf}}; constexpr std::array oui_ios{{0x00, 0x17, 0xab}}; MACAddress mac{}; switch (type) { case MACConsumer::BBA: std::copy(oui_bba.begin(), oui_bba.end(), mac.begin()); break; case MACConsumer::IOS: std::copy(oui_ios.begin(), oui_ios.end(), mac.begin()); break; } // Generate the 24-bit NIC-specific portion of the MAC address. Random::Generate(&mac[3], 3); return mac; } std::string MacAddressToString(const MACAddress& mac) { return fmt::format("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } std::optional StringToMacAddress(std::string_view mac_string) { if (mac_string.empty()) return std::nullopt; int x = 0; MACAddress mac{}; for (size_t i = 0; i < mac_string.size() && x < (MAC_ADDRESS_SIZE * 2); ++i) { char c = Common::ToLower(mac_string.at(i)); if (c >= '0' && c <= '9') { mac[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); ++x; } else if (c >= 'a' && c <= 'f') { mac[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); ++x; } } // A valid 48-bit MAC address consists of 6 octets, where each // nibble is a character in the MAC address, making 12 characters // in total. if (x / 2 != MAC_ADDRESS_SIZE) return std::nullopt; return std::make_optional(mac); } EthernetHeader::EthernetHeader() = default; EthernetHeader::EthernetHeader(u16 ether_type) { ethertype = htons(ether_type); } u16 EthernetHeader::Size() const { return static_cast(SIZE); } IPv4Header::IPv4Header() = default; IPv4Header::IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to) { version_ihl = 0x45; total_len = htons(Size() + data_size); flags_fragment_offset = htons(0x4000); ttl = 0x40; protocol = ip_proto; std::memcpy(source_addr.data(), &from.sin_addr, IPV4_ADDR_LEN); std::memcpy(destination_addr.data(), &to.sin_addr, IPV4_ADDR_LEN); header_checksum = htons(ComputeNetworkChecksum(this, Size())); } u16 IPv4Header::Size() const { return static_cast(SIZE); } TCPHeader::TCPHeader() = default; TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length) { std::memcpy(&source_port, &from.sin_port, 2); std::memcpy(&destination_port, &to.sin_port, 2); sequence_number = htonl(seq); // TODO: Write flags // Write data offset std::memset(&properties, 0x50, 1); window_size = 0xFFFF; // Compute the TCP checksum with its pseudo header const u32 source_addr = ntohl(from.sin_addr.s_addr); const u32 destination_addr = ntohl(to.sin_addr.s_addr); const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) + (destination_addr >> 16) + (destination_addr & 0xFFFF) + IPProto() + Size() + length; u32 tcp_checksum = ComputeNetworkChecksum(this, Size(), initial_value); tcp_checksum += ComputeNetworkChecksum(data, length); while (tcp_checksum > 0xFFFF) tcp_checksum = (tcp_checksum >> 16) + (tcp_checksum & 0xFFFF); checksum = htons(static_cast(tcp_checksum)); } TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags) { source_port = from.sin_port; destination_port = to.sin_port; sequence_number = htonl(seq); acknowledgement_number = htonl(ack); properties = 0x50 | flags; window_size = 0x7c; checksum = 0; } u16 TCPHeader::Size() const { return static_cast(SIZE); } u8 TCPHeader::IPProto() const { return static_cast(IPPROTO_TCP); } UDPHeader::UDPHeader() = default; UDPHeader::UDPHeader(const sockaddr_in& from, const sockaddr_in& to, u16 data_length) { std::memcpy(&source_port, &from.sin_port, 2); std::memcpy(&destination_port, &to.sin_port, 2); length = htons(Size() + data_length); } u16 UDPHeader::Size() const { return static_cast(SIZE); } u8 UDPHeader::IPProto() const { return static_cast(IPPROTO_UDP); } ARPHeader::ARPHeader() = default; ARPHeader::ARPHeader(u32 from_ip, MACAddress from_mac, u32 to_ip, MACAddress to_mac) { hardware_type = htons(BBA_HARDWARE_TYPE); protocol_type = IPV4_HEADER_TYPE; hardware_size = MAC_ADDRESS_SIZE; protocol_size = IPV4_ADDR_LEN; opcode = 0x200; sender_ip = from_ip; target_ip = to_ip; targer_address = to_mac; sender_address = from_mac; } u16 ARPHeader::Size() const { return static_cast(SIZE); } DHCPBody::DHCPBody() = default; DHCPBody::DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 serv_ip) { transaction_id = transaction; message_type = DHCPConst::MESSAGE_REPLY; hardware_type = BBA_HARDWARE_TYPE; hardware_addr = MAC_ADDRESS_SIZE; client_mac = client_address; your_ip = new_ip; server_ip = serv_ip; } // Add an option to the DHCP Body bool DHCPBody::AddDHCPOption(u8 size, u8 fnc, const std::vector& params) { int i = 0; while (options[i] != 0) { i += options[i + 1] + 2; if (i >= std::size(options)) { return false; } } options[i++] = fnc; options[i++] = size; for (auto val : params) options[i++] = val; return true; } // Compute the network checksum with a 32-bit accumulator using the // "Normal" order, see RFC 1071 for more details. u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value) { u32 checksum = initial_value; std::size_t index = 0; const std::string_view data_view{reinterpret_cast(data), length}; for (u8 b : data_view) { const bool is_hi = index++ % 2 == 0; checksum += is_hi ? b << 8 : b; } while (checksum > 0xFFFF) checksum = (checksum >> 16) + (checksum & 0xFFFF); return ~static_cast(checksum); } // Compute the TCP network checksum with a fake header u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, const void* data, u16 length, u8 protocol) { // Compute the TCP checksum with its pseudo header const u32 source_addr = ntohl(from.sin_addr.s_addr); const u32 destination_addr = ntohl(to.sin_addr.s_addr); const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) + (destination_addr >> 16) + (destination_addr & 0xFFFF) + protocol + length; const u32 tcp_checksum = ComputeNetworkChecksum(data, length, initial_value); return htons(static_cast(tcp_checksum)); } NetworkErrorState SaveNetworkErrorState() { return { errno, #ifdef _WIN32 WSAGetLastError(), #endif }; } void RestoreNetworkErrorState(const NetworkErrorState& state) { errno = state.error; #ifdef _WIN32 WSASetLastError(state.wsa_error); #endif } } // namespace Common