• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
ObjectID(DHCPv4Config * d)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 
DHCPv4Config(ControlInterface * control_interface,EventDispatcher * dispatcher,DHCPProvider * provider,const string & device_name,const string & lease_file_suffix,bool arp_gateway,const DhcpProperties & dhcp_props,Metrics * metrics)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 
~DHCPv4Config()120 DHCPv4Config::~DHCPv4Config() {
121   SLOG(this, 2) << __func__ << ": " << device_name();
122 }
123 
ProcessEventSignal(const string & reason,const KeyValueStore & configuration)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 
ProcessStatusChangeSignal(const string & status)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 
CleanupClientState()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 
ShouldFailOnAcquisitionTimeout()225 bool DHCPv4Config::ShouldFailOnAcquisitionTimeout() {
226   // Continue to use previous lease if gateway ARP is active.
227   return !is_gateway_arp_active_;
228 }
229 
ShouldKeepLeaseOnDisconnect()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 
GetFlags()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
GetIPv4AddressString(unsigned int address)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
ParseClasslessStaticRoutes(const string & classless_routes,IPConfig::Properties * properties)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
ParseConfiguration(const KeyValueStore & configuration,IPConfig::Properties * properties)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