| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/system/diagnostics/networking_log.h" |
| |
| #include <sstream> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/check_is_test.h" |
| #include "base/containers/contains.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| |
| namespace ash { |
| namespace diagnostics { |
| namespace { |
| |
| const char kNewline[] = "\n"; |
| |
| // NetworkingInfo constants: |
| const char kNetworkingInfoSectionName[] = "--- Network Info ---"; |
| const char kNetworkEventsSectionName[] = "--- Network Events ---"; |
| const char kNetworkNameTitle[] = "Name: "; |
| const char kNetworkTypeTitle[] = "Type: "; |
| const char kNetworkStateTitle[] = "State: "; |
| const char kActiveTitle[] = "Active: "; |
| const char kMacAddressTitle[] = "MAC Address: "; |
| |
| // CellularStateProperties constants: |
| const char kCellularIccidTitle[] = "ICCID: "; |
| const char kCellularEidTitle[] = "EID: "; |
| const char kCellularNetworkTechnologyTitle[] = "Technology: "; |
| const char kCellularRoamingTitle[] = "Roaming: "; |
| const char kCellularRoamingStateTitle[] = "Roaming State: "; |
| const char kCellularSignalStrengthTitle[] = "Signal Strength: "; |
| const char kCellularSimLockedTitle[] = "SIM Locked: "; |
| const char kCellularLockTypeTitle[] = "SIM Lock Type: "; |
| |
| // EthernetStateProperties constants: |
| const char kEthernetAuthenticationTitle[] = "Authentication: "; |
| |
| // WiFiStateProperties constants: |
| const char kWifiSignalStrengthTitle[] = "Signal Strength: "; |
| const char kWifiFrequencyTitle[] = "Frequency: "; |
| const char kWifiSsidTitle[] = "SSID: "; |
| const char kWifiBssidTitle[] = "BSSID: "; |
| const char kSecurityTitle[] = "Security: "; |
| |
| // IpConfigProperties constants: |
| const char kNameServersTitle[] = "Name Servers: "; |
| const char kGatewayTitle[] = "Gateway: "; |
| const char kIPAddressTitle[] = "IP Address: "; |
| const char kSubnetMaskTitle[] = "Subnet Mask: "; |
| |
| // Event log entries |
| const char kEventLogFilename[] = "network_events.log"; |
| const char kNetworkAddedEventTemplate[] = |
| "%s network [%s] started in state %s\n"; |
| const char kNetworkRemovedEventTemplate[] = "%s network [%s] removed\n"; |
| const char kNetworkStateChangedEventTemplate[] = |
| "%s network [%s] changed state from %s to %s\n"; |
| const char kJoinedWiFiEventTemplate[] = |
| "%s network [%s] joined SSID '%s' on access point [%s]\n"; |
| const char kLeftWiFiEventTemplate[] = "%s network [%s] left SSID '%s'\n"; |
| const char kAccessPointRoamingEventTemplate[] = |
| "%s network [%s] on SSID '%s' roamed from access point [%s] to [%s]\n"; |
| |
| std::string GetSubnetMask(int prefix) { |
| uint32_t mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF; |
| std::vector<uint32_t> pieces = {mask >> 24, (mask >> 16) & 0xFF, |
| (mask >> 8) & 0xFF, mask & 0xFF}; |
| std::vector<std::string> vec; |
| for (const auto& piece : pieces) { |
| vec.push_back(base::NumberToString(piece)); |
| } |
| |
| return base::JoinString(vec, "."); |
| } |
| |
| std::string GetSecurityType(mojom::SecurityType type) { |
| switch (type) { |
| case mojom::SecurityType::kNone: |
| return "None"; |
| case mojom::SecurityType::kWep8021x: |
| case mojom::SecurityType::kWpaEap: |
| return "EAP"; |
| case mojom::SecurityType::kWepPsk: |
| return "WEP"; |
| case mojom::SecurityType::kWpaPsk: |
| return "PSK (WPA or RSN)"; |
| } |
| } |
| |
| std::string GetSsid(const mojom::NetworkPtr& network) { |
| DCHECK(network->type == mojom::NetworkType::kWiFi); |
| return network->type_properties ? network->type_properties->get_wifi()->ssid |
| : ""; |
| } |
| |
| std::string GetBssid(const mojom::NetworkPtr& network) { |
| DCHECK(network->type == mojom::NetworkType::kWiFi); |
| return network->type_properties ? network->type_properties->get_wifi()->bssid |
| : ""; |
| } |
| |
| bool HasJoinedWiFiNetwork(const mojom::NetworkPtr& old_state, |
| const mojom::NetworkPtr& new_state) { |
| const std::string old_ssid = GetSsid(old_state); |
| return old_ssid.empty() && (old_ssid != GetSsid(new_state)); |
| } |
| |
| bool HasLeftWiFiNetwork(const mojom::NetworkPtr& old_state, |
| const mojom::NetworkPtr& new_state) { |
| const std::string new_ssid = GetSsid(new_state); |
| return new_ssid.empty() && (new_ssid != GetSsid(old_state)); |
| } |
| |
| bool HasRoamedAccessPoint(const mojom::NetworkPtr& old_state, |
| const mojom::NetworkPtr& new_state) { |
| const std::string new_ssid = GetSsid(new_state); |
| return !new_ssid.empty() && (new_ssid == GetSsid(old_state)) && |
| (GetBssid(old_state) != GetBssid(new_state)); |
| } |
| |
| void AddWifiInfoToLog(const mojom::NetworkTypeProperties& type_props, |
| std::stringstream& output) { |
| output << kWifiSignalStrengthTitle << type_props.get_wifi()->signal_strength |
| << kNewline << kWifiFrequencyTitle << type_props.get_wifi()->frequency |
| << kNewline << kWifiSsidTitle << type_props.get_wifi()->ssid |
| << kNewline << kWifiBssidTitle << type_props.get_wifi()->bssid |
| << kNewline << kSecurityTitle |
| << GetSecurityType(type_props.get_wifi()->security) << kNewline; |
| } |
| |
| std::string GetBoolAsString(bool value) { |
| return value ? "True" : "False"; |
| } |
| |
| std::string GetCellularRoamingState(mojom::RoamingState state) { |
| switch (state) { |
| case mojom::RoamingState::kNone: |
| return "None"; |
| case mojom::RoamingState::kHome: |
| return "Home"; |
| case mojom::RoamingState::kRoaming: |
| return "Roaming"; |
| } |
| } |
| |
| std::string GetCellularLockType(mojom::LockType lock_type) { |
| switch (lock_type) { |
| case mojom::LockType::kNone: |
| return "None"; |
| case mojom::LockType::kSimPin: |
| return "sim-pin"; |
| case mojom::LockType::kSimPuk: |
| return "sim-puk"; |
| case mojom::LockType::kNetworkPin: |
| return "network-pin"; |
| } |
| } |
| |
| void AddCellularInfoToLog(const mojom::NetworkTypeProperties& type_props, |
| std::stringstream& output) { |
| output << kCellularIccidTitle << type_props.get_cellular()->iccid << kNewline |
| << kCellularEidTitle << type_props.get_cellular()->eid << kNewline |
| << kCellularNetworkTechnologyTitle |
| << type_props.get_cellular()->network_technology << kNewline |
| << kCellularRoamingTitle |
| << GetBoolAsString(type_props.get_cellular()->roaming) << kNewline |
| << kCellularRoamingStateTitle |
| << GetCellularRoamingState(type_props.get_cellular()->roaming_state) |
| << kNewline << kCellularSignalStrengthTitle |
| << type_props.get_cellular()->signal_strength << kNewline |
| << kCellularSimLockedTitle |
| << GetBoolAsString(type_props.get_cellular()->sim_locked) << kNewline |
| << kCellularLockTypeTitle |
| << GetCellularLockType(type_props.get_cellular()->lock_type) |
| << kNewline; |
| } |
| |
| std::string GetEthernetAuthenticationType(mojom::AuthenticationType type) { |
| switch (type) { |
| case mojom::AuthenticationType::kNone: |
| return "None"; |
| case mojom::AuthenticationType::k8021x: |
| return "EAP"; |
| } |
| } |
| |
| void AddEthernetInfoToLog(const mojom::NetworkTypeProperties& type_props, |
| std::stringstream& output) { |
| output << kEthernetAuthenticationTitle |
| << GetEthernetAuthenticationType( |
| type_props.get_ethernet()->authentication) |
| << kNewline; |
| } |
| |
| void AddTypePropertiesToLog(const mojom::NetworkTypeProperties& type_props, |
| mojom::NetworkType type, |
| std::stringstream& output) { |
| switch (type) { |
| case mojom::NetworkType::kWiFi: |
| AddWifiInfoToLog(type_props, output); |
| break; |
| case mojom::NetworkType::kCellular: |
| AddCellularInfoToLog(type_props, output); |
| break; |
| case mojom::NetworkType::kEthernet: |
| AddEthernetInfoToLog(type_props, output); |
| break; |
| case mojom::NetworkType::kUnsupported: |
| NOTREACHED(); |
| } |
| } |
| |
| void AddIPConfigPropertiesToLog(const mojom::IPConfigProperties& ip_config, |
| std::stringstream& output) { |
| output << kGatewayTitle << ip_config.gateway.value_or("") << kNewline; |
| |
| output << kIPAddressTitle << ip_config.ip_address.value_or("") << kNewline; |
| |
| auto name_servers = base::JoinString( |
| ip_config.name_servers.value_or(std::vector<std::string>()), ", "); |
| |
| output << kNameServersTitle << name_servers << kNewline; |
| |
| // A routing prefix can not be 0, 0 indicates an unset value. |
| auto subnet_mask = ip_config.routing_prefix != 0 |
| ? GetSubnetMask(ip_config.routing_prefix) |
| : ""; |
| output << kSubnetMaskTitle << subnet_mask << kNewline; |
| } |
| |
| std::string GetNetworkStateString(mojom::NetworkState state) { |
| switch (state) { |
| case mojom::NetworkState::kOnline: |
| return "Online"; |
| case mojom::NetworkState::kConnected: |
| return "Connected"; |
| case mojom::NetworkState::kConnecting: |
| return "Connecting"; |
| case mojom::NetworkState::kNotConnected: |
| return "Not Connected"; |
| case mojom::NetworkState::kDisabled: |
| return "Disabled"; |
| case mojom::NetworkState::kPortal: |
| return "Portal"; |
| } |
| } |
| |
| std::string GetNetworkType(mojom::NetworkType type) { |
| switch (type) { |
| case mojom::NetworkType::kWiFi: |
| return "WiFi"; |
| case mojom::NetworkType::kCellular: |
| return "Cellular"; |
| case mojom::NetworkType::kEthernet: |
| return "Ethernet"; |
| case mojom::NetworkType::kUnsupported: |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace |
| |
| NetworkingLog::NetworkingLog(const base::FilePath& log_base_path) |
| : event_log_(log_base_path.Append(kEventLogFilename)) {} |
| |
| NetworkingLog::~NetworkingLog() = default; |
| |
| std::string NetworkingLog::GetNetworkInfo() const { |
| std::stringstream output; |
| |
| output << kNetworkingInfoSectionName << kNewline; |
| for (const auto& pair : latest_network_states_) { |
| const mojom::NetworkPtr& network = pair.second; |
| |
| output << kNewline << kNetworkNameTitle << network->name << kNewline |
| << kNetworkTypeTitle << GetNetworkType(network->type) << kNewline |
| << kNetworkStateTitle << GetNetworkStateString(network->state) |
| << kNewline << kActiveTitle |
| << (network->observer_guid == active_guid_ ? "True" : "False") |
| << kNewline << kMacAddressTitle << network->mac_address.value_or("") |
| << kNewline; |
| |
| if (network->type_properties) { |
| AddTypePropertiesToLog(*network->type_properties, network->type, output); |
| } |
| |
| if (network->ip_config) { |
| AddIPConfigPropertiesToLog(*network->ip_config, output); |
| } |
| } |
| |
| return output.str(); |
| } |
| |
| std::string NetworkingLog::GetNetworkEvents() const { |
| return std::string(kNetworkEventsSectionName) + kNewline + kNewline + |
| event_log_.GetContents(); |
| } |
| |
| void NetworkingLog::UpdateNetworkList( |
| const std::vector<std::string>& observer_guids, |
| std::string active_guid) { |
| // If a network is no longer valid, remove it from the map. |
| for (auto iter = latest_network_states_.begin(); |
| iter != latest_network_states_.end();) { |
| if (!base::Contains(observer_guids, iter->first)) { |
| LogNetworkRemoved(iter->second); |
| iter = latest_network_states_.erase(iter); |
| continue; |
| } |
| |
| ++iter; |
| } |
| |
| active_guid_ = active_guid; |
| ++update_network_list_call_count_for_testing_; |
| } |
| |
| void NetworkingLog::UpdateNetworkState(mojom::NetworkPtr network) { |
| if (network.is_null()) { |
| LOG(ERROR) << "Network to log update is null"; |
| return; |
| } |
| |
| if (!base::Contains(latest_network_states_, network->observer_guid)) { |
| LogNetworkAdded(network); |
| latest_network_states_.emplace(network->observer_guid, std::move(network)); |
| return; |
| } |
| |
| LogNetworkChanges(network); |
| latest_network_states_[network->observer_guid] = std::move(network); |
| } |
| |
| void NetworkingLog::LogEvent(const std::string& event_string) { |
| const std::string datetime = |
| base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(base::Time::Now())); |
| event_log_.Append(datetime + " - " + event_string); |
| } |
| |
| void NetworkingLog::LogNetworkAdded(const mojom::NetworkPtr& network) { |
| const std::string line = base::StringPrintf( |
| kNetworkAddedEventTemplate, GetNetworkType(network->type).c_str(), |
| network->mac_address.value_or("").c_str(), |
| GetNetworkStateString(network->state).c_str()); |
| LogEvent(line); |
| } |
| |
| void NetworkingLog::LogNetworkRemoved(const mojom::NetworkPtr& network) { |
| if (network.is_null()) { |
| LOG(ERROR) << "Network to log removal is null"; |
| return; |
| } |
| |
| const std::string line = base::StringPrintf( |
| kNetworkRemovedEventTemplate, GetNetworkType(network->type).c_str(), |
| network->mac_address.value_or("").c_str()); |
| LogEvent(line); |
| } |
| |
| void NetworkingLog::LogNetworkChanges(const mojom::NetworkPtr& new_state) { |
| DCHECK(base::Contains(latest_network_states_, new_state->observer_guid)); |
| const mojom::NetworkPtr& old_state = |
| latest_network_states_.at(new_state->observer_guid); |
| |
| if (new_state->type == mojom::NetworkType::kWiFi) { |
| if (HasJoinedWiFiNetwork(old_state, new_state)) { |
| LogJoinedWiFiNetwork(new_state); |
| } else if (HasLeftWiFiNetwork(old_state, new_state)) { |
| LogLeftWiFiNetwork(new_state, GetSsid(old_state)); |
| } else if (HasRoamedAccessPoint(old_state, new_state)) { |
| LogWiFiRoamedAccessPoint(new_state, GetBssid(old_state)); |
| } |
| } |
| |
| if (old_state->state != new_state->state) { |
| LogNetworkStateChanged(old_state, new_state); |
| } |
| } |
| |
| void NetworkingLog::LogNetworkStateChanged(const mojom::NetworkPtr& old_state, |
| const mojom::NetworkPtr& new_state) { |
| const std::string line = |
| base::StringPrintf(kNetworkStateChangedEventTemplate, |
| GetNetworkType(new_state->type).c_str(), |
| new_state->mac_address.value_or("").c_str(), |
| GetNetworkStateString(old_state->state).c_str(), |
| GetNetworkStateString(new_state->state).c_str()); |
| LogEvent(line); |
| } |
| |
| void NetworkingLog::LogJoinedWiFiNetwork(const mojom::NetworkPtr& network) { |
| const std::string line = base::StringPrintf( |
| kJoinedWiFiEventTemplate, GetNetworkType(network->type).c_str(), |
| network->mac_address.value_or("").c_str(), GetSsid(network).c_str(), |
| GetBssid(network).c_str()); |
| LogEvent(line); |
| } |
| |
| void NetworkingLog::LogLeftWiFiNetwork(const mojom::NetworkPtr& network, |
| const std::string& old_ssid) { |
| const std::string line = base::StringPrintf( |
| kLeftWiFiEventTemplate, GetNetworkType(network->type).c_str(), |
| network->mac_address.value_or("").c_str(), old_ssid.c_str()); |
| LogEvent(line); |
| } |
| |
| void NetworkingLog::LogWiFiRoamedAccessPoint(const mojom::NetworkPtr& network, |
| const std::string& old_bssid) { |
| const std::string line = base::StringPrintf( |
| kAccessPointRoamingEventTemplate, GetNetworkType(network->type).c_str(), |
| network->mac_address.value_or("").c_str(), GetSsid(network).c_str(), |
| old_bssid.c_str(), GetBssid(network).c_str()); |
| LogEvent(line); |
| } |
| |
| size_t NetworkingLog::update_network_list_call_count_for_testing() const { |
| CHECK_IS_TEST(); |
| return update_network_list_call_count_for_testing_; |
| } |
| |
| } // namespace diagnostics |
| } // namespace ash |