// Copyright (C) 2017-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace isc::asiolink; using namespace isc::data; using namespace isc::util; namespace isc { namespace dhcp { SharedNetwork4Parser::SharedNetwork4Parser(bool check_iface) : check_iface_(check_iface) { } SharedNetwork4Ptr SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data, bool encapsulate_options) { SharedNetwork4Ptr shared_network; try { // Check parameters. checkKeywords(SimpleParser4::SHARED_NETWORK4_PARAMETERS, shared_network_data); // Make sure that the network name has been specified. The name is required // to create a SharedNetwork4 object. std::string name = getString(shared_network_data, "name"); shared_network.reset(new SharedNetwork4(name)); // Move from reservation mode to new reservations flags. ElementPtr mutable_params; mutable_params = boost::const_pointer_cast(shared_network_data); BaseNetworkParser::moveReservationMode(mutable_params); // Parse parameters common to all Network derivations. NetworkPtr network = boost::dynamic_pointer_cast(shared_network); parseCommon(mutable_params, network); // interface is an optional parameter if (shared_network_data->contains("interface")) { std::string iface = getString(shared_network_data, "interface"); if (!iface.empty()) { if (check_iface_ && !IfaceMgr::instance().getIface(iface)) { ConstElementPtr error = shared_network_data->get("interface"); isc_throw(DhcpConfigError, "Specified network interface name " << iface << " for shared network " << name << " is not present in the system (" << error->getPosition() << ")"); } shared_network->setIface(iface); } } if (shared_network_data->contains("option-data")) { auto json = shared_network_data->get("option-data"); // Create parser instance for option-data. CfgOptionPtr cfg_option = shared_network->getCfgOption(); auto parser = createOptionDataListParser(); parser->parse(cfg_option, json, encapsulate_options); } if (shared_network_data->contains("subnet4")) { auto json = shared_network_data->get("subnet4"); // Create parser instance of subnet4. auto parser = createSubnetsListParser(); Subnet4Collection subnets; parser->parse(subnets, json); // Add all returned subnets into shared network. for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) { shared_network->add(*subnet); } } if (shared_network_data->contains("match-client-id")) { shared_network->setMatchClientId(getBoolean(shared_network_data, "match-client-id")); } if (shared_network_data->contains("authoritative")) { shared_network->setAuthoritative(getBoolean(shared_network_data, "authoritative")); } // Set next-server if (shared_network_data->contains("next-server")) { std::string next_server; try { next_server = getString(shared_network_data, "next-server"); if (!next_server.empty()) { shared_network->setSiaddr(IOAddress(next_server)); } } catch (...) { ConstElementPtr next = shared_network_data->get("next-server"); std::string pos; if (next) { pos = next->getPosition().str(); } else { pos = shared_network_data->getPosition().str(); } isc_throw(DhcpConfigError, "invalid parameter next-server : " << next_server << "(" << pos << ")"); } } // Set server-hostname. if (shared_network_data->contains("server-hostname")) { std::string sname = getString(shared_network_data, "server-hostname"); if (!sname.empty()) { if (sname.length() >= Pkt4::MAX_SNAME_LEN) { ConstElementPtr error = shared_network_data->get("server-hostname"); isc_throw(DhcpConfigError, "server-hostname must be at most " << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is " << sname.length() << " (" << error->getPosition() << ")"); } shared_network->setSname(sname); } } // Set boot-file-name. if (shared_network_data->contains("boot-file-name")) { std::string filename = getString(shared_network_data, "boot-file-name"); if (!filename.empty()) { if (filename.length() > Pkt4::MAX_FILE_LEN) { ConstElementPtr error = shared_network_data->get("boot-file-name"); isc_throw(DhcpConfigError, "boot-file-name must be at most " << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is " << filename.length() << " (" << error->getPosition() << ")"); } shared_network->setFilename(filename); } } if (shared_network_data->contains("client-class")) { std::string client_class = getString(shared_network_data, "client-class"); if (!client_class.empty()) { shared_network->allowClientClass(client_class); } } ConstElementPtr user_context = shared_network_data->get("user-context"); if (user_context) { shared_network->setContext(user_context); } if (shared_network_data->contains("require-client-classes")) { const std::vector& class_list = shared_network_data->get("require-client-classes")->listValue(); for (auto cclass = class_list.cbegin(); cclass != class_list.cend(); ++cclass) { if (((*cclass)->getType() != Element::string) || (*cclass)->stringValue().empty()) { isc_throw(DhcpConfigError, "invalid class name (" << (*cclass)->getPosition() << ")"); } shared_network->requireClientClass((*cclass)->stringValue()); } } if (shared_network_data->contains("relay")) { auto relay_parms = shared_network_data->get("relay"); if (relay_parms) { RelayInfoParser parser(Option::V4); Network::RelayInfoPtr relay_info(new Network::RelayInfo()); parser.parse(relay_info, relay_parms); shared_network->setRelayInfo(*relay_info); } } parseTeePercents(shared_network_data, network); // Parse DDNS parameters parseDdnsParams(shared_network_data, network); // Parse lease cache parameters parseCacheParams(shared_network_data, network); // Parse allocator params. parseAllocatorParams(shared_network_data, network); // Parse offer-lifetime parameter. Network4Ptr network4 = boost::dynamic_pointer_cast(shared_network); parseOfferLft(shared_network_data, network4); } catch (const DhcpConfigError&) { // Position was already added throw; } catch (const std::exception& ex) { isc_throw(DhcpConfigError, ex.what() << " (" << shared_network_data->getPosition() << ")"); } // In order to take advantage of the dynamic inheritance of global // parameters to a shared network we need to set a callback function // for each shared network to allow for fetching global parameters. shared_network->setFetchGlobalsFn([]() -> ConstCfgGlobalsPtr { return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals()); }); return (shared_network); } boost::shared_ptr SharedNetwork4Parser::createOptionDataListParser() const { auto parser = boost::make_shared(AF_INET); return (parser); } boost::shared_ptr SharedNetwork4Parser::createSubnetsListParser() const { auto parser = boost::make_shared(check_iface_); return (parser); } SharedNetwork6Parser::SharedNetwork6Parser(bool check_iface) : check_iface_(check_iface) { } SharedNetwork6Ptr SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data, bool encapsulate_options) { SharedNetwork6Ptr shared_network; std::string name; try { // Check parameters. checkKeywords(SimpleParser6::SHARED_NETWORK6_PARAMETERS, shared_network_data); // Make sure that the network name has been specified. The name is required // to create a SharedNetwork6 object. std::string name = getString(shared_network_data, "name"); shared_network.reset(new SharedNetwork6(name)); // Move from reservation mode to new reservations flags. ElementPtr mutable_params; mutable_params = boost::const_pointer_cast(shared_network_data); BaseNetworkParser::moveReservationMode(mutable_params); // Parse parameters common to all Network derivations. NetworkPtr network = boost::dynamic_pointer_cast(shared_network); parseCommon(mutable_params, network); // preferred-lifetime shared_network->setPreferred(parseIntTriplet(shared_network_data, "preferred-lifetime")); // Get interface-id option content. For now we support string // representation only Optional ifaceid; if (shared_network_data->contains("interface-id")) { ifaceid = getString(shared_network_data, "interface-id"); } // Interface is an optional parameter Optional iface; if (shared_network_data->contains("interface")) { iface = getString(shared_network_data, "interface"); } // Specifying both interface for locally reachable subnets and // interface id for relays is mutually exclusive. Need to test for // this condition. if (!ifaceid.unspecified() && !iface.unspecified() && !ifaceid.empty() && !iface.empty()) { isc_throw(isc::dhcp::DhcpConfigError, "parser error: interface (defined for locally reachable " "subnets) and interface-id (defined for subnets reachable" " via relays) cannot be defined at the same time for " "shared network " << name << "(" << shared_network_data->getPosition() << ")"); } // Configure interface-id for remote interfaces, if defined if (!ifaceid.unspecified() && !ifaceid.empty()) { std::string ifaceid_value = ifaceid.get(); OptionBuffer tmp(ifaceid_value.begin(), ifaceid_value.end()); OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp)); shared_network->setInterfaceId(opt); } // Set interface name. If it is defined, then subnets are available // directly over specified network interface. if (!iface.unspecified() && !iface.empty()) { if (check_iface_ && !IfaceMgr::instance().getIface(iface)) { ConstElementPtr error = shared_network_data->get("interface"); isc_throw(DhcpConfigError, "Specified network interface name " << iface << " for shared network " << name << " is not present in the system (" << error->getPosition() << ")"); } shared_network->setIface(iface); } if (shared_network_data->contains("rapid-commit")) { shared_network->setRapidCommit(getBoolean(shared_network_data, "rapid-commit")); } if (shared_network_data->contains("option-data")) { auto json = shared_network_data->get("option-data"); // Create parser instance for option-data. CfgOptionPtr cfg_option = shared_network->getCfgOption(); auto parser = createOptionDataListParser(); parser->parse(cfg_option, json, encapsulate_options); } if (shared_network_data->contains("client-class")) { std::string client_class = getString(shared_network_data, "client-class"); if (!client_class.empty()) { shared_network->allowClientClass(client_class); } } ConstElementPtr user_context = shared_network_data->get("user-context"); if (user_context) { shared_network->setContext(user_context); } if (shared_network_data->contains("require-client-classes")) { const std::vector& class_list = shared_network_data->get("require-client-classes")->listValue(); for (auto cclass = class_list.cbegin(); cclass != class_list.cend(); ++cclass) { if (((*cclass)->getType() != Element::string) || (*cclass)->stringValue().empty()) { isc_throw(DhcpConfigError, "invalid class name (" << (*cclass)->getPosition() << ")"); } shared_network->requireClientClass((*cclass)->stringValue()); } } if (shared_network_data->contains("subnet6")) { auto json = shared_network_data->get("subnet6"); // Create parser instance of subnet6. auto parser = createSubnetsListParser(); Subnet6Collection subnets; parser->parse(subnets, json); // Add all returned subnets into shared network. for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) { shared_network->add(*subnet); } } if (shared_network_data->contains("relay")) { auto relay_parms = shared_network_data->get("relay"); if (relay_parms) { RelayInfoParser parser(Option::V6); Network::RelayInfoPtr relay_info(new Network::RelayInfo()); parser.parse(relay_info, relay_parms); shared_network->setRelayInfo(*relay_info); } } parseTeePercents(shared_network_data, network); // Parse DDNS parameters parseDdnsParams(shared_network_data, network); // Parse lease cache parameters parseCacheParams(shared_network_data, network); // Parse allocator params. parseAllocatorParams(shared_network_data, network); if (network->getAllocatorType() == "flq") { isc_throw(BadValue, "Free Lease Queue allocator is not supported for IPv6 address pools"); } // Parse prefix delegation allocator params. auto network6 = boost::dynamic_pointer_cast(shared_network); parsePdAllocatorParams(shared_network_data, network6); } catch (const std::exception& ex) { isc_throw(DhcpConfigError, ex.what() << " (" << shared_network_data->getPosition() << ")"); } // In order to take advantage of the dynamic inheritance of global // parameters to a shared network we need to set a callback function // for each shared network which can be used to fetch global parameters. shared_network->setFetchGlobalsFn([]() -> ConstCfgGlobalsPtr { return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals()); }); return (shared_network); } boost::shared_ptr SharedNetwork6Parser::createOptionDataListParser() const { auto parser = boost::make_shared(AF_INET6); return (parser); } boost::shared_ptr SharedNetwork6Parser::createSubnetsListParser() const { auto parser = boost::make_shared(check_iface_); return (parser); } } // end of namespace isc::dhcp } // end of namespace isc