1 /*
2 * Copyright 2017, 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 "dhcpclient.h"
18 #include "dhcp.h"
19 #include "interface.h"
20 #include "log.h"
21
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <linux/if_ether.h>
25 #include <poll.h>
26 #include <unistd.h>
27
28 #include <cutils/properties.h>
29
30 #include <inttypes.h>
31
32 // The initial retry timeout for DHCP is 4000 milliseconds
33 static const uint32_t kInitialTimeout = 4000;
34 // The maximum retry timeout for DHCP is 64000 milliseconds
35 static const uint32_t kMaxTimeout = 64000;
36 // A specific value that indicates that no timeout should happen and that
37 // the state machine should immediately transition to the next state
38 static const uint32_t kNoTimeout = 0;
39
40 // Enable debug messages
41 static const bool kDebug = false;
42
43 // The number of milliseconds that the timeout should vary (up or down) from the
44 // base timeout. DHCP requires a -1 to +1 second variation in timeouts.
45 static const int kTimeoutSpan = 1000;
46
addrToStr(in_addr_t address)47 static std::string addrToStr(in_addr_t address) {
48 struct in_addr addr = {address};
49 char buffer[64];
50 return inet_ntop(AF_INET, &addr, buffer, sizeof(buffer));
51 }
52
DhcpClient(uint32_t options)53 DhcpClient::DhcpClient(uint32_t options)
54 : mOptions(options), mRandomEngine(std::random_device()()),
55 mRandomDistribution(-kTimeoutSpan, kTimeoutSpan), mState(State::Init),
56 mNextTimeout(kInitialTimeout), mFuzzNextTimeout(true) {}
57
init(const char * interfaceName)58 Result DhcpClient::init(const char* interfaceName) {
59 Result res = mInterface.init(interfaceName);
60 if (!res) {
61 return res;
62 }
63
64 res = mRouter.init();
65 if (!res) {
66 return res;
67 }
68
69 res = mSocket.open(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
70 if (!res) {
71 return res;
72 }
73
74 res = mSocket.bindRaw(mInterface.getIndex());
75 if (!res) {
76 return res;
77 }
78 return Result::success();
79 }
80
run()81 Result DhcpClient::run() {
82 // Block all signals while we're running. This way we don't have to deal
83 // with things like EINTR. waitAndReceive then uses ppoll to set the
84 // original mask while polling. This way polling can be interrupted but
85 // socket writing, reading and ioctl remain interrupt free. If a signal
86 // arrives while we're blocking it will be placed in the signal queue
87 // and handled once ppoll sets the original mask. This way no signals are
88 // lost.
89 sigset_t blockMask, originalMask;
90 int status = ::sigfillset(&blockMask);
91 if (status != 0) {
92 return Result::error("Unable to fill signal set: %s", strerror(errno));
93 }
94 status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask);
95 if (status != 0) {
96 return Result::error("Unable to set signal mask: %s", strerror(errno));
97 }
98
99 for (;;) {
100 // Before waiting, polling or receiving we check the current state and
101 // see what we should do next. This may result in polling but could
102 // also lead to instant state changes without any polling. The new state
103 // will then be evaluated instead, most likely leading to polling.
104 switch (mState) {
105 case State::Init:
106 // The starting state. This is the state the client is in when
107 // it first starts. It's also the state that the client returns
108 // to when things go wrong in other states.
109 setNextState(State::Selecting);
110 break;
111 case State::Selecting:
112 // In the selecting state the client attempts to find DHCP
113 // servers on the network. The client remains in this state
114 // until a suitable server responds.
115 sendDhcpDiscover();
116 increaseTimeout();
117 break;
118 case State::Requesting:
119 // In the requesting state the client has found a suitable
120 // server. The next step is to send a request directly to that
121 // server.
122 if (mNextTimeout >= kMaxTimeout) {
123 // We've tried to request a bunch of times, start over
124 setNextState(State::Init);
125 } else {
126 sendDhcpRequest(mServerAddress);
127 increaseTimeout();
128 }
129 break;
130 case State::Bound:
131 // The client enters the bound state when the server has
132 // accepted and acknowledged a request and given us a lease. At
133 // this point the client will wait until the lease is close to
134 // expiring and then it will try to renew the lease.
135 if (mT1.expired()) {
136 // Lease expired, renew lease
137 setNextState(State::Renewing);
138 } else {
139 // Spurious wake-up, continue waiting. Do not fuzz the
140 // timeout with a random offset. Doing so can cause wakeups
141 // before the timer has expired causing unnecessary
142 // processing. Even worse it can cause the timer to expire
143 // after the lease has ended.
144 mNextTimeout = mT1.remainingMillis();
145 mFuzzNextTimeout = false;
146 }
147 break;
148 case State::Renewing:
149 // In the renewing state the client is sending a request for the
150 // same address it had was previously bound to. If the second
151 // timer expires when in this state the client will attempt to
152 // do a full rebind.
153 if (mT2.expired()) {
154 // Timeout while renewing, move to rebinding
155 setNextState(State::Rebinding);
156 } else {
157 sendDhcpRequest(mServerAddress);
158 increaseTimeout();
159 }
160 break;
161 case State::Rebinding:
162 // The client was unable to renew the lease and moved to the
163 // rebinding state. In this state the client sends a request for
164 // the same address it had before to the broadcast address. This
165 // means that any DHCP server on the network is free to respond.
166 // After attempting this a few times the client will give up and
167 // move to the Init state to try to find a new DHCP server.
168 if (mNextTimeout >= kMaxTimeout) {
169 // We've tried to rebind a bunch of times, start over
170 setNextState(State::Init);
171 } else {
172 // Broadcast a request
173 sendDhcpRequest(INADDR_BROADCAST);
174 increaseTimeout();
175 }
176 break;
177 default:
178 break;
179 }
180 // The proper action for the current state has been taken, perform any
181 // polling and/or waiting needed.
182 waitAndReceive(originalMask);
183 }
184
185 return Result::error("Client terminated unexpectedly");
186 }
187
stateToStr(State state)188 const char* DhcpClient::stateToStr(State state) {
189 switch (state) {
190 case State::Init:
191 return "Init";
192 case State::Selecting:
193 return "Selecting";
194 case State::Requesting:
195 return "Requesting";
196 case State::Bound:
197 return "Bound";
198 case State::Renewing:
199 return "Renewing";
200 case State::Rebinding:
201 return "Rebinding";
202 }
203 return "<unknown>";
204 }
205
waitAndReceive(const sigset_t & pollSignalMask)206 void DhcpClient::waitAndReceive(const sigset_t& pollSignalMask) {
207 if (mNextTimeout == kNoTimeout) {
208 // If there is no timeout the state machine has indicated that it wants
209 // an immediate transition to another state. Do nothing.
210 return;
211 }
212
213 struct pollfd fds;
214 fds.fd = mSocket.get();
215 fds.events = POLLIN;
216
217 uint32_t timeout = calculateTimeoutMillis();
218 for (;;) {
219 uint64_t startedAt = now();
220
221 struct timespec ts;
222 ts.tv_sec = timeout / 1000;
223 ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;
224
225 // Poll for any incoming traffic with the calculated timeout. While
226 // polling the original signal mask is set so that the polling can be
227 // interrupted.
228 int res = ::ppoll(&fds, 1, &ts, &pollSignalMask);
229 if (res == 0) {
230 // Timeout, return to let the caller evaluate
231 return;
232 } else if (res > 0) {
233 // Something to read
234 Message msg;
235 if (receiveDhcpMessage(&msg)) {
236 // We received a DHCP message, check if it's of interest
237 uint8_t msgType = msg.type();
238 switch (mState) {
239 case State::Selecting:
240 if (msgType == DHCPOFFER) {
241 // Received an offer, move to the Requesting state
242 // to request it.
243 mServerAddress = msg.serverId();
244 mRequestAddress = msg.dhcpData.yiaddr;
245 setNextState(State::Requesting);
246 return;
247 }
248 break;
249 case State::Requesting:
250 case State::Renewing:
251 case State::Rebinding:
252 // All of these states have sent a DHCP request and are
253 // now waiting for an ACK so the behavior is the same.
254 if (msgType == DHCPACK) {
255 // Request approved
256 if (configureDhcp(msg)) {
257 // Successfully configured DHCP, move to Bound
258 setNextState(State::Bound);
259 return;
260 }
261 // Unable to configure DHCP, keep sending requests.
262 // This may not fix the issue but eventually it will
263 // allow for a full timeout which will lead to a
264 // move to the Init state. This might still not fix
265 // the issue but at least the client keeps trying.
266 } else if (msgType == DHCPNAK) {
267 // Request denied, halt network and start over
268 haltNetwork();
269 setNextState(State::Init);
270 return;
271 }
272 break;
273 default:
274 // For the other states the client is not expecting any
275 // network messages so we ignore those messages.
276 break;
277 }
278 }
279 } else {
280 // An error occurred in polling, don't do anything here. The client
281 // should keep going anyway to try to acquire a lease in the future
282 // if things start working again.
283 }
284 // If we reach this point we received something that's not a DHCP,
285 // message, we timed out, or an error occurred. Go again with whatever
286 // time remains.
287 uint64_t currentTime = now();
288 uint64_t end = startedAt + timeout;
289 if (currentTime >= end) {
290 // We're done anyway, return and let caller evaluate
291 return;
292 }
293 // Wait whatever the remaining time is
294 timeout = end - currentTime;
295 }
296 }
297
configureDhcp(const Message & msg)298 bool DhcpClient::configureDhcp(const Message& msg) {
299 size_t optsSize = msg.optionsSize();
300 if (optsSize < 4) {
301 // Message is too small
302 if (kDebug) ALOGD("Opts size too small %d", static_cast<int>(optsSize));
303 return false;
304 }
305
306 const uint8_t* options = msg.dhcpData.options;
307
308 memset(&mDhcpInfo, 0, sizeof(mDhcpInfo));
309
310 // Inspect all options in the message to try to find the ones we want
311 for (size_t i = 4; i + 1 < optsSize;) {
312 uint8_t optCode = options[i];
313 uint8_t optLength = options[i + 1];
314 if (optCode == OPT_END) {
315 break;
316 }
317
318 if (options + optLength + i >= msg.end()) {
319 // Invalid option length, drop it
320 if (kDebug)
321 ALOGD("Invalid opt length %d for opt %d", static_cast<int>(optLength),
322 static_cast<int>(optCode));
323 return false;
324 }
325 const uint8_t* opt = options + i + 2;
326 switch (optCode) {
327 case OPT_LEASE_TIME:
328 if (optLength == 4) {
329 mDhcpInfo.leaseTime = ntohl(*reinterpret_cast<const uint32_t*>(opt));
330 }
331 break;
332 case OPT_T1:
333 if (optLength == 4) {
334 mDhcpInfo.t1 = ntohl(*reinterpret_cast<const uint32_t*>(opt));
335 }
336 break;
337 case OPT_T2:
338 if (optLength == 4) {
339 mDhcpInfo.t2 = ntohl(*reinterpret_cast<const uint32_t*>(opt));
340 }
341 break;
342 case OPT_SUBNET_MASK:
343 if (optLength == 4) {
344 mDhcpInfo.subnetMask = *reinterpret_cast<const in_addr_t*>(opt);
345 }
346 break;
347 case OPT_GATEWAY:
348 if (optLength >= 4) {
349 mDhcpInfo.gateway = *reinterpret_cast<const in_addr_t*>(opt);
350 }
351 break;
352 case OPT_MTU:
353 if (optLength == 2) {
354 mDhcpInfo.mtu = ntohs(*reinterpret_cast<const uint16_t*>(opt));
355 }
356 break;
357 case OPT_DNS:
358 if (optLength >= 4) {
359 mDhcpInfo.dns[0] = *reinterpret_cast<const in_addr_t*>(opt);
360 }
361 if (optLength >= 8) {
362 mDhcpInfo.dns[1] = *reinterpret_cast<const in_addr_t*>(opt + 4);
363 }
364 if (optLength >= 12) {
365 mDhcpInfo.dns[2] = *reinterpret_cast<const in_addr_t*>(opt + 8);
366 }
367 if (optLength >= 16) {
368 mDhcpInfo.dns[3] = *reinterpret_cast<const in_addr_t*>(opt + 12);
369 }
370 break;
371 case OPT_SERVER_ID:
372 if (optLength == 4) {
373 mDhcpInfo.serverId = *reinterpret_cast<const in_addr_t*>(opt);
374 }
375 break;
376 default:
377 break;
378 }
379 i += 2 + optLength;
380 }
381 mDhcpInfo.offeredAddress = msg.dhcpData.yiaddr;
382
383 if (mDhcpInfo.leaseTime == 0) {
384 // We didn't get a lease time, ignore this offer
385 return false;
386 }
387 // If there is no T1 or T2 timer given then we create an estimate as
388 // suggested for servers in RFC 2131.
389 uint32_t t1 = mDhcpInfo.t1, t2 = mDhcpInfo.t2;
390 mT1.expireSeconds(t1 > 0 ? t1 : (mDhcpInfo.leaseTime / 2));
391 mT2.expireSeconds(t2 > 0 ? t2 : ((mDhcpInfo.leaseTime * 7) / 8));
392
393 Result res = mInterface.bringUp();
394 if (!res) {
395 ALOGE("Could not configure DHCP: %s", res.c_str());
396 return false;
397 }
398
399 if (mDhcpInfo.mtu != 0) {
400 res = mInterface.setMtu(mDhcpInfo.mtu);
401 if (!res) {
402 // Consider this non-fatal, the system will not perform at its best
403 // but should still work.
404 ALOGE("Could not configure DHCP: %s", res.c_str());
405 }
406 }
407
408 char propName[64];
409 snprintf(propName, sizeof(propName), "vendor.net.%s.gw", mInterface.getName().c_str());
410 if (property_set(propName, addrToStr(mDhcpInfo.gateway).c_str()) != 0) {
411 ALOGE("Failed to set %s: %s", propName, strerror(errno));
412 }
413
414 int numDnsEntries = sizeof(mDhcpInfo.dns) / sizeof(mDhcpInfo.dns[0]);
415 for (int i = 0; i < numDnsEntries; ++i) {
416 snprintf(propName, sizeof(propName), "vendor.net.%s.dns%d", mInterface.getName().c_str(),
417 i + 1);
418 if (mDhcpInfo.dns[i] != 0) {
419 if (property_set(propName, addrToStr(mDhcpInfo.dns[i]).c_str()) != 0) {
420 ALOGE("Failed to set %s: %s", propName, strerror(errno));
421 }
422 } else {
423 // Clear out any previous value here in case it was set
424 if (property_set(propName, "") != 0) {
425 ALOGE("Failed to clear %s: %s", propName, strerror(errno));
426 }
427 }
428 }
429
430 res = mInterface.setAddress(mDhcpInfo.offeredAddress, mDhcpInfo.subnetMask);
431 if (!res) {
432 ALOGE("Could not configure DHCP: %s", res.c_str());
433 return false;
434 }
435
436 if ((mOptions & static_cast<uint32_t>(ClientOption::NoGateway)) == 0) {
437 res = mRouter.setDefaultGateway(mDhcpInfo.gateway, mInterface.getIndex());
438 if (!res) {
439 ALOGE("Could not configure DHCP: %s", res.c_str());
440 return false;
441 }
442 }
443 return true;
444 }
445
haltNetwork()446 void DhcpClient::haltNetwork() {
447 Result res = mInterface.setAddress(0, 0);
448 if (!res) {
449 ALOGE("Could not halt network: %s", res.c_str());
450 }
451 res = mInterface.bringDown();
452 if (!res) {
453 ALOGE("Could not halt network: %s", res.c_str());
454 }
455 }
456
receiveDhcpMessage(Message * msg)457 bool DhcpClient::receiveDhcpMessage(Message* msg) {
458 bool isValid = false;
459 Result res = mSocket.receiveRawUdp(PORT_BOOTP_CLIENT, msg, &isValid);
460 if (!res) {
461 if (kDebug) ALOGD("Discarding message: %s", res.c_str());
462 return false;
463 }
464
465 return isValid && msg->isValidDhcpMessage(OP_BOOTREPLY, mLastMsg.dhcpData.xid);
466 }
467
calculateTimeoutMillis()468 uint32_t DhcpClient::calculateTimeoutMillis() {
469 if (!mFuzzNextTimeout) {
470 return mNextTimeout;
471 }
472 int adjustment = mRandomDistribution(mRandomEngine);
473 if (adjustment < 0 && static_cast<uint32_t>(-adjustment) > mNextTimeout) {
474 // Underflow, return a timeout of zero milliseconds
475 return 0;
476 }
477 return mNextTimeout + adjustment;
478 }
479
increaseTimeout()480 void DhcpClient::increaseTimeout() {
481 if (mNextTimeout == kNoTimeout) {
482 mNextTimeout = kInitialTimeout;
483 } else {
484 if (mNextTimeout < kMaxTimeout) {
485 mNextTimeout *= 2;
486 }
487 if (mNextTimeout > kMaxTimeout) {
488 mNextTimeout = kMaxTimeout;
489 }
490 }
491 }
492
setNextState(State state)493 void DhcpClient::setNextState(State state) {
494 if (kDebug) ALOGD("Moving from state %s to %s", stateToStr(mState), stateToStr(state));
495 mState = state;
496 mNextTimeout = kNoTimeout;
497 mFuzzNextTimeout = true;
498 }
499
sendDhcpRequest(in_addr_t destination)500 void DhcpClient::sendDhcpRequest(in_addr_t destination) {
501 if (kDebug) ALOGD("Sending DHCPREQUEST");
502 mLastMsg = Message::request(mInterface.getMacAddress(), mRequestAddress, destination);
503 sendMessage(mLastMsg);
504 }
505
sendDhcpDiscover()506 void DhcpClient::sendDhcpDiscover() {
507 if (kDebug) ALOGD("Sending DHCPDISCOVER");
508 mLastMsg = Message::discover(mInterface.getMacAddress());
509 sendMessage(mLastMsg);
510 }
511
sendMessage(const Message & message)512 void DhcpClient::sendMessage(const Message& message) {
513 Result res = mSocket.sendRawUdp(INADDR_ANY, PORT_BOOTP_CLIENT, INADDR_BROADCAST,
514 PORT_BOOTP_SERVER, mInterface.getIndex(), message);
515 if (!res) {
516 ALOGE("Unable to send message: %s", res.c_str());
517 }
518 }
519