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