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 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 53 DhcpClient::DhcpClient() 54 : mRandomEngine(std::random_device()()), 55 mRandomDistribution(-kTimeoutSpan, kTimeoutSpan), 56 mState(State::Init), 57 mNextTimeout(kInitialTimeout), 58 mFuzzNextTimeout(true) { 59 } 60 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 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 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 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 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 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 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 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 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 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 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 523 void DhcpClient::sendDhcpDiscover() { 524 if (kDebug) ALOGD("Sending DHCPDISCOVER"); 525 mLastMsg = Message::discover(mInterface.getMacAddress()); 526 sendMessage(mLastMsg); 527 } 528 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