Home | History | Annotate | Download | only in dhcp
      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