1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef PERF_SOCKET_H
#define PERF_SOCKET_H

#include <perfdhcp/command_options.h>

#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/socket_info.h>
#include <dhcp/iface_mgr.h>

namespace isc {
namespace perfdhcp {

/// \brief Socket wrapper structure.
///
/// This is a base class that is inherited by PerfSocket
/// and unit tests derived that. This way it allows mocking
/// out socket operations and avoid using real network
/// interfaces.
class BasePerfSocket : public dhcp::SocketInfo {
public:
    /// Interface index.
    unsigned int ifindex_;

    /// \brief Default constructor of BasePerfSocket.
    BasePerfSocket() :
        SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, 0),
        ifindex_(0) {}

    /// \brief Destructor of the socket wrapper class.
    virtual ~BasePerfSocket() = default;

    /// \brief See description of this method in PerfSocket class below.
    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) = 0;

    /// \brief See description of this method in PerfSocket class below.
    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) = 0;

    /// \brief See description of this method in PerfSocket class below.
    virtual bool send(const dhcp::Pkt4Ptr& pkt) = 0;

    /// \brief See description of this method in PerfSocket class below.
    virtual bool send(const dhcp::Pkt6Ptr& pkt) = 0;

    /// \brief See description of this method in PerfSocket class below.
    virtual dhcp::IfacePtr getIface() = 0;
};

/// \brief Socket wrapper structure.
///
/// This is the wrapper that holds descriptor of the socket
/// used to run DHCP test. The wrapped socket is closed in
/// the destructor. This prevents resource leaks when
/// function that created the socket ends (normally or
/// when exception occurs). This structure extends parent
/// structure with new field ifindex_ that holds interface
/// index where socket is bound to.
class PerfSocket : public BasePerfSocket {
public:
    /// \brief Constructor of socket wrapper class.
    ///
    /// This constructor uses provided socket descriptor to
    /// find the name of the interface where socket has been
    /// bound to.
    PerfSocket(CommandOptions& options);

    /// \brief Destructor of the socket wrapper class.
    ///
    /// Destructor closes wrapped socket.
    virtual ~PerfSocket();

    /// \brief Receive DHCPv4 packet from interface.
    ///
    /// \param timeout_sec number of seconds for waiting for a packet,
    /// \param timeout_usec number of microseconds for waiting for a packet,
    /// \return received packet or nullptr if timed out
    virtual dhcp::Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec) override;

    /// \brief Receive DHCPv6 packet from interface.
    ///
    /// \param timeout_sec number of seconds for waiting for a packet,
    /// \param timeout_usec number of microseconds for waiting for a packet,
    /// \return received packet or nullptr if timed out
    virtual dhcp::Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec) override;

    /// \brief Send DHCPv4 packet through interface.
    ///
    /// \param pkt a packet for sending
    /// \return true if operation succeeded
    virtual bool send(const dhcp::Pkt4Ptr& pkt) override;

    /// \brief Send DHCPv6 packet through interface.
    ///
    /// \param pkt a packet for sending
    /// \return true if operation succeeded
    virtual bool send(const dhcp::Pkt6Ptr& pkt) override;

    /// \brief Get interface from IfaceMgr.
    ///
    /// \return shared pointer to Iface.
    virtual dhcp::IfacePtr getIface() override;

protected:
    /// \brief Initialize socket data.
    ///
    /// This method initializes members of the class that Interface
    /// Manager holds: interface name, local address.
    ///
    /// \throw isc::BadValue if interface for specified socket
    /// descriptor does not exist.
    void initSocketData();

    /// \brief Open socket to communicate with DHCP server.
    ///
    /// Method opens socket and binds it to local address. Function will
    /// use either interface name, local address or server address
    /// to create a socket, depending on what is available (specified
    /// from the command line). If socket can't be created for any
    /// reason, exception is thrown.
    /// If destination address is broadcast (for DHCPv4) or multicast
    /// (for DHCPv6) than broadcast or multicast option is set on
    /// the socket. Opened socket is registered and managed by IfaceMgr.
    ///
    /// \throw isc::BadValue if socket can't be created for given
    /// interface, local address or remote address.
    /// \throw isc::InvalidOperation if broadcast option can't be
    /// set for the v4 socket or if multicast option can't be set
    /// for the v6 socket.
    /// \throw isc::Unexpected if internal unexpected error occurred.
    /// \return socket descriptor.
    int openSocket(CommandOptions& options) const;
};

}
}

#endif /* PERF_SOCKET_H */