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