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