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