| // Copyright 2016 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "patchpanel/dbus/client.h" | |
| #include <fcntl.h> | |
| #include <algorithm> | |
| #include <optional> | |
| #include <ostream> | |
| #include <base/functional/bind.h> | |
| #include <base/logging.h> | |
| #include <base/memory/weak_ptr.h> | |
| #include <base/strings/string_util.h> | |
| #include <base/synchronization/waitable_event.h> | |
| #include <base/task/bind_post_task.h> | |
| #include <base/time/time.h> | |
| #include <brillo/errors/error.h> | |
| #include <brillo/http/http_transport.h> | |
| #include <chromeos/dbus/service_constants.h> | |
| #include <chromeos/net-base/technology.h> | |
| #include <dbus/message.h> | |
| #include <dbus/object_path.h> | |
| #include <patchpanel/proto_bindings/patchpanel_service.pb.h> | |
| #include <patchpanel/proto_bindings/traffic_annotation.pb.h> | |
| #include "patchpanel/dbus-proxies.h" | |
| namespace patchpanel { | |
| namespace { | |
| using org::chromium::PatchPanelProxyInterface; | |
| patchpanel::TrafficCounter::Source ConvertTrafficSource( | |
| Client::TrafficSource source) { | |
| switch (source) { | |
| case Client::TrafficSource::kUnknown: | |
| return patchpanel::TrafficCounter::UNKNOWN; | |
| case Client::TrafficSource::kChrome: | |
| return patchpanel::TrafficCounter::CHROME; | |
| case Client::TrafficSource::kUser: | |
| return patchpanel::TrafficCounter::USER; | |
| case Client::TrafficSource::kUpdateEngine: | |
| return patchpanel::TrafficCounter::UPDATE_ENGINE; | |
| case Client::TrafficSource::kSystem: | |
| return patchpanel::TrafficCounter::SYSTEM; | |
| case Client::TrafficSource::kVpn: | |
| return patchpanel::TrafficCounter::VPN; | |
| case Client::TrafficSource::kArc: | |
| return patchpanel::TrafficCounter::ARC; | |
| case Client::TrafficSource::kBorealisVM: | |
| return patchpanel::TrafficCounter::BOREALIS_VM; | |
| case Client::TrafficSource::kBruschettaVM: | |
| return patchpanel::TrafficCounter::BRUSCHETTA_VM; | |
| case Client::TrafficSource::kCrostiniVM: | |
| return patchpanel::TrafficCounter::CROSTINI_VM; | |
| case Client::TrafficSource::kParallelsVM: | |
| return patchpanel::TrafficCounter::PARALLELS_VM; | |
| case Client::TrafficSource::kTethering: | |
| return patchpanel::TrafficCounter::TETHERING; | |
| case Client::TrafficSource::kWiFiDirect: | |
| return patchpanel::TrafficCounter::WIFI_DIRECT; | |
| case Client::TrafficSource::kWiFiLOHS: | |
| return patchpanel::TrafficCounter::WIFI_LOHS; | |
| } | |
| } | |
| Client::TrafficSource ConvertTrafficSource( | |
| patchpanel::TrafficCounter::Source source) { | |
| switch (source) { | |
| case patchpanel::TrafficCounter::CHROME: | |
| return Client::TrafficSource::kChrome; | |
| case patchpanel::TrafficCounter::USER: | |
| return Client::TrafficSource::kUser; | |
| case patchpanel::TrafficCounter::UPDATE_ENGINE: | |
| return Client::TrafficSource::kUpdateEngine; | |
| case patchpanel::TrafficCounter::SYSTEM: | |
| return Client::TrafficSource::kSystem; | |
| case patchpanel::TrafficCounter::VPN: | |
| return Client::TrafficSource::kVpn; | |
| case patchpanel::TrafficCounter::ARC: | |
| return Client::TrafficSource::kArc; | |
| case patchpanel::TrafficCounter::BOREALIS_VM: | |
| return Client::TrafficSource::kBorealisVM; | |
| case patchpanel::TrafficCounter::BRUSCHETTA_VM: | |
| return Client::TrafficSource::kBruschettaVM; | |
| case patchpanel::TrafficCounter::CROSTINI_VM: | |
| return Client::TrafficSource::kCrostiniVM; | |
| case patchpanel::TrafficCounter::PARALLELS_VM: | |
| return Client::TrafficSource::kParallelsVM; | |
| case patchpanel::TrafficCounter::TETHERING: | |
| return Client::TrafficSource::kTethering; | |
| case patchpanel::TrafficCounter::WIFI_DIRECT: | |
| return Client::TrafficSource::kWiFiDirect; | |
| case patchpanel::TrafficCounter::WIFI_LOHS: | |
| return Client::TrafficSource::kWiFiLOHS; | |
| default: | |
| return Client::TrafficSource::kUnknown; | |
| } | |
| } | |
| patchpanel::NeighborReachabilityEventSignal::Role ConvertNeighborRole( | |
| Client::NeighborRole role) { | |
| switch (role) { | |
| case Client::NeighborRole::kGateway: | |
| return patchpanel::NeighborReachabilityEventSignal::GATEWAY; | |
| case Client::NeighborRole::kDnsServer: | |
| return patchpanel::NeighborReachabilityEventSignal::DNS_SERVER; | |
| case Client::NeighborRole::kGatewayAndDnsServer: | |
| return patchpanel::NeighborReachabilityEventSignal:: | |
| GATEWAY_AND_DNS_SERVER; | |
| } | |
| } | |
| patchpanel::NeighborReachabilityEventSignal::EventType ConvertNeighborStatus( | |
| Client::NeighborStatus status) { | |
| switch (status) { | |
| case Client::NeighborStatus::kFailed: | |
| return patchpanel::NeighborReachabilityEventSignal::FAILED; | |
| case Client::NeighborStatus::kReachable: | |
| return patchpanel::NeighborReachabilityEventSignal::REACHABLE; | |
| } | |
| } | |
| patchpanel::ModifyPortRuleRequest::Operation ConvertFirewallRequestOperation( | |
| Client::FirewallRequestOperation op) { | |
| switch (op) { | |
| case Client::FirewallRequestOperation::kCreate: | |
| return ModifyPortRuleRequest::CREATE; | |
| case Client::FirewallRequestOperation::kDelete: | |
| return ModifyPortRuleRequest::DELETE; | |
| } | |
| } | |
| patchpanel::ModifyPortRuleRequest::RuleType ConvertFirewallRequestType( | |
| Client::FirewallRequestType type) { | |
| switch (type) { | |
| case Client::FirewallRequestType::kAccess: | |
| return ModifyPortRuleRequest::ACCESS; | |
| case Client::FirewallRequestType::kLockdown: | |
| return ModifyPortRuleRequest::LOCKDOWN; | |
| case Client::FirewallRequestType::kForwarding: | |
| return ModifyPortRuleRequest::FORWARDING; | |
| } | |
| } | |
| patchpanel::ModifyPortRuleRequest::Protocol ConvertFirewallRequestProtocol( | |
| Client::FirewallRequestProtocol protocol) { | |
| switch (protocol) { | |
| case Client::FirewallRequestProtocol::kTcp: | |
| return ModifyPortRuleRequest::TCP; | |
| case Client::FirewallRequestProtocol::kUdp: | |
| return ModifyPortRuleRequest::UDP; | |
| } | |
| } | |
| patchpanel::SetDnsRedirectionRuleRequest::RuleType | |
| ConvertDnsRedirectionRequestType(Client::DnsRedirectionRequestType type) { | |
| switch (type) { | |
| case Client::DnsRedirectionRequestType::kDefault: | |
| return patchpanel::SetDnsRedirectionRuleRequest::DEFAULT; | |
| case Client::DnsRedirectionRequestType::kArc: | |
| return patchpanel::SetDnsRedirectionRuleRequest::ARC; | |
| case Client::DnsRedirectionRequestType::kUser: | |
| return patchpanel::SetDnsRedirectionRuleRequest::USER; | |
| case Client::DnsRedirectionRequestType::kExcludeDestination: | |
| return patchpanel::SetDnsRedirectionRuleRequest::EXCLUDE_DESTINATION; | |
| } | |
| } | |
| patchpanel::SetFeatureFlagRequest::FeatureFlag ConvertFeatureFlag( | |
| Client::FeatureFlag flag) { | |
| switch (flag) { | |
| case Client::FeatureFlag::kWiFiQoS: | |
| return patchpanel::SetFeatureFlagRequest::FeatureFlag:: | |
| SetFeatureFlagRequest_FeatureFlag_WIFI_QOS; | |
| case Client::FeatureFlag::kClat: | |
| return patchpanel::SetFeatureFlagRequest::FeatureFlag:: | |
| SetFeatureFlagRequest_FeatureFlag_CLAT; | |
| } | |
| } | |
| std::optional<net_base::IPv4CIDR> ConvertIPv4Subnet(const IPv4Subnet& in) { | |
| return net_base::IPv4CIDR::CreateFromBytesAndPrefix( | |
| in.addr(), static_cast<int>(in.prefix_len())); | |
| } | |
| std::optional<Client::TrafficCounter> ConvertTrafficCounter( | |
| const TrafficCounter& in) { | |
| auto out = std::make_optional<Client::TrafficCounter>(); | |
| out->traffic.rx_bytes = in.rx_bytes(); | |
| out->traffic.tx_bytes = in.tx_bytes(); | |
| out->traffic.rx_packets = in.rx_packets(); | |
| out->traffic.tx_packets = in.tx_packets(); | |
| out->ifname = in.device(); | |
| out->source = ConvertTrafficSource(in.source()); | |
| switch (in.ip_family()) { | |
| case patchpanel::TrafficCounter::IPV4: | |
| out->ip_family = Client::IPFamily::kIPv4; | |
| break; | |
| case patchpanel::TrafficCounter::IPV6: | |
| out->ip_family = Client::IPFamily::kIPv6; | |
| break; | |
| default: | |
| LOG(ERROR) << __func__ << ": Unknown IpFamily " | |
| << patchpanel::TrafficCounter::IpFamily_Name(in.ip_family()); | |
| return std::nullopt; | |
| } | |
| return out; | |
| } | |
| std::optional<Client::VirtualDevice> ConvertVirtualDevice( | |
| const NetworkDevice& in) { | |
| auto out = std::make_optional<Client::VirtualDevice>(); | |
| out->ifname = in.ifname(); | |
| out->phys_ifname = in.phys_ifname(); | |
| out->guest_ifname = in.guest_ifname(); | |
| out->ipv4_addr = net_base::IPv4Address(in.ipv4_addr()); | |
| out->host_ipv4_addr = net_base::IPv4Address(in.host_ipv4_addr()); | |
| out->ipv4_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| out->dns_proxy_ipv4_addr = | |
| net_base::IPv4Address::CreateFromBytes(in.dns_proxy_ipv4_addr()); | |
| out->dns_proxy_ipv6_addr = | |
| net_base::IPv6Address::CreateFromBytes(in.dns_proxy_ipv6_addr()); | |
| switch (in.technology_type()) { | |
| case patchpanel::NetworkDevice::CELLULAR: | |
| out->technology = net_base::Technology::kCellular; | |
| break; | |
| case patchpanel::NetworkDevice::ETHERNET: | |
| out->technology = net_base::Technology::kEthernet; | |
| break; | |
| case patchpanel::NetworkDevice::WIFI: | |
| out->technology = net_base::Technology::kWiFi; | |
| break; | |
| default: | |
| out->technology = std::nullopt; | |
| break; | |
| } | |
| switch (in.guest_type()) { | |
| case patchpanel::NetworkDevice::ARC: | |
| out->guest_type = Client::GuestType::kArcContainer; | |
| break; | |
| case patchpanel::NetworkDevice::ARCVM: | |
| out->guest_type = Client::GuestType::kArcVm; | |
| break; | |
| case patchpanel::NetworkDevice::TERMINA_VM: | |
| out->guest_type = Client::GuestType::kTerminaVm; | |
| break; | |
| case patchpanel::NetworkDevice::PARALLELS_VM: | |
| out->guest_type = Client::GuestType::kParallelsVm; | |
| break; | |
| default: | |
| LOG(ERROR) << __func__ << ": Unknown GuestType " | |
| << patchpanel::NetworkDevice::GuestType_Name(in.guest_type()); | |
| return std::nullopt; | |
| } | |
| return out; | |
| } | |
| std::optional<Client::TerminaAllocation> ConvertTerminaAllocation( | |
| const TerminaVmStartupResponse& in) { | |
| if (!in.has_ipv4_subnet()) { | |
| LOG(ERROR) << __func__ << ": No Termina IPv4 subnet found"; | |
| return std::nullopt; | |
| } | |
| if (!in.has_container_ipv4_subnet()) { | |
| LOG(ERROR) << __func__ << ": No Termina container IPv4 subnet found"; | |
| return std::nullopt; | |
| } | |
| const auto termina_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| const auto termina_address = | |
| net_base::IPv4Address::CreateFromBytes(in.ipv4_address()); | |
| const auto gateway_address = | |
| net_base::IPv4Address::CreateFromBytes(in.gateway_ipv4_address()); | |
| const auto container_subnet = ConvertIPv4Subnet(in.container_ipv4_subnet()); | |
| const auto container_address = | |
| net_base::IPv4Address::CreateFromBytes(in.container_ipv4_address()); | |
| if (!termina_subnet) { | |
| LOG(ERROR) << __func__ << ": Invalid Termina IPv4 subnet"; | |
| return std::nullopt; | |
| } | |
| if (!termina_address || !termina_subnet->InSameSubnetWith(*termina_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Termina IPv4 address"; | |
| return std::nullopt; | |
| } | |
| if (!gateway_address || !termina_subnet->InSameSubnetWith(*gateway_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Termina gateway IPv4 address"; | |
| return std::nullopt; | |
| } | |
| if (!container_subnet) { | |
| LOG(ERROR) << __func__ << ": Invalid Termina container IPv4 subnet"; | |
| return std::nullopt; | |
| } | |
| if (!container_address) { | |
| LOG(ERROR) << __func__ << ": Invalid Termina container IPv4 address"; | |
| return std::nullopt; | |
| } | |
| Client::TerminaAllocation termina_alloc; | |
| termina_alloc.tap_device_ifname = in.tap_device_ifname(); | |
| termina_alloc.termina_ipv4_subnet = *termina_subnet; | |
| termina_alloc.termina_ipv4_address = *termina_address; | |
| termina_alloc.gateway_ipv4_address = *gateway_address; | |
| termina_alloc.container_ipv4_subnet = *container_subnet; | |
| termina_alloc.container_ipv4_address = *container_address; | |
| return termina_alloc; | |
| } | |
| std::optional<Client::ParallelsAllocation> ConvertParallelsAllocation( | |
| const ParallelsVmStartupResponse& in) { | |
| if (!in.has_ipv4_subnet()) { | |
| LOG(ERROR) << __func__ << ": No Parallels IPv4 subnet found"; | |
| return std::nullopt; | |
| } | |
| const auto parallels_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| const auto parallels_address = | |
| net_base::IPv4Address::CreateFromBytes(in.ipv4_address()); | |
| if (!parallels_subnet) { | |
| LOG(ERROR) << __func__ << ": Invalid Parallels IPv4 subnet"; | |
| return std::nullopt; | |
| } | |
| if (!parallels_address || | |
| !parallels_subnet->InSameSubnetWith(*parallels_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Parallels IPv4 address"; | |
| return std::nullopt; | |
| } | |
| Client::ParallelsAllocation parallels_alloc; | |
| parallels_alloc.tap_device_ifname = in.tap_device_ifname(); | |
| parallels_alloc.parallels_ipv4_subnet = *parallels_subnet; | |
| parallels_alloc.parallels_ipv4_address = *parallels_address; | |
| return parallels_alloc; | |
| } | |
| std::optional<Client::BruschettaAllocation> ConvertBruschettaAllocation( | |
| const BruschettaVmStartupResponse& in) { | |
| if (in.tap_device_ifname().empty()) { | |
| LOG(ERROR) << __func__ << ": No Bruschetta device interface found"; | |
| return std::nullopt; | |
| } | |
| if (!in.has_ipv4_subnet()) { | |
| LOG(ERROR) << __func__ << ": No Bruschetta IPv4 subnet found"; | |
| return std::nullopt; | |
| } | |
| const auto bruschetta_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| const auto bruschetta_address = | |
| net_base::IPv4Address::CreateFromBytes(in.ipv4_address()); | |
| const auto gateway_address = | |
| net_base::IPv4Address::CreateFromBytes(in.gateway_ipv4_address()); | |
| if (!bruschetta_subnet) { | |
| LOG(ERROR) << __func__ << ": Invalid Bruschetta IPv4 subnet"; | |
| return std::nullopt; | |
| } | |
| if (!bruschetta_address || | |
| !bruschetta_subnet->InSameSubnetWith(*bruschetta_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Bruschetta IPv4 address"; | |
| return std::nullopt; | |
| } | |
| if (!gateway_address || | |
| !bruschetta_subnet->InSameSubnetWith(*gateway_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Bruschetta gateway IPv4 address"; | |
| return std::nullopt; | |
| } | |
| Client::BruschettaAllocation bruschetta_alloc; | |
| bruschetta_alloc.tap_device_ifname = in.tap_device_ifname(); | |
| bruschetta_alloc.bruschetta_ipv4_subnet = *bruschetta_subnet; | |
| bruschetta_alloc.bruschetta_ipv4_address = *bruschetta_address; | |
| bruschetta_alloc.gateway_ipv4_address = *gateway_address; | |
| return bruschetta_alloc; | |
| } | |
| std::optional<Client::BorealisAllocation> ConvertBorealisAllocation( | |
| const BorealisVmStartupResponse& in) { | |
| if (in.tap_device_ifname().empty()) { | |
| LOG(ERROR) << __func__ << ": No Borealis device interface found"; | |
| return std::nullopt; | |
| } | |
| if (!in.has_ipv4_subnet()) { | |
| LOG(ERROR) << __func__ << ": No Borealis IPv4 subnet found"; | |
| return std::nullopt; | |
| } | |
| const auto borealis_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| const auto borealis_address = | |
| net_base::IPv4Address::CreateFromBytes(in.ipv4_address()); | |
| const auto gateway_address = | |
| net_base::IPv4Address::CreateFromBytes(in.gateway_ipv4_address()); | |
| if (!borealis_subnet) { | |
| LOG(ERROR) << __func__ << ": Invalid Borealis IPv4 subnet"; | |
| return std::nullopt; | |
| } | |
| if (!borealis_address || | |
| !borealis_subnet->InSameSubnetWith(*borealis_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Borealis IPv4 address"; | |
| return std::nullopt; | |
| } | |
| if (!gateway_address || | |
| !borealis_subnet->InSameSubnetWith(*gateway_address)) { | |
| LOG(ERROR) << __func__ << ": Invalid Borealis gateway IPv4 address"; | |
| return std::nullopt; | |
| } | |
| Client::BorealisAllocation borealis_alloc; | |
| borealis_alloc.tap_device_ifname = in.tap_device_ifname(); | |
| borealis_alloc.borealis_ipv4_subnet = *borealis_subnet; | |
| borealis_alloc.borealis_ipv4_address = *borealis_address; | |
| borealis_alloc.gateway_ipv4_address = *gateway_address; | |
| return borealis_alloc; | |
| } | |
| std::optional<Client::NetworkClientInfo> ConvertNetworkClientInfo( | |
| const NetworkClientInfo& in) { | |
| auto out = std::make_optional<Client::NetworkClientInfo>(); | |
| std::copy(in.mac_addr().begin(), in.mac_addr().end(), | |
| std::back_inserter(out->mac_addr)); | |
| const auto ipv4_addr = net_base::IPv4Address::CreateFromBytes(in.ipv4_addr()); | |
| if (!ipv4_addr) { | |
| LOG(ERROR) << "Failed to convert protobuf bytes to IPv4Address. size=" | |
| << in.ipv4_addr().size(); | |
| return std::nullopt; | |
| } | |
| out->ipv4_addr = *ipv4_addr; | |
| for (const auto& in_ipv6_addr : in.ipv6_addresses()) { | |
| const auto ipv6_addr = net_base::IPv6Address::CreateFromBytes(in_ipv6_addr); | |
| if (!ipv6_addr) { | |
| LOG(ERROR) << "Failed to convert protobuf bytes to IPv6Address. size=" | |
| << in_ipv6_addr.size(); | |
| return std::nullopt; | |
| } | |
| out->ipv6_addresses.push_back(*ipv6_addr); | |
| } | |
| out->hostname = in.hostname(); | |
| out->vendor_class = in.vendor_class(); | |
| return out; | |
| } | |
| std::optional<Client::DownstreamNetwork> ConvertDownstreamNetwork( | |
| const DownstreamNetwork& in) { | |
| auto out = std::make_optional<Client::DownstreamNetwork>(); | |
| out->network_id = in.network_id(); | |
| out->ifname = in.downstream_ifname(); | |
| const auto ipv4_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| if (!ipv4_subnet) { | |
| LOG(ERROR) << "Failed to create IPv4CIDR for ipv4_subnet"; | |
| return std::nullopt; | |
| } | |
| out->ipv4_subnet = *ipv4_subnet; | |
| const auto ipv4_gateway_addr = | |
| net_base::IPv4Address::CreateFromBytes(in.ipv4_gateway_addr()); | |
| if (!ipv4_gateway_addr) { | |
| LOG(ERROR) << "Failed to create IPv4Address for gateway address: size=" | |
| << in.ipv4_gateway_addr().size(); | |
| return std::nullopt; | |
| } | |
| out->ipv4_gateway_addr = *ipv4_gateway_addr; | |
| return out; | |
| } | |
| patchpanel::NetworkTechnology ConvertNetworkTechnology( | |
| Client::NetworkTechnology network_technology) { | |
| switch (network_technology) { | |
| case Client::NetworkTechnology::kCellular: | |
| return patchpanel::NetworkTechnology::CELLULAR; | |
| case Client::NetworkTechnology::kEthernet: | |
| return patchpanel::NetworkTechnology::ETHERNET; | |
| case Client::NetworkTechnology::kVPN: | |
| return patchpanel::NetworkTechnology::VPN; | |
| case Client::NetworkTechnology::kWiFi: | |
| return patchpanel::NetworkTechnology::WIFI; | |
| } | |
| } | |
| TagSocketRequest::VpnRoutingPolicy ConvertVpnRoutingPolicy( | |
| Client::VpnRoutingPolicy policy) { | |
| switch (policy) { | |
| case Client::VpnRoutingPolicy::kDefaultRouting: | |
| return TagSocketRequest::DEFAULT_ROUTING; | |
| case Client::VpnRoutingPolicy::kBypassVpn: | |
| return TagSocketRequest::BYPASS_VPN; | |
| case Client::VpnRoutingPolicy::kRouteOnVpn: | |
| return TagSocketRequest::ROUTE_ON_VPN; | |
| } | |
| } | |
| traffic_annotation::TrafficAnnotation::Id ConvertTrafficAnnotationId( | |
| Client::TrafficAnnotationId id) { | |
| switch (id) { | |
| case Client::TrafficAnnotationId::kUnspecified: | |
| return traffic_annotation::TrafficAnnotation::UNSPECIFIED; | |
| case Client::TrafficAnnotationId::kShillPortalDetector: | |
| return traffic_annotation::TrafficAnnotation::SHILL_PORTAL_DETECTOR; | |
| case Client::TrafficAnnotationId::kShillCapportClient: | |
| return traffic_annotation::TrafficAnnotation::SHILL_CAPPORT_CLIENT; | |
| case Client::TrafficAnnotationId::kShillCarrierEntitlement: | |
| return traffic_annotation::TrafficAnnotation::SHILL_CARRIER_ENTITLEMENT; | |
| } | |
| } | |
| std::optional<Client::NeighborReachabilityEvent> | |
| ConvertNeighborReachabilityEvent(const NeighborReachabilityEventSignal& in) { | |
| auto out = std::make_optional<Client::NeighborReachabilityEvent>(); | |
| out->ifindex = in.ifindex(); | |
| out->ip_addr = in.ip_addr(); | |
| switch (in.role()) { | |
| case patchpanel::NeighborReachabilityEventSignal::GATEWAY: | |
| out->role = Client::NeighborRole::kGateway; | |
| break; | |
| case patchpanel::NeighborReachabilityEventSignal::DNS_SERVER: | |
| out->role = Client::NeighborRole::kDnsServer; | |
| break; | |
| case patchpanel::NeighborReachabilityEventSignal::GATEWAY_AND_DNS_SERVER: | |
| out->role = Client::NeighborRole::kGatewayAndDnsServer; | |
| break; | |
| default: | |
| LOG(ERROR) << __func__ << ": Unknown NeighborReachability role " | |
| << patchpanel::NeighborReachabilityEventSignal::Role_Name( | |
| in.role()); | |
| return std::nullopt; | |
| } | |
| switch (in.type()) { | |
| case patchpanel::NeighborReachabilityEventSignal::FAILED: | |
| out->status = Client::NeighborStatus::kFailed; | |
| break; | |
| case patchpanel::NeighborReachabilityEventSignal::REACHABLE: | |
| out->status = Client::NeighborStatus::kReachable; | |
| break; | |
| default: | |
| LOG(ERROR) << __func__ << ": Unknown NeighborReachability event type " | |
| << patchpanel::NeighborReachabilityEventSignal::EventType_Name( | |
| in.type()); | |
| return std::nullopt; | |
| } | |
| return out; | |
| } | |
| std::optional<Client::VirtualDeviceEvent> ConvertVirtualDeviceEvent( | |
| const NetworkDeviceChangedSignal& in) { | |
| switch (in.event()) { | |
| case patchpanel::NetworkDeviceChangedSignal::DEVICE_ADDED: | |
| return Client::VirtualDeviceEvent::kAdded; | |
| case patchpanel::NetworkDeviceChangedSignal::DEVICE_REMOVED: | |
| return Client::VirtualDeviceEvent::kRemoved; | |
| default: | |
| LOG(ERROR) << __func__ << ": Unknown NetworkDeviceChangedSignal event " | |
| << patchpanel::NetworkDeviceChangedSignal::Event_Name( | |
| in.event()); | |
| return std::nullopt; | |
| } | |
| } | |
| std::optional<Client::ConnectedNamespace> ConvertConnectedNamespace( | |
| const ConnectNamespaceResponse& in) { | |
| auto out = std::make_optional<Client::ConnectedNamespace>(); | |
| const auto ipv4_subnet = ConvertIPv4Subnet(in.ipv4_subnet()); | |
| if (!ipv4_subnet) { | |
| LOG(ERROR) << "Failed to create IPv4CIDR for ipv4_subnet"; | |
| return std::nullopt; | |
| } | |
| out->ipv4_subnet = *ipv4_subnet; | |
| out->peer_ifname = in.peer_ifname(); | |
| out->peer_ipv4_address = net_base::IPv4Address(in.peer_ipv4_address()); | |
| out->host_ifname = in.host_ifname(); | |
| out->host_ipv4_address = net_base::IPv4Address(in.host_ipv4_address()); | |
| out->netns_name = in.netns_name(); | |
| return out; | |
| } | |
| std::ostream& operator<<(std::ostream& stream, | |
| const ModifyPortRuleRequest& request) { | |
| stream << "{ operation: " | |
| << ModifyPortRuleRequest::Operation_Name(request.op()) | |
| << ", rule type: " | |
| << ModifyPortRuleRequest::RuleType_Name(request.type()) | |
| << ", protocol: " | |
| << ModifyPortRuleRequest::Protocol_Name(request.proto()); | |
| if (!request.input_ifname().empty()) { | |
| stream << ", input interface name: " << request.input_ifname(); | |
| } | |
| if (!request.input_dst_ip().empty()) { | |
| stream << ", input destination IP: " << request.input_dst_ip(); | |
| } | |
| stream << ", input destination port: " << request.input_dst_port(); | |
| if (!request.dst_ip().empty()) { | |
| stream << ", destination IP: " << request.dst_ip(); | |
| } | |
| if (request.dst_port() != 0) { | |
| stream << ", destination port: " << request.dst_port(); | |
| } | |
| stream << " }"; | |
| return stream; | |
| } | |
| std::ostream& operator<<(std::ostream& stream, | |
| const SetDnsRedirectionRuleRequest& request) { | |
| stream << "{ proxy type: " | |
| << SetDnsRedirectionRuleRequest::RuleType_Name(request.type()); | |
| if (!request.input_ifname().empty()) { | |
| stream << ", input interface name: " << request.input_ifname(); | |
| } | |
| if (!request.proxy_address().empty()) { | |
| stream << ", proxy IPv4 address: " << request.proxy_address(); | |
| } | |
| if (!request.nameservers().empty()) { | |
| std::vector<std::string> nameservers; | |
| for (const auto& ns : request.nameservers()) { | |
| nameservers.emplace_back(ns); | |
| } | |
| stream << ", nameserver(s): " << base::JoinString(nameservers, ","); | |
| } | |
| stream << " }"; | |
| return stream; | |
| } | |
| std::ostream& operator<<(std::ostream& stream, const Client::FeatureFlag flag) { | |
| switch (flag) { | |
| case Client::FeatureFlag::kWiFiQoS: | |
| return stream << "WiFiQoS"; | |
| case Client::FeatureFlag::kClat: | |
| return stream << "Clat"; | |
| } | |
| } | |
| // Prepares a pair of ScopedFDs corresponding to the write end (pair first | |
| // element) and read end (pair second element) of a Linux pipe. The client must | |
| // keep the write end alive until the setup requested from patchpanel is not | |
| // necessary anymore. | |
| std::pair<base::ScopedFD, base::ScopedFD> CreateLifelineFd() { | |
| int pipe_fds[2] = {-1, -1}; | |
| if (pipe2(pipe_fds, O_CLOEXEC) < 0) { | |
| PLOG(ERROR) << "Failed to create a pair of fds with pipe2()"; | |
| return {}; | |
| } | |
| return {base::ScopedFD(pipe_fds[0]), base::ScopedFD(pipe_fds[1])}; | |
| } | |
| void OnGetTrafficCountersDBusResponse( | |
| Client::GetTrafficCountersCallback callback, | |
| const TrafficCountersResponse& response) { | |
| std::vector<Client::TrafficCounter> counters; | |
| for (const auto& proto_counter : response.counters()) { | |
| auto client_counter = ConvertTrafficCounter(proto_counter); | |
| if (client_counter) { | |
| counters.push_back(*client_counter); | |
| } | |
| } | |
| std::move(callback).Run(counters); | |
| } | |
| void OnGetTrafficCountersError(Client::GetTrafficCountersCallback callback, | |
| brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "(): " << error->GetMessage(); | |
| std::move(callback).Run({}); | |
| } | |
| void OnNetworkDeviceChanged( | |
| Client::VirtualDeviceEventHandler handler, | |
| const patchpanel::NetworkDeviceChangedSignal& signal) { | |
| const auto event = ConvertVirtualDeviceEvent(signal); | |
| if (!event) { | |
| return; | |
| } | |
| const auto device = ConvertVirtualDevice(signal.device()); | |
| if (!device) { | |
| return; | |
| } | |
| handler.Run(*event, *device); | |
| } | |
| void OnNeighborReachabilityEvent( | |
| const Client::NeighborReachabilityEventHandler& handler, | |
| const NeighborReachabilityEventSignal& signal) { | |
| const auto event = ConvertNeighborReachabilityEvent(signal); | |
| if (event) { | |
| handler.Run(*event); | |
| } | |
| } | |
| void OnSignalConnectedCallback(const std::string& interface_name, | |
| const std::string& signal_name, | |
| bool success) { | |
| if (!success) | |
| LOG(ERROR) << "Failed to connect to " << signal_name; | |
| } | |
| // Helper static function to process answers to CreateTetheredNetwork calls. | |
| void OnTetheredNetworkResponse(Client::CreateTetheredNetworkCallback callback, | |
| base::ScopedFD fd_local, | |
| const TetheredNetworkResponse& response) { | |
| if (response.response_code() != DownstreamNetworkResult::SUCCESS) { | |
| LOG(ERROR) << kCreateTetheredNetworkMethod << " failed: " | |
| << patchpanel::DownstreamNetworkResult_Name( | |
| response.response_code()); | |
| std::move(callback).Run({}, {}); | |
| return; | |
| } | |
| std::optional<Client::DownstreamNetwork> downstream_network = | |
| ConvertDownstreamNetwork(response.downstream_network()); | |
| if (!downstream_network) { | |
| std::move(callback).Run({}, {}); | |
| return; | |
| } | |
| std::move(callback).Run(std::move(fd_local), *downstream_network); | |
| } | |
| void OnTetheredNetworkError(Client::CreateTetheredNetworkCallback callback, | |
| brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "(): " << error->GetMessage(); | |
| std::move(callback).Run({}, {}); | |
| } | |
| // Helper static function to process answers to CreateLocalOnlyNetwork calls. | |
| void OnLocalOnlyNetworkResponse(Client::CreateLocalOnlyNetworkCallback callback, | |
| base::ScopedFD fd_local, | |
| const LocalOnlyNetworkResponse& response) { | |
| if (response.response_code() != DownstreamNetworkResult::SUCCESS) { | |
| LOG(ERROR) << kCreateLocalOnlyNetworkMethod << " failed: " | |
| << patchpanel::DownstreamNetworkResult_Name( | |
| response.response_code()); | |
| std::move(callback).Run({}, {}); | |
| return; | |
| } | |
| std::optional<Client::DownstreamNetwork> downstream_network = | |
| ConvertDownstreamNetwork(response.downstream_network()); | |
| if (!downstream_network) { | |
| std::move(callback).Run({}, {}); | |
| return; | |
| } | |
| std::move(callback).Run(std::move(fd_local), *downstream_network); | |
| } | |
| void OnLocalOnlyNetworkError(Client::CreateLocalOnlyNetworkCallback callback, | |
| brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "(): " << error->GetMessage(); | |
| std::move(callback).Run({}, {}); | |
| } | |
| // Helper static function to process answers to GetDownstreamNetworkInfo calls. | |
| void OnGetDownstreamNetworkInfoResponse( | |
| Client::GetDownstreamNetworkInfoCallback callback, | |
| const GetDownstreamNetworkInfoResponse& response) { | |
| std::optional<Client::DownstreamNetwork> downstream_network = | |
| ConvertDownstreamNetwork(response.downstream_network()); | |
| if (!downstream_network) { | |
| std::move(callback).Run(false, {}, {}); | |
| return; | |
| } | |
| std::vector<Client::NetworkClientInfo> clients_info; | |
| for (const auto& ci : response.clients_info()) { | |
| const auto info = ConvertNetworkClientInfo(ci); | |
| if (info) { | |
| clients_info.push_back(*info); | |
| } | |
| } | |
| std::move(callback).Run(true, *downstream_network, clients_info); | |
| } | |
| void OnGetDownstreamNetworkInfoError( | |
| Client::GetDownstreamNetworkInfoCallback callback, brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "(): " << error->GetMessage(); | |
| std::move(callback).Run(false, {}, {}); | |
| } | |
| void OnConfigureNetworkResponse( | |
| Client::ConfigureNetworkCallback callback, | |
| const std::string& ifname, | |
| const patchpanel::ConfigureNetworkResponse& response) { | |
| if (!response.success()) { | |
| LOG(ERROR) << __func__ << ": Failed to configure Network on " << ifname; | |
| std::move(callback).Run(false); | |
| return; | |
| } | |
| std::move(callback).Run(true); | |
| } | |
| void OnConfigureNetworkError(Client::ConfigureNetworkCallback callback, | |
| const std::string& ifname, | |
| brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "() on " << ifname << ": " << error->GetMessage(); | |
| std::move(callback).Run(false); | |
| } | |
| void OnTagSocketResponse(Client::TagSocketCallback callback, | |
| const patchpanel::TagSocketResponse& response) { | |
| if (!response.success()) { | |
| LOG(ERROR) << __func__ << "(): failed to tag socket"; | |
| std::move(callback).Run(false); | |
| return; | |
| } | |
| std::move(callback).Run(true); | |
| } | |
| void OnTagSocketError(Client::TagSocketCallback callback, | |
| brillo::Error* error) { | |
| LOG(ERROR) << __func__ << "(): " << error->GetMessage(); | |
| std::move(callback).Run(false); | |
| } | |
| class ClientImpl : public Client { | |
| public: | |
| ClientImpl(scoped_refptr<dbus::Bus> bus, | |
| std::unique_ptr<org::chromium::PatchPanelProxyInterface> proxy, | |
| bool owns_bus) | |
| : bus_(std::move(bus)), proxy_(std::move(proxy)), owns_bus_(owns_bus) {} | |
| ClientImpl(const ClientImpl&) = delete; | |
| ClientImpl& operator=(const ClientImpl&) = delete; | |
| ~ClientImpl() override; | |
| void RegisterOnAvailableCallback( | |
| base::OnceCallback<void(bool)> callback) override; | |
| void RegisterProcessChangedCallback( | |
| base::RepeatingCallback<void(bool)> callback) override; | |
| bool NotifyArcStartup(pid_t pid) override; | |
| bool NotifyArcShutdown() override; | |
| std::optional<Client::ArcVMAllocation> NotifyArcVmStartup( | |
| uint32_t cid) override; | |
| bool NotifyArcVmShutdown(uint32_t cid) override; | |
| std::optional<Client::TerminaAllocation> NotifyTerminaVmStartup( | |
| uint32_t cid) override; | |
| bool NotifyTerminaVmShutdown(uint32_t cid) override; | |
| std::optional<ParallelsAllocation> NotifyParallelsVmStartup( | |
| uint64_t vm_id, int subnet_index) override; | |
| bool NotifyParallelsVmShutdown(uint64_t vm_id) override; | |
| std::optional<BruschettaAllocation> NotifyBruschettaVmStartup( | |
| uint64_t vm_id) override; | |
| bool NotifyBruschettaVmShutdown(uint64_t vm_id) override; | |
| std::optional<BorealisAllocation> NotifyBorealisVmStartup( | |
| uint32_t vm_id) override; | |
| bool NotifyBorealisVmShutdown(uint32_t vm_id) override; | |
| std::pair<base::ScopedFD, Client::ConnectedNamespace> ConnectNamespace( | |
| pid_t pid, | |
| const std::string& outbound_ifname, | |
| bool forward_user_traffic, | |
| bool route_on_vpn, | |
| Client::TrafficSource traffic_source, | |
| bool static_ipv6) override; | |
| void GetTrafficCounters(const std::set<std::string>& devices, | |
| GetTrafficCountersCallback callback) override; | |
| bool ModifyPortRule(Client::FirewallRequestOperation op, | |
| Client::FirewallRequestType type, | |
| Client::FirewallRequestProtocol proto, | |
| const std::string& input_ifname, | |
| const std::string& input_dst_ip, | |
| uint32_t input_dst_port, | |
| const std::string& dst_ip, | |
| uint32_t dst_port) override; | |
| void SetVpnLockdown(bool enable) override; | |
| base::ScopedFD RedirectDns(Client::DnsRedirectionRequestType type, | |
| const std::string& input_ifname, | |
| const std::string& proxy_address, | |
| const std::vector<std::string>& nameservers, | |
| const std::string& host_ifname) override; | |
| std::vector<Client::VirtualDevice> GetDevices() override; | |
| void RegisterVirtualDeviceEventHandler( | |
| VirtualDeviceEventHandler handler) override; | |
| void RegisterNeighborReachabilityEventHandler( | |
| NeighborReachabilityEventHandler handler) override; | |
| bool CreateTetheredNetwork( | |
| const std::string& downstream_ifname, | |
| const std::string& upstream_ifname, | |
| const std::optional<DHCPOptions>& dhcp_options, | |
| const std::optional<UplinkIPv6Configuration>& uplink_ipv6_config, | |
| const std::optional<int>& mtu, | |
| CreateTetheredNetworkCallback callback) override; | |
| bool CreateLocalOnlyNetwork(const std::string& ifname, | |
| CreateLocalOnlyNetworkCallback callback) override; | |
| bool GetDownstreamNetworkInfo( | |
| const std::string& ifname, | |
| GetDownstreamNetworkInfoCallback callback) override; | |
| bool ConfigureNetwork(int interface_index, | |
| std::string_view interface_name, | |
| uint32_t area, | |
| const net_base::NetworkConfig& network_config, | |
| net_base::NetworkPriority priority, | |
| NetworkTechnology technology, | |
| ConfigureNetworkCallback callback) override; | |
| bool SendSetFeatureFlagRequest(FeatureFlag flag, bool enable) override; | |
| bool TagSocket(base::ScopedFD fd, | |
| std::optional<int> network_id, | |
| std::optional<VpnRoutingPolicy> vpn_policy, | |
| std::optional<TrafficAnnotation> traffic_annotation, | |
| TagSocketCallback callback) override; | |
| void PrepareTagSocket( | |
| const TrafficAnnotation& annotation, | |
| std::shared_ptr<brillo::http::Transport> transport) override; | |
| private: | |
| // Runs the |task| on the DBus thread synchronously. | |
| // The generated proxy uses brillo::dbus_utils::CallMethod*(), which asserts | |
| // to be executed on the DBus thread, instead of hopping on the DBus thread. | |
| // Therefore we need to do it by ourselves. | |
| bool RunOnDBusThreadSync(base::OnceCallback<bool()> task) { | |
| if (!bus_->HasDBusThread() || | |
| bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) { | |
| return std::move(task).Run(); | |
| } | |
| base::WaitableEvent event; | |
| bool result = false; | |
| bus_->GetDBusTaskRunner()->PostTask( | |
| FROM_HERE, base::BindOnce( | |
| [](base::OnceCallback<bool()> task, bool* result, | |
| base::WaitableEvent* event) { | |
| *result = std::move(task).Run(); | |
| event->Signal(); | |
| }, | |
| std::move(task), base::Unretained(&result), | |
| base::Unretained(&event))); | |
| event.Wait(); | |
| return result; | |
| } | |
| // Runs the |task| on the DBus thread asynchronously. | |
| // The generated proxy uses brillo::dbus_utils::CallMethod*(), which asserts | |
| // to be executed on the DBus thread, instead of hopping on the DBus thread. | |
| // Therefore we need to do it by ourselves. | |
| void RunOnDBusThreadAsync(base::OnceClosure task) { | |
| if (!bus_->HasDBusThread() || | |
| bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) { | |
| std::move(task).Run(); | |
| return; | |
| } | |
| bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task)); | |
| } | |
| scoped_refptr<dbus::Bus> bus_; | |
| std::unique_ptr<org::chromium::PatchPanelProxyInterface> proxy_; | |
| bool owns_bus_; // Yes if |bus_| is created by Client::New | |
| base::RepeatingCallback<void(bool)> owner_callback_; | |
| void OnOwnerChanged(const std::string& old_owner, | |
| const std::string& new_owner); | |
| base::WeakPtrFactory<ClientImpl> weak_factory_{this}; | |
| }; | |
| ClientImpl::~ClientImpl() { | |
| if (bus_ && owns_bus_) | |
| bus_->ShutdownAndBlock(); | |
| } | |
| void ClientImpl::RegisterOnAvailableCallback( | |
| base::OnceCallback<void(bool)> callback) { | |
| auto* object_proxy = proxy_->GetObjectProxy(); | |
| if (!object_proxy) { | |
| LOG(ERROR) << "Cannot register callback - no proxy"; | |
| return; | |
| } | |
| object_proxy->WaitForServiceToBeAvailable(std::move(callback)); | |
| } | |
| void ClientImpl::RegisterProcessChangedCallback( | |
| base::RepeatingCallback<void(bool)> callback) { | |
| owner_callback_ = callback; | |
| bus_->GetObjectProxy(kPatchPanelServiceName, dbus::ObjectPath{"/"}) | |
| ->SetNameOwnerChangedCallback(base::BindRepeating( | |
| &ClientImpl::OnOwnerChanged, weak_factory_.GetWeakPtr())); | |
| } | |
| void ClientImpl::OnOwnerChanged(const std::string& old_owner, | |
| const std::string& new_owner) { | |
| if (new_owner.empty()) { | |
| LOG(INFO) << "Patchpanel lost"; | |
| if (!owner_callback_.is_null()) | |
| owner_callback_.Run(false); | |
| return; | |
| } | |
| LOG(INFO) << "Patchpanel reset"; | |
| if (!owner_callback_.is_null()) | |
| owner_callback_.Run(true); | |
| } | |
| bool ClientImpl::NotifyArcStartup(pid_t pid) { | |
| ArcStartupRequest request; | |
| request.set_pid(pid); | |
| // TODO(b/284076578): Check if we can call the DBus method asynchronously. | |
| ArcStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const ArcStartupRequest& request, | |
| ArcStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ArcStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ARC network startup failed: " << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ClientImpl::NotifyArcShutdown() { | |
| // TODO(b/284076578): Check if we can call the DBus method asynchronously. | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, brillo::ErrorPtr* error) { | |
| ArcShutdownResponse response; | |
| return proxy->ArcShutdown({}, &response, error); | |
| }, | |
| proxy_.get(), &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ARC network shutdown failed: " << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::optional<Client::ArcVMAllocation> ClientImpl::NotifyArcVmStartup( | |
| uint32_t cid) { | |
| ArcVmStartupRequest request; | |
| request.set_cid(cid); | |
| // TODO(b/284076578): Check if concierge can handle the result asynchronously. | |
| ArcVmStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const ArcVmStartupRequest& request, | |
| ArcVmStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ArcVmStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ARCVM network startup failed: " << error->GetMessage(); | |
| return std::nullopt; | |
| } | |
| const auto arc0_addr = | |
| net_base::IPv4Address::CreateFromBytes(response.arc0_ipv4_address()); | |
| if (!arc0_addr) { | |
| LOG(ERROR) << "Could not deserialize arc0 IPv4 address"; | |
| return std::nullopt; | |
| } | |
| ArcVMAllocation arcvm_alloc; | |
| arcvm_alloc.arc0_ipv4_address = *arc0_addr; | |
| for (const auto& tap : response.tap_device_ifnames()) { | |
| arcvm_alloc.tap_device_ifnames.push_back(tap); | |
| } | |
| return arcvm_alloc; | |
| } | |
| bool ClientImpl::NotifyArcVmShutdown(uint32_t cid) { | |
| ArcVmShutdownRequest request; | |
| request.set_cid(cid); | |
| // TODO(b/284076578): Check if concierge can handle the result asynchronously. | |
| ArcVmShutdownResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const ArcVmShutdownRequest& request, | |
| ArcVmShutdownResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ArcVmShutdown(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ARCVM network shutdown failed: " << error->GetMessage(); | |
| } | |
| return result; | |
| } | |
| std::optional<Client::TerminaAllocation> ClientImpl::NotifyTerminaVmStartup( | |
| uint32_t cid) { | |
| TerminaVmStartupRequest request; | |
| request.set_cid(cid); | |
| TerminaVmStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const TerminaVmStartupRequest& request, | |
| TerminaVmStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->TerminaVmStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << __func__ << "(cid: " << cid | |
| << "): TerminaVM network startup failed: " | |
| << error->GetMessage(); | |
| return std::nullopt; | |
| } | |
| const auto termina_alloc = ConvertTerminaAllocation(response); | |
| if (!termina_alloc) { | |
| LOG(ERROR) << __func__ << "(cid: " << cid | |
| << "): Failed to convert network allocation"; | |
| } | |
| return termina_alloc; | |
| } | |
| bool ClientImpl::NotifyTerminaVmShutdown(uint32_t cid) { | |
| TerminaVmShutdownRequest request; | |
| request.set_cid(cid); | |
| TerminaVmShutdownResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const TerminaVmShutdownRequest& request, | |
| TerminaVmShutdownResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->TerminaVmShutdown(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "TerminaVM network shutdown failed: " << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::optional<Client::ParallelsAllocation> ClientImpl::NotifyParallelsVmStartup( | |
| uint64_t vm_id, int subnet_index) { | |
| ParallelsVmStartupRequest request; | |
| request.set_id(vm_id); | |
| request.set_subnet_index(subnet_index); | |
| ParallelsVmStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const ParallelsVmStartupRequest& request, | |
| ParallelsVmStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ParallelsVmStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << __func__ << "(cid: " << vm_id | |
| << ", subnet_index: " << subnet_index | |
| << "): Parallels VM network startup failed: " | |
| << error->GetMessage(); | |
| return std::nullopt; | |
| } | |
| const auto network_alloc = ConvertParallelsAllocation(response); | |
| if (!network_alloc) { | |
| LOG(ERROR) << __func__ << "(cid: " << vm_id | |
| << ", subnet_index: " << subnet_index | |
| << "): Failed to convert Parallels VM network configuration"; | |
| } | |
| return network_alloc; | |
| } | |
| bool ClientImpl::NotifyParallelsVmShutdown(uint64_t vm_id) { | |
| ParallelsVmShutdownRequest request; | |
| request.set_id(vm_id); | |
| ParallelsVmShutdownResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const ParallelsVmShutdownRequest& request, | |
| ParallelsVmShutdownResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ParallelsVmShutdown(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ParallelsVM network shutdown failed: " | |
| << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::optional<Client::BruschettaAllocation> | |
| ClientImpl::NotifyBruschettaVmStartup(uint64_t vm_id) { | |
| BruschettaVmStartupRequest request; | |
| request.set_id(vm_id); | |
| BruschettaVmStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const BruschettaVmStartupRequest& request, | |
| BruschettaVmStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->BruschettaVmStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << __func__ << "(vm_id: " << vm_id | |
| << "): Bruschetta VM network startup failed: " | |
| << error->GetMessage(); | |
| return std::nullopt; | |
| } | |
| const auto network_alloc = ConvertBruschettaAllocation(response); | |
| if (!network_alloc) { | |
| LOG(ERROR) << __func__ << "(vm_id: " << vm_id | |
| << "): Failed to convert Bruschetta VM network configuration"; | |
| } | |
| return network_alloc; | |
| } | |
| bool ClientImpl::NotifyBruschettaVmShutdown(uint64_t vm_id) { | |
| BruschettaVmShutdownRequest request; | |
| request.set_id(vm_id); | |
| BruschettaVmShutdownResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const BruschettaVmShutdownRequest& request, | |
| BruschettaVmShutdownResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->BruschettaVmShutdown(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "BruschettaVM network shutdown failed: " | |
| << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::optional<Client::BorealisAllocation> ClientImpl::NotifyBorealisVmStartup( | |
| uint32_t vm_id) { | |
| BorealisVmStartupRequest request; | |
| request.set_id(vm_id); | |
| BorealisVmStartupResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const BorealisVmStartupRequest& request, | |
| BorealisVmStartupResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->BorealisVmStartup(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << __func__ << "(vm_id: " << vm_id | |
| << "): Borealis VM network startup failed: " | |
| << error->GetMessage(); | |
| return std::nullopt; | |
| } | |
| const auto network_alloc = ConvertBorealisAllocation(response); | |
| if (!network_alloc) { | |
| LOG(ERROR) << __func__ << "(vm_id: " << vm_id | |
| << "): Failed to convert Borealis VM network configuration"; | |
| } | |
| return network_alloc; | |
| } | |
| bool ClientImpl::NotifyBorealisVmShutdown(uint32_t vm_id) { | |
| BorealisVmShutdownRequest request; | |
| request.set_id(vm_id); | |
| BorealisVmShutdownResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const BorealisVmShutdownRequest& request, | |
| BorealisVmShutdownResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->BorealisVmShutdown(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "Borealis VM network shutdown failed: " | |
| << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::pair<base::ScopedFD, Client::ConnectedNamespace> | |
| ClientImpl::ConnectNamespace(pid_t pid, | |
| const std::string& outbound_ifname, | |
| bool forward_user_traffic, | |
| bool route_on_vpn, | |
| Client::TrafficSource traffic_source, | |
| bool static_ipv6) { | |
| // Prepare and serialize the request proto. | |
| ConnectNamespaceRequest request; | |
| request.set_pid(static_cast<int32_t>(pid)); | |
| request.set_outbound_physical_device(outbound_ifname); | |
| request.set_allow_user_traffic(forward_user_traffic); | |
| request.set_route_on_vpn(route_on_vpn); | |
| request.set_traffic_source(ConvertTrafficSource(traffic_source)); | |
| request.set_static_ipv6(static_ipv6); | |
| auto [fd_local, fd_remote] = CreateLifelineFd(); | |
| if (!fd_local.is_valid()) { | |
| LOG(ERROR) | |
| << "Cannot send ConnectNamespace message to patchpanel: no lifeline fd"; | |
| return {}; | |
| } | |
| ConnectNamespaceResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const ConnectNamespaceRequest& request, base::ScopedFD fd_remote, | |
| ConnectNamespaceResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ConnectNamespace(request, fd_remote, response, error); | |
| }, | |
| proxy_.get(), request, std::move(fd_remote), &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ConnectNamespace failed: " << error->GetMessage(); | |
| return {}; | |
| } | |
| if (response.peer_ifname().empty() || response.host_ifname().empty()) { | |
| LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed"; | |
| return {}; | |
| } | |
| const auto connected_ns = ConvertConnectedNamespace(response); | |
| if (!connected_ns) { | |
| LOG(ERROR) << "Failed to convert ConnectedNamespace"; | |
| return {}; | |
| } | |
| LOG(INFO) << "ConnectNamespace for netns pid " << pid | |
| << " succeeded: peer_ifname=" << connected_ns->peer_ifname | |
| << " peer_ipv4_address=" << connected_ns->peer_ipv4_address | |
| << " host_ifname=" << connected_ns->host_ifname | |
| << " host_ipv4_address=" << connected_ns->host_ipv4_address | |
| << " subnet=" << connected_ns->ipv4_subnet.ToString(); | |
| return std::make_pair(std::move(fd_local), *connected_ns); | |
| } | |
| void ClientImpl::GetTrafficCounters(const std::set<std::string>& devices, | |
| GetTrafficCountersCallback callback) { | |
| TrafficCountersRequest request; | |
| for (const auto& device : devices) { | |
| request.add_devices(device); | |
| } | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const TrafficCountersRequest& request, | |
| GetTrafficCountersCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->GetTrafficCountersAsync( | |
| request, | |
| base::BindOnce(&OnGetTrafficCountersDBusResponse, | |
| std::move(split_callback.first)), | |
| base::BindOnce(&OnGetTrafficCountersError, | |
| std::move(split_callback.second))); | |
| }, | |
| proxy_.get(), request, | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| } | |
| bool ClientImpl::ModifyPortRule(Client::FirewallRequestOperation op, | |
| Client::FirewallRequestType type, | |
| Client::FirewallRequestProtocol proto, | |
| const std::string& input_ifname, | |
| const std::string& input_dst_ip, | |
| uint32_t input_dst_port, | |
| const std::string& dst_ip, | |
| uint32_t dst_port) { | |
| ModifyPortRuleRequest request; | |
| request.set_op(ConvertFirewallRequestOperation(op)); | |
| request.set_type(ConvertFirewallRequestType(type)); | |
| request.set_proto(ConvertFirewallRequestProtocol(proto)); | |
| request.set_input_ifname(input_ifname); | |
| request.set_input_dst_ip(input_dst_ip); | |
| request.set_input_dst_port(input_dst_port); | |
| request.set_dst_ip(dst_ip); | |
| request.set_dst_port(dst_port); | |
| // TODO(b/284797476): Switch permission_brokker to use the async DBus call. | |
| ModifyPortRuleResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const ModifyPortRuleRequest& request, | |
| ModifyPortRuleResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->ModifyPortRule(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "ModifyPortRule failed: " << error->GetMessage(); | |
| return false; | |
| } | |
| if (!response.success()) { | |
| LOG(ERROR) << "ModifyPortRuleRequest failed " << request; | |
| return false; | |
| } | |
| return true; | |
| } | |
| void ClientImpl::SetVpnLockdown(bool enable) { | |
| SetVpnLockdownRequest request; | |
| request.set_enable_vpn_lockdown(enable); | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const SetVpnLockdownRequest& request) { | |
| // This API doesn't return anything. | |
| auto success_callback = base::DoNothing(); | |
| // The current use case do not care about failures. Leaving a log is | |
| // enough now. | |
| auto error_callback = [](brillo::Error* error) { | |
| LOG(ERROR) << "SetVpnLockdown failed: " << error->GetMessage(); | |
| }; | |
| proxy->SetVpnLockdownAsync(request, success_callback, | |
| base::BindOnce(error_callback)); | |
| }, | |
| proxy_.get(), request)); | |
| } | |
| base::ScopedFD ClientImpl::RedirectDns( | |
| Client::DnsRedirectionRequestType type, | |
| const std::string& input_ifname, | |
| const std::string& proxy_address, | |
| const std::vector<std::string>& nameservers, | |
| const std::string& host_ifname) { | |
| SetDnsRedirectionRuleRequest request; | |
| request.set_type(ConvertDnsRedirectionRequestType(type)); | |
| request.set_input_ifname(input_ifname); | |
| request.set_proxy_address(proxy_address); | |
| request.set_host_ifname(host_ifname); | |
| for (const auto& nameserver : nameservers) { | |
| request.add_nameservers(nameserver); | |
| } | |
| // Prepare an fd pair and append one fd directly after the serialized request. | |
| auto [fd_local, fd_remote] = CreateLifelineFd(); | |
| if (!fd_local.is_valid()) { | |
| LOG(ERROR) << "Cannot send SetDnsRedirectionRuleRequest message to " | |
| "patchpanel: no lifeline fd"; | |
| return {}; | |
| } | |
| SetDnsRedirectionRuleResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const SetDnsRedirectionRuleRequest& request, base::ScopedFD fd_remote, | |
| SetDnsRedirectionRuleResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->SetDnsRedirectionRule(request, fd_remote, response, | |
| error); | |
| }, | |
| proxy_.get(), request, std::move(fd_remote), &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "SetDnsRedirectionRule failed: " << error->GetMessage(); | |
| return {}; | |
| } | |
| if (!response.success()) { | |
| LOG(ERROR) << "SetDnsRedirectionRuleRequest failed " << request; | |
| return {}; | |
| } | |
| return std::move(fd_local); | |
| } | |
| std::vector<Client::VirtualDevice> ClientImpl::GetDevices() { | |
| // TODO(b/284797476): Add a DBus service in dns-proxy to let patchpanel push | |
| // information to dns-proxy. | |
| GetDevicesResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, GetDevicesResponse* response, | |
| brillo::ErrorPtr* error) { | |
| return proxy->GetDevices({}, response, error); | |
| }, | |
| proxy_.get(), &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "GetDevices failed: " << error->GetMessage(); | |
| return {}; | |
| } | |
| std::vector<Client::VirtualDevice> devices; | |
| for (const auto& d : response.devices()) { | |
| const auto device = ConvertVirtualDevice(d); | |
| if (device) { | |
| devices.push_back(*device); | |
| } | |
| } | |
| return devices; | |
| } | |
| void ClientImpl::RegisterVirtualDeviceEventHandler( | |
| VirtualDeviceEventHandler handler) { | |
| proxy_->RegisterNetworkDeviceChangedSignalHandler( | |
| base::BindRepeating(OnNetworkDeviceChanged, std::move(handler)), | |
| base::BindOnce(OnSignalConnectedCallback)); | |
| } | |
| void ClientImpl::RegisterNeighborReachabilityEventHandler( | |
| NeighborReachabilityEventHandler handler) { | |
| proxy_->RegisterNeighborReachabilityEventSignalHandler( | |
| base::BindRepeating(OnNeighborReachabilityEvent, std::move(handler)), | |
| base::BindOnce(OnSignalConnectedCallback)); | |
| } | |
| bool ClientImpl::CreateTetheredNetwork( | |
| const std::string& downstream_ifname, | |
| const std::string& upstream_ifname, | |
| const std::optional<DHCPOptions>& dhcp_options, | |
| const std::optional<UplinkIPv6Configuration>& uplink_ipv6_config, | |
| const std::optional<int>& mtu, | |
| CreateTetheredNetworkCallback callback) { | |
| TetheredNetworkRequest request; | |
| request.set_ifname(downstream_ifname); | |
| request.set_upstream_ifname(upstream_ifname); | |
| if (mtu) { | |
| request.set_mtu(*mtu); | |
| } | |
| if (dhcp_options.has_value()) { | |
| auto* ipv4_config = request.mutable_ipv4_config(); | |
| ipv4_config->set_use_dhcp(true); | |
| for (const auto& dns_server : dhcp_options->dns_server_addresses) { | |
| ipv4_config->add_dns_servers(dns_server.ToByteString()); | |
| } | |
| for (const auto& domain_search : dhcp_options->domain_search_list) { | |
| ipv4_config->add_domain_searches(domain_search); | |
| } | |
| if (dhcp_options->is_android_metered) { | |
| auto options = ipv4_config->add_options(); | |
| // RFC 3925 defines the DHCP option 43 is Vendor Specific. | |
| options->set_code(43); | |
| options->set_content("ANDROID_METERED"); | |
| } | |
| } | |
| request.set_enable_ipv6(true); | |
| if (uplink_ipv6_config.has_value()) { | |
| auto* ipv6_config = request.mutable_uplink_ipv6_config(); | |
| auto* uplink_ipv6_cidr = ipv6_config->mutable_uplink_ipv6_cidr(); | |
| uplink_ipv6_cidr->set_addr( | |
| uplink_ipv6_config->uplink_address.address().ToByteString()); | |
| uplink_ipv6_cidr->set_prefix_len( | |
| uplink_ipv6_config->uplink_address.prefix_length()); | |
| for (const auto& dns_server : uplink_ipv6_config->dns_server_addresses) { | |
| ipv6_config->add_dns_servers(dns_server.ToByteString()); | |
| } | |
| } | |
| // Prepare an fd pair and append one fd directly after the serialized request. | |
| auto [fd_local, fd_remote] = CreateLifelineFd(); | |
| if (!fd_local.is_valid()) { | |
| LOG(ERROR) << kCreateTetheredNetworkMethod << "(" << downstream_ifname | |
| << "," << upstream_ifname << "): Cannot create lifeline fds"; | |
| return false; | |
| } | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const TetheredNetworkRequest& request, | |
| base::ScopedFD fd_local, base::ScopedFD fd_remote, | |
| CreateTetheredNetworkCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->CreateTetheredNetworkAsync( | |
| request, fd_remote, | |
| base::BindOnce(&OnTetheredNetworkResponse, | |
| std::move(split_callback.first), | |
| std::move(fd_local)), | |
| base::BindOnce(&OnTetheredNetworkError, | |
| std::move(split_callback.second))); | |
| }, | |
| proxy_.get(), request, std::move(fd_local), std::move(fd_remote), | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| return true; | |
| } | |
| bool ClientImpl::CreateLocalOnlyNetwork( | |
| const std::string& ifname, CreateLocalOnlyNetworkCallback callback) { | |
| LocalOnlyNetworkRequest request; | |
| request.set_ifname(ifname); | |
| auto* ipv4_config = request.mutable_ipv4_config(); | |
| ipv4_config->set_use_dhcp(true); | |
| // Prepare an fd pair and append one fd directly after the serialized request. | |
| auto [fd_local, fd_remote] = CreateLifelineFd(); | |
| if (!fd_local.is_valid()) { | |
| LOG(ERROR) << kCreateLocalOnlyNetworkMethod | |
| << ": Cannot create lifeline fds"; | |
| return false; | |
| } | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const LocalOnlyNetworkRequest& request, base::ScopedFD fd_local, | |
| base::ScopedFD fd_remote, CreateLocalOnlyNetworkCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->CreateLocalOnlyNetworkAsync( | |
| request, fd_remote, | |
| base::BindOnce(&OnLocalOnlyNetworkResponse, | |
| std::move(split_callback.first), | |
| std::move(fd_local)), | |
| base::BindOnce(&OnLocalOnlyNetworkError, | |
| std::move(split_callback.second))); | |
| }, | |
| proxy_.get(), request, std::move(fd_local), std::move(fd_remote), | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| return true; | |
| } | |
| bool ClientImpl::GetDownstreamNetworkInfo( | |
| const std::string& ifname, GetDownstreamNetworkInfoCallback callback) { | |
| GetDownstreamNetworkInfoRequest request; | |
| request.set_downstream_ifname(ifname); | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const GetDownstreamNetworkInfoRequest& request, | |
| GetDownstreamNetworkInfoCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->GetDownstreamNetworkInfoAsync( | |
| request, | |
| base::BindOnce(&OnGetDownstreamNetworkInfoResponse, | |
| std::move(split_callback.first)), | |
| base::BindOnce(&OnGetDownstreamNetworkInfoError, | |
| std::move(split_callback.second))); | |
| }, | |
| proxy_.get(), request, | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| return true; | |
| } | |
| bool ClientImpl::ConfigureNetwork(int interface_index, | |
| std::string_view interface_name, | |
| uint32_t area, | |
| const net_base::NetworkConfig& network_config, | |
| net_base::NetworkPriority priority, | |
| NetworkTechnology technology, | |
| ConfigureNetworkCallback callback) { | |
| ConfigureNetworkRequest request; | |
| request.set_technology(ConvertNetworkTechnology(technology)); | |
| request.set_ifindex(interface_index); | |
| request.set_ifname(std::string(interface_name)); | |
| request.set_area(area); | |
| auto request_priority = request.mutable_priority(); | |
| request_priority->set_is_primary_logical(priority.is_primary_logical); | |
| request_priority->set_is_primary_physical(priority.is_primary_physical); | |
| request_priority->set_is_primary_for_dns(priority.is_primary_for_dns); | |
| request_priority->set_ranking_order(priority.ranking_order); | |
| SerializeNetworkConfig(network_config, request.mutable_network_config()); | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, | |
| const ConfigureNetworkRequest& request, | |
| std::string_view interface_name, ConfigureNetworkCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->ConfigureNetworkAsync( | |
| request, | |
| base::BindOnce(&OnConfigureNetworkResponse, | |
| std::move(split_callback.first), | |
| std::string(interface_name)), | |
| base::BindOnce(&OnConfigureNetworkError, | |
| std::move(split_callback.second), | |
| std::string(interface_name))); | |
| }, | |
| proxy_.get(), request, interface_name, | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| return true; | |
| } | |
| bool ClientImpl::SendSetFeatureFlagRequest(FeatureFlag flag, bool enable) { | |
| SetFeatureFlagRequest request; | |
| request.set_enabled(enable); | |
| request.set_flag(ConvertFeatureFlag(flag)); | |
| SetFeatureFlagResponse response; | |
| brillo::ErrorPtr error; | |
| const bool result = RunOnDBusThreadSync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const SetFeatureFlagRequest& request, | |
| SetFeatureFlagResponse* response, brillo::ErrorPtr* error) { | |
| return proxy->SetFeatureFlag(request, response, error); | |
| }, | |
| proxy_.get(), request, &response, &error)); | |
| if (!result) { | |
| LOG(ERROR) << "Failed to set feature flag of " << flag << ": " | |
| << error->GetMessage(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool ClientImpl::TagSocket(base::ScopedFD fd, | |
| std::optional<int> network_id, | |
| std::optional<VpnRoutingPolicy> vpn_policy, | |
| std::optional<TrafficAnnotation> traffic_annotation, | |
| TagSocketCallback callback) { | |
| TagSocketRequest request; | |
| if (network_id.has_value()) { | |
| request.set_network_id(network_id.value()); | |
| } | |
| if (vpn_policy.has_value()) { | |
| request.set_vpn_policy(ConvertVpnRoutingPolicy(vpn_policy.value())); | |
| } | |
| if (traffic_annotation.has_value()) { | |
| auto annotation = request.mutable_traffic_annotation(); | |
| annotation->set_host_id( | |
| ConvertTrafficAnnotationId(traffic_annotation.value().id)); | |
| } | |
| TagSocketResponse response; | |
| brillo::ErrorPtr error; | |
| RunOnDBusThreadAsync(base::BindOnce( | |
| [](PatchPanelProxyInterface* proxy, const TagSocketRequest& request, | |
| base::ScopedFD fd, TagSocketCallback callback) { | |
| auto split_callback = SplitOnceCallback(std::move(callback)); | |
| proxy->TagSocketAsync(request, fd, | |
| base::BindOnce(&OnTagSocketResponse, | |
| std::move(split_callback.first)), | |
| base::BindOnce(&OnTagSocketError, | |
| std::move(split_callback.second))); | |
| }, | |
| proxy_.get(), request, std::move(fd), | |
| base::BindPostTaskToCurrentDefault(std::move(callback)))); | |
| return true; | |
| } | |
| bool OnSocketAnnotation(base::WeakPtr<ClientImpl> client, | |
| const Client::TrafficAnnotationId& id, | |
| int fd) { | |
| // The callback might be still registered in the transport while the client | |
| // has been destroyed. Ensure the client is still valid before doing anything | |
| // else (see b/345769752). | |
| if (!client) { | |
| LOG(ERROR) << __func__ << ": client is not valid anymore"; | |
| return false; | |
| } | |
| // The socket fd has to be duplicated to prevent the DBus proxy base::ScopedFD | |
| // to close the fd owned by curl. | |
| int tag_fd = dup(fd); | |
| if (tag_fd < 0) { | |
| LOG(ERROR) << __func__ << ": failed to dup socket descriptor"; | |
| return false; | |
| } | |
| Client::TrafficAnnotation annotation(id); | |
| // TODO(b/331620358) use a synchronous call to a dedicated TagSocket service | |
| // to guarantee the socket is tagged when the call returns. | |
| return client->TagSocket(base::ScopedFD(tag_fd), std::nullopt, std::nullopt, | |
| std::move(annotation), base::DoNothing()); | |
| } | |
| void ClientImpl::PrepareTagSocket( | |
| const TrafficAnnotation& annotation, | |
| std::shared_ptr<brillo::http::Transport> transport) { | |
| // Bind OnSocketAnnotation as a RepeatingCallback with the annotation id as a | |
| // bound parameter. | |
| transport->SetSockOptCallback(base::BindRepeating( | |
| &OnSocketAnnotation, weak_factory_.GetWeakPtr(), annotation.id)); | |
| } | |
| } // namespace | |
| void SerializeNetworkConfig(const net_base::NetworkConfig& in, | |
| patchpanel::NetworkConfig* out) { | |
| if (in.ipv4_address) { | |
| auto* ipv4_address = out->mutable_ipv4_address(); | |
| ipv4_address->set_addr(in.ipv4_address->address().ToByteString()); | |
| ipv4_address->set_prefix_len(in.ipv4_address->prefix_length()); | |
| } | |
| if (in.ipv4_broadcast) { | |
| out->set_ipv4_broadcast(in.ipv4_broadcast->ToByteString()); | |
| } | |
| if (in.ipv4_gateway) { | |
| out->set_ipv4_gateway(in.ipv4_gateway->ToByteString()); | |
| } | |
| for (const auto& ipv6_address : in.ipv6_addresses) { | |
| auto* out_addr = out->add_ipv6_addresses(); | |
| out_addr->set_addr(ipv6_address.address().ToByteString()); | |
| out_addr->set_prefix_len(ipv6_address.prefix_length()); | |
| } | |
| if (in.ipv6_gateway) { | |
| out->set_ipv6_gateway(in.ipv6_gateway->ToByteString()); | |
| } | |
| out->set_ipv6_blackhole_route(in.ipv6_blackhole_route); | |
| for (const auto& prefix : in.excluded_route_prefixes) { | |
| auto* out_prefix = out->add_excluded_route_prefixes(); | |
| out_prefix->set_addr(prefix.address().ToByteString()); | |
| out_prefix->set_prefix_len(prefix.prefix_length()); | |
| } | |
| for (const auto& prefix : in.included_route_prefixes) { | |
| auto* out_prefix = out->add_included_route_prefixes(); | |
| out_prefix->set_addr(prefix.address().ToByteString()); | |
| out_prefix->set_prefix_len(prefix.prefix_length()); | |
| } | |
| for (const auto& route : in.rfc3442_routes) { | |
| auto* out_route = out->add_rfc3442_routes(); | |
| auto* out_prefix = out_route->mutable_prefix(); | |
| out_prefix->set_addr(route.first.address().ToByteString()); | |
| out_prefix->set_prefix_len(route.first.prefix_length()); | |
| out_route->set_gateway(route.second.ToByteString()); | |
| } | |
| for (const auto& dns : in.dns_servers) { | |
| out->add_dns_servers(dns.ToByteString()); | |
| } | |
| for (const auto& dnssl : in.dns_search_domains) { | |
| out->add_dns_search_domains(dnssl); | |
| } | |
| if (in.mtu) { | |
| out->set_mtu(*in.mtu); | |
| } | |
| if (in.captive_portal_uri) { | |
| out->set_captive_portal_uri(in.captive_portal_uri->ToString()); | |
| } | |
| } | |
| // static | |
| std::unique_ptr<Client> Client::New() { | |
| dbus::Bus::Options opts; | |
| opts.bus_type = dbus::Bus::SYSTEM; | |
| scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts))); | |
| if (!bus->Connect()) { | |
| LOG(ERROR) << "Failed to connect to system bus"; | |
| return nullptr; | |
| } | |
| auto proxy = std::make_unique<org::chromium::PatchPanelProxy>(bus); | |
| if (!proxy) { | |
| LOG(ERROR) << "Failed to create proxy"; | |
| return nullptr; | |
| } | |
| return std::make_unique<ClientImpl>(std::move(bus), std::move(proxy), | |
| /*owns_bus=*/true); | |
| } | |
| // static | |
| std::unique_ptr<Client> Client::New(const scoped_refptr<dbus::Bus>& bus) { | |
| auto proxy = std::make_unique<org::chromium::PatchPanelProxy>(bus); | |
| if (!proxy) { | |
| LOG(ERROR) << "Failed to create proxy"; | |
| return nullptr; | |
| } | |
| return std::make_unique<ClientImpl>(bus, std::move(proxy), | |
| /*owns_bus=*/false); | |
| } | |
| // static | |
| std::unique_ptr<Client> Client::NewForTesting( | |
| scoped_refptr<dbus::Bus> bus, | |
| std::unique_ptr<org::chromium::PatchPanelProxyInterface> proxy) { | |
| return std::make_unique<ClientImpl>(std::move(bus), std::move(proxy), | |
| /*owns_bus=*/false); | |
| } | |
| // static | |
| bool Client::IsArcGuest(Client::GuestType guest_type) { | |
| switch (guest_type) { | |
| case Client::GuestType::kArcContainer: | |
| case Client::GuestType::kArcVm: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // static | |
| std::string Client::TrafficSourceName( | |
| patchpanel::Client::TrafficSource source) { | |
| return patchpanel::TrafficCounter::Source_Name(ConvertTrafficSource(source)); | |
| } | |
| // static | |
| std::string Client::ProtocolName( | |
| patchpanel::Client::FirewallRequestProtocol protocol) { | |
| return patchpanel::ModifyPortRuleRequest::Protocol_Name( | |
| ConvertFirewallRequestProtocol(protocol)); | |
| } | |
| // static | |
| std::string Client::NeighborRoleName(patchpanel::Client::NeighborRole role) { | |
| return NeighborReachabilityEventSignal::Role_Name(ConvertNeighborRole(role)); | |
| } | |
| // static | |
| std::string Client::NeighborStatusName( | |
| patchpanel::Client::NeighborStatus status) { | |
| return NeighborReachabilityEventSignal::EventType_Name( | |
| ConvertNeighborStatus(status)); | |
| } | |
| bool Client::TrafficVector::operator==( | |
| const Client::TrafficVector& that) const = default; | |
| Client::TrafficVector& Client::TrafficVector::operator+=( | |
| const TrafficVector& that) { | |
| rx_bytes += that.rx_bytes; | |
| tx_bytes += that.tx_bytes; | |
| rx_packets += that.rx_packets; | |
| tx_packets += that.tx_packets; | |
| return *this; | |
| } | |
| Client::TrafficVector& Client::TrafficVector::operator-=( | |
| const TrafficVector& that) { | |
| rx_bytes -= that.rx_bytes; | |
| tx_bytes -= that.tx_bytes; | |
| rx_packets -= that.rx_packets; | |
| tx_packets -= that.tx_packets; | |
| return *this; | |
| } | |
| Client::TrafficVector Client::TrafficVector::operator+( | |
| const TrafficVector& that) const { | |
| auto r = *this; | |
| r += that; | |
| return r; | |
| } | |
| Client::TrafficVector Client::TrafficVector::operator-( | |
| const TrafficVector& that) const { | |
| auto r = *this; | |
| r -= that; | |
| return r; | |
| } | |
| Client::TrafficVector Client::TrafficVector::operator-() const { | |
| auto r = *this; | |
| r.rx_bytes = -r.rx_bytes; | |
| r.tx_bytes = -r.tx_bytes; | |
| r.rx_packets = -r.rx_packets; | |
| r.tx_packets = -r.tx_packets; | |
| return r; | |
| } | |
| bool Client::TrafficCounter::operator==( | |
| const Client::TrafficCounter& rhs) const = default; | |
| BRILLO_EXPORT std::ostream& operator<<( | |
| std::ostream& stream, const Client::NeighborReachabilityEvent& event) { | |
| return stream << "{ifindex: " << event.ifindex | |
| << ", ip_address: " << event.ip_addr | |
| << ", role: " << Client::NeighborRoleName(event.role) | |
| << ", status: " << Client::NeighborStatusName(event.status) | |
| << "}"; | |
| } | |
| BRILLO_EXPORT std::ostream& operator<<( | |
| std::ostream& stream, const Client::NetworkTechnology& technology) { | |
| switch (technology) { | |
| case Client::NetworkTechnology::kCellular: | |
| return stream << "Cellular"; | |
| case Client::NetworkTechnology::kEthernet: | |
| return stream << "Ethernet"; | |
| case Client::NetworkTechnology::kVPN: | |
| return stream << "VPN"; | |
| case Client::NetworkTechnology::kWiFi: | |
| return stream << "WiFi"; | |
| } | |
| } | |
| BRILLO_EXPORT std::ostream& operator<<( | |
| std::ostream& stream, const Client::TrafficSource& traffic_source) { | |
| switch (traffic_source) { | |
| case patchpanel::Client::TrafficSource::kUnknown: | |
| return stream << "Unknown"; | |
| case patchpanel::Client::TrafficSource::kChrome: | |
| return stream << "Chrome"; | |
| case patchpanel::Client::TrafficSource::kUser: | |
| return stream << "User"; | |
| case patchpanel::Client::TrafficSource::kUpdateEngine: | |
| return stream << "UE"; | |
| case patchpanel::Client::TrafficSource::kSystem: | |
| return stream << "System"; | |
| case patchpanel::Client::TrafficSource::kVpn: | |
| return stream << "VPN"; | |
| case patchpanel::Client::TrafficSource::kArc: | |
| return stream << "ARC"; | |
| case patchpanel::Client::TrafficSource::kBorealisVM: | |
| return stream << "Borealis"; | |
| case patchpanel::Client::TrafficSource::kBruschettaVM: | |
| return stream << "Bruschetta"; | |
| case patchpanel::Client::TrafficSource::kCrostiniVM: | |
| return stream << "Crostini"; | |
| case patchpanel::Client::TrafficSource::kParallelsVM: | |
| return stream << "Parallels"; | |
| case patchpanel::Client::TrafficSource::kTethering: | |
| return stream << "Tethering"; | |
| case patchpanel::Client::TrafficSource::kWiFiDirect: | |
| return stream << "WiFi Direct"; | |
| case patchpanel::Client::TrafficSource::kWiFiLOHS: | |
| return stream << "WiFi LOHS"; | |
| } | |
| } | |
| BRILLO_EXPORT std::ostream& operator<<( | |
| std::ostream& stream, const Client::TrafficVector& traffic_vector) { | |
| return stream << "[rx=" << traffic_vector.rx_bytes | |
| << ", tx=" << traffic_vector.tx_bytes << "]"; | |
| } | |
| } // namespace patchpanel |