diff --git a/etc/conf.yaml b/etc/conf.yaml index e92a1320de6465401891afa8cf706b03fa3b8c47..e2a07a1abb466108ef4b807e2f6fe47eeadb00ee 100755 --- a/etc/conf.yaml +++ b/etc/conf.yaml @@ -21,34 +21,43 @@ ##### Base configuration ##### -# Name of the component, used towards NRF. Valid values: any string +# Name of the component, used towards NRF. Valid values: any string, DEFAULT="PCF" name: "PCF" -# Directories where policy configuration is stored +# Directories where policy configuration is stored, absolute path pcc_rules_directory: "/openair-pcf/policies/pcc_rules" traffic_rules_directory: "/openair-pcf/policies/traffic_rules" policy_decisions_directory: "/openair-pcf/policies/policy_decisions" ##### Options to enable/disable ##### -# If "yes": Register to NRF +# If "on": Register to NRF, DEFAULT= off register_nrf: on -# If "yes": Send only HTTP/2 requests +# If "on": Send only HTTP/2 requests, DEFAULT=off use_http2: off ##### Local interface ##### -# Local SBI interface, you can choose any key, but it must be unique +# Local SBI interface ## name: Name of the NIC to read the IP address from ## Port: unsigned int between 0-65,535 +## http_version: Allowed values: "1", "2" ## api_version: Allowed values: "v1", "v2", used to create the 3GPP API version for the SBI URL local_sbi_interface: name: "eth0" port: 80 + http_version: 1 + api_version: "v1" + +# Local HTTP2 SBI interface, optional. When configured, HTTP2 server is used +local_sbi_interface_http2: + name: "eth0" + port: 8080 + http_version: 2 api_version: "v1" ##### Next hop interfaces ##### -# Next hop SBI interfaces, you can choose any key, but it must be unique -## url: has to follow this scheme: http://<name>:<port>/ +# Next hop SBI interfaces +## url: has to follow this scheme: http://<name>:<port> ## api_version: Allowed values: "v1", "v2", used to create the 3GPP API version for the SBI URL nrf: - url: "http://oai-nrf:80/" + url: "http://oai-nrf:80" api_version: "v1" diff --git a/src/common/config/config.cmake b/src/common/config/config.cmake index 6a8558d84b53e3b49e2c51906dcfa63158d9b282..453a0f9a7488e4d98d77167390e0ca249dd3accc 100644 --- a/src/common/config/config.cmake +++ b/src/common/config/config.cmake @@ -44,7 +44,7 @@ target_sources(${NF_TARGET_LIB} PRIVATE endif() ## CONFIG used in NF_TARGET_API (API library) -#target_include_directories(${NF_TARGET_API_LIB} PUBLIC ${CONFIG_DIR}) -#target_sources(${NF_TARGET_API_LIB} PRIVATE -# ${CONFIG_SRC_FILES} -# ) +target_include_directories(${NF_TARGET_API_LIB} PUBLIC ${CONFIG_DIR}) +target_sources(${NF_TARGET_API_LIB} PRIVATE + ${CONFIG_SRC_FILES} + ) diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 69f367ddf7530f2d57b4633e177b5debde9c8668..7ea5fb6a966a58eed747bc844c5aa8fac27c97a7 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -52,16 +52,18 @@ bool config::validate() const { for (const auto& key : m_mandatory_keys) { const auto it = m_config.find(key); if (it == m_config.end()) { - logger::logger_registry::get_logger(LOGGER_NAME).error( - "Mandatory configuration %s does not exist in configuration", key); + logger::logger_registry::get_logger(LOGGER_NAME) + .error( + "Mandatory configuration %s does not exist in configuration", + key); success = false; } } for (const auto& conf : m_config) { if (!conf.second->validate()) { - logger::logger_registry::get_logger(LOGGER_NAME).error( - "Validation of %s not successful", conf.first); + logger::logger_registry::get_logger(LOGGER_NAME) + .error("Validation of %s not successful", conf.first); success = false; } } @@ -79,7 +81,6 @@ std::string config::to_string() const { std::string others_out; for (const auto& conf : m_config) { - std::string val = fmt::format( BASE_FORMATTER, "-", conf.first, COLUMN_WIDTH, conf.second->to_string(" ")); @@ -98,8 +99,10 @@ std::string config::to_string() const { sbi_out.append(val); break; case config_type_e::INVALID: - logger::logger_registry::get_logger(LOGGER_NAME).error( - "General error in configuration. Invalid type of: %s", conf.first); + logger::logger_registry::get_logger(LOGGER_NAME) + .error( + "General error in configuration. Invalid type of: %s", + conf.first); others_out.append(val); break; } @@ -133,9 +136,18 @@ bool config::get_support_feature(const std::string& name) const { return get<option_config_value>(name).value; } -const network_interface& config::get_network_interface( +const sbi_interface& config::get_sbi_interface(const std::string& name) const { + return get<sbi_interface>(name); +} + +const local_interface& config::get_local_interface( + const std::string& name) const { + return get<local_interface>(name); +} + +const local_sbi_interface& config::get_local_sbi_interface( const std::string& name) const { - return get<network_interface>(name); + return get<local_sbi_interface>(name); } template<typename T> diff --git a/src/common/config/config.hpp b/src/common/config/config.hpp index 89c94590d60bc804aa85454475531c2fb2a7dee7..f3b7a35f1234d9c04d44975efe42ec84cd1c00f7 100644 --- a/src/common/config/config.hpp +++ b/src/common/config/config.hpp @@ -39,9 +39,9 @@ namespace oai::config { -const int COLUMN_WIDTH = 20; +const int COLUMN_WIDTH = 20; const std::string BASE_FORMATTER = " {} {:.<{}}: {}\n"; -const std::string LOGGER_NAME = "config "; +const std::string LOGGER_NAME = "config "; class config_iface { public: @@ -51,10 +51,12 @@ class config_iface { * @param name name of the configuration * @param val value of the configuration */ - virtual void set_configuration(const std::string& name, std::unique_ptr<config_type> val) = 0; + virtual void set_configuration( + const std::string& name, std::unique_ptr<config_type> val) = 0; /** - * Set a configuration of any type with name to be mandatory, used for validation + * Set a configuration of any type with name to be mandatory, used for + * validation * @param name name of the configuration */ virtual void set_configuration_mandatory(const std::string& name) = 0; @@ -73,45 +75,72 @@ class config_iface { */ [[nodiscard]] virtual std::string to_string() const = 0; - // The get method is not defined here, as we cannot have virtual template members - // Annoying that I cannot have a virtual template method just called "get" + // The get method is not defined here, as we cannot have virtual template + // members Annoying that I cannot have a virtual template method just called + // "get" /** * Get a string base configuration * @throws std::invalid_argument when name does not exist in configuration * @param name of the configuration + * @return value */ - [[nodiscard]] virtual const std::string& get_base_conf_val(const std::string& name) const = 0; + [[nodiscard]] virtual const std::string& get_base_conf_val( + const std::string& name) const = 0; /** * Get a boolean configuration * @throws std::invalid_argument when name does not exist in configuration * @param name of the configuration + * @return value */ - [[nodiscard]] virtual bool get_support_feature(const std::string& name) const = 0; + [[nodiscard]] virtual bool get_support_feature( + const std::string& name) const = 0; /** - * Get a network interface configuration + * Get a SBI interface configuration * @throws std::invalid_argument when name does not exist in configuration * @param name of the configuration + * @return value */ - [[nodiscard]] virtual const network_interface& get_network_interface(const std::string& name) const = 0 ; + [[nodiscard]] virtual const sbi_interface& get_sbi_interface( + const std::string& name) const = 0; + + /** + * Get a local SBI interface configuration + * @throws std::invalid_argument when name does not exist in configuration + * @param name of the configuration + * @return value + */ + [[nodiscard]] virtual const local_sbi_interface& get_local_sbi_interface( + const std::string& name) const = 0; + + /** + * Get a local interface configuration + * @throws std::invalid_argument when name does not exist in configuration + * @param name of the configuration + * @return value + */ + [[nodiscard]] virtual const local_interface& get_local_interface( + const std::string& name) const = 0; /** * Display the to_string method to the config logger */ virtual void display() const = 0; + virtual ~config_iface() = default; }; -class config : public config_iface{ - +class config : public config_iface { public: - - explicit config(bool log_stdout, bool log_rot_file) { - logger::logger_registry::register_logger(PACKAGE_NAME, LOGGER_NAME, log_stdout, log_rot_file); + explicit config( + const std::string& nf_name, bool log_stdout, bool log_rot_file) { + logger::logger_registry::register_logger( + nf_name, LOGGER_NAME, log_stdout, log_rot_file); } - void set_configuration(const std::string& name, std::unique_ptr<config_type> val) override; + void set_configuration( + const std::string& name, std::unique_ptr<config_type> val) override; void set_configuration_mandatory(const std::string& name) override; @@ -129,16 +158,24 @@ class config : public config_iface{ template<typename T> [[nodiscard]] const T& get(const std::string& name) const; - [[nodiscard]] const std::string& get_base_conf_val(const std::string& name) const override; + [[nodiscard]] const std::string& get_base_conf_val( + const std::string& name) const override; - [[nodiscard]] bool get_support_feature(const std::string& name) const override; + [[nodiscard]] bool get_support_feature( + const std::string& name) const override; - [[nodiscard]] const network_interface& get_network_interface(const std::string& name) const override; + [[nodiscard]] const sbi_interface& get_sbi_interface( + const std::string& name) const override; + + [[nodiscard]] const local_interface& get_local_interface( + const std::string& name) const override; + + [[nodiscard]] const local_sbi_interface& get_local_sbi_interface( + const std::string& name) const override; void display() const override; private: - std::map<std::string, std::unique_ptr<config_type>> m_config; std::vector<std::string> m_mandatory_keys; @@ -146,5 +183,4 @@ class config : public config_iface{ mutable std::shared_mutex m_config_mutex; }; - -} \ No newline at end of file +} // namespace oai::config \ No newline at end of file diff --git a/src/common/config/config_types.cpp b/src/common/config/config_types.cpp index 692ad1f3017d86fa37522fbeca062be29623ce2c..59997c689bf2ecec335f258b03d100218cbaae82 100644 --- a/src/common/config/config_types.cpp +++ b/src/common/config/config_types.cpp @@ -28,18 +28,38 @@ */ #include "config_types.hpp" -#include "fmt/format.h" #include "config.hpp" #include "conversions.hpp" -#include "algorithm" #include "logger_base.hpp" +#include "if.hpp" +#include "common_defs.h" + +#include <fmt/format.h> +#include <algorithm> +#include <regex> +#include <string> using namespace oai::config; const std::string INNER_LIST_ELEM = "+"; -bool sbi_interface::validate() const { - // TODO validation +bool config_type::matches_regex( + const std::string& value, const std::string& regex) { + std::regex re(regex); + + if (!std::regex_match(value, re)) { + logger::logger_registry::get_logger(LOGGER_NAME) + .error("%s does not follow regex specification: %s", value, regex); + return false; + } + return true; +} + +bool sbi_interface::validate() { + if (!matches_regex(url, URL_REGEX)) { + return false; + } + set = true; return true; } @@ -59,8 +79,26 @@ config_type_e sbi_interface::get_config_type() const { return type; } -bool local_interface::validate() const { - // TODO validation +bool sbi_interface::is_set() const { + return set; +} + +bool local_interface::validate() { + unsigned int _mtu{}; + in_addr _addr4{}; + in_addr _netmask{}; + if (get_inet_addr_infos_from_iface(if_name, _addr4, _netmask, _mtu) == + RETURNerror) { + logger::logger_registry::get_logger(LOGGER_NAME) + .error( + "Error in reading configuration from network interface %s", + if_name); + return false; + } + mtu = _mtu; + addr4 = _addr4; + + set = true; return true; } @@ -73,15 +111,15 @@ std::string local_interface::to_string(const std::string& indent) const { out.append(indent).append(fmt::format( BASE_FORMATTER, INNER_LIST_ELEM, "IPv4 Address ", inner_width, ip4)); - if (!ip6.empty()) { + if (ip6 != "::") { out.append(indent).append(fmt::format( - BASE_FORMATTER, INNER_LIST_ELEM, "IPv6 Address", inner_width, ip4)); + BASE_FORMATTER, INNER_LIST_ELEM, "IPv6 Address", inner_width, ip6)); } out.append(indent).append( fmt::format(BASE_FORMATTER, INNER_LIST_ELEM, "MTU", inner_width, mtu)); out.append(indent).append(fmt::format( BASE_FORMATTER, INNER_LIST_ELEM, "Interface name: ", inner_width, - iface_name)); + if_name)); out.append(indent).append( fmt::format(BASE_FORMATTER, INNER_LIST_ELEM, "Port", inner_width, port)); @@ -92,11 +130,16 @@ config_type_e local_interface::get_config_type() const { return type; } -bool local_sbi_interface::validate() const { +bool local_interface::is_set() const { + return set; +} + +bool local_sbi_interface::validate() { bool sbi_validate = validate_sbi_api_version(api_version); if (!sbi_validate) { return false; } + set = true; return local_interface::validate(); } @@ -106,9 +149,20 @@ std::string local_sbi_interface::to_string(const std::string& indent) const { out.append(indent).append(fmt::format( BASE_FORMATTER, INNER_LIST_ELEM, "API Version", inner_width, api_version)); + + std::string http_version = use_http2 ? "2" : "1"; + + out.append(indent).append(fmt::format( + BASE_FORMATTER, INNER_LIST_ELEM, "HTTP Version", inner_width, + http_version)); + return out; } +bool local_sbi_interface::is_set() const { + return set; +} + bool network_interface::validate_sbi_api_version(const std::string& v) { auto it = std::find(allowed_api_versions.begin(), allowed_api_versions.end(), v); @@ -125,23 +179,45 @@ std::string string_config_value::to_string(const std::string&) const { return out.append(value); } -bool string_config_value::validate() const { - return false; +bool string_config_value::validate() { + set = true; + return true; } config_type_e string_config_value::get_config_type() const { return type; } +bool string_config_value::is_set() const { + return set; +} + std::string option_config_value::to_string(const std::string&) const { std::string val = value ? "Yes" : "No"; return val; } -bool option_config_value::validate() const { +bool option_config_value::validate() { + set = true; return true; } config_type_e option_config_value::get_config_type() const { return type; } + +bool option_config_value::is_set() const { + return set; +} + +option_config_value factory::get_option_config(bool val) { + option_config_value v; + v.value = val; + return v; +} + +string_config_value factory::get_string_config(const std::string& val) { + string_config_value v; + v.value = val; + return v; +} diff --git a/src/common/config/config_types.hpp b/src/common/config/config_types.hpp index 65e36e565945b4fe7d73f5445a36437dac902c7d..ec4f07b39da8efcfae09ff33f762fcc01b8b2e12 100644 --- a/src/common/config/config_types.hpp +++ b/src/common/config/config_types.hpp @@ -38,16 +38,24 @@ namespace oai::config { const std::vector<std::string> allowed_api_versions{"v1", "v2"}; +const std::string URL_REGEX = "http://.*:[0-9]*"; + // Only used for pretty-printing enum class config_type_e { STRING, OPTION, SBI, LOCAL, INVALID }; class config_type { public: [[nodiscard]] virtual std::string to_string( - const std::string& indent) const = 0; - [[nodiscard]] virtual bool validate() const = 0; + const std::string& indent) const = 0; + + [[nodiscard]] virtual bool validate() = 0; + [[nodiscard]] virtual config_type_e get_config_type() const = 0; + [[nodiscard]] virtual bool is_set() const = 0; + + static bool matches_regex(const std::string& value, const std::string& regex); + virtual ~config_type() = default; }; @@ -57,11 +65,13 @@ class string_config_value : public config_type { std::string regex; [[nodiscard]] std::string to_string(const std::string& indent) const override; - [[nodiscard]] bool validate() const override; + [[nodiscard]] bool validate() override; [[nodiscard]] config_type_e get_config_type() const override; + [[nodiscard]] bool is_set() const override; private: config_type_e type = config_type_e::STRING; + bool set = false; }; class option_config_value : public config_type { @@ -69,11 +79,13 @@ class option_config_value : public config_type { bool value = false; [[nodiscard]] std::string to_string(const std::string& indent) const override; - [[nodiscard]] bool validate() const override; + [[nodiscard]] bool validate() override; [[nodiscard]] config_type_e get_config_type() const override; + [[nodiscard]] bool is_set() const override; private: config_type_e type = config_type_e::OPTION; + bool set = false; }; class network_interface : public config_type { @@ -86,39 +98,53 @@ class sbi_interface : public network_interface { std::string api_version; std::string url; - [[nodiscard]] bool validate() const override; + [[nodiscard]] bool validate() override; [[nodiscard]] std::string to_string(const std::string& indent) const override; [[nodiscard]] config_type_e get_config_type() const override; + [[nodiscard]] bool is_set() const override; private: config_type_e type = config_type_e::SBI; + bool set = false; }; class local_interface : public network_interface { public: - std::string iface_name; - in_addr addr4; - in6_addr addr6; - unsigned int mtu; - uint16_t port; + std::string if_name{}; + in_addr addr4{}; + in6_addr addr6{}; + unsigned int mtu{}; + uint16_t port{}; - [[nodiscard]] bool validate() const override; + [[nodiscard]] bool validate() override; [[nodiscard]] std::string to_string(const std::string& indent) const override; [[nodiscard]] config_type_e get_config_type() const override; + [[nodiscard]] bool is_set() const override; private: config_type_e type = config_type_e::LOCAL; + bool set = false; }; class local_sbi_interface : public local_interface { public: std::string api_version; + bool use_http2 = false; - [[nodiscard]] bool validate() const override; + [[nodiscard]] bool validate() override; [[nodiscard]] std::string to_string(const std::string& indent) const override; + [[nodiscard]] bool is_set() const override; private: config_type_e type = config_type_e::LOCAL; + bool set = false; +}; + +class factory { + public: + static option_config_value get_option_config(bool val); + + static string_config_value get_string_config(const std::string& val); }; } // namespace oai::config \ No newline at end of file diff --git a/src/common/config/config_yaml_file.hpp b/src/common/config/config_yaml_file.hpp index 6cd11d3513c1a487a152bb020f1f2190cb590b90..c468c4a65921dd4ae2e11590324afab88dae2b5f 100644 --- a/src/common/config/config_yaml_file.hpp +++ b/src/common/config/config_yaml_file.hpp @@ -99,8 +99,8 @@ struct convert<oai::config::local_interface> { if (!node["name"] || !node["port"]) { return false; } - val.port = node["port"].as<uint16_t>(); - val.iface_name = node["name"].as<std::string>(); + val.port = node["port"].as<uint16_t>(); + val.if_name = node["name"].as<std::string>(); return true; } }; @@ -109,14 +109,18 @@ template<> struct convert<oai::config::local_sbi_interface> { static bool decode(const Node& node, oai::config::local_sbi_interface& val) { auto iface = node.as<oai::config::local_interface>(); - if (!node["api_version"]) { + if (!node["api_version"] || !node["http_version"]) { return false; } - val.port = iface.port; - val.iface_name = iface.iface_name; - val.api_version = node["api_version"].as<std::string>(); + val.port = iface.port; + val.if_name = iface.if_name; + val.api_version = node["api_version"].as<std::string>(); + auto http_version = node["http_version"].as<std::string>(); + if (http_version == "2") { + val.use_http2 = true; + } return true; } }; diff --git a/src/oai_pcf/main.cpp b/src/oai_pcf/main.cpp index 3a7c45d776b8bb4a92557bfd013334f6cfff1c9a..e7607aa174601853687f5df031eaf52f4ded1b1d 100644 --- a/src/oai_pcf/main.cpp +++ b/src/oai_pcf/main.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "common_defs.h" #include "logger.hpp" #include "pcf-api-server.hpp" #include "pcf-http2-server.hpp" @@ -24,7 +23,6 @@ #include "pistache/http.h" #include "nf_launch.hpp" -#include <algorithm> #include <iostream> #include <csignal> #include <thread> @@ -35,11 +33,13 @@ using namespace oai::pcf::app; using namespace oai::pcf::config; using namespace oai::utils; +using namespace oai::config; + std::unique_ptr<pcf_app> pcf_app_inst; // TODO Stefan: I am not happy with these global variables // We could make a singleton getInstance in config // or we handle everything in smf_app init and have a reference to config there -std::unique_ptr<pcf_config> pcf_cfg = std::make_unique<pcf_config>(); +std::unique_ptr<pcf_config> pcf_cfg; std::unique_ptr<PCFApiServer> pcf_api_server_1; std::unique_ptr<pcf_http2_server> pcf_api_server_2; @@ -86,36 +86,42 @@ int main(int argc, char** argv) { std::signal(SIGINT, signal_handler_sigint); - // Event subsystem - pcf_event ev; - - // Config - if (pcf_cfg->load(oai::utils::options::getlibconfigConfig()) == RETURNerror) { - exit(-1); + pcf_cfg = std::make_unique<pcf_config>( + oai::utils::options::getlibconfigConfig(), + oai::utils::options::getlogStdout(), + oai::utils::options::getlogRotFilelog()); + if (!pcf_cfg->init()) { + pcf_cfg->display(); + return 1; } pcf_cfg->display(); + // Event subsystem + pcf_event ev; + // PCF application layer pcf_app_inst = std::make_unique<pcf_app>(ev); std::string v4_address = conv::toString(pcf_cfg->sbi.addr4); // PCF Pistache API server (HTTP1) - Pistache::Address addr(v4_address, Pistache::Port(pcf_cfg->sbi.http1_port)); + Pistache::Address addr(v4_address, Pistache::Port(pcf_cfg->sbi.port)); PCFApiServer test(addr, pcf_app_inst); pcf_api_server_1 = std::make_unique<PCFApiServer>(addr, pcf_app_inst); pcf_api_server_1->init(2); std::thread pcf_http1_manager(&PCFApiServer::start, pcf_api_server_1.get()); - // PCF NGHTTP API server (HTTP2) - pcf_api_server_2 = std::make_unique<pcf_http2_server>( - v4_address, pcf_cfg->sbi.http2_port, pcf_app_inst); - std::thread pcf_http2_manager( - &pcf_http2_server::start, pcf_api_server_2.get()); + if (pcf_cfg->sbi_http2.is_set()) { + // PCF NGHTTP API server (HTTP2) + pcf_api_server_2 = std::make_unique<pcf_http2_server>( + v4_address, pcf_cfg->sbi_http2.port, pcf_app_inst); + std::thread pcf_http2_manager( + &pcf_http2_server::start, pcf_api_server_2.get()); + pcf_http2_manager.join(); + } pcf_http1_manager.join(); - pcf_http2_manager.join(); Logger::pcf_app().info("HTTP servers successfully stopped. Exiting"); diff --git a/src/pcf_app/pcf_config.cpp b/src/pcf_app/pcf_config.cpp index 5446940f8c387149090c5c0fe77d04d646552bc7..d4a3c1f2efe8610e5d216cb76efae55cc6342ec9 100644 --- a/src/pcf_app/pcf_config.cpp +++ b/src/pcf_app/pcf_config.cpp @@ -21,339 +21,81 @@ /*! \file pcf_config.cpp \brief - \author Rohan Kharade - \company Openairinterface Software Allianse + \author Rohan Kharade, Stefan Spettel + \company OpenAirInterface Software Alliance \date 2022 \email: rohan.kharade@openairinterface.org */ +#include <config_yaml_file.hpp> #include "pcf_config.hpp" -#include "common_defs.h" -#include "conversions.hpp" -#include "if.hpp" -#include "string.hpp" -#include "fqdn.hpp" -#include <fstream> -#include <nlohmann/json.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/split.hpp> +using namespace oai::config; -#include <cstdlib> -#include <iomanip> -#include <iostream> +bool oai::pcf::config::pcf_config::init() { + // Default configuration + std::unique_ptr<config_type> use_http2 = + std::make_unique<option_config_value>( + factory::get_option_config(USE_HTTP2_DEFAULT_VALUE)); + std::unique_ptr<config_type> register_nrf = + std::make_unique<option_config_value>( + factory::get_option_config(REGISTER_NRF_DEFAULT_VALUE)); + std::unique_ptr<config_type> pcf_name = std::make_unique<string_config_value>( + factory::get_string_config(NAME_DEFAULT_VALUE)); -using namespace std; -using namespace libconfig; + m_cfg->set_configuration(PCF_CONFIG_STRING_USE_HTTP2, std::move(use_http2)); + m_cfg->set_configuration( + PCF_CONFIG_STRING_REGISTER_NRF, std::move(register_nrf)); + m_cfg->set_configuration(PCF_CONFIG_STRING_NAME, std::move(pcf_name)); -namespace oai::pcf::config { -// C includes -#include <arpa/inet.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> -#include <sys/stat.h> + m_cfg->set_configuration_mandatory(PCF_CONFIG_STRING_TRAFFIC_RULES_DIR); + m_cfg->set_configuration_mandatory(PCF_CONFIG_STRING_POLICY_DECISIONS_DIR); + m_cfg->set_configuration_mandatory(PCF_CONFIG_STRING_PCC_RULES_DIR); -//------------------------------------------------------------------------------ -// int pcf_config::execute() { return RETURNok; } + m_cfg->set_configuration_mandatory(PCF_CONFIG_STRING_SBI_IFACE); -//------------------------------------------------------------------------------ -int pcf_config::load_interface(const Setting& if_cfg, interface_cfg_t& cfg) { - if_cfg.lookupValue(PCF_CONFIG_STRING_INTERFACE_NAME, cfg.if_name); - util::trim(cfg.if_name); - if (not boost::iequals(cfg.if_name, "none")) { - std::string address = {}; - if_cfg.lookupValue(PCF_CONFIG_STRING_IPV4_ADDRESS, address); - util::trim(address); - if (boost::iequals(address, "read")) { - if (get_inet_addr_infos_from_iface( - cfg.if_name, cfg.addr4, cfg.network4, cfg.mtu)) { - Logger::pcf_app().error( - "Could not read %s network interface configuration", cfg.if_name); - return RETURNerror; - } - } else { - std::vector<std::string> words; - boost::split( - words, address, boost::is_any_of("/"), boost::token_compress_on); - if (words.size() != 2) { - Logger::pcf_app().error( - "Bad value " PCF_CONFIG_STRING_IPV4_ADDRESS " = %s in config file", - address.c_str()); - return RETURNerror; - } - unsigned char buf_in_addr[sizeof(struct in6_addr)]; // you never know... - if (inet_pton(AF_INET, util::trim(words.at(0)).c_str(), buf_in_addr) == - 1) { - memcpy(&cfg.addr4, buf_in_addr, sizeof(struct in_addr)); - } else { - Logger::pcf_app().error( - "In conversion: Bad value " PCF_CONFIG_STRING_IPV4_ADDRESS - " = %s in config file", - util::trim(words.at(0)).c_str()); - return RETURNerror; - } - cfg.network4.s_addr = htons( - ntohs(cfg.addr4.s_addr) & - 0xFFFFFFFF << (32 - std::stoi(util::trim(words.at(1))))); - } - if_cfg.lookupValue(PCF_CONFIG_STRING_SBI_PORT_HTTP1, cfg.http1_port); - if_cfg.lookupValue(PCF_CONFIG_STRING_SBI_PORT_HTTP2, cfg.http2_port); - } - return RETURNok; -} -//------------------------------------------------------------------------------ -int pcf_config::check_directory( - const Setting& pcf_cfg, const std::string& path_config, std::string& path) { - try { - pcf_cfg.lookupValue(path_config, path); - - struct stat info; - if (stat(path.c_str(), &info) != 0) { - Logger::pcf_app().info("Path %s cannot be accessed.", path.c_str()); - return RETURNerror; - } else if (!(info.st_mode & S_IFDIR)) { - Logger::pcf_app().info("Path %s is not a directory.", path.c_str()); - return RETURNerror; - } - } catch (const SettingNotFoundException& nfex) { - Logger::pcf_app().info("Configuration %s is not set.", path_config.c_str()); - return RETURNerror; - } - return RETURNok; -} -//------------------------------------------------------------------------------ -int pcf_config::load(const string& config_file) { - Config cfg; - - // Read the file. If there is an error, report it and exit. + yaml_file file; try { - cfg.readFile(config_file.c_str()); - } catch (const FileIOException& fioex) { - Logger::pcf_app().error( - "I/O error while reading file %s - %s", config_file.c_str(), - fioex.what()); - throw; - } catch (const ParseException& pex) { - Logger::pcf_app().error( - "Parse error at %s:%d - %s", pex.getFile(), pex.getLine(), - pex.getError()); - throw; - } - - const Setting& root = cfg.getRoot(); - - if (root.exists(PCF_CONFIG_STRING_PCF_CONFIG) == false) { - Logger::pcf_app().error( - "Setting does not exist: " + std::string{PCF_CONFIG_STRING_PCF_CONFIG}); - return RETURNerror; + file.read_from_file(m_config_path, *m_cfg); + } catch (std::runtime_error& err) { + return false; } - - const Setting& pcf_cfg = root[PCF_CONFIG_STRING_PCF_CONFIG]; - - try { - pcf_cfg.lookupValue(PCF_CONFIG_STRING_FQDN, fqdn); - util::trim(fqdn); - } catch (const SettingNotFoundException& nfex) { - Logger::pcf_app().info( - "%s : %s, No FQDN configured", nfex.what(), nfex.getPath()); + // this is only mandatory when REGISTER_NRF is set + if (m_cfg->get_support_feature(PCF_CONFIG_STRING_REGISTER_NRF)) { + m_cfg->set_configuration_mandatory(PCF_CONFIG_STRING_NRF); } - if (check_directory( - pcf_cfg, PCF_CONFIG_STRING_PCC_RULES_DIRECTORY, pcc_rules_path) != - RETURNok) { - Logger::pcf_app().error( - "PCC Rules Path is not set or not a valid directory. Exiting"); - return RETURNerror; - } - if (check_directory( - pcf_cfg, PCF_CONFIG_STRING_POLICY_DECISIONS_DIRECTORY, - policy_decisions_path) != RETURNok) { - Logger::pcf_app().error( - "Policy Decisions Path is not set or not a valid directory. Exiting"); - return RETURNerror; + bool validated = m_cfg->validate(); + if (!validated) { + logger::logger_registry::get_logger(LOGGER_NAME) + .error("Configuration validation not successful!"); + return false; } - if (check_directory( - pcf_cfg, PCF_CONFIG_STRING_TRAFFIC_RULES_DIRECTORY, - traffic_rules_path) != RETURNok) { - Logger::pcf_app().warn( - "Traffic Rules Path is not set or not a valid directory. This feature " - "is disabled."); - } - - try { - const Setting& nw_if_cfg = pcf_cfg[PCF_CONFIG_STRING_INTERFACES]; - - const Setting& sbi_cfg = nw_if_cfg[PCF_CONFIG_STRING_SBI_INTERFACE]; - load_interface(sbi_cfg, sbi); + set_direct_variables(); + return true; +} - sbi_cfg.lookupValue(PCF_CONFIG_STRING_API_VERSION, sbi_api_version); - } catch (const SettingNotFoundException& nfex) { - Logger::pcf_app().error("%s : %s", nfex.what(), nfex.getPath()); - return RETURNerror; - } - // Support features +void oai::pcf::config::pcf_config::set_direct_variables() { + sbi = m_cfg->get_local_sbi_interface(PCF_CONFIG_STRING_SBI_IFACE); try { - const Setting& support_features = - pcf_cfg[PCF_CONFIG_STRING_SUPPORT_FEATURES]; - string opt; - support_features.lookupValue( - PCF_CONFIG_STRING_SUPPORT_FEATURES_REGISTER_NRF, opt); - if (boost::iequals(opt, "yes")) { - pcf_features.register_nrf = true; - } else { - pcf_features.register_nrf = false; - } - - support_features.lookupValue( - PCF_CONFIG_STRING_SUPPORT_FEATURES_USE_FQDN_DNS, opt); - if (boost::iequals(opt, "yes")) { - pcf_features.use_fqdn = true; - } else { - pcf_features.use_fqdn = false; - } - - support_features.lookupValue( - PCF_CONFIG_STRING_SUPPORT_FEATURES_USE_HTTP2, opt); - if (boost::iequals(opt, "yes")) { - pcf_features.use_http2 = true; - } else { - pcf_features.use_http2 = false; - } - - } catch (const SettingNotFoundException& nfex) { - Logger::pcf_app().error( - "%s : %s, using defaults", nfex.what(), nfex.getPath()); - return -1; + sbi_http2 = + m_cfg->get_local_sbi_interface(PCF_CONFIG_STRING_SBI_IFACE_HTTP2); + } catch (std::invalid_argument&) { } - try { - string astring = {}; - // NRF - if (pcf_features.register_nrf) { - const Setting& nrf_cfg = pcf_cfg[PCF_CONFIG_STRING_NRF]; - struct in_addr nrf_ipv4_addr = {}; - unsigned int nrf_port = {0}; - std::string nrf_api_version = {}; - - if (!pcf_features.use_fqdn) { - nrf_cfg.lookupValue(PCF_CONFIG_STRING_NRF_IPV4_ADDRESS, astring); - IPV4_STR_ADDR_TO_INADDR( - util::trim(astring).c_str(), nrf_ipv4_addr, - "BAD IPv4 ADDRESS FORMAT FOR NRF !"); - nrf_addr.ipv4_addr = nrf_ipv4_addr; - if (!(nrf_cfg.lookupValue(PCF_CONFIG_STRING_NRF_PORT, nrf_port))) { - Logger::pcf_app().error(PCF_CONFIG_STRING_NRF_PORT "failed"); - throw(PCF_CONFIG_STRING_NRF_PORT "failed"); - } - nrf_addr.port = nrf_port; - - if (!(nrf_cfg.lookupValue( - PCF_CONFIG_STRING_API_VERSION, nrf_api_version))) { - Logger::pcf_app().error(PCF_CONFIG_STRING_API_VERSION "failed"); - throw(PCF_CONFIG_STRING_API_VERSION "failed"); - } - nrf_addr.api_version = nrf_api_version; - } else { - nrf_cfg.lookupValue(PCF_CONFIG_STRING_FQDN, astring); - uint8_t addr_type = {0}; - std::string address = {}; - fqdn::resolve(astring, address, nrf_port, addr_type); - if (addr_type != 0) { // IPv6 - // TODO: - throw("DO NOT SUPPORT IPV6 ADDR FOR NRF!"); - } else { // IPv4 - IPV4_STR_ADDR_TO_INADDR( - util::trim(address).c_str(), nrf_ipv4_addr, - "BAD IPv4 ADDRESS FORMAT FOR NRF !"); - nrf_addr.ipv4_addr = nrf_ipv4_addr; - // nrf_addr.port = nrf_port; - // We hardcode nrf port from config for the moment - if (!(nrf_cfg.lookupValue(PCF_CONFIG_STRING_NRF_PORT, nrf_port))) { - Logger::pcf_app().error(PCF_CONFIG_STRING_NRF_PORT "failed"); - throw(PCF_CONFIG_STRING_NRF_PORT "failed"); - } - nrf_addr.port = nrf_port; - nrf_addr.api_version = "v1"; // TODO: to get API version from DNS - nrf_addr.fqdn = astring; - } - } - } - } catch (const SettingNotFoundException& nfex) { - Logger::pcf_app().error("%s : %s", nfex.what(), nfex.getPath()); - return RETURNerror; - } - return RETURNok; + nrf_addr = m_cfg->get_sbi_interface(PCF_CONFIG_STRING_NRF); + pcc_rules_path = m_cfg->get_base_conf_val(PCF_CONFIG_STRING_PCC_RULES_DIR); + policy_decisions_path = + m_cfg->get_base_conf_val(PCF_CONFIG_STRING_POLICY_DECISIONS_DIR); + traffic_rules_path = + m_cfg->get_base_conf_val(PCF_CONFIG_STRING_TRAFFIC_RULES_DIR); + + pcf_features.register_nrf = + m_cfg->get_support_feature(PCF_CONFIG_STRING_REGISTER_NRF); + pcf_features.use_http2 = + m_cfg->get_support_feature(PCF_CONFIG_STRING_USE_HTTP2); } -//------------------------------------------------------------------------------ -void pcf_config::display() { - Logger::pcf_app().info( - "==== OPENAIRINTERFACE %s v%s ====", PACKAGE_NAME, PACKAGE_VERSION); - Logger::pcf_app().info("Configuration:"); - Logger::pcf_app().info("- FQDN ..................: %s", fqdn.c_str()); - Logger::pcf_app().info( - "- Policies Path..........: %s", policy_decisions_path.c_str()); - Logger::pcf_app().info( - "- PCC Rules Path.........: %s", pcc_rules_path.c_str()); - Logger::pcf_app().info( - "- Traffic Rules Path.....: %s", traffic_rules_path.c_str()); - - Logger::pcf_app().info("- SBI:"); - Logger::pcf_app().info(" iface ............: %s", sbi.if_name.c_str()); - Logger::pcf_app().info(" ipv4.addr ........: %s", inet_ntoa(sbi.addr4)); - Logger::pcf_app().info(" ipv4.mask ........: %s", inet_ntoa(sbi.network4)); - Logger::pcf_app().info(" mtu ..............: %d", sbi.mtu); - Logger::pcf_app().info(" http1_port .......: %u", sbi.http1_port); - Logger::pcf_app().info(" http2_port .......: %u", sbi.http2_port); - Logger::pcf_app().info(" api_version ......: %s", sbi_api_version.c_str()); - if (pcf_features.register_nrf) { - Logger::pcf_app().info("- NRF:"); - Logger::pcf_app().info( - " IPv4 Addr ...........: %s", - inet_ntoa(*((struct in_addr*) &nrf_addr.ipv4_addr))); - Logger::pcf_app().info(" Port ................: %lu ", nrf_addr.port); - Logger::pcf_app().info( - " API version .........: %s", nrf_addr.api_version.c_str()); - if (pcf_features.use_fqdn) - Logger::pcf_app().info( - " FQDN ................: %s", nrf_addr.fqdn.c_str()); - } - Logger::pcf_app().info("- Supported Features:"); - Logger::pcf_app().info( - " Register to NRF........: %s", - pcf_features.register_nrf ? "Yes" : "No"); - Logger::pcf_app().info( - " Use FQDN ..............: %s", pcf_features.use_fqdn ? "Yes" : "No"); - Logger::pcf_app().info( - " Use HTTP2..............: %s", pcf_features.use_http2 ? "Yes" : "No"); +void oai::pcf::config::pcf_config::display() { + m_cfg->display(); } - -//------------------------------------------------------------------------------ -// bool pcf_config::get_slice_config(nlohmann::json& slice_config) { -// slice_config = pcf_slice_config; -// return true; -// } - -//------------------------------------------------------------------------------ -// bool pcf_config::get_api_list(nlohmann::json &api_list) { -// api_list["OAI-PCF"] = { -// {"Organisation", "Openairinterface Software Aliance"}, -// {"Description", "OAI-PCF initial Release"}, -// {"Version", "1.0.0"}, -// {"Supported APIs", -// {{"API", "Network Slice Information (Document)"}, -// {"Method", "GET"}, -// {"URI Path", -// "/npcf-nsselection/<api_version>/network-slice-information"}, -// {"Details", -// "Retrieve the Network Slice Selection Information (PDU -// Session)"}}}}; -// return true; -// } -//------------------------------------------------------------------------------ - -} // namespace oai::pcf::config diff --git a/src/pcf_app/pcf_config.hpp b/src/pcf_app/pcf_config.hpp index 4519d26698397301d2928fe315a5e5166a90c7e0..e64ef3b9fc0ddecf702ea999b2ce505c5b557ddd 100644 --- a/src/pcf_app/pcf_config.hpp +++ b/src/pcf_app/pcf_config.hpp @@ -21,138 +21,74 @@ /*! \file pcf_config.hpp \brief - \author Rohan Kharade - \company Openairinterface Software Allianse + \author Rohan Kharade, Stefan Spettel + \company OpenAirInterface Software Alliance \date 2022 \email: rohan.kharade@openairinterface.org */ -#ifndef FILE_PCF_CONFIG_HPP_SEEN -#define FILE_PCF_CONFIG_HPP_SEEN +#pragma once -#include "3gpp_29.510.h" -#include "logger.hpp" -#include <libconfig.h++> -#include <mutex> -#include <netinet/in.h> -#include <nlohmann/json.hpp> -#include <stdbool.h> -#include <stdint.h> -#include <string> -#include <unistd.h> - -// using namespace oai::pcf::model; - -#define PCF_CONFIG_STRING_PCF_CONFIG "PCF" -#define PCF_CONFIG_STRING_FQDN "FQDN" -#define PCF_CONFIG_STRING_PCF_SLICE_CONFIG "PCF_SLICE_CONFIG" -#define PCF_CONFIG_STRING_INTERFACES "INTERFACES" -#define PCF_CONFIG_STRING_INTERFACE_NAME "INTERFACE_NAME" -#define PCF_CONFIG_STRING_IPV4_ADDRESS "IPV4_ADDRESS" -#define PCF_CONFIG_STRING_SBI_PORT_HTTP1 "HTTP1_PORT" -#define PCF_CONFIG_STRING_SBI_PORT_HTTP2 "HTTP2_PORT" -#define PCF_CONFIG_STRING_SBI_INTERFACE "SBI" -#define PCF_CONFIG_STRING_API_VERSION "API_VERSION" - -#define PCF_CONFIG_STRING_NETWORK_IPV4 "NETWORK_IPV4" -#define PCF_CONFIG_STRING_NETWORK_IPV6 "NETWORK_IPV6" -#define PCF_CONFIG_STRING_ADDRESS_PREFIX_DELIMITER "/" -#define PCF_CONFIG_STRING_ITTI_TASKS "ITTI_TASKS" -#define PCF_CONFIG_STRING_ITTI_TIMER_SCHED_PARAMS "ITTI_TIMER_SCHED_PARAMS" -#define PCF_CONFIG_STRING_SBI_SCHED_PARAMS "SBI_SCHED_PARAMS" - -#define PCF_CONFIG_STRING_SUPPORT_FEATURES "SUPPORT_FEATURES" -#define PCF_CONFIG_STRING_SUPPORT_FEATURES_REGISTER_NRF "REGISTER_NRF" -#define PCF_CONFIG_STRING_NRF "NRF" -#define PCF_CONFIG_STRING_NRF_IPV4_ADDRESS "IPV4_ADDRESS" -#define PCF_CONFIG_STRING_NRF_PORT "PORT" -#define PCF_CONFIG_STRING_NRF_HTTP_VERSION "HTTP_VERSION" -#define PCF_CONFIG_STRING_SUPPORT_FEATURES_USE_HTTP2 "USE_HTTP2" -#define PCF_CONFIG_STRING_SUPPORT_FEATURES_USE_FQDN_DNS "USE_FQDN" - -#define PCF_CONFIG_STRING_PCC_RULES_DIRECTORY "PCC_RULES_DIRECTORY" -#define PCF_CONFIG_STRING_POLICY_DECISIONS_DIRECTORY \ - "POLICY_DECISIONS_DIRECTORY" -#define PCF_CONFIG_STRING_TRAFFIC_RULES_DIRECTORY "TRAFFIC_RULES_DIRECTORY" +#include "config.hpp" namespace oai::pcf::config { -typedef struct interface_cfg_s { - std::string if_name; - struct in_addr addr4; - struct in_addr network4; - struct in6_addr addr6; - unsigned int mtu; - unsigned int http1_port; - unsigned int http2_port; -} interface_cfg_t; +const std::string NAME_DEFAULT_VALUE = "PCF"; +const bool REGISTER_NRF_DEFAULT_VALUE = false; +const bool USE_HTTP2_DEFAULT_VALUE = false; + +const std::string PCF_CONFIG_STRING_REGISTER_NRF = "register_nrf"; +const std::string PCF_CONFIG_STRING_USE_HTTP2 = "use_http2"; +const std::string PCF_CONFIG_STRING_NAME = "name"; +const std::string PCF_CONFIG_STRING_PCC_RULES_DIR = "pcc_rules_directory"; +const std::string PCF_CONFIG_STRING_POLICY_DECISIONS_DIR = + "policy_decisions_directory"; +const std::string PCF_CONFIG_STRING_TRAFFIC_RULES_DIR = + "traffic_rules_directory"; +const std::string PCF_CONFIG_STRING_SBI_IFACE = "local_sbi_interface"; +const std::string PCF_CONFIG_STRING_SBI_IFACE_HTTP2 = + "local_sbi_interface_http2"; +const std::string PCF_CONFIG_STRING_NRF = "nrf"; + +struct support_features { + bool register_nrf; + bool use_http2; +}; class pcf_config { - protected: - private: - int load_interface(const libconfig::Setting& if_cfg, interface_cfg_t& cfg); - int check_directory( - const libconfig::Setting& pcf_cfg, const std::string& path_config, - std::string& path); - public: - /* Reader/writer lock for this configuration */ - std::mutex m_rw_lock; - std::string pid_dir; - unsigned int instance; - std::string fqdn; - interface_cfg_t sbi; - std::string sbi_api_version; - - std::string gateway; - + oai::config::local_sbi_interface sbi; + oai::config::local_sbi_interface sbi_http2; std::string pcc_rules_path; std::string policy_decisions_path; std::string traffic_rules_path; - struct { - bool register_nrf; - bool use_fqdn; - bool use_http2; - - struct { - struct in_addr ipv4_addr; - unsigned int port; - unsigned int http_version; - std::string api_version; - std::string fqdn; - } nrf_addr; - } pcf_features; - - struct { - struct in_addr ipv4_addr; - unsigned int port; - unsigned int http_version; - std::string api_version; - std::string fqdn; - } nrf_addr; + oai::config::sbi_interface nrf_addr; - // struct { - // struct in_addr ipv4_addr; - // unsigned int port; - // unsigned int http_version; - // std::string api_version; - // std::string fqdn; - // } smf_addr; + support_features pcf_features; - pcf_config() : m_rw_lock(), pid_dir(), instance(0) { - sbi.http1_port = 8080; - sbi.http2_port = 80; + explicit pcf_config( + const std::string& config_path, bool log_stdout, bool log_rot_file) + : pcf_features() { + m_cfg = + std::make_unique<oai::config::config>("pcf", log_stdout, log_rot_file); + m_config_path = config_path; }; - void lock() { m_rw_lock.lock(); }; - void unlock() { m_rw_lock.unlock(); }; - int load(const std::string& config_file); - int execute(); + /** + * Initialize the configuration, sets mandatory values for validation, sets + * default values, reads YAML configuration file and validates the + * configuration + * @return True on success + */ + bool init(); + void display(); - static bool parse_config(); + private: + std::unique_ptr<oai::config::config_iface> m_cfg; + std::string m_config_path; + + void set_direct_variables(); }; } // namespace oai::pcf::config - -#endif /* FILE_PCF_CONFIG_HPP_SEEN */ diff --git a/src/pcf_app/pcf_nrf.cpp b/src/pcf_app/pcf_nrf.cpp index 141837bceb3012a469ef1ffb8f5f888dcb814cdd..b000b728d997ac785f9a6a2a03ee9c182fdd296f 100644 --- a/src/pcf_app/pcf_nrf.cpp +++ b/src/pcf_app/pcf_nrf.cpp @@ -58,11 +58,8 @@ pcf_nrf::pcf_nrf(pcf_event& ev) : m_event_sub(ev) { //------------------------------------------------------------------------------ void pcf_nrf::generate_nrf_api_url() { - m_nrf_url = ""; - m_nrf_url.append(conv::toString(pcf_cfg->nrf_addr.ipv4_addr)) - .append(":") - .append(to_string(pcf_cfg->nrf_addr.port)) - .append(NNRF_NFM_BASE) + m_nrf_url = pcf_cfg->nrf_addr.url; + m_nrf_url.append(NNRF_NFM_BASE) .append(pcf_cfg->nrf_addr.api_version) .append(NNRF_DISC_INSTANCES) .append(m_pcf_instance_id); @@ -86,7 +83,7 @@ void pcf_nrf::generate_pcf_profile() { nf_service.service_instance_id = SM_POLICY_API_NAME; nf_service.service_name = SM_POLICY_API_NAME; nf_service_version_t version = {}; - version.api_version_in_uri = pcf_cfg->sbi_api_version; + version.api_version_in_uri = pcf_cfg->sbi.api_version; version.api_full_version = "1.0.0"; // TODO: to be updated nf_service.versions.push_back(version); nf_service.scheme = "http"; @@ -96,8 +93,7 @@ void pcf_nrf::generate_pcf_profile() { // TODO: use only one IP address from cfg for now endpoint.ipv4_address = pcf_cfg->sbi.addr4; endpoint.transport = "TCP"; - endpoint.port = pcf_cfg->sbi.http1_port; - if (pcf_cfg->pcf_features.use_http2) endpoint.port = pcf_cfg->sbi.http2_port; + endpoint.port = pcf_cfg->sbi.port; nf_service.ip_endpoints.push_back(endpoint); m_nf_instance_profile.add_nf_service(nf_service);