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