// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/connection.h" #include #include #include #include #include "shill/control_interface.h" #include "shill/device_info.h" #include "shill/firewall_proxy_interface.h" #include "shill/logging.h" #include "shill/net/rtnl_handler.h" #include "shill/routing_table.h" #if !defined(__ANDROID__) #include "shill/resolver.h" #else #include "shill/dns_server_proxy.h" #include "shill/dns_server_proxy_factory.h" #endif // __ANDROID__ using base::Bind; using base::Closure; using base::Unretained; using std::deque; using std::set; using std::string; using std::vector; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kConnection; static string ObjectID(Connection* c) { if (c == nullptr) return "(connection)"; return c->interface_name(); } } #if defined(__ANDROID__) namespace { const char* kGoogleDNSServers[] = { "8.8.4.4", "8.8.8.8" }; } // namespace #endif // __ANDROID__ // static const uint32_t Connection::kDefaultMetric = 1; // static const uint32_t Connection::kNonDefaultMetricBase = 10; // static const uint32_t Connection::kMarkForUserTraffic = 0x1; // static const uint8_t Connection::kSecondaryTableId = 0x1; Connection::Binder::Binder(const string& name, const Closure& disconnect_callback) : name_(name), client_disconnect_callback_(disconnect_callback) {} Connection::Binder::~Binder() { Attach(nullptr); } void Connection::Binder::Attach(const ConnectionRefPtr& to_connection) { if (connection_) { connection_->DetachBinder(this); LOG(INFO) << name_ << ": unbound from connection: " << connection_->interface_name(); connection_.reset(); } if (to_connection) { connection_ = to_connection->weak_ptr_factory_.GetWeakPtr(); connection_->AttachBinder(this); LOG(INFO) << name_ << ": bound to connection: " << connection_->interface_name(); } } void Connection::Binder::OnDisconnect() { LOG(INFO) << name_ << ": bound connection disconnected: " << connection_->interface_name(); connection_.reset(); if (!client_disconnect_callback_.is_null()) { SLOG(connection_.get(), 2) << "Running client disconnect callback."; client_disconnect_callback_.Run(); } } Connection::Connection(int interface_index, const std::string& interface_name, Technology::Identifier technology, const DeviceInfo* device_info, ControlInterface* control_interface) : weak_ptr_factory_(this), is_default_(false), has_broadcast_domain_(false), routing_request_count_(0), interface_index_(interface_index), interface_name_(interface_name), technology_(technology), user_traffic_only_(false), table_id_(RT_TABLE_MAIN), local_(IPAddress::kFamilyUnknown), gateway_(IPAddress::kFamilyUnknown), lower_binder_( interface_name_, // Connection owns a single instance of |lower_binder_| so it's safe // to use an Unretained callback. Bind(&Connection::OnLowerDisconnect, Unretained(this))), device_info_(device_info), #if !defined(__ANDROID__) resolver_(Resolver::GetInstance()), #else dns_server_proxy_factory_(DNSServerProxyFactory::GetInstance()), #endif // __ANDROID__ routing_table_(RoutingTable::GetInstance()), rtnl_handler_(RTNLHandler::GetInstance()), control_interface_(control_interface) { SLOG(this, 2) << __func__ << "(" << interface_index << ", " << interface_name << ", " << Technology::NameFromIdentifier(technology) << ")"; } Connection::~Connection() { SLOG(this, 2) << __func__ << " " << interface_name_; NotifyBindersOnDisconnect(); DCHECK(!routing_request_count_); routing_table_->FlushRoutes(interface_index_); routing_table_->FlushRoutesWithTag(interface_index_); device_info_->FlushAddresses(interface_index_); TearDownIptableEntries(); } void Connection::UpdateFromIPConfig(const IPConfigRefPtr& config) { SLOG(this, 2) << __func__ << " " << interface_name_; const IPConfig::Properties& properties = config->properties(); user_traffic_only_ = properties.user_traffic_only; table_id_ = user_traffic_only_ ? kSecondaryTableId : (uint8_t)RT_TABLE_MAIN; IPAddress gateway(properties.address_family); if (!properties.gateway.empty() && !gateway.SetAddressFromString(properties.gateway)) { LOG(ERROR) << "Gateway address " << properties.gateway << " is invalid"; return; } excluded_ips_cidr_ = properties.exclusion_list; IPAddress trusted_ip(properties.address_family); if (!excluded_ips_cidr_.empty()) { const std::string first_excluded_ip = excluded_ips_cidr_[0]; excluded_ips_cidr_.erase(excluded_ips_cidr_.begin()); // A VPN connection can currently be bound to exactly one lower connection // such as eth0 or wan0. The excluded IPs are pinned to the gateway of // that connection. Setting up the routing table this way ensures that when // the lower connection goes offline, the associated entries in the routing // table are removed. On the flip side, when there are multiple connections // such as eth0 and wan0 and some IPs can be reached quickly over one // connection and the others over a different connection, all routes are // still pinned to a connection. // // The optimal connection to reach the first excluded IP is found below. // When this is found the route for the remaining excluded IPs are pinned in // the method PinPendingRoutes below. if (!trusted_ip.SetAddressAndPrefixFromString(first_excluded_ip)) { LOG(ERROR) << "Trusted IP address " << first_excluded_ip << " is invalid"; return; } if (!PinHostRoute(trusted_ip, gateway)) { LOG(ERROR) << "Unable to pin host route to " << first_excluded_ip; return; } } IPAddress local(properties.address_family); if (!local.SetAddressFromString(properties.address)) { LOG(ERROR) << "Local address " << properties.address << " is invalid"; return; } local.set_prefix(properties.subnet_prefix); IPAddress broadcast(properties.address_family); if (properties.broadcast_address.empty()) { if (properties.peer_address.empty()) { LOG(WARNING) << "Broadcast address is not set. Using default."; broadcast = local.GetDefaultBroadcast(); } } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) { LOG(ERROR) << "Broadcast address " << properties.broadcast_address << " is invalid"; return; } IPAddress peer(properties.address_family); if (!properties.peer_address.empty() && !peer.SetAddressFromString(properties.peer_address)) { LOG(ERROR) << "Peer address " << properties.peer_address << " is invalid"; return; } if (!FixGatewayReachability(local, &peer, &gateway, trusted_ip)) { LOG(WARNING) << "Expect limited network connectivity."; } if (device_info_->HasOtherAddress(interface_index_, local)) { // The address has changed for this interface. We need to flush // everything and start over. LOG(INFO) << __func__ << ": Flushing old addresses and routes."; routing_table_->FlushRoutes(interface_index_); device_info_->FlushAddresses(interface_index_); } LOG(INFO) << __func__ << ": Installing with parameters:" << " local=" << local.ToString() << " broadcast=" << broadcast.ToString() << " peer=" << peer.ToString() << " gateway=" << gateway.ToString(); rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer); if (gateway.IsValid() && properties.default_route) { routing_table_->SetDefaultRoute(interface_index_, gateway, GetMetric(is_default_), table_id_); } if (user_traffic_only_) { SetupIptableEntries(); } // Install any explicitly configured routes at the default metric. routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric, table_id_); SetMTU(properties.mtu); if (properties.blackhole_ipv6) { routing_table_->CreateBlackholeRoute(interface_index_, IPAddress::kFamilyIPv6, kDefaultMetric, table_id_); } // Save a copy of the last non-null DNS config. if (!config->properties().dns_servers.empty()) { dns_servers_ = config->properties().dns_servers; } #if defined(__ANDROID__) // Default to Google's DNS server if it is not provided through DHCP. if (config->properties().dns_servers.empty()) { LOG(INFO) << "Default to use Google DNS servers"; dns_servers_ = vector(std::begin(kGoogleDNSServers), std::end(kGoogleDNSServers)); } #endif // __ANDROID__ if (!config->properties().domain_search.empty()) { dns_domain_search_ = config->properties().domain_search; } if (!config->properties().domain_name.empty()) { dns_domain_name_ = config->properties().domain_name; } ipconfig_rpc_identifier_ = config->GetRpcIdentifier(); PushDNSConfig(); local_ = local; gateway_ = gateway; has_broadcast_domain_ = !peer.IsValid(); } bool Connection::SetupIptableEntries() { if (!firewall_proxy_) { firewall_proxy_.reset(control_interface_->CreateFirewallProxy()); } std::vector user_names; user_names.push_back("chronos"); user_names.push_back("debugd"); if (!firewall_proxy_->RequestVpnSetup(user_names, interface_name_)) { LOG(ERROR) << "VPN iptables setup request failed."; return false; } return true; } bool Connection::TearDownIptableEntries() { return firewall_proxy_ ? firewall_proxy_->RemoveVpnSetup() : true; } void Connection::SetIsDefault(bool is_default) { SLOG(this, 2) << __func__ << " " << interface_name_ << " (index " << interface_index_ << ") " << is_default_ << " -> " << is_default; if (is_default == is_default_) { return; } routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default)); is_default_ = is_default; PushDNSConfig(); if (is_default) { DeviceRefPtr device = device_info_->GetDevice(interface_index_); if (device) { device->RequestPortalDetection(); } } routing_table_->FlushCache(); } void Connection::UpdateDNSServers(const vector& dns_servers) { dns_servers_ = dns_servers; PushDNSConfig(); } void Connection::PushDNSConfig() { if (!is_default_) { #if defined(__ANDROID__) // Stop DNS server proxy to avoid having multiple instances of it running. // Only run DNS server proxy for the current default connection. dns_server_proxy_.reset(); #endif // __ANDROID__ return; } vector domain_search = dns_domain_search_; if (domain_search.empty() && !dns_domain_name_.empty()) { SLOG(this, 2) << "Setting domain search to domain name " << dns_domain_name_; domain_search.push_back(dns_domain_name_ + "."); } #if !defined(__ANDROID__) resolver_->SetDNSFromLists(dns_servers_, domain_search); #else dns_server_proxy_.reset( dns_server_proxy_factory_->CreateDNSServerProxy(dns_servers_)); dns_server_proxy_->Start(); #endif // __ANDROID__ } void Connection::RequestRouting() { if (routing_request_count_++ == 0) { DeviceRefPtr device = device_info_->GetDevice(interface_index_); DCHECK(device.get()); if (!device.get()) { LOG(ERROR) << "Device is NULL!"; return; } device->SetLooseRouting(true); } } void Connection::ReleaseRouting() { DCHECK_GT(routing_request_count_, 0); if (--routing_request_count_ == 0) { DeviceRefPtr device = device_info_->GetDevice(interface_index_); DCHECK(device.get()); if (!device.get()) { LOG(ERROR) << "Device is NULL!"; return; } device->SetLooseRouting(false); // Clear any cached routes that might have accumulated while reverse-path // filtering was disabled. routing_table_->FlushCache(); } } bool Connection::RequestHostRoute(const IPAddress& address) { // Do not set interface_index_ since this may not be the default route through // which this destination can be found. However, we should tag the created // route with our interface index so we can clean this route up when this // connection closes. Also, add route query callback to determine the lower // connection and bind to it. if (!routing_table_->RequestRouteToHost( address, -1, interface_index_, Bind(&Connection::OnRouteQueryResponse, weak_ptr_factory_.GetWeakPtr()), table_id_)) { LOG(ERROR) << "Could not request route to " << address.ToString(); return false; } return true; } bool Connection::PinPendingRoutes(int interface_index, RoutingTableEntry entry) { // The variable entry is locally modified, hence is passed by value in the // second argument above. for (auto excluded_ip = excluded_ips_cidr_.begin(); excluded_ip != excluded_ips_cidr_.end(); ++excluded_ip) { if (!entry.dst.SetAddressAndPrefixFromString(*excluded_ip) || !entry.dst.IsValid() || !routing_table_->AddRoute(interface_index, entry)) { LOG(ERROR) << "Unable to setup route for " << *excluded_ip << "."; } } return true; } string Connection::GetSubnetName() const { if (!local().IsValid()) { return ""; } return base::StringPrintf("%s/%d", local().GetNetworkPart().ToString().c_str(), local().prefix()); } bool Connection::FixGatewayReachability(const IPAddress& local, IPAddress* peer, IPAddress* gateway, const IPAddress& trusted_ip) { SLOG(nullptr, 2) << __func__ << " local " << local.ToString() << ", peer " << peer->ToString() << ", gateway " << gateway->ToString() << ", trusted_ip " << trusted_ip.ToString(); if (!gateway->IsValid()) { LOG(WARNING) << "No gateway address was provided for this connection."; return false; } if (peer->IsValid()) { if (!gateway->HasSameAddressAs(*peer)) { LOG(WARNING) << "Gateway address " << gateway->ToString() << " does not match peer address " << peer->ToString(); return false; } if (gateway->HasSameAddressAs(trusted_ip)) { // In order to send outgoing traffic in a point-to-point network, // the gateway IP address isn't of significance. As opposed to // broadcast networks, we never ARP for the gateway IP address, // but just send the IP packet addressed to the recipient. As // such, since using the external trusted IP address as the // gateway or peer wreaks havoc on the routing rules, we choose // not to supply a gateway address. Here's an example: // // Client <-> Internet <-> VPN Gateway <-> Internal Network // 192.168.1.2 10.0.1.25 172.16.5.0/24 // // In this example, a client connects to a VPN gateway on its // public IP address 10.0.1.25. It gets issued an IP address // from the VPN internal pool. For some VPN gateways, this // results in a pushed-down PPP configuration which specifies: // // Client local address: 172.16.5.13 // Client peer address: 10.0.1.25 // Client default gateway: 10.0.1.25 // // If we take this literally, we need to resolve the fact that // 10.0.1.25 is now listed as the default gateway and interface // peer address for the point-to-point interface. However, in // order to route tunneled packets to the VPN gateway we must // use the external route through the physical interface and // not the tunnel, or else we end up in an infinite loop // re-entering the tunnel trying to route towards the VPN server. // // We can do this by pinning a route, but we would need to wait // for the pinning process to complete before assigning this // address. Currently this process is asynchronous and will // complete only after returning to the event loop. Additionally, // since there's no metric associated with assigning an address // to an interface, it's always possible that having the peer // address of the interface might still trump a host route. // // To solve this problem, we reset the peer and gateway // addresses. Neither is required in order to perform the // underlying routing task. A gateway route can be specified // without an IP endpoint on point-to-point links, and simply // specify the outbound interface index. Similarly, a peer // IP address is not necessary either, and will be assigned // the same IP address as the local IP. This approach // simplifies routing and doesn't change the desired // functional behavior. // LOG(INFO) << "Removing gateway and peer addresses to preserve " << "routability to trusted IP address."; peer->SetAddressToDefault(); gateway->SetAddressToDefault(); } return true; } if (local.CanReachAddress(*gateway)) { return true; } LOG(WARNING) << "Gateway " << gateway->ToString() << " is unreachable from local address/prefix " << local.ToString() << "/" << local.prefix(); IPAddress gateway_with_max_prefix(*gateway); gateway_with_max_prefix.set_prefix( IPAddress::GetMaxPrefixLength(gateway_with_max_prefix.family())); IPAddress default_address(gateway->family()); RoutingTableEntry entry(gateway_with_max_prefix, default_address, default_address, 0, RT_SCOPE_LINK, false, table_id_, RoutingTableEntry::kDefaultTag); if (!routing_table_->AddRoute(interface_index_, entry)) { LOG(ERROR) << "Unable to add link-scoped route to gateway."; return false; } LOG(WARNING) << "Mitigating this by creating a link route to the gateway."; return true; } uint32_t Connection::GetMetric(bool is_default) { // If this is not the default route, assign a metric based on the interface // index. This way all non-default routes (even to the same gateway IP) end // up with unique metrics so they do not collide. return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_; } bool Connection::PinHostRoute(const IPAddress& trusted_ip, const IPAddress& gateway) { SLOG(this, 2) << __func__; if (!trusted_ip.IsValid()) { LOG(ERROR) << "No trusted IP -- unable to pin host route."; return false; } if (!gateway.IsValid()) { // Although we cannot pin a host route, we are also not going to create // a gateway route that will interfere with our primary connection, so // it is okay to return success here. LOG(WARNING) << "No gateway -- unable to pin host route."; return true; } return RequestHostRoute(trusted_ip); } void Connection::SetMTU(int32_t mtu) { SLOG(this, 2) << __func__ << " " << mtu; // Make sure the MTU value is valid. if (mtu == IPConfig::kUndefinedMTU) { mtu = IPConfig::kDefaultMTU; } else { int min_mtu = IsIPv6() ? IPConfig::kMinIPv6MTU : IPConfig::kMinIPv4MTU; if (mtu < min_mtu) { SLOG(this, 2) << __func__ << " MTU " << mtu << " is too small; adjusting up to " << min_mtu; mtu = min_mtu; } } rtnl_handler_->SetInterfaceMTU(interface_index_, mtu); } void Connection::OnRouteQueryResponse(int interface_index, const RoutingTableEntry& entry) { SLOG(this, 2) << __func__ << "(" << interface_index << ", " << entry.tag << ")" << " @ " << interface_name_; lower_binder_.Attach(nullptr); DeviceRefPtr device = device_info_->GetDevice(interface_index); if (!device) { LOG(ERROR) << "Unable to lookup device for index " << interface_index; return; } ConnectionRefPtr connection = device->connection(); if (!connection) { LOG(ERROR) << "Device " << interface_index << " has no connection."; return; } if (connection == this) { LOG(ERROR) << "Avoiding a connection bind loop for " << interface_name(); return; } lower_binder_.Attach(connection); connection->CreateGatewayRoute(); device->OnConnectionUpdated(); PinPendingRoutes(interface_index, entry); } bool Connection::CreateGatewayRoute() { // Ensure that the gateway for the lower connection remains reachable, // since we may create routes that conflict with it. if (!has_broadcast_domain_) { return false; } // If there is no gateway, don't try to create a route to it. if (!gateway_.IsValid()) { return false; } // It is not worth keeping track of this route, since it is benign, // and only pins persistent state that was already true of the connection. // If DHCP parameters change later (without the connection having been // destroyed and recreated), the binding processes will likely terminate // and restart, causing a new link route to be created. return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_, table_id_); } void Connection::OnLowerDisconnect() { SLOG(this, 2) << __func__ << " @ " << interface_name_; // Ensures that |this| instance doesn't get destroyed in the middle of // notifying the binders. This method needs to be separate from // NotifyBindersOnDisconnect because the latter may be invoked by Connection's // destructor when |this| instance's reference count is already 0. ConnectionRefPtr connection(this); connection->NotifyBindersOnDisconnect(); } void Connection::NotifyBindersOnDisconnect() { // Note that this method may be invoked by the destructor. SLOG(this, 2) << __func__ << " @ " << interface_name_; // Unbinds the lower connection before notifying the binders. This ensures // correct behavior in case of circular binding. lower_binder_.Attach(nullptr); while (!binders_.empty()) { // Pop the binder first and then notify it to ensure that each binder is // notified only once. Binder* binder = binders_.front(); binders_.pop_front(); binder->OnDisconnect(); } } void Connection::AttachBinder(Binder* binder) { SLOG(this, 2) << __func__ << "(" << binder->name() << ")" << " @ " << interface_name_; binders_.push_back(binder); } void Connection::DetachBinder(Binder* binder) { SLOG(this, 2) << __func__ << "(" << binder->name() << ")" << " @ " << interface_name_; for (auto it = binders_.begin(); it != binders_.end(); ++it) { if (binder == *it) { binders_.erase(it); return; } } } ConnectionRefPtr Connection::GetCarrierConnection() { SLOG(this, 2) << __func__ << " @ " << interface_name_; set visited; ConnectionRefPtr carrier = this; while (carrier->GetLowerConnection()) { if (ContainsKey(visited, carrier.get())) { LOG(ERROR) << "Circular connection chain starting at: " << carrier->interface_name(); // If a loop is detected return a NULL value to signal that the carrier // connection is unknown. return nullptr; } visited.insert(carrier.get()); carrier = carrier->GetLowerConnection(); } SLOG(this, 2) << "Carrier connection: " << carrier->interface_name() << " @ " << interface_name_; return carrier; } bool Connection::IsIPv6() { return local_.family() == IPAddress::kFamilyIPv6; } } // namespace shill