1 // Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef SHARED_NETWORK_H
8 #define SHARED_NETWORK_H
9 
10 #include <asiolink/io_address.h>
11 #include <cc/data.h>
12 #include <dhcpsrv/subnet.h>
13 #include <dhcpsrv/subnet_id.h>
14 #include <boost/enable_shared_from_this.hpp>
15 #include <boost/multi_index/mem_fun.hpp>
16 #include <boost/multi_index/hashed_index.hpp>
17 #include <boost/multi_index/indexed_by.hpp>
18 #include <boost/multi_index/ordered_index.hpp>
19 #include <boost/multi_index/random_access_index.hpp>
20 #include <boost/multi_index_container.hpp>
21 #include <boost/shared_ptr.hpp>
22 #include <string>
23 
24 namespace isc {
25 namespace dhcp {
26 
27 /// @brief A tag for accessing random access index.
28 struct SharedNetworkRandomAccessIndexTag { };
29 
30 /// @brief A tag for accessing index by id.
31 struct SharedNetworkIdIndexTag { };
32 
33 /// @brief A tag for accessing index by shared network name.
34 struct SharedNetworkNameIndexTag { };
35 
36 /// @brief A tag for accessing index by server identifier.
37 struct SharedNetworkServerIdIndexTag { };
38 
39 /// @brief Tag for the index for searching by shared network modification
40 /// time.
41 struct SharedNetworkModificationTimeIndexTag { };
42 
43 class SharedNetwork4;
44 
45 /// @brief Pointer to @ref SharedNetwork4 object.
46 typedef boost::shared_ptr<SharedNetwork4> SharedNetwork4Ptr;
47 
48 /// @brief Shared network holding IPv4 subnets.
49 ///
50 /// Specialization of the @ref Network4 class for IPv4 shared networks.
51 class SharedNetwork4 : public virtual Network4,
52                        public boost::enable_shared_from_this<SharedNetwork4> {
53 public:
54 
55     /// @brief Constructor.
56     ///
57     /// Sets name of the shared network.
58     ///
59     /// @param name Name of the shared network.
SharedNetwork4(const std::string & name)60     explicit SharedNetwork4(const std::string& name)
61         : name_(name), subnets_() {
62     }
63 
64     /// @brief Factory function creating an instance of the @c SharedNetwork4.
65     ///
66     /// This function should be used to create an instance of the shared
67     /// network within a hooks library in cases when the library may be
68     /// unloaded before the object is destroyed. This ensures that the
69     /// ownership of the object by the Kea process is retained.
70     ///
71     /// @param name Name of the shared network.
72     ///
73     /// @return Pointer to the @c SharedNetwork4 instance.
74     static SharedNetwork4Ptr create(const std::string& name);
75 
76     /// @brief Returns a name of the shared network.
getName()77     std::string getName() const {
78         return (name_);
79     }
80 
81     /// @brief Sets new name for the shared network.
82     ///
83     /// @param name New name for the shared network.
setName(const std::string & name)84     void setName(const std::string& name) {
85         name_ = name;
86     }
87 
88     /// @brief Adds IPv4 subnet to a shared network.
89     ///
90     /// @param subnet Pointer to a subnet being added to this shared network.
91     ///
92     /// @throw isc::BadValue if subnet is null.
93     /// @throw isc::DuplicateSubnetID if a subnet with the given subnet id
94     /// already exists in this shared network.
95     /// @throw InvalidOperation if a subnet is already associated with some
96     /// shared network.
97     void add(const Subnet4Ptr& subnet);
98 
99     /// @brief Replaces IPv4 subnet in a shared network.
100     ///
101     /// This method replaces a subnet by another subnet with the same ID.
102     /// The prefix should be the same too.
103     ///
104     /// @param subnet Pointer to a subnet replacing the subnet with the same ID
105     /// in this shared network.
106     /// @throw isc::BadValue if subnet is null.
107     /// @throw InvalidOperation if a subnet is already associated with some
108     /// shared network.
109     /// @return true if the operation succeeded, false otherwise.
110     bool replace(const Subnet4Ptr& subnet);
111 
112     /// @brief Removes subnet from a shared network.
113     ///
114     /// @param subnet_id Identifier of a subnet to be removed.
115     ///
116     /// @throw BadValue When specified subnet doesn't exist.
117     void del(const SubnetID& subnet_id);
118 
119     /// @brief Removes all subnets from a shared network.
120     void delAll();
121 
122     /// @brief Returns a pointer to the collection of subnets within this
123     /// shared network.
getAllSubnets()124     const Subnet4Collection* getAllSubnets() const {
125         return (&subnets_);
126     }
127 
128     /// @brief Returns a subnet for a specified subnet id.
129     ///
130     /// @param subnet_id Subnet identifier.
131     ///
132     /// @return Shared pointer to a subnet using this id or null pointer
133     /// if such subnet doesn't exist within shared network.
134     Subnet4Ptr getSubnet(const SubnetID& subnet_id) const;
135 
136     /// @brief Returns a subnet for a specified subnet prefix.
137     ///
138     /// @param subnet_prefix Subnet prefix.
139     ///
140     /// @return Shared pointer to a subnet using this prefix or null pointer
141     /// if such subnet doesn't exist within shared network.
142     Subnet4Ptr getSubnet(const std::string& subnet_prefix) const;
143 
144     /// @brief Retrieves next available IPv4 subnet within shared network.
145     ///
146     /// See documentation for @ref SharedNetwork4::getNextSubnet.
147     ///
148     /// @param first_subnet Pointer to a subnet from which the caller is
149     /// iterating over subnets within shared network. This is typically a
150     /// subnet selected during "subnet selection" step.
151     /// @param current_subnet Identifier of a subnet for which next subnet is
152     /// to be found.
153     ///
154     /// @return Pointer to next subnet or null pointer if no more subnets found.
155     ///
156     /// @throw isc::BadValue if invalid arguments specified, e.g. unable to
157     /// find first or current subnet within shared network.
158     Subnet4Ptr getNextSubnet(const Subnet4Ptr& first_subnet,
159                              const SubnetID& current_subnet) const;
160 
161     /// @brief Attempts to find a subnet which is more likely to include available
162     /// leases than selected subnet.
163     ///
164     /// When allocating unreserved leases from a shared network it is important to
165     /// remember from which subnet within the shared network we have been recently
166     /// handing out leases. The allocation engine can use that information to start
167     /// trying allocation of the leases from that subnet rather than from the default
168     /// subnet selected for this client. Starting from the default subnet causes a
169     /// risk of having to walk over many subnets with exhausted address pools before
170     /// getting to the subnet with available leases. This method attempts to find
171     /// such subnet by inspecting "last allocation" timestamps. The one with most
172     /// recent timestamp is selected.
173     ///
174     /// The preferred subnet must also fulfil the condition of equal client classes
175     /// with the @c selected_subnet.
176     ///
177     /// @todo Need extensions to this logic when we support more than one client
178     /// class for a subnet.
179     ///
180     /// @param selected_subnet Pointer to a currently selected subnet.
181     ///
182     /// @return Pointer to a preferred subnet. It may be the same as @c selected_subnet
183     /// if no better subnet was found.
184     Subnet4Ptr getPreferredSubnet(const Subnet4Ptr& selected_subnet) const;
185 
186     /// @brief Checks if the shared network includes a subnet with
187     /// the match client ID flag set to true.
188     ///
189     /// @param first_subnet Pointer to the subnet from which iteration starts.
190     /// @param client_classes List of classes that the client belongs to.
191     /// @return true if the shared network includes at least one subnet
192     /// guarded by a given class with the match client ID flag set to true.
193     /// False otherwise.
194     static
195     bool subnetsIncludeMatchClientId(const Subnet4Ptr& first_subnet,
196                                      const ClientClasses& client_classes);
197 
198     /// @brief Unparses shared network object.
199     ///
200     /// @return A pointer to unparsed shared network configuration.
201     virtual data::ElementPtr toElement() const;
202 
203 private:
204 
205     /// @brief Holds a name of a shared network.
206     std::string name_;
207 
208     /// @brief Collection of IPv4 subnets within shared network.
209     Subnet4Collection subnets_;
210 };
211 
212 /// @brief Multi index container holding shared networks.
213 ///
214 /// This is multi index container can hold pointers to @ref SharedNetwork4
215 /// objects. It provides indexes for shared network lookups using properties
216 /// such as shared network's name.
217 typedef boost::multi_index_container<
218     // Multi index container holds pointers to the shared networks.
219     SharedNetwork4Ptr,
220     boost::multi_index::indexed_by<
221         // First is the random access index allowing for accessing objects
222         // just like we'd do with vector.
223         boost::multi_index::random_access<
224             boost::multi_index::tag<SharedNetworkRandomAccessIndexTag>
225         >,
226         // Second index allows for access by shared network id.
227         boost::multi_index::hashed_non_unique<
228             boost::multi_index::tag<SharedNetworkIdIndexTag>,
229             boost::multi_index::const_mem_fun<data::BaseStampedElement, uint64_t,
230                                               &data::BaseStampedElement::getId>
231         >,
232         // Third index allows for access by shared network's name.
233         boost::multi_index::ordered_unique<
234             boost::multi_index::tag<SharedNetworkNameIndexTag>,
235             boost::multi_index::const_mem_fun<SharedNetwork4, std::string,
236                                               &SharedNetwork4::getName>
237         >,
238         // Fourth index allows for access by server identifier specified for the
239         // network.
240         boost::multi_index::ordered_non_unique<
241             boost::multi_index::tag<SharedNetworkServerIdIndexTag>,
242             boost::multi_index::const_mem_fun<Network4, asiolink::IOAddress,
243                                               &Network4::getServerId>
244         >,
245         // Fifth index allows for searching using subnet modification time.
246         boost::multi_index::ordered_non_unique<
247             boost::multi_index::tag<SharedNetworkModificationTimeIndexTag>,
248             boost::multi_index::const_mem_fun<data::BaseStampedElement,
249                                               boost::posix_time::ptime,
250                                               &data::BaseStampedElement::getModificationTime>
251         >
252     >
253 > SharedNetwork4Collection;
254 
255 class SharedNetwork6;
256 
257 /// @brief Pointer to @ref SharedNetwork6 object.
258 typedef boost::shared_ptr<SharedNetwork6> SharedNetwork6Ptr;
259 
260 /// @brief Shared network holding IPv6 subnets.
261 ///
262 /// Specialization of the @ref Network6 class for IPv6 shared networks.
263 class SharedNetwork6 : public virtual Network6,
264                        public boost::enable_shared_from_this<SharedNetwork6> {
265 public:
266 
267     /// @brief Constructor.
268     ///
269     /// Sets name of the shared network.
SharedNetwork6(const std::string & name)270     explicit SharedNetwork6(const std::string& name)
271         : name_(name), subnets_() {
272     }
273 
274     /// @brief Factory function creating an instance of the @c SharedNetwork6.
275     ///
276     /// This function should be used to create an instance of the shared
277     /// network within a hooks library in cases when the library may be
278     /// unloaded before the object is destroyed. This ensures that the
279     /// ownership of the object by the Kea process is retained.
280     ///
281     /// @param name Name of the shared network.
282     ///
283     /// @return Pointer to the @c SharedNetwork6 instance.
284     static SharedNetwork6Ptr create(const std::string& name);
285 
286     /// @brief Returns a name of the shared network.
getName()287     std::string getName() const {
288         return (name_);
289     }
290 
291     /// @brief Sets new name for the shared network.
292     ///
293     /// @param name New name for the shared network.
setName(const std::string & name)294     void setName(const std::string& name) {
295         name_ = name;
296     }
297 
298     /// @brief Adds IPv6 subnet to a shared network.
299     ///
300     /// @param subnet Pointer to a subnet being added to this shared network.
301     ///
302     /// @throw isc::BadValue if subnet is null.
303     /// @throw isc::DuplicateSubnetID if a subnet with the given subnet id
304     /// already exists in this shared network.
305     /// @throw InvalidOperation if a subnet is already associated with some
306     /// shared network.
307     void add(const Subnet6Ptr& subnet);
308 
309     /// @brief Replaces IPv6 subnet in a shared network.
310     ///
311     /// This method replaces a subnet by another subnet with the same ID.
312     /// The prefix should be the same too.
313     ///
314     /// @param subnet Pointer to a subnet replacing the subnet with the same ID
315     /// in this shared network.
316     /// @throw isc::BadValue if subnet is null.
317     /// @throw InvalidOperation if a subnet is already associated with some
318     /// shared network.
319     /// @return true if the operation succeeded, false otherwise.
320     bool replace(const Subnet6Ptr& subnet);
321 
322     /// @brief Removes subnet from a shared network.
323     ///
324     /// @param subnet_id Identifier of a subnet to be removed.
325     ///
326     /// @throw BadValue When specified subnet doesn't exist.
327     void del(const SubnetID& subnet_id);
328 
329     /// @brief Removes all subnets from a shared network.
330     void delAll();
331 
332     /// @brief Returns a pointer to the collection of subnets within this
333     /// shared network.
getAllSubnets()334     const Subnet6Collection* getAllSubnets() const {
335         return (&subnets_);
336     }
337 
338     /// @brief Returns a subnet for a specified subnet id.
339     ///
340     /// @param subnet_id Subnet identifier.
341     ///
342     /// @return Shared pointer to a subnet using this id or null pointer
343     /// if such subnet doesn't exist within shared network.
344     Subnet6Ptr getSubnet(const SubnetID& subnet_id) const;
345 
346     /// @brief Returns a subnet for a specified subnet prefix.
347     ///
348     /// @param subnet_prefix Subnet prefix.
349     ///
350     /// @return Shared pointer to a subnet using this prefix or null pointer
351     /// if such subnet doesn't exist within shared network.
352     Subnet6Ptr getSubnet(const std::string& subnet_prefix) const;
353 
354     /// @brief Retrieves next available IPv6 subnet within shared network.
355     ///
356     /// See documentation for @ref SharedNetwork6::getNextSubnet.
357     ///
358     /// @param first_subnet Pointer to a subnet from which the caller is
359     /// iterating over subnets within shared network. This is typically a
360     /// subnet selected during "subnet selection" step.
361     /// @param current_subnet Identifier of a subnet for which next subnet is
362     /// to be found.
363     ///
364     /// @return Pointer to next subnet or null pointer if no more subnets found.
365     ///
366     /// @throw isc::BadValue if invalid arguments specified, e.g. unable to
367     /// find first or current subnet within shared network.
368     Subnet6Ptr getNextSubnet(const Subnet6Ptr& first_subnet,
369                              const SubnetID& current_subnet) const;
370 
371     /// @brief Attempts to find a subnet which is more likely to include available
372     /// leases than selected subnet.
373     ///
374     /// When allocating unreserved leases from a shared network it is important to
375     /// remember from which subnet within the shared network we have been recently
376     /// handing out leases. The allocation engine can use that information to start
377     /// trying allocation of the leases from that subnet rather than from the default
378     /// subnet selected for this client. Starting from the default subnet causes a
379     /// risk of having to walk over many subnets with exhausted address pools before
380     /// getting to the subnet with available leases. This method attempts to find
381     /// such subnet by inspecting "last allocation" timestamps. The one with most
382     /// recent timestamp is selected.
383     ///
384     /// The preferred subnet must also fulfil the condition of equal client classes
385     /// with the @c selected_subnet.
386     ///
387     /// @param selected_subnet Pointer to a currently selected subnet.
388     /// @param lease_type Type of the lease for which preferred subnet should be
389     /// returned.
390     ///
391     /// @return Pointer to a preferred subnet. It may be the same as @c selected_subnet
392     /// if no better subnet was found.
393     Subnet6Ptr getPreferredSubnet(const Subnet6Ptr& selected_subnet,
394                                   const Lease::Type& lease_type) const;
395 
396     /// @brief Unparses shared network object.
397     ///
398     /// @return A pointer to unparsed shared network configuration.
399     virtual data::ElementPtr toElement() const;
400 
401 private:
402 
403     /// @brief Holds a name of a shared network.
404     std::string name_;
405 
406     /// @brief Collection of IPv6 subnets within shared network.
407     Subnet6Collection subnets_;
408 };
409 
410 /// @brief Multi index container holding shared networks.
411 ///
412 /// This is multi index container can hold pointers to @ref SharedNetwork6
413 /// objects. It provides indexes for shared network lookups using properties
414 /// such as shared network's name.
415 typedef boost::multi_index_container<
416     // Multi index container holds pointers to the shared networks.
417     SharedNetwork6Ptr,
418     boost::multi_index::indexed_by<
419         // First is the random access index allowing for accessing objects
420         // just like we'd do with vector.
421         boost::multi_index::random_access<
422             boost::multi_index::tag<SharedNetworkRandomAccessIndexTag>
423         >,
424         // Second index allows for access by shared network id.
425         boost::multi_index::hashed_non_unique<
426             boost::multi_index::tag<SharedNetworkIdIndexTag>,
427             boost::multi_index::const_mem_fun<data::BaseStampedElement, uint64_t,
428                                               &data::BaseStampedElement::getId>
429         >,
430         // Third index allows for access by shared network's name.
431         boost::multi_index::ordered_unique<
432             boost::multi_index::tag<SharedNetworkNameIndexTag>,
433             boost::multi_index::const_mem_fun<SharedNetwork6, std::string,
434                                               &SharedNetwork6::getName>
435         >,
436         // Fourth index allows for searching using subnet modification time.
437         boost::multi_index::ordered_non_unique<
438             boost::multi_index::tag<SharedNetworkModificationTimeIndexTag>,
439             boost::multi_index::const_mem_fun<data::BaseStampedElement,
440                                               boost::posix_time::ptime,
441                                               &data::BaseStampedElement::getModificationTime>
442         >
443     >
444 > SharedNetwork6Collection;
445 
446 /// @brief A class containing static convenience methods to fetch the shared
447 /// networks from the containers.
448 ///
449 /// @tparam ReturnPtrType Type of the returned object, i.e. @c SharedNetwork4Ptr
450 /// or @c SharedNetwork6Ptr.
451 /// @tparam CollectionType One of the @c SharedNetwork4Collection or
452 /// @c SharedNetwork6Collection.
453 template<typename ReturnPtrType, typename CollectionType>
454 class SharedNetworkFetcher {
455 public:
456 
457     /// @brief Fetches shared network by name.
458     ///
459     /// @param collection Const reference to the collection from which the shared
460     /// network is to be fetched.
461     /// @param name Name of the shared network to be fetched.
462     /// @return Pointer to the fetched shared network or null if no such shared
463     /// network could be found.
get(const CollectionType & collection,const std::string & name)464     static ReturnPtrType get(const CollectionType& collection, const std::string& name) {
465         auto& index = collection.template get<SharedNetworkNameIndexTag>();
466         auto sn = index.find(name);
467         if (sn != index.end()) {
468             return (*sn);
469         }
470         // No network found. Return null pointer.
471         return (ReturnPtrType());
472     }
473 };
474 
475 /// @brief Type of the @c SharedNetworkFetcher used for IPv4.
476 using SharedNetworkFetcher4 = SharedNetworkFetcher<SharedNetwork4Ptr, SharedNetwork4Collection>;
477 
478 /// @brief Type of the @c SharedNetworkFetcher used for IPv6.
479 using SharedNetworkFetcher6 = SharedNetworkFetcher<SharedNetwork6Ptr, SharedNetwork6Collection>;
480 
481 } // end of namespace isc::dhcp
482 } // end of namespace isc
483 
484 #endif // SHARED_NETWORK_H
485