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