blob: cb05058f34294f73a6348c8f01ec0e928523fefb [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHILL_NETWORK_DHCP_CONTROLLER_H_
#define SHILL_NETWORK_DHCP_CONTROLLER_H_
#include <memory>
#include <string>
#include <base/cancelable_callback.h>
#include <base/files/file_path.h>
#include <base/memory/weak_ptr.h>
#include <base/time/time.h>
#include <metrics/timer.h>
#include <net-base/network_config.h>
#include <net-base/process_manager.h>
#include "shill/event_dispatcher.h"
#include "shill/metrics.h"
#include "shill/mockable.h"
#include "shill/network/dhcp_client_proxy.h"
#include "shill/network/dhcpv4_config.h"
#include "shill/store/key_value_store.h"
#include "shill/technology.h"
#include "shill/time.h"
namespace shill {
// This class provides a DHCP client instance for the device |device_name|.
//
// The DHCPController instance asks the DHCP client to create a lease file
// containing the name |lease_file|. If this suffix is the same as
// |device_name|, the lease is considered to be ephemeral, and the lease file is
// removed whenever this DHCPController instance is no longer needed.
// Otherwise, the lease file persists and will be re-used in future attempts.
// If |hostname| is not empty, it will be used in the DHCP request as DHCP
// option 12. This asks the DHCP server to register this hostname on our
// behalf, for purposes of administration or creating a dynamic DNS entry.
class DHCPController : public DHCPClientProxy::EventHandler {
public:
// Time to wait for a DHCP lease.
static constexpr base::TimeDelta kAcquisitionTimeout = base::Seconds(30);
using Options = DHCPClientProxy::Options;
// Called when the IPConfig got from DHCP is updated. |network_config|
// contains the parameters we get from DHCP and will be used for network
// configuration. |dhcp_data| contains the other parameters that needs to be
// exposed to user.|new_lease_acquired| indicates whether or not a DHCP lease
// was acquired from the server.
using UpdateCallback = base::RepeatingCallback<void(
const net_base::NetworkConfig& network_config,
const DHCPv4Config::Data& dhcp_data,
bool new_lease_acquired)>;
// Called when DHCP process ended without getting a lease. |is_voluntary|
// indicates whether that was a voluntary stop per option 108, or because of a
// failure.
using DropCallback = base::RepeatingCallback<void(bool is_voluntary)>;
enum class ReleaseReason { kDisconnect, kStaticIP };
DHCPController(EventDispatcher* dispatcher,
Metrics* metrics,
Time* time,
DHCPClientProxyFactory* dhcp_client_proxy_factory,
std::string_view device_name,
Technology technology,
const Options& options,
UpdateCallback update_callback,
DropCallback drop_callback);
virtual ~DHCPController();
// Renews and releases IP configuration. Returns true on success, false
// otherwise. ReleaseIP is advisory: if we are no longer connected, it is not
// possible to properly vacate the lease on the remote server. Also,
// depending on the configuration of the specific IPConfig subclass, we may
// end up holding on to the lease so we can resume to the network lease
// faster.
mockable bool RenewIP();
mockable bool ReleaseIP(ReleaseReason reason);
std::string device_name() const { return device_name_; }
// Returns the time left (in seconds) till the current DHCP lease is to be
// renewed in |time_left|. Returns nullopt if an error occurs (i.e. current
// lease has already expired or no current DHCP lease), true otherwise.
std::optional<base::TimeDelta> TimeToLeaseExpiry();
// Returns the duration from Start() until the first time that this class gets
// the DHCP lease information from the DHCP client, and then resets the value
// (i.e., consumes the value). The next call to this function will return
// std::nullopt, unless the DHCPController is Start()-ed again. Note that the
// timer will only be started in Start(), which means the duration include the
// time for starting the DHCP client, and the renewal process triggered by
// sending a D-Bus signal to an existing DHCP client won't be counted.
std::optional<base::TimeDelta> GetAndResetLastProvisionDuration();
// Implement DHCPClientProxy::EventHandler.
void OnDHCPEvent(DHCPClientProxy::EventReason reason,
const KeyValueStore& configuration) override;
void OnProcessExited(int pid, int exit_status) override;
private:
using CreateDHCPClientProxyCB =
base::RepeatingCallback<std::unique_ptr<DHCPClientProxy>()>;
// Starts the DHCP client if no DHCP client is running. Returns false if
// any error occurs and the DHCP client is not running.
bool Start();
// Stops the DHCP client.
void Stop();
void UpdateConfiguration(const KeyValueStore& configuration,
bool is_gateway_arp);
// On we get a new network config via DHCP. |new_lease_acquired| indicates
// whether this is an authoritative confirmation.
void OnIPConfigUpdated(const net_base::NetworkConfig& network_config,
const DHCPv4Config::Data& dhcp_data,
bool new_lease_acquired);
void NotifyDropCallback(bool is_voluntary);
// Initialize a callback that will invoke ProcessAcquisitionTimeout if we
// do not get a lease in a reasonable amount of time.
void StartAcquisitionTimeout();
// Cancel callback created by StartAcquisitionTimeout. One-liner included
// for symmetry.
void StopAcquisitionTimeout();
// Called if we do not get a DHCP lease in a reasonable amount of time.
// Informs upper layers of the failure.
void ProcessAcquisitionTimeout();
// Initialize a callback that will invoke ProcessExpirationTimeout if we
// do not renew a lease in a |lease_duration|.
void StartExpirationTimeout(base::TimeDelta lease_duration);
// Cancel callback created by StartExpirationTimeout. One-liner included
// for symmetry.
void StopExpirationTimeout();
// Called if we do not renew a DHCP lease by the time the lease expires.
// Informs upper layers of the expiration and restarts the DHCP client.
void ProcessExpirationTimeout(base::TimeDelta lease_duration);
// Updates |current_lease_expiration_time_| by adding |new_lease_duration| to
// the current time.
void UpdateLeaseExpirationTime(uint32_t new_lease_duration);
// Resets |current_lease_expiration_time_| to its default value.
void ResetLeaseExpirationTime();
// The lifetime of these variables should outlive this instance.
EventDispatcher* dispatcher_;
Metrics* metrics_;
Time* time_;
const std::string device_name_;
const Technology technology_;
const DHCPClientProxy::Options options_;
const UpdateCallback update_callback_;
const DropCallback drop_callback_;
const bool use_arp_gateway_;
const CreateDHCPClientProxyCB create_dhcp_client_proxy_cb_;
std::unique_ptr<DHCPClientProxy> dhcp_client_proxy_;
// Indicates whether a lease has been acquired from the DHCP server or gateway
// ARP.
bool is_lease_active_{false};
// Indicates whether it is valid to retain the lease acquired via gateway ARP.
bool is_gateway_arp_active_{false};
// Called if we fail to get a DHCP lease in a timely manner.
base::CancelableOnceClosure lease_acquisition_timeout_callback_;
std::optional<struct timeval> current_lease_expiration_time_;
// Called if a DHCP lease expires.
base::CancelableOnceClosure lease_expiration_callback_;
// The timer to measure the duration from the last Start() until we get the
// DHCP lease information from the DHCP client for the first time.
std::unique_ptr<chromeos_metrics::Timer> last_provision_timer_;
base::WeakPtrFactory<DHCPController> weak_ptr_factory_{this};
};
// The factory class of DHCPController. It's used to inject mock instances at
// testing.
class DHCPControllerFactory {
public:
DHCPControllerFactory(EventDispatcher* dispatcher,
Metrics* metrics,
Time* time,
DHCPClientProxyFactory* dhcp_client_proxy_factory);
virtual ~DHCPControllerFactory();
virtual std::unique_ptr<DHCPController> Create(
std::string_view device_name,
Technology technology,
const DHCPController::Options& options,
DHCPController::UpdateCallback update_callback,
DHCPController::DropCallback drop_callback);
private:
EventDispatcher* dispatcher_;
Metrics* metrics_;
Time* time_;
DHCPClientProxyFactory* dhcp_client_proxy_factory_;
};
} // namespace shill
#endif // SHILL_NETWORK_DHCP_CONTROLLER_H_