Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek dhcpclient.cpp

  Sprache: C
 

/*
 * Copyright 2017, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "dhcpclient.h"
#include "dhcp.h"
#include "interface.h"
#include "log.h"

#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_ether.h>
#include <poll.h>
#include <unistd.h>

#include <cutils/properties.h>

#include <inttypes.h>

// The initial retry timeout for DHCP is 4000 milliseconds
static const uint32_t kInitialTimeout = 4000;
// The maximum retry timeout for DHCP is 64000 milliseconds
static const uint32_t kMaxTimeout = 64000;
// A specific value that indicates that no timeout should happen and that
// the state machine should immediately transition to the next state
static const uint32_t kNoTimeout = 0;

// Enable debug messages
static const bool kDebug = false;

// The number of milliseconds that the timeout should vary (up or down) from the
// base timeout. DHCP requires a -1 to +1 second variation in timeouts.
static const int kTimeoutSpan = 1000;

static std::string addrToStr(in_addr_t address) {
    struct in_addr addr = {address};
    char buffer[64];
    return inet_ntop(AF_INET, &addr, buffer, sizeof(buffer));
}

DhcpClient::DhcpClient(uint32_t options)
    : mOptions(options), mRandomEngine(std::random_device()()),
      mRandomDistribution(-kTimeoutSpan, kTimeoutSpan), mState(State::Init),
      mNextTimeout(kInitialTimeout), mFuzzNextTimeout(true) {}

Result DhcpClient::init(const char* interfaceName) {
    Result res = mInterface.init(interfaceName);
    if (!res) {
        return res;
    }

    res = mRouter.init();
    if (!res) {
        return res;
    }

    res = mSocket.open(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
    if (!res) {
        return res;
    }

    res = mSocket.bindRaw(mInterface.getIndex());
    if (!res) {
        return res;
    }
    return Result::success();
}

Result DhcpClient::run() {
    // Block all signals while we're running. This way we don't have to deal
    // with things like EINTR. waitAndReceive then uses ppoll to set the
    // original mask while polling. This way polling can be interrupted but
    // socket writing, reading and ioctl remain interrupt free. If a signal
    // arrives while we're blocking it will be placed in the signal queue
    // and handled once ppoll sets the original mask. This way no signals are
    // lost.
    sigset_t blockMask, originalMask;
    int status = ::sigfillset(&blockMask);
    if (status != 0) {
        return Result::error("Unable to fill signal set: %s", strerror(errno));
    }
    status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask);
    if (status != 0) {
        return Result::error("Unable to set signal mask: %s", strerror(errno));
    }

    for (;;) {
        // Before waiting, polling or receiving we check the current state and
        // see what we should do next. This may result in polling but could
        // also lead to instant state changes without any polling. The new state
        // will then be evaluated instead, most likely leading to polling.
        switch (mState) {
        case State::Init:
            // The starting state. This is the state the client is in when
            // it first starts. It's also the state that the client returns
            // to when things go wrong in other states.
            setNextState(State::Selecting);
            break;
        case State::Selecting:
            // In the selecting state the client attempts to find DHCP
            // servers on the network. The client remains in this state
            // until a suitable server responds.
            sendDhcpDiscover();
            increaseTimeout();
            break;
        case State::Requesting:
            // In the requesting state the client has found a suitable
            // server. The next step is to send a request directly to that
            // server.
            if (mNextTimeout >= kMaxTimeout) {
                // We've tried to request a bunch of times, start over
                setNextState(State::Init);
            } else {
                sendDhcpRequest(mServerAddress);
                increaseTimeout();
            }
            break;
        case State::Bound:
            // The client enters the bound state when the server has
            // accepted and acknowledged a request and given us a lease. At
            // this point the client will wait until the lease is close to
            // expiring and then it will try to renew the lease.
            if (mT1.expired()) {
                // Lease expired, renew lease
                setNextState(State::Renewing);
            } else {
                // Spurious wake-up, continue waiting. Do not fuzz the
                // timeout with a random offset. Doing so can cause wakeups
                // before the timer has expired causing unnecessary
                // processing. Even worse it can cause the timer to expire
                // after the lease has ended.
                mNextTimeout = mT1.remainingMillis();
                mFuzzNextTimeout = false;
            }
            break;
        case State::Renewing:
            // In the renewing state the client is sending a request for the
            // same address it had was previously bound to. If the second
            // timer expires when in this state the client will attempt to
            // do a full rebind.
            if (mT2.expired()) {
                // Timeout while renewing, move to rebinding
                setNextState(State::Rebinding);
            } else {
                sendDhcpRequest(mServerAddress);
                increaseTimeout();
            }
            break;
        case State::Rebinding:
            // The client was unable to renew the lease and moved to the
            // rebinding state. In this state the client sends a request for
            // the same address it had before to the broadcast address. This
            // means that any DHCP server on the network is free to respond.
            // After attempting this a few times the client will give up and
            // move to the Init state to try to find a new DHCP server.
            if (mNextTimeout >= kMaxTimeout) {
                // We've tried to rebind a bunch of times, start over
                setNextState(State::Init);
            } else {
                // Broadcast a request
                sendDhcpRequest(INADDR_BROADCAST);
                increaseTimeout();
            }
            break;
        default:
            break;
        }
        // The proper action for the current state has been taken, perform any
        // polling and/or waiting needed.
        waitAndReceive(originalMask);
    }

    return Result::error("Client terminated unexpectedly");
}

const char* DhcpClient::stateToStr(State state) {
    switch (state) {
    case State::Init:
        return "Init";
    case State::Selecting:
        return "Selecting";
    case State::Requesting:
        return "Requesting";
    case State::Bound:
        return "Bound";
    case State::Renewing:
        return "Renewing";
    case State::Rebinding:
        return "Rebinding";
    }
    return "<unknown>";
}

void DhcpClient::waitAndReceive(const sigset_t& pollSignalMask) {
    if (mNextTimeout == kNoTimeout) {
        // If there is no timeout the state machine has indicated that it wants
        // an immediate transition to another state. Do nothing.
        return;
    }

    struct pollfd fds;
    fds.fd = mSocket.get();
    fds.events = POLLIN;

    uint32_t timeout = calculateTimeoutMillis();
    for (;;) {
        uint64_t startedAt = now();

        struct timespec ts;
        ts.tv_sec = timeout / 1000;
        ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;

        // Poll for any incoming traffic with the calculated timeout. While
        // polling the original signal mask is set so that the polling can be
        // interrupted.
        int res = ::ppoll(&fds, 1, &ts, &pollSignalMask);
        if (res == 0) {
            // Timeout, return to let the caller evaluate
            return;
        } else if (res > 0) {
            // Something to read
            Message msg;
            if (receiveDhcpMessage(&msg)) {
                // We received a DHCP message, check if it's of interest
                uint8_t msgType = msg.type();
                switch (mState) {
                case State::Selecting:
                    if (msgType == DHCPOFFER) {
                        // Received an offer, move to the Requesting state
                        // to request it.
                        mServerAddress = msg.serverId();
                        mRequestAddress = msg.dhcpData.yiaddr;
                        setNextState(State::Requesting);
                        return;
                    }
                    break;
                case State::Requesting:
                case State::Renewing:
                case State::Rebinding:
                    // All of these states have sent a DHCP request and are
                    // now waiting for an ACK so the behavior is the same.
                    if (msgType == DHCPACK) {
                        // Request approved
                        if (configureDhcp(msg)) {
                            // Successfully configured DHCP, move to Bound
                            setNextState(State::Bound);
                            return;
                        }
                        // Unable to configure DHCP, keep sending requests.
                        // This may not fix the issue but eventually it will
                        // allow for a full timeout which will lead to a
                        // move to the Init state. This might still not fix
                        // the issue but at least the client keeps trying.
                    } else if (msgType == DHCPNAK) {
                        // Request denied, halt network and start over
                        haltNetwork();
                        setNextState(State::Init);
                        return;
                    }
                    break;
                default:
                    // For the other states the client is not expecting any
                    // network messages so we ignore those messages.
                    break;
                }
            }
        } else {
            // An error occurred in polling, don't do anything here. The client
            // should keep going anyway to try to acquire a lease in the future
            // if things start working again.
        }
        // If we reach this point we received something that's not a DHCP,
        // message, we timed out, or an error occurred. Go again with whatever
        // time remains.
        uint64_t currentTime = now();
        uint64_t end = startedAt + timeout;
        if (currentTime >= end) {
            // We're done anyway, return and let caller evaluate
            return;
        }
        // Wait whatever the remaining time is
        timeout = end - currentTime;
    }
}

bool DhcpClient::configureDhcp(const Message& msg) {
    size_t optsSize = msg.optionsSize();
    if (optsSize < 4) {
        // Message is too small
        if (kDebug) ALOGD("Opts size too small %d"static_cast<int>(optsSize));
        return false;
    }

    const uint8_t* options = msg.dhcpData.options;

    memset(&mDhcpInfo, 0sizeof(mDhcpInfo));

    // Inspect all options in the message to try to find the ones we want
    for (size_t i = 4; i + 1 < optsSize;) {
        uint8_t optCode = options[i];
        uint8_t optLength = options[i + 1];
        if (optCode == OPT_END) {
            break;
        }

        if (options + optLength + i >= msg.end()) {
            // Invalid option length, drop it
            if (kDebug)
                ALOGD("Invalid opt length %d for opt %d"static_cast<int>(optLength),
                      static_cast<int>(optCode));
            return false;
        }
        const uint8_t* opt = options + i + 2;
        switch (optCode) {
        case OPT_LEASE_TIME:
            if (optLength == 4) {
                mDhcpInfo.leaseTime = ntohl(*reinterpret_cast<const uint32_t*>(opt));
            }
            break;
        case OPT_T1:
            if (optLength == 4) {
                mDhcpInfo.t1 = ntohl(*reinterpret_cast<const uint32_t*>(opt));
            }
            break;
        case OPT_T2:
            if (optLength == 4) {
                mDhcpInfo.t2 = ntohl(*reinterpret_cast<const uint32_t*>(opt));
            }
            break;
        case OPT_SUBNET_MASK:
            if (optLength == 4) {
                mDhcpInfo.subnetMask = *reinterpret_cast<const in_addr_t*>(opt);
            }
            break;
        case OPT_GATEWAY:
            if (optLength >= 4) {
                mDhcpInfo.gateway = *reinterpret_cast<const in_addr_t*>(opt);
            }
            break;
        case OPT_MTU:
            if (optLength == 2) {
                mDhcpInfo.mtu = ntohs(*reinterpret_cast<const uint16_t*>(opt));
            }
            break;
        case OPT_DNS:
            if (optLength >= 4) {
                mDhcpInfo.dns[0] = *reinterpret_cast<const in_addr_t*>(opt);
            }
            if (optLength >= 8) {
                mDhcpInfo.dns[1] = *reinterpret_cast<const in_addr_t*>(opt + 4);
            }
            if (optLength >= 12) {
                mDhcpInfo.dns[2] = *reinterpret_cast<const in_addr_t*>(opt + 8);
            }
            if (optLength >= 16) {
                mDhcpInfo.dns[3] = *reinterpret_cast<const in_addr_t*>(opt + 12);
            }
            break;
        case OPT_SERVER_ID:
            if (optLength == 4) {
                mDhcpInfo.serverId = *reinterpret_cast<const in_addr_t*>(opt);
            }
            break;
        default:
            break;
        }
        i += 2 + optLength;
    }
    mDhcpInfo.offeredAddress = msg.dhcpData.yiaddr;

    if (mDhcpInfo.leaseTime == 0) {
        // We didn't get a lease time, ignore this offer
        return false;
    }
    // If there is no T1 or T2 timer given then we create an estimate as
    // suggested for servers in RFC 2131.
    uint32_t t1 = mDhcpInfo.t1, t2 = mDhcpInfo.t2;
    mT1.expireSeconds(t1 > 0 ? t1 : (mDhcpInfo.leaseTime / 2));
    mT2.expireSeconds(t2 > 0 ? t2 : ((mDhcpInfo.leaseTime * 7) / 8));

    Result res = mInterface.bringUp();
    if (!res) {
        ALOGE("Could not configure DHCP: %s", res.c_str());
        return false;
    }

    if (mDhcpInfo.mtu != 0) {
        res = mInterface.setMtu(mDhcpInfo.mtu);
        if (!res) {
            // Consider this non-fatal, the system will not perform at its best
            // but should still work.
            ALOGE("Could not configure DHCP: %s", res.c_str());
        }
    }

    char propName[64];
    snprintf(propName, sizeof(propName), "vendor.net.%s.gw", mInterface.getName().c_str());
    if (property_set(propName, addrToStr(mDhcpInfo.gateway).c_str()) != 0) {
        ALOGE("Failed to set %s: %s", propName, strerror(errno));
    }

    int numDnsEntries = sizeof(mDhcpInfo.dns) / sizeof(mDhcpInfo.dns[0]);
    for (int i = 0; i < numDnsEntries; ++i) {
        snprintf(propName, sizeof(propName), "vendor.net.%s.dns%d", mInterface.getName().c_str(),
                 i + 1);
        if (mDhcpInfo.dns[i] != 0) {
            if (property_set(propName, addrToStr(mDhcpInfo.dns[i]).c_str()) != 0) {
                ALOGE("Failed to set %s: %s", propName, strerror(errno));
            }
        } else {
            // Clear out any previous value here in case it was set
            if (property_set(propName, "") != 0) {
                ALOGE("Failed to clear %s: %s", propName, strerror(errno));
            }
        }
    }

    res = mInterface.setAddress(mDhcpInfo.offeredAddress, mDhcpInfo.subnetMask);
    if (!res) {
        ALOGE("Could not configure DHCP: %s", res.c_str());
        return false;
    }

    if ((mOptions & static_cast<uint32_t>(ClientOption::NoGateway)) == 0) {
        res = mRouter.setDefaultGateway(mDhcpInfo.gateway, mInterface.getIndex());
        if (!res) {
            ALOGE("Could not configure DHCP: %s", res.c_str());
            return false;
        }
    }
    return true;
}

void DhcpClient::haltNetwork() {
    Result res = mInterface.setAddress(00);
    if (!res) {
        ALOGE("Could not halt network: %s", res.c_str());
    }
    res = mInterface.bringDown();
    if (!res) {
        ALOGE("Could not halt network: %s", res.c_str());
    }
}

bool DhcpClient::receiveDhcpMessage(Message* msg) {
    bool isValid = false;
    Result res = mSocket.receiveRawUdp(PORT_BOOTP_CLIENT, msg, &isValid);
    if (!res) {
        if (kDebug) ALOGD("Discarding message: %s", res.c_str());
        return false;
    }

    return isValid && msg->isValidDhcpMessage(OP_BOOTREPLY, mLastMsg.dhcpData.xid);
}

uint32_t DhcpClient::calculateTimeoutMillis() {
    if (!mFuzzNextTimeout) {
        return mNextTimeout;
    }
    int adjustment = mRandomDistribution(mRandomEngine);
    if (adjustment < 0 && static_cast<uint32_t>(-adjustment) > mNextTimeout) {
        // Underflow, return a timeout of zero milliseconds
        return 0;
    }
    return mNextTimeout + adjustment;
}

void DhcpClient::increaseTimeout() {
    if (mNextTimeout == kNoTimeout) {
        mNextTimeout = kInitialTimeout;
    } else {
        if (mNextTimeout < kMaxTimeout) {
            mNextTimeout *= 2;
        }
        if (mNextTimeout > kMaxTimeout) {
            mNextTimeout = kMaxTimeout;
        }
    }
}

void DhcpClient::setNextState(State state) {
    if (kDebug) ALOGD("Moving from state %s to %s", stateToStr(mState), stateToStr(state));
    mState = state;
    mNextTimeout = kNoTimeout;
    mFuzzNextTimeout = true;
}

void DhcpClient::sendDhcpRequest(in_addr_t destination) {
    if (kDebug) ALOGD("Sending DHCPREQUEST");
    mLastMsg = Message::request(mInterface.getMacAddress(), mRequestAddress, destination);
    sendMessage(mLastMsg);
}

void DhcpClient::sendDhcpDiscover() {
    if (kDebug) ALOGD("Sending DHCPDISCOVER");
    mLastMsg = Message::discover(mInterface.getMacAddress());
    sendMessage(mLastMsg);
}

void DhcpClient::sendMessage(const Message& message) {
    Result res = mSocket.sendRawUdp(INADDR_ANY, PORT_BOOTP_CLIENT, INADDR_BROADCAST,
                                    PORT_BOOTP_SERVER, mInterface.getIndex(), message);
    if (!res) {
        ALOGE("Unable to send message: %s", res.c_str());
    }
}

Messung V0.5 in Prozent
C=87 H=83 G=84

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.12Bemerkung:  (vorverarbeitet am  2026-06-27) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

sichere Verbindung

sichere Verbindung