1 // 2 // Copyright (C) 2015 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "shill/dhcp/dhcpv4_config.h" 18 19 #include <arpa/inet.h> 20 21 #include <base/files/file_util.h> 22 #include <base/strings/string_split.h> 23 #include <base/strings/stringprintf.h> 24 #if defined(__ANDROID__) 25 #include <dbus/service_constants.h> 26 #else 27 #include <chromeos/dbus/service_constants.h> 28 #endif // __ANDROID__ 29 30 #include "shill/dhcp/dhcp_provider.h" 31 #include "shill/logging.h" 32 #include "shill/metrics.h" 33 #include "shill/net/ip_address.h" 34 35 using std::string; 36 using std::vector; 37 38 namespace shill { 39 40 namespace Logging { 41 static auto kModuleLogScope = ScopeLogger::kDHCP; 42 static string ObjectID(DHCPv4Config* d) { 43 if (d == nullptr) 44 return "(DHCPv4_config)"; 45 else 46 return d->device_name(); 47 } 48 } 49 50 // static 51 const char DHCPv4Config::kDHCPCDPathFormatPID[] = 52 "var/run/dhcpcd/dhcpcd-%s-4.pid"; 53 const char DHCPv4Config::kConfigurationKeyBroadcastAddress[] = 54 "BroadcastAddress"; 55 const char DHCPv4Config::kConfigurationKeyClasslessStaticRoutes[] = 56 "ClasslessStaticRoutes"; 57 const char DHCPv4Config::kConfigurationKeyDNS[] = "DomainNameServers"; 58 const char DHCPv4Config::kConfigurationKeyDomainName[] = "DomainName"; 59 const char DHCPv4Config::kConfigurationKeyDomainSearch[] = "DomainSearch"; 60 const char DHCPv4Config::kConfigurationKeyHostname[] = "Hostname"; 61 const char DHCPv4Config::kConfigurationKeyIPAddress[] = "IPAddress"; 62 const char DHCPv4Config::kConfigurationKeyLeaseTime[] = "DHCPLeaseTime"; 63 const char DHCPv4Config::kConfigurationKeyMTU[] = "InterfaceMTU"; 64 const char DHCPv4Config::kConfigurationKeyRouters[] = "Routers"; 65 const char DHCPv4Config::kConfigurationKeySubnetCIDR[] = "SubnetCIDR"; 66 const char DHCPv4Config::kConfigurationKeyVendorEncapsulatedOptions[] = 67 "VendorEncapsulatedOptions"; 68 const char DHCPv4Config::kConfigurationKeyWebProxyAutoDiscoveryUrl[] = 69 "WebProxyAutoDiscoveryUrl"; 70 const char DHCPv4Config::kReasonBound[] = "BOUND"; 71 const char DHCPv4Config::kReasonFail[] = "FAIL"; 72 const char DHCPv4Config::kReasonGatewayArp[] = "GATEWAY-ARP"; 73 const char DHCPv4Config::kReasonNak[] = "NAK"; 74 const char DHCPv4Config::kReasonRebind[] = "REBIND"; 75 const char DHCPv4Config::kReasonReboot[] = "REBOOT"; 76 const char DHCPv4Config::kReasonRenew[] = "RENEW"; 77 const char DHCPv4Config::kStatusArpGateway[] = "ArpGateway"; 78 const char DHCPv4Config::kStatusArpSelf[] = "ArpSelf"; 79 const char DHCPv4Config::kStatusBound[] = "Bound"; 80 const char DHCPv4Config::kStatusDiscover[] = "Discover"; 81 const char DHCPv4Config::kStatusIgnoreAdditionalOffer[] = 82 "IgnoreAdditionalOffer"; 83 const char DHCPv4Config::kStatusIgnoreFailedOffer[] = "IgnoreFailedOffer"; 84 const char DHCPv4Config::kStatusIgnoreInvalidOffer[] = "IgnoreInvalidOffer"; 85 const char DHCPv4Config::kStatusIgnoreNonOffer[] = "IgnoreNonOffer"; 86 const char DHCPv4Config::kStatusInform[] = "Inform"; 87 const char DHCPv4Config::kStatusInit[] = "Init"; 88 const char DHCPv4Config::kStatusNakDefer[] = "NakDefer"; 89 const char DHCPv4Config::kStatusRebind[] = "Rebind"; 90 const char DHCPv4Config::kStatusReboot[] = "Reboot"; 91 const char DHCPv4Config::kStatusRelease[] = "Release"; 92 const char DHCPv4Config::kStatusRenew[] = "Renew"; 93 const char DHCPv4Config::kStatusRequest[] = "Request"; 94 const char DHCPv4Config::kType[] = "dhcp"; 95 96 97 DHCPv4Config::DHCPv4Config(ControlInterface* control_interface, 98 EventDispatcher* dispatcher, 99 DHCPProvider* provider, 100 const string& device_name, 101 const string& lease_file_suffix, 102 bool arp_gateway, 103 const DhcpProperties& dhcp_props, 104 Metrics* metrics) 105 : DHCPConfig(control_interface, 106 dispatcher, 107 provider, 108 device_name, 109 kType, 110 lease_file_suffix), 111 arp_gateway_(arp_gateway), 112 is_gateway_arp_active_(false), 113 metrics_(metrics) { 114 dhcp_props.GetValueForProperty(DhcpProperties::kHostnameProperty, &hostname_); 115 dhcp_props.GetValueForProperty(DhcpProperties::kVendorClassProperty, 116 &vendor_class_); 117 SLOG(this, 2) << __func__ << ": " << device_name; 118 } 119 120 DHCPv4Config::~DHCPv4Config() { 121 SLOG(this, 2) << __func__ << ": " << device_name(); 122 } 123 124 void DHCPv4Config::ProcessEventSignal(const string& reason, 125 const KeyValueStore& configuration) { 126 LOG(INFO) << "Event reason: " << reason; 127 if (reason == kReasonFail) { 128 LOG(ERROR) << "Received failure event from DHCP client."; 129 NotifyFailure(); 130 return; 131 } else if (reason == kReasonNak) { 132 // If we got a NAK, this means the DHCP server is active, and any 133 // Gateway ARP state we have is no longer sufficient. 134 LOG_IF(ERROR, is_gateway_arp_active_) 135 << "Received NAK event for our gateway-ARP lease."; 136 is_gateway_arp_active_ = false; 137 return; 138 } else if (reason != kReasonBound && 139 reason != kReasonRebind && 140 reason != kReasonReboot && 141 reason != kReasonRenew && 142 reason != kReasonGatewayArp) { 143 LOG(WARNING) << "Event ignored."; 144 return; 145 } 146 IPConfig::Properties properties; 147 CHECK(ParseConfiguration(configuration, &properties)); 148 149 // This needs to be set before calling UpdateProperties() below since 150 // those functions may indirectly call other methods like ReleaseIP that 151 // depend on or change this value. 152 set_is_lease_active(true); 153 154 if (reason == kReasonGatewayArp) { 155 // This is a non-authoritative confirmation that we or on the same 156 // network as the one we received a lease on previously. The DHCP 157 // client is still running, so we should not cancel the timeout 158 // until that completes. In the meantime, however, we can tentatively 159 // configure our network in anticipation of successful completion. 160 IPConfig::UpdateProperties(properties, false); 161 is_gateway_arp_active_ = true; 162 } else { 163 DHCPConfig::UpdateProperties(properties, true); 164 is_gateway_arp_active_ = false; 165 } 166 } 167 168 void DHCPv4Config::ProcessStatusChangeSignal(const string& status) { 169 SLOG(this, 2) << __func__ << ": " << status; 170 171 if (status == kStatusArpGateway) { 172 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusArpGateway); 173 } else if (status == kStatusArpSelf) { 174 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusArpSelf); 175 } else if (status == kStatusBound) { 176 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusBound); 177 } else if (status == kStatusDiscover) { 178 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusDiscover); 179 } else if (status == kStatusIgnoreAdditionalOffer) { 180 metrics_->NotifyDhcpClientStatus( 181 Metrics::kDhcpClientStatusIgnoreAdditionalOffer); 182 } else if (status == kStatusIgnoreFailedOffer) { 183 metrics_->NotifyDhcpClientStatus( 184 Metrics::kDhcpClientStatusIgnoreFailedOffer); 185 } else if (status == kStatusIgnoreInvalidOffer) { 186 metrics_->NotifyDhcpClientStatus( 187 Metrics::kDhcpClientStatusIgnoreInvalidOffer); 188 } else if (status == kStatusIgnoreNonOffer) { 189 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusIgnoreNonOffer); 190 } else if (status == kStatusInform) { 191 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusInform); 192 } else if (status == kStatusInit) { 193 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusInit); 194 } else if (status == kStatusNakDefer) { 195 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusNakDefer); 196 } else if (status == kStatusRebind) { 197 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRebind); 198 } else if (status == kStatusReboot) { 199 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusReboot); 200 } else if (status == kStatusRelease) { 201 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRelease); 202 } else if (status == kStatusRenew) { 203 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRenew); 204 } else if (status == kStatusRequest) { 205 metrics_->NotifyDhcpClientStatus(Metrics::kDhcpClientStatusRequest); 206 } else { 207 LOG(ERROR) << "DHCP client reports unknown status " << status; 208 } 209 } 210 211 void DHCPv4Config::CleanupClientState() { 212 DHCPConfig::CleanupClientState(); 213 214 // Delete lease file if it is ephemeral. 215 if (IsEphemeralLease()) { 216 base::DeleteFile(root().Append( 217 base::StringPrintf(DHCPProvider::kDHCPCDPathFormatLease, 218 device_name().c_str())), false); 219 } 220 base::DeleteFile(root().Append( 221 base::StringPrintf(kDHCPCDPathFormatPID, device_name().c_str())), false); 222 is_gateway_arp_active_ = false; 223 } 224 225 bool DHCPv4Config::ShouldFailOnAcquisitionTimeout() { 226 // Continue to use previous lease if gateway ARP is active. 227 return !is_gateway_arp_active_; 228 } 229 230 bool DHCPv4Config::ShouldKeepLeaseOnDisconnect() { 231 // If we are using gateway unicast ARP to speed up re-connect, don't 232 // give up our leases when we disconnect. 233 return arp_gateway_; 234 } 235 236 vector<string> DHCPv4Config::GetFlags() { 237 // Get default flags first. 238 vector<string> flags = DHCPConfig::GetFlags(); 239 240 flags.push_back("-4"); // IPv4 only. 241 242 // Apply options from DhcpProperties when applicable. 243 if (!hostname_.empty()) { 244 flags.push_back("-h"); // Request hostname from server 245 flags.push_back(hostname_.c_str()); 246 } 247 if (!vendor_class_.empty()) { 248 flags.push_back("-i"); 249 flags.push_back(vendor_class_.c_str()); 250 } 251 252 if (arp_gateway_) { 253 flags.push_back("-R"); // ARP for default gateway. 254 flags.push_back("-P"); // Enable unicast ARP on renew. 255 } 256 return flags; 257 } 258 259 // static 260 string DHCPv4Config::GetIPv4AddressString(unsigned int address) { 261 char str[INET_ADDRSTRLEN]; 262 if (inet_ntop(AF_INET, &address, str, arraysize(str))) { 263 return str; 264 } 265 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address; 266 return ""; 267 } 268 269 // static 270 bool DHCPv4Config::ParseClasslessStaticRoutes( 271 const string& classless_routes, IPConfig::Properties* properties) { 272 if (classless_routes.empty()) { 273 // It is not an error for this string to be empty. 274 return true; 275 } 276 277 vector<string> route_strings = base::SplitString( 278 classless_routes, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 279 if (route_strings.size() % 2) { 280 LOG(ERROR) << "In " << __func__ << ": Size of route_strings array " 281 << "is a non-even number: " << route_strings.size(); 282 return false; 283 } 284 285 vector<IPConfig::Route> routes; 286 vector<string>::iterator route_iterator = route_strings.begin(); 287 // Classless routes are a space-delimited array of 288 // "destination/prefix gateway" values. As such, we iterate twice 289 // for each pass of the loop below. 290 while (route_iterator != route_strings.end()) { 291 const string& destination_as_string(*route_iterator); 292 route_iterator++; 293 IPAddress destination(IPAddress::kFamilyIPv4); 294 if (!destination.SetAddressAndPrefixFromString( 295 destination_as_string)) { 296 LOG(ERROR) << "In " << __func__ << ": Expected an IP address/prefix " 297 << "but got an unparsable: " << destination_as_string; 298 return false; 299 } 300 301 CHECK(route_iterator != route_strings.end()); 302 const string& gateway_as_string(*route_iterator); 303 route_iterator++; 304 IPAddress gateway(IPAddress::kFamilyIPv4); 305 if (!gateway.SetAddressFromString(gateway_as_string)) { 306 LOG(ERROR) << "In " << __func__ << ": Expected a router IP address " 307 << "but got an unparsable: " << gateway_as_string; 308 return false; 309 } 310 311 if (destination.prefix() == 0 && properties->gateway.empty()) { 312 // If a default route is provided in the classless parameters and 313 // we don't already have one, apply this as the default route. 314 SLOG(nullptr, 2) << "In " << __func__ << ": Setting default gateway to " 315 << gateway_as_string; 316 CHECK(gateway.IntoString(&properties->gateway)); 317 } else { 318 IPConfig::Route route; 319 CHECK(destination.IntoString(&route.host)); 320 IPAddress netmask(IPAddress::GetAddressMaskFromPrefix( 321 destination.family(), destination.prefix())); 322 CHECK(netmask.IntoString(&route.netmask)); 323 CHECK(gateway.IntoString(&route.gateway)); 324 routes.push_back(route); 325 SLOG(nullptr, 2) << "In " << __func__ << ": Adding route to to " 326 << destination_as_string << " via " << gateway_as_string; 327 } 328 } 329 330 if (!routes.empty()) { 331 properties->routes.swap(routes); 332 } 333 334 return true; 335 } 336 337 // static 338 bool DHCPv4Config::ParseConfiguration(const KeyValueStore& configuration, 339 IPConfig::Properties* properties) { 340 SLOG(nullptr, 2) << __func__; 341 properties->method = kTypeDHCP; 342 properties->address_family = IPAddress::kFamilyIPv4; 343 string classless_static_routes; 344 bool default_gateway_parse_error = false; 345 for (const auto it : configuration.properties()) { 346 const string& key = it.first; 347 const brillo::Any& value = it.second; 348 SLOG(nullptr, 2) << "Processing key: " << key; 349 if (key == kConfigurationKeyIPAddress) { 350 properties->address = GetIPv4AddressString(value.Get<uint32_t>()); 351 if (properties->address.empty()) { 352 return false; 353 } 354 } else if (key == kConfigurationKeySubnetCIDR) { 355 properties->subnet_prefix = value.Get<uint8_t>(); 356 } else if (key == kConfigurationKeyBroadcastAddress) { 357 properties->broadcast_address = 358 GetIPv4AddressString(value.Get<uint32_t>()); 359 if (properties->broadcast_address.empty()) { 360 return false; 361 } 362 } else if (key == kConfigurationKeyRouters) { 363 vector<uint32_t> routers = value.Get<vector<uint32_t>>(); 364 if (routers.empty()) { 365 LOG(ERROR) << "No routers provided."; 366 default_gateway_parse_error = true; 367 } else { 368 properties->gateway = GetIPv4AddressString(routers[0]); 369 if (properties->gateway.empty()) { 370 LOG(ERROR) << "Failed to parse router parameter provided."; 371 default_gateway_parse_error = true; 372 } 373 } 374 } else if (key == kConfigurationKeyDNS) { 375 vector<uint32_t> servers = value.Get<vector<uint32_t>>(); 376 for (vector<unsigned int>::const_iterator it = servers.begin(); 377 it != servers.end(); ++it) { 378 string server = GetIPv4AddressString(*it); 379 if (server.empty()) { 380 return false; 381 } 382 properties->dns_servers.push_back(server); 383 } 384 } else if (key == kConfigurationKeyDomainName) { 385 properties->domain_name = value.Get<string>(); 386 } else if (key == kConfigurationKeyHostname) { 387 properties->accepted_hostname = value.Get<string>(); 388 } else if (key == kConfigurationKeyDomainSearch) { 389 properties->domain_search = value.Get<vector<string>>(); 390 } else if (key == kConfigurationKeyMTU) { 391 int mtu = value.Get<uint16_t>(); 392 metrics_->SendSparseToUMA(Metrics::kMetricDhcpClientMTUValue, mtu); 393 if (mtu >= minimum_mtu() && mtu != kMinIPv4MTU) { 394 properties->mtu = mtu; 395 } 396 } else if (key == kConfigurationKeyClasslessStaticRoutes) { 397 classless_static_routes = value.Get<string>(); 398 } else if (key == kConfigurationKeyVendorEncapsulatedOptions) { 399 properties->vendor_encapsulated_options = value.Get<ByteArray>(); 400 } else if (key == kConfigurationKeyWebProxyAutoDiscoveryUrl) { 401 properties->web_proxy_auto_discovery = value.Get<string>(); 402 } else if (key == kConfigurationKeyLeaseTime) { 403 properties->lease_duration_seconds = value.Get<uint32_t>(); 404 } else { 405 SLOG(nullptr, 2) << "Key ignored."; 406 } 407 } 408 ParseClasslessStaticRoutes(classless_static_routes, properties); 409 if (default_gateway_parse_error && properties->gateway.empty()) { 410 return false; 411 } 412 return true; 413 } 414 415 } // namespace shill 416