summaryrefslogtreecommitdiffstats
path: root/src/bin/dhcp4/client_handler.h
blob: 384fba908dccb075c6b72379abc79bc44dcb8d2e (plain)
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright (C) 2020-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 CLIENT_HANDLER_H
#define CLIENT_HANDLER_H

#include <dhcp/pkt4.h>
#include <boost/noncopyable.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <mutex>
#include <thread>

namespace isc {
namespace dhcp {

/// @brief Define the type of packet processing continuation.
typedef std::function<void()> Continuation;

/// @brief Define the type of shared pointers to continuations.
typedef boost::shared_ptr<Continuation> ContinuationPtr;

/// @brief Continuation factory.
///
/// @param cont Continuation rvalue.
inline ContinuationPtr makeContinuation(Continuation&& cont) {
    return (boost::make_shared<Continuation>(cont));
}

/// @brief Client race avoidance RAII handler.
class ClientHandler : public boost::noncopyable {
private:

    /// Class (aka static) types, methods and members.

    /// @brief Structure representing a client.
    struct Client {

        /// @brief Constructor.
        ///
        /// @param query The query.
        /// @param client_id The client ID.
        /// @param hwaddr The hardware address.
        /// @throw if the query is null or client_id and hwaddr are null.
        Client(Pkt4Ptr query, ClientIdPtr client_id, HWAddrPtr hwaddr);

        /// @brief The query being processed.
        Pkt4Ptr query_;

        /// @brief Cached binary client ID.
        std::vector<uint8_t> client_id_;

        /// @brief Cached hardware type.
        uint16_t htype_;

        /// @brief Cached binary hardware address.
        std::vector<uint8_t> hwaddr_;

        /// @brief The ID of the thread processing the query.
        std::thread::id thread_;

        /// @brief The next query.
        ///
        /// @note This field can be modified from another handler
        /// holding the mutex.
        Pkt4Ptr next_query_;

        /// @brief The continuation to process next query for the client.
        ///
        /// @note This field can be modified from another handler
        /// holding the mutex.
        ContinuationPtr cont_;
    };

    /// @brief The type of shared pointers to clients.
    typedef boost::shared_ptr<Client> ClientPtr;

    /// @brief The type of the client-by-id container.
    typedef boost::multi_index_container<

        // This container stores pointers to client-by-id objects.
        ClientPtr,

        // Start specification of indexes here.
        boost::multi_index::indexed_by<

            // First index is used to search by Duid.
            boost::multi_index::hashed_unique<

                // Client ID binary content as a member of the Client object.
                boost::multi_index::member<
                    Client, std::vector<uint8_t>, &Client::client_id_
                >
            >
        >
    > ClientByIdContainer;

    /// @brief The type of the client-by-hwaddr container.
    typedef boost::multi_index_container<

        // This container stores pointers to client-by-hwaddr objects.
        ClientPtr,

        // Start specification of indexes here.
        boost::multi_index::indexed_by<

            // First index is used to search by HWAddr.
            boost::multi_index::hashed_unique<

                // This is a composite index for type and binary components.
                boost::multi_index::composite_key<
                    Client,
                    boost::multi_index::member<
                        Client, uint16_t, &Client::htype_
                    >,
                    boost::multi_index::member<
                        Client, std::vector<uint8_t>, &Client::hwaddr_
                    >
                >
            >
        >
    > ClientByHWAddrContainer;

    /// @brief Lookup a client by id.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param client_id The duid of the query from the client.
    /// @return The client found in the by client id container or null.
    static ClientPtr lookup(const ClientIdPtr& client_id);

    /// @brief Lookup a client by hwaddr.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param hwaddr The hardware address of the query from the client.
    /// @return The client found in the by hardware address container or null.
    static ClientPtr lookup(const HWAddrPtr& hwaddr);

    /// @brief Add a client by id.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param client The client to insert into the by id client container.
    static void addById(const ClientPtr& client);

    /// @brief Add a client by hwaddr.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param client The client to insert into the by hwaddr client container.
    static void addByHWAddr(const ClientPtr& client);

    /// @brief Delete a client by id.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param client_id The duid to delete from the by id client container.
    static void del(const ClientIdPtr& client_id);

    /// @brief Delete a client by hwaddr.
    ///
    /// The mutex must be held by the caller.
    ///
    /// @param hwaddr The hwaddr to delete from the by hwaddr client container.
    static void del(const HWAddrPtr& hwaddr);

    /// @brief Mutex to protect client containers.
    ///
    /// The mutex is used only by public methods for guards.
    static std::mutex mutex_;

    /// @brief The client-by-id container.
    static ClientByIdContainer clients_client_id_;

    /// @brief The client-by-hwaddr container.
    static ClientByHWAddrContainer clients_hwaddr_;

public:

    /// Public interface.

    /// @brief Constructor.
    ClientHandler();

    /// @brief Destructor.
    ///
    /// Releases the client if it was acquired.
    virtual ~ClientHandler();

    /// @brief Tries to acquires a client.
    ///
    /// Lookup the client:
    ///  - if not found insert the client in the clients map and return true
    ///  - if found, if has a continuation put it in the holder,
    ///    and return false
    ///
    /// @param query The query from the client.
    /// @param cont The continuation in the case the client was held.
    /// @return true if the client was acquired, false if there is already
    /// a query from the same client.
    bool tryLock(Pkt4Ptr query, ContinuationPtr cont = ContinuationPtr());

private:

    /// Instance methods and members.

    /// @brief Acquire a client by client ID option.
    ///
    /// The mutex must be held by the caller.
    void lockById();

    /// @brief Acquire a client by hardware address.
    ///
    /// The mutex must be held by the caller.
    void lockByHWAddr();

    /// @brief Release a client by client ID option.
    ///
    /// The mutex must be held by the caller.
    void unLockById();

    /// @brief Release a client by hardware address.
    ///
    /// The mutex must be held by the caller.
    void unLockByHWAddr();

    /// @brief Local client.
    ClientPtr client_;

    /// @brief Client ID locked by this handler.
    ClientIdPtr locked_client_id_;

    /// @brief Hardware address locked by this handler.
    HWAddrPtr locked_hwaddr_;
};

} // namespace isc
} // namespace dhcp

#endif // CLIENT_HANDLER_H