summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/high_availability/query_filter.h
blob: 64405e43d0e3a0d666fd2f6ab1be32bcef11863b (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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
// Copyright (C) 2018-2022 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 QUERY_FILTER_H
#define QUERY_FILTER_H

#include <ha_config.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>

#include <boost/scoped_ptr.hpp>

#include <cstdint>
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <vector>

namespace isc {
namespace ha {

/// @brief DHCP query filtering class.
///
/// This class is a central point of information about distribution of the
/// DHCP queries processed by the servers within HA setup. It also implements
/// load balancing of the DHCP queries, when configured to do so.
///
/// The query filter uses a term "scope" to identify group of DHCP queries
/// processed by a given server. Currently, we support load balancing
/// between two servers. Therefore, in this mode of operation, there are two
/// scopes named after servers responsible for processing packets belonging
/// to those scopes, e.g. "server1" and "server2".
///
/// In the hot-standby mode, there is only one server processing incoming
/// DHCP queries. Thus, there is only one scope named after the primary
/// server, e.g. "server1".
///
/// This class allows for assigning the server to process queries belonging
/// to various scopes. For example: when a failure of the partner server
/// is detected, the @c QueryFilter::serveFailoverScopes is called to
/// indicate that this server instance should start handling queries
/// belonging to the scope(s) of the server which have died. Converesly,
/// a call to @c QueryFilter::serveDefaultScopes reverts to the default
/// state.
///
/// When DHCP query is received, the @c QueryFilter class is used to determine
/// whether this query should be processed by this server. The
/// @c QueryFilter::inScope methods return boolean value indicating
/// whether the query should be processed or not. If not, the query is
/// dropped.
///
/// The server administrator may force the server to start processing queries
/// from the selected scopes, e.g. the administrator may manually instruct the
/// backup server to take over the traffic of the primary and secondary servers.
/// The 'ha-scopes' command is sent in such case, which enables/disables
/// scopes within this class instance.
class QueryFilter {
public:

    /// @brief Constructor.
    ///
    /// This constructor puts HA peers configurations in the following order:
    /// - primary server configuration,
    /// - secondary or standby server configuration,
    /// - backup servers configurations
    ///
    /// It also sets the @c active_servers_ value to the number of active
    /// servers (responding to DHCP queries) for a given HA mode. In our case,
    /// this is 2 for the load balancing case and 1 for the hot-standby.
    /// Such organization of the configurations makes it easier for the
    /// load balancing algorithm to distribute queries between active servers.
    /// In our simple case, the load balancing algorithm can produce a value
    /// of 0 or 1, which points to a primary or secondary server in the
    /// configuration vector.
    ///
    /// @param config pointer to the HA configuration.
    /// @throw HAConfigValidationError if provided configuration is invalid.
    explicit QueryFilter(const HAConfigPtr& config);

    /// @brief Enable scope.
    ///
    /// Starts serving queries from the specified scope. It doesn't affect
    /// other scopes.
    ///
    /// @param scope_name name of the scope/server to be enabled.
    /// @throw BadValue if scope name doesn't match any of the server names.
    void serveScope(const std::string& scope_name);

    /// @brief Enable scope and disable all other scopes.
    ///
    /// Starts serving queries from the specified scope. Disable all other
    /// scopes.
    ///
    /// @param scope_name name of the scope/server to be enabled.
    /// @throw BadValue if scope name doesn't match any of the server names.
    void serveScopeOnly(const std::string& scope_name);

    /// @brief Enables selected scopes.
    ///
    /// All non listed scopes are disabled.
    ///
    /// @param scopes vector of scope names to be enabled.
    void serveScopes(const std::vector<std::string>& scopes);

    /// @brief Serve default scopes for the given HA mode.
    ///
    /// If this server is primary or secondary (load balancing), the scope
    /// of this server is enabled. All other scopes are disabled.
    void serveDefaultScopes();

    /// @brief Enable scopes required in failover case.
    ///
    /// In the load balancing case, the scopes of the primary and secondary
    /// servers are enabled (this server will handle the entire traffic).
    /// In the hot standby case, the primary server's scope is enabled
    /// (this server will handle the entire traffic normally processed by
    /// the primary server).
    void serveFailoverScopes();

    /// @brief Disables all scopes.
    void serveNoScopes();

    /// @brief Checks if this server instance is configured to process traffic
    /// belonging to a particular scope.
    ///
    /// @param scope_name name of the scope/server.
    /// @return true if the scope is enabled.
    bool amServingScope(const std::string& scope_name) const;

    /// @brief Returns served scopes.
    ///
    /// This method is mostly useful for testing purposes.
    std::set<std::string> getServedScopes() const;

    /// @brief Checks if this server should process the DHCPv4 query.
    ///
    /// This method uses a table of DHCPv4 message types to determine whether or
    /// not a query needs further analysis to determine which HA server
    /// should handle it or if it should simply be processed by this server.
    ///
    /// For message types that qualify (e.g. DHCPDISCOVER, DHCPREQUEST, etc),
    /// it then takes into account enabled scopes for this server and HA mode
    /// to determine whether this query should be processed. It triggers load
    /// balancing when load balancing mode is enabled.
    ///
    /// Non-qualifying message types (e.g. DHCPLEASEQUERY) are passed
    /// through for handling by this server.
    ///
    /// @param query4 pointer to the DHCPv4 query instance.
    /// @param [out] scope_class name of the class which corresponds to the
    /// name of the server which owns the packet. Those class names are used
    /// in subnets, pools and network configurations to associate them with
    /// different servers.
    ///
    /// @return true if the specified query should be processed by this
    /// server, false otherwise.
    bool inScope(const dhcp::Pkt4Ptr& query4, std::string& scope_class) const;

    /// @brief Checks if this server should process the DHCPv6 query.
    ///
    /// This method uses a table of DHCPv6 message types to determine whether or
    /// not a query needs further analysis to determine which HA server
    /// should handle it or if it should simply be processed by this server.
    ///
    /// For message types that qualify (e.g. DHCPV6_SOLICIT, DHCPV6_REQUEST, etc),
    /// it then takes into account enabled scopes for this server and HA mode
    /// to determine whether this query should be processed. It triggers load
    /// balancing when load balancing mode is enabled.
    ///
    /// Non-qualifying message types (e.g. DHCPV6_LEASEQUERY) are passed through
    /// for handling by this server.
    ///
    /// @param query6 pointer to the DHCPv6 query instance.
    /// @param [out] scope_class name of the class which corresponds to the
    /// name of the server which owns the packet. Those class names are used
    /// in subnets, pools and network configurations to associate them with
    /// different servers.
    ///
    /// @return true if the specified query should be processed by this
    /// server, false otherwise.
    bool inScope(const dhcp::Pkt6Ptr& query6, std::string& scope_class) const;

    /// @brief Determines if a DHCPv4 query is a message type HA should process.
    ///
    /// @param query4 DHCPv4 packet to test. Must not be null.
    ///
    /// @return
    static bool isHaType(const dhcp::Pkt4Ptr& query4);

    /// @brief Determines if a DHCPv6 query is a message type HA should process.
    ///
    /// @param query6 DHCPv6 packet to test. Must not be null.
    ///
    /// @return
    static bool isHaType(const dhcp::Pkt6Ptr& query6);

private:
    /// @brief Enable scope.
    ///
    /// Should be called in a thread safe context.
    ///
    /// Starts serving queries from the specified scope. It doesn't affect
    /// other scopes.
    ///
    /// @param scope_name name of the scope/server to be enabled.
    /// @throw BadValue if scope name doesn't match any of the server names.
    void serveScopeInternal(const std::string& scope_name);

    /// @brief Enable scope and disable all other scopes.
    ///
    /// Should be called in a thread safe context.
    ///
    /// Starts serving queries from the specified scope. Disable all other
    /// scopes.
    ///
    /// @param scope_name name of the scope/server to be enabled.
    /// @throw BadValue if scope name doesn't match any of the server names.
    void serveScopeOnlyInternal(const std::string& scope_name);

    /// @brief Enables selected scopes.
    ///
    /// Should be called in a thread safe context.
    ///
    /// All non listed scopes are disabled.
    ///
    /// @param scopes vector of scope names to be enabled.
    void serveScopesInternal(const std::vector<std::string>& scopes);

    /// @brief Serve default scopes for the given HA mode.
    ///
    /// Should be called in a thread safe context.
    ///
    /// If this server is primary or secondary (load balancing), the scope
    /// of this server is enabled. All other scopes are disabled.
    void serveDefaultScopesInternal();

    /// @brief Enable scopes required in failover case.
    ///
    /// Should be called in a thread safe context.
    ///
    /// In the load balancing case, the scopes of the primary and secondary
    /// servers are enabled (this server will handle the entire traffic).
    /// In the hot standby case, the primary server's scope is enabled
    /// (this server will handle the entire traffic normally processed by
    /// the primary server).
    void serveFailoverScopesInternal();

    /// @brief Disables all scopes.
    ///
    /// Should be called in a thread safe context.
    void serveNoScopesInternal();

    /// @brief Checks if this server instance is configured to process traffic
    /// belonging to a particular scope.
    ///
    /// Should be called in a thread safe context.
    ///
    /// @param scope_name name of the scope/server.
    /// @return true if the scope is enabled.
    bool amServingScopeInternal(const std::string& scope_name) const;

    /// @brief Returns served scopes.
    ///
    /// Should be called in a thread safe context.
    ///
    /// This method is mostly useful for testing purposes.
    std::set<std::string> getServedScopesInternal() const;

    /// @brief Generic implementation of the @c inScope function for DHCPv4
    /// and DHCPv6 queries.
    ///
    /// Should be called in a thread safe context.
    ///
    /// @tparam QueryPtrType type of the query, i.e. DHCPv4 or DHCPv6 query.
    /// @param query pointer to the DHCP query instance.
    /// @param [out] scope_class name of the class which corresponds to the
    /// name of the server which owns the packet. Those class names are used
    /// in subnets, pools and network configurations to associate them with
    /// different servers.
    ///
    /// @return true if the specified query should be processed by this
    /// server, false otherwise.
    template<typename QueryPtrType>
    bool inScopeInternal(const QueryPtrType& query, std::string& scope_class) const;

protected:

    /// @brief Performs load balancing of the DHCPv4 queries.
    ///
    /// This method returns an index of the server configuration
    /// held within @c peers_ vector. This points to a server
    /// which should process the given query. Currently, we only
    /// support load balancing between two servers, therefore this
    /// value should be 0 or 1.
    ///
    /// @param query4 pointer to the DHCPv4 query instance.
    /// @return Index of the server which should process the query. It
    /// returns negative value if the query is malformed, i.e. contains
    /// no HW address and no client identifier.
    int loadBalance(const dhcp::Pkt4Ptr& query4) const;

    /// @brief Performs load balancing of the DHCPv6 queries.
    ///
    /// This method returns an index of the server configuration
    /// held within @c peers_ vector. This points to a server
    /// which should process the given query. Currently, we only
    /// support load balancing between two servers, therefore this
    /// value should be 0 or 1.
    ///
    /// @param query6 pointer to the DHCPv6 query instance.
    /// @return Index of the server which should process the query. It
    /// returns negative value if the query is malformed, i.e. contains
    /// no DUID.
    int loadBalance(const dhcp::Pkt6Ptr& query6) const;

    /// @brief Compute load balancing hash.
    ///
    /// The load balancing hash is computed according to section 6
    /// if RFC3074.
    ///
    /// @param key identifier used to compute a hash, i.e. HW address
    /// or client identifier.
    /// @param key_len length of the key.
    ///
    /// @return Computed hash value.
    uint8_t loadBalanceHash(const uint8_t* key, const size_t key_len) const;

    /// @brief Checks if the scope name matches a name of any of the
    /// configured servers.
    ///
    /// @param scope_name scope name to be tested.
    /// @throw BadValue if no server is found for a given scope name.
    void validateScopeName(const std::string& scope_name) const;

    /// @brief Returns scope class name for the specified scope name.
    ///
    /// When the server is designated to process a received DHCP query, it
    /// is often required to assign a class to this query which corresponds
    /// to the particular server. This class name is associated with the
    /// pools, subnets and/or networks which this server should hand out
    /// leases from.
    ///
    /// This function converts scope name to the class name by prefixing
    /// the scope name with "ha_" string.
    ///
    /// @param scope_name scope name to be converted to class name.
    ///
    /// @return Scope class name.
    std::string makeScopeClass(const std::string& scope_name) const;

    /// @brief Pointer to the HA configuration.
    HAConfigPtr config_;

    /// @brief Vector of HA peers configurations.
    std::vector<HAConfig::PeerConfigPtr> peers_;

    /// @brief Holds mapping of the scope names to the flag which indicates
    /// if the scopes are enabled or disabled.
    std::map<std::string, bool> scopes_;

    /// @brief Number of the active servers in the given HA mode.
    int active_servers_;

    /// @brief Mutex to protect the internal state.
    boost::scoped_ptr<std::mutex> mutex_;
};

} // end of namespace isc::ha
} // end of namespace isc

#endif // QUERY_FILTER_H