1 // Copyright (C) 2014-2019 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 CFG_SUBNETS6_H
8 #define CFG_SUBNETS6_H
9 
10 #include <asiolink/io_address.h>
11 #include <dhcp/option.h>
12 #include <dhcp/pkt6.h>
13 #include <cc/cfg_to_element.h>
14 #include <dhcpsrv/cfg_shared_networks.h>
15 #include <dhcpsrv/subnet.h>
16 #include <dhcpsrv/subnet_id.h>
17 #include <dhcpsrv/subnet_selector.h>
18 #include <util/optional.h>
19 #include <boost/shared_ptr.hpp>
20 #include <string>
21 
22 namespace isc {
23 namespace dhcp {
24 
25 /// @brief Holds subnets configured for the DHCPv6 server.
26 ///
27 /// This class holds a collection of subnets configured for the DHCPv6 server.
28 /// It allows for retrieving a subnet for the particular client using various
29 /// parameters extracted from the DHCPv6 message. These parameters must be
30 /// assigned to the appropriate members of the @c SubnetSelector structure.
31 ///
32 /// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet
33 /// is selected for the client.
34 class CfgSubnets6 : public isc::data::CfgToElement {
35 public:
36 
37     /// @brief Adds new subnet to the configuration.
38     ///
39     /// @param subnet Pointer to the subnet being added.
40     ///
41     /// @throw isc::DuplicateSubnetID If the subnet id for the new subnet
42     /// duplicates id of an existing subnet.
43     void add(const Subnet6Ptr& subnet);
44 
45     /// @brief Replaces subnet in the configuration.
46     ///
47     /// This method replaces a subnet by another subnet with the same ID.
48     /// The prefix should be the same too.
49     ///
50     /// @param subnet Pointer to the subnet being updated.
51     /// @throw BadValue if the subnet to update does not exit.
52     /// @return Pointer to the replaced subnet or NULL if it failed.
53     Subnet6Ptr replace(const Subnet6Ptr& subnet);
54 
55     /// @brief Removes subnet from the configuration.
56     ///
57     /// @param subnet Pointer to the subnet to be removed.
58     ///
59     /// @throw isc::BadValue if such subnet doesn't exist.
60     void del(const ConstSubnet6Ptr& subnet);
61 
62     /// @brief Removes subnet from the configuration.
63     ///
64     /// @param subnet_id Identifier of the subnet to be removed.
65     ///
66     /// @throw isc::BadValue if such subnet doesn't exist.
67     void del(const SubnetID& subnet_id);
68 
69     /// @brief Merges specified subnet configuration into this configuration.
70     ///
71     /// This method merges subnets from the @c other configuration into this
72     /// configuration. The general rule is that existing subnets are replaced
73     /// by the subnets from @c other. If there is no corresponding subnet in
74     /// this configuration the subnet from @c other configuration is inserted.
75     ///
76     /// The complexity of the merge process stems from the associations between
77     /// the subnets and shared networks.  It is assumed that subnets in @c other
78     /// are the authority on their shared network assignments. It is also
79     /// assumed that @ networks is the list of shared networks that should be
80     /// used in making assignments.  The general concept is that the overarching
81     /// merge process will first merge shared networks and then pass that list
82     /// of networks into this method. Subnets from @c other are then merged
83     /// into this configuration as follows:
84     ///
85     /// For each subnet in @c other:
86     ///
87     /// - If a subnet of the same ID already exists in this configuration:
88     ///    -# If it belongs to a shared network, remove it from that network
89     ///    -# Remove the subnet from this configuration and discard it
90     ///
91     /// - Create the subnet's option instance, as well as any options
92     ///   that belong to any of the subnet's pools.
93     /// - Add the subnet from @c other to this configuration.
94     /// - If that subnet is associated to shared network, find that network
95     ///   in @ networks and add that subnet to it.
96     ///
97     /// @warning The merge operation affects the @c other configuration.
98     /// Therefore, the caller must not rely on the data held in the @c other
99     /// object after the call to @c merge. Also, the data held in @c other must
100     /// not be modified after the call to @c merge because it may affect the
101     /// merged configuration.
102     ///
103     /// @param cfg_def set of of user-defined option definitions to use
104     /// when creating option instances.
105     /// @param networks collection of shared networks that to which assignments
106     /// should be added. In other words, the list of shared networks that belong
107     /// to the same SrvConfig instance we are merging into.
108     /// @param other the subnet configuration to be merged into this
109     /// configuration.
110     void merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks6Ptr networks,
111                CfgSubnets6& other);
112 
113     /// @brief Returns pointer to the collection of all IPv6 subnets.
114     ///
115     /// This is used in a hook (subnet6_select), where the hook is able
116     /// to choose a different subnet. Server code has to offer a list
117     /// of possible choices (i.e. all subnets).
118     ///
119     /// @return A pointer to const Subnet6 collection
getAll()120     const Subnet6Collection* getAll() const {
121         return (&subnets_);
122     }
123 
124     /// @brief Returns const pointer to a subnet identified by the specified
125     /// subnet identifier.
126     ///
127     /// The const pointer is returned by this method to prevent a caller from
128     /// modifying the subnet configuration. Modifications to subnet configuration
129     /// is dangerous and must be done carefully. The subnets' configuration is
130     /// held in the multi index container and any modifications to the subnet
131     /// id or subnet prefix must trigger re-indexing of multi index container.
132     /// There is no possibility to enforce this when the non-const pointer is
133     /// returned.
134     ///
135     /// @param subnet_id Subnet identifier.
136     ///
137     /// @return Pointer to the @c Subnet6 object or null pointer if such
138     /// subnet doesn't exist.
139     ConstSubnet6Ptr getBySubnetId(const SubnetID& subnet_id) const;
140 
141     /// @brief Returns const pointer to a subnet which matches the specified
142     /// prefix in the canonical form.
143     ///
144     /// The const pointer is returned by this method to prevent a caller from
145     /// modifying the subnet configuration. Modifications to subnet configuration
146     /// is dangerous and must be done carefully. The subnets' configuration is
147     /// held in the multi index container and any modifications to the subnet
148     /// id or subnet prefix must trigger re-indexing of multi index container.
149     /// There is no possibility to enforce this when the non-const pointer is
150     /// returned.
151     ///
152     /// @param subnet_prefix Subnet prefix, e.g. 2001:db8:1::/64
153     ///
154     /// @return Pointer to the @c Subnet6 object or null pointer if such
155     /// subnet doesn't exist.
156     ConstSubnet6Ptr getByPrefix(const std::string& subnet_prefix) const;
157 
158     /// @brief Build selector from a client's message.
159     ///
160     /// @note: code moved from server.
161     ///
162     /// @param query client's message.
163     /// @return filled selector.
164     static SubnetSelector initSelector(const Pkt6Ptr& query);
165 
166     /// @brief Selects a subnet using parameters specified in the selector.
167     ///
168     /// This method tries to retrieve the subnet for the client using various
169     /// parameters extracted from the client's message using the following
170     /// logic.
171     ///
172     /// If the relay agent link address is set to zero it is assumed that
173     /// the subnet is selected for the directly connected client.
174     /// In this case it is checked if there is any subnet associated with the
175     /// interface over which the message has been received. If there is no
176     /// subnet explicitly associated with this interface the client's address
177     /// will be used to check if the address is in range with any of the
178     /// subnets.
179     ///
180     /// If the message was relayed it is possible that the relay agent has
181     /// appended an Interface ID option. If this option is present, the method
182     /// will check if it matches with any explicitly specified interface id
183     /// for any subnet. If it does, the subnet is returned. Otherwise, the
184     /// relay agents link address is used to select the subnet. In this case,
185     /// the method will first check if this link address is explicitly
186     /// associated with any subnet. If not, it is checked if the link address
187     /// is in range with any of the subnets.
188     ///
189     /// @todo This method requires performance improvement! It currently
190     /// iterates over all existing subnets (possibly a couple of times)
191     /// to find the one which fulfills the search criteria. The subnet storage
192     /// is implemented as a simple STL vector which precludes fast searches
193     /// using specific keys. Hence, full scan is required. To improve the
194     /// search performance a different container type is required, e.g.
195     /// multi-index container, or something of a similar functionality.
196     ///
197     /// @param selector Const reference to the selector structure which holds
198     /// various information extracted from the client's packet which are used
199     /// to find appropriate subnet.
200     ///
201     /// @return Pointer to the selected subnet or NULL if no subnet found.
202     Subnet6Ptr selectSubnet(const SubnetSelector& selector) const;
203 
204     /// @brief Returns subnet with specified subnet-id value
205     ///
206     /// Warning: this method uses full scan. Its use is not recommended for
207     /// packet processing.
208     ///
209     /// @return Subnet (or NULL)
210     Subnet6Ptr getSubnet(const SubnetID id) const;
211 
212     /// @brief Selects the subnet using a specified address.
213     ///
214     /// This method searches for the subnet using the specified address. If
215     /// the specified address is a link address on the relay agent (which is
216     /// indicated by the 3rd argument) the method will first try to match the
217     /// specified address with the relay addresses explicitly specified for
218     /// existing subnets. If no match is found, the method will check if the
219     /// address is in range with any of the subnets.
220     ///
221     /// If the address is not a relay agent link address (@c is_relay_address
222     /// is set to false), the method will simply check if the address is in
223     /// range with any of the subnets.
224     ///
225     /// @note This method is mainly to be used in unit tests, which often
226     /// require sanity-checking if the subnet exists for the particular
227     /// address. For other purposes the @c selectSubnet(SubnetSelector) should
228     /// rather be used instead.
229     ///
230     /// @todo This method requires performance improvement! It currently
231     /// iterates over all existing subnets (possibly a couple of times)
232     /// to find the one which fulfills the search criteria. The subnet storage
233     /// is implemented as a simple STL vector which precludes fast searches
234     /// using specific keys. Hence, full scan is required. To improve the
235     /// search performance a different container type is required, e.g.
236     /// multi-index container, or something of a similar functionality.
237     ///
238     /// @param address Address for which the subnet is searched.
239     /// @param client_classes Optional parameter specifying the classes that
240     /// the client belongs to.
241     /// @param is_relay_address Specifies if the provided address is an
242     /// address of the relay agent (true) or not (false).
243     ///
244     /// @return Pointer to the selected subnet or NULL if no subnet found.
245     Subnet6Ptr
246     selectSubnet(const asiolink::IOAddress& address,
247                  const ClientClasses& client_classes = ClientClasses(),
248                  const bool is_relay_address = false) const;
249 
250     /// @brief Updates statistics.
251     ///
252     /// This method updates statistics that are affected by the newly committed
253     /// configuration. In particular, it updates the number of available addresses
254     /// and prefixes in each subnet. Other statistics may be added in the future. In
255     /// general, these are statistics that are dependent only on configuration, so
256     /// they are not expected to change until the next reconfiguration event.
257     void updateStatistics();
258 
259     /// @brief Removes statistics.
260     ///
261     /// During commitment of a new configuration, we need to get rid of the old
262     /// statistics for the old configuration. In particular, we need to remove
263     /// anything related to subnets, as there may be fewer subnets in the new
264     /// configuration and also subnet-ids may change.
265     void removeStatistics();
266 
267     /// @brief Unparse a configuration object
268     ///
269     /// @return a pointer to unparsed configuration
270     virtual isc::data::ElementPtr toElement() const;
271 
272 private:
273 
274     /// @brief Selects a subnet using the interface name.
275     ///
276     /// This method searches for the subnet using the name of the interface.
277     /// If any of the subnets is explicitly associated with the interface
278     /// name, the subnet is returned.
279     ///
280     /// @todo This method requires performance improvement! It currently
281     /// iterates over all existing subnets to find the one which fulfills
282     /// the search criteria. The subnet storage is implemented as a
283     /// simple STL vector which precludes fast searches using specific
284     /// keys. Hence, full scan is required. To improve the search
285     /// performance a different container type is required, e.g.
286     /// multi-index container, or something of a similar functionality.
287     ///
288     /// @param iface_name Interface name.
289     /// @param client_classes Optional parameter specifying the classes that
290     /// the client belongs to.
291     ///
292     /// @return Pointer to the selected subnet or NULL if no subnet found.
293     Subnet6Ptr
294     selectSubnet(const std::string& iface_name,
295                  const ClientClasses& client_classes) const;
296 
297     /// @brief Selects a subnet using Interface ID option.
298     ///
299     /// This method searches for the subnet using the Interface ID option
300     /// inserted by the relay agent to the message from a client. If any
301     /// of the subnets is explicitly associated with that interface id, the
302     /// subnet is returned.
303     ///
304     /// @todo This method requires performance improvement! It currently
305     /// iterates over all existing subnets to find the one which fulfills
306     /// the search criteria. The subnet storage is implemented as a
307     /// simple STL vector which precludes fast searches using specific
308     /// keys. Hence, full scan is required. To improve the search
309     /// performance a different container type is required, e.g.
310     /// multi-index container, or something of a similar functionality.
311     ///
312     /// @param interface_id An instance of the Interface ID option received
313     /// from the client.
314     /// @param client_classes Optional parameter specifying the classes that
315     /// the client belongs to.
316     ///
317     /// @return Pointer to the selected subnet or NULL if no subnet found.
318     Subnet6Ptr
319     selectSubnet(const OptionPtr& interface_id,
320                  const ClientClasses& client_classes) const;
321 
322     /// @brief A container for IPv6 subnets.
323     Subnet6Collection subnets_;
324 
325 };
326 
327 /// @name Pointer to the @c CfgSubnets6 objects.
328 //@{
329 /// @brief Non-const pointer.
330 typedef boost::shared_ptr<CfgSubnets6> CfgSubnets6Ptr;
331 
332 /// @brief Const pointer.
333 typedef boost::shared_ptr<const CfgSubnets6> ConstCfgSubnets6Ptr;
334 
335 //@}
336 
337 }
338 }
339 
340 #endif // CFG_SUBNETS6_H
341