Home | History | Annotate | Download | only in client
      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