1 // Copyright (C) 2017-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_SHARED_NETWORKS_H
8 #define CFG_SHARED_NETWORKS_H
9 
10 #include <asiolink/io_address.h>
11 #include <cc/cfg_to_element.h>
12 #include <cc/data.h>
13 #include <exceptions/exceptions.h>
14 #include <dhcpsrv/shared_network.h>
15 #include <boost/shared_ptr.hpp>
16 #include <string>
17 
18 namespace isc {
19 namespace dhcp {
20 
21 /// @brief This class holds configuration of shared networks.
22 ///
23 /// This is a generic class implementing basic functions such as shared network
24 /// addition, removal and retrieval. It also dumps configuration in the JSON
25 /// format.
26 ///
27 /// There are specializations of this class implemented as
28 /// @ref CfgSharedNetworks4 and @ref CfgSharedNetworks6 for IPv4 and IPv6 cases
29 /// repspectively.
30 ///
31 /// @tparam Type of the pointer to a shared network, i.e. @ref SharedNetwork4Ptr
32 /// or @ref SharedNetwork6Ptr.
33 template<typename SharedNetworkPtrType, typename SharedNetworkCollection>
34 class CfgSharedNetworks : public data::CfgToElement {
35 public:
36     /// @brief Returns pointer to all configured shared networks.
getAll()37     const SharedNetworkCollection* getAll() const {
38         return (&networks_);
39     }
40 
41     /// @brief Adds new shared network to the configuration.
42     ///
43     /// @param network Pointer to a network
44     ///
45     /// @throw isc::BadValue when name is a duplicate of existing network's
46     /// name.
add(const SharedNetworkPtrType & network)47     void add(const SharedNetworkPtrType& network) {
48         if (getByName(network->getName())) {
49             isc_throw(BadValue, "duplicate network '" << network->getName() <<
50                       "' found in the configuration");
51         }
52 
53         static_cast<void>(networks_.push_back(network));
54     }
55 
56     /// @brief Deletes shared network from the configuration.
57     ///
58     /// @param name Name of the network to be deleted.
59     ///
60     /// @throw isc::BadValue if the network can't be found.
del(const std::string & name)61     void del(const std::string& name) {
62         auto& index = networks_.template get<SharedNetworkNameIndexTag>();
63         auto shared_network = index.find(name);
64         if (shared_network != index.end()) {
65             // Delete all subnets from the network
66             (*shared_network)->delAll();
67 
68             // Then delete the network from the networks list.
69             index.erase(shared_network);
70         } else {
71             isc_throw(BadValue, "unable to delete non-existing network '"
72                       << name << "' from shared networks configuration");
73         }
74     }
75 
76     /// @brief Deletes shared networks from the configuration by id.
77     ///
78     /// Note that there are cases when there will be multiple shared
79     /// networks having the same id (typically id of 0). When configuration
80     /// backend is in use it sets the unique ids from the database.
81     /// In cases when the configuration backend is not used, the ids
82     /// default to 0. Passing the id of 0 would result in deleting all
83     /// shared networks that were not added via the database.
84     ///
85     /// @param id Identifier of the shared networks to be deleted.
86     ///
87     /// @return Number of deleted shared networks.
del(const uint64_t id)88     uint64_t del(const uint64_t id) {
89         auto& index = networks_.template get<SharedNetworkIdIndexTag>();
90         auto sn_range = index.equal_range(id);
91 
92         // For each shared network found, dereference the subnets belonging
93         // to it.
94         for (auto it = sn_range.first; it != sn_range.second; ++it) {
95             (*it)->delAll();
96         }
97 
98         // Remove the shared networks.
99         return (static_cast<uint64_t>(index.erase(id)));
100     }
101 
102     /// @brief Retrieves shared network by name.
103     ///
104     /// @param name Name of the network to be retrieved.
105     ///
106     /// @return Pointer to the shared network or null pointer if the network
107     /// is not found.
getByName(const std::string & name)108     SharedNetworkPtrType getByName(const std::string& name) const {
109         const auto& index = networks_.template get<SharedNetworkNameIndexTag>();
110         auto shared_network = index.find(name);
111         if (shared_network != index.cend()) {
112             return (*shared_network);
113         }
114         return (SharedNetworkPtrType());
115     }
116 
117     /// @brief Unparses shared networks configuration.
118     ///
119     /// @return Element object representing a list of shared networks held
120     /// within configuration. The networks are sorted by their names.
toElement()121     virtual data::ElementPtr toElement() const {
122         data::ElementPtr list = data::Element::createList();
123 
124         // Insert shared networks sorted by their names into the list.
125         const auto& index = networks_.template get<SharedNetworkNameIndexTag>();
126         for (auto shared_network = index.begin(); shared_network != index.end();
127              ++shared_network) {
128             list->add((*shared_network)->toElement());
129         }
130         return (list);
131     }
132 
133     /// @brief Merges specified shared network configuration into this
134     /// configuration.
135     ///
136     /// This method merges networks from the @c other configuration into this
137     /// configuration. The general rule is that existing networks are replaced
138     /// by the networks from @c other.
139     ///
140     /// For each network in @c other, do the following:
141     ///
142     /// - Any associated subnets are removed.  Shared networks retrieved from
143     /// config backends, do not carry their associated subnets (if any) with
144     /// them. (Subnet assignments are maintained by subnet merges).
145     /// - If a shared network of the same name already exists in this
146     /// configuration:
147     ///     - All of its associated subnets are moved to the "other" network.
148     ///     - The existing network is removed from this configuration.
149     /// - The "other" network's option instances are created.
150     /// - The "other" network is added to this configuration.
151     ///
152     /// @warning The merge operation may affect the @c other configuration.
153     /// Therefore, the caller must not rely on the data held in the @c other
154     /// object after the call to @c merge. Also, the data held in @c other must
155     /// not be modified after the call to @c merge because it may affect the
156     /// merged configuration.
157     ///
158     /// @param cfg_def set of of user-defined option definitions to use
159     /// when creating option instances.
160     /// @param other the shared network configuration to be merged into this
161     /// configuration.
merge(CfgOptionDefPtr cfg_def,CfgSharedNetworks & other)162     void merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks& other) {
163         auto& index = networks_.template get<SharedNetworkNameIndexTag>();
164 
165         // Iterate over the subnets to be merged. They will replace the existing
166         // subnets with the same id. All new subnets will be inserted into this
167         // configuration.
168         auto other_networks = other.getAll();
169         for (auto other_network = other_networks->begin();
170             other_network != other_networks->end(); ++other_network) {
171 
172             // In theory we should drop subnet assignments from "other". The
173             // idea being  those that come from the CB should not have subnets_
174             // populated.  We will quietly throw them away, just in case.
175             (*other_network)->delAll();
176 
177             // Check if the other network exists in this config.
178             auto existing_network = index.find((*other_network)->getName());
179             if (existing_network != index.end()) {
180 
181                 // Somehow the same instance is in both, skip it.
182                 if (*existing_network == *other_network) {
183                     continue;
184                 }
185 
186                 // Network exists, which means we're updating it.
187                 // First we need to move its subnets to the new
188                 // version of the network.
189                 const auto subnets = (*existing_network)->getAllSubnets();
190 
191                 auto copy_subnets(*subnets);
192                 for (auto subnet = copy_subnets.cbegin(); subnet != copy_subnets.cend(); ++subnet) {
193                     (*existing_network)->del((*subnet)->getID());
194                     (*other_network)->add(*subnet);
195                 }
196 
197                 // Now we discard the existing copy of the network.
198                 index.erase(existing_network);
199             }
200 
201             // Create the network's options based on the given definitions.
202             (*other_network)->getCfgOption()->createOptions(cfg_def);
203 
204             // Add the new/updated nework.
205             static_cast<void>(networks_.push_back(*other_network));
206         }
207     }
208 
209 protected:
210 
211     /// @brief Multi index container holding shared networks.
212     SharedNetworkCollection networks_;
213 };
214 
215 /// @brief Represents configuration of IPv4 shared networks.
216 class CfgSharedNetworks4 : public CfgSharedNetworks<SharedNetwork4Ptr,
217                                                     SharedNetwork4Collection> {
218 public:
219     /// @brief Checks if specified server identifier has been specified for
220     /// any network.
221     ///
222     /// @param server_id Server identifier.
223     ///
224     /// @return true if there is a network with a specified server identifier.
225     bool hasNetworkWithServerId(const asiolink::IOAddress& server_id) const;
226 };
227 
228 /// @brief Pointer to the configuration of IPv4 shared networks.
229 typedef boost::shared_ptr<CfgSharedNetworks4> CfgSharedNetworks4Ptr;
230 
231 /// @brief Represents configuration of IPv6 shared networks.
232 class CfgSharedNetworks6 : public CfgSharedNetworks<SharedNetwork6Ptr,
233                                                     SharedNetwork6Collection> {
234 };
235 
236 /// @brief Pointer to the configuration of IPv6 shared networks.
237 typedef boost::shared_ptr<CfgSharedNetworks6> CfgSharedNetworks6Ptr;
238 
239 
240 } // end of namespace isc::dhcp
241 } // end of namespace isc
242 
243 #endif // CFG_SHARED_NETWORKS_H
244