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