diff --git a/autotest/units/001_one_port/061_nat64stateful/autotest.yaml b/autotest/units/001_one_port/061_nat64stateful/autotest.yaml index 23e87935..2dafdfb2 100644 --- a/autotest/units/001_one_port/061_nat64stateful/autotest.yaml +++ b/autotest/units/001_one_port/061_nat64stateful/autotest.yaml @@ -1,6 +1,11 @@ steps: - ipv4Update: "0.0.0.0/0 -> 200.0.0.1" - ipv6Update: "::/0 -> fe80::1" +- cli_check: | + YANET_FORMAT_COLUMNS=module,source_ipv6_prefixes nat64stateful + module source_ipv6_prefixes + ------ -------------------- + nat0 ::/0 - sendPackets: - port: kni0 send: 001-send.pcap diff --git a/cli/nat64stateful.h b/cli/nat64stateful.h index ffe1412e..e6f186e7 100644 --- a/cli/nat64stateful.h +++ b/cli/nat64stateful.h @@ -17,7 +17,8 @@ void summary() TablePrinter table; table.insert_row("module", "ipv4_pool_size", - "next_module"); + "next_module", + "source_ipv6_prefixes"); for (const auto& [name, nat64stateful] : response) { @@ -29,7 +30,8 @@ void summary() table.insert_row(name, ipv4_pool_size, - nat64stateful.next_module); + nat64stateful.next_module, + nat64stateful.source_ipv6_prefixes); } table.Print(); diff --git a/common/controlplaneconfig.h b/common/controlplaneconfig.h index f0bcae9b..532c234c 100644 --- a/common/controlplaneconfig.h +++ b/common/controlplaneconfig.h @@ -293,7 +293,7 @@ class config_t public: config_t() = default; - SERIALIZABLE(nat64stateful_id, dscp_mark_type, dscp, ipv6_prefixes, ipv4_prefixes, announces, next_module, vrf_lan_name, vrf_wan_name, vrf_lan, vrf_wan); + SERIALIZABLE(nat64stateful_id, dscp_mark_type, dscp, ipv6_prefixes, ipv4_prefixes, announces, next_module, vrf_lan_name, vrf_wan_name, vrf_lan, vrf_wan, source_ipv6_prefixes); public: nat64stateful_id_t nat64stateful_id; @@ -301,6 +301,7 @@ class config_t uint8_t dscp{}; std::vector ipv6_prefixes; std::vector ipv4_prefixes; + std::vector source_ipv6_prefixes{common::ipv6_prefix_default}; std::set announces; std::string vrf_lan_name; std::string vrf_wan_name; diff --git a/controlplane/configconverter.cpp b/controlplane/configconverter.cpp index 62a93ece..ada5e125 100644 --- a/controlplane/configconverter.cpp +++ b/controlplane/configconverter.cpp @@ -1018,10 +1018,16 @@ void config_converter_t::acl_rules_nat64stateful(controlplane::base::acl_t& acl, { const auto& nat64stateful = baseNext.nat64statefuls.at(next_module); - std::set ipv6_prefixes; + std::set destination_ipv6_prefixes; for (const auto& ipv6_prefix : nat64stateful.ipv6_prefixes) { - ipv6_prefixes.emplace(ipv6_prefix); + destination_ipv6_prefixes.emplace(ipv6_prefix); + } + + std::set source_ipv6_prefixes; + for (const auto& ipv6_prefix : nat64stateful.source_ipv6_prefixes) + { + source_ipv6_prefixes.emplace(ipv6_prefix); } std::set ipv4_prefixes; @@ -1038,7 +1044,7 @@ void config_converter_t::acl_rules_nat64stateful(controlplane::base::acl_t& acl, /// ipv6 { - controlplane::base::acl_rule_network_ipv6_t rule_network({common::ipv6_prefix_default}, ipv6_prefixes); + controlplane::base::acl_rule_network_ipv6_t rule_network(source_ipv6_prefixes, destination_ipv6_prefixes); controlplane::base::acl_rule_t rule(rule_network, flow_drop); rule.fragment = {fragState::firstFragment, fragState::notFirstFragment}; @@ -1061,7 +1067,7 @@ void config_converter_t::acl_rules_nat64stateful(controlplane::base::acl_t& acl, { auto flow = convertToFlow(next_module, "lan"); - controlplane::base::acl_rule_network_ipv6_t rule_network({common::ipv6_prefix_default}, ipv6_prefixes); + controlplane::base::acl_rule_network_ipv6_t rule_network(source_ipv6_prefixes, destination_ipv6_prefixes); acl.nextModuleRules.emplace_back(rule_network, controlplane::acl_rule_transport_tcp_any, @@ -1101,7 +1107,7 @@ void config_converter_t::acl_rules_nat64stateful(controlplane::base::acl_t& acl, /// ipv6 { - controlplane::base::acl_rule_network_ipv6_t rule_network({common::ipv6_prefix_default}, ipv6_prefixes); + controlplane::base::acl_rule_network_ipv6_t rule_network(source_ipv6_prefixes, destination_ipv6_prefixes); acl.nextModuleRules.emplace_back(rule_network, flow_drop); } diff --git a/controlplane/configparser.cpp b/controlplane/configparser.cpp index 878025f4..34a9d5b0 100644 --- a/controlplane/configparser.cpp +++ b/controlplane/configparser.cpp @@ -522,6 +522,60 @@ void config_parser_t::loadConfig_nat64stateful(controlplane::base_t& baseNext, nat64stateful.ipv6_prefixes.emplace_back(ipv6_prefix); } + if (exist(moduleJson, "source_ipv6_prefixes")) + { + const auto& source_json = moduleJson["source_ipv6_prefixes"]; + + if (!source_json.is_array()) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: source_ipv6_prefixes must be an array"); + } + if (source_json.empty()) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: source_ipv6_prefixes must not be empty"); + } + + std::vector source_ipv6_prefixes; + for (const auto& prefix_json : source_json) + { + if (!prefix_json.is_string()) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: source_ipv6_prefixes element must be a string"); + } + + const std::string prefix_string = prefix_json.get(); + + const auto slash_pos = prefix_string.find('/'); + if (slash_pos != std::string::npos) + { + const std::string mask_string = prefix_string.substr(slash_pos + 1); + + if (mask_string.empty() || + prefix_string.find('/', slash_pos + 1) != std::string::npos || + mask_string.find_first_not_of("0123456789") != std::string::npos) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: invalid source_ipv6_prefix mask: " + prefix_string); + } + + if (std::stoul(mask_string) > 128) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: source_ipv6_prefix mask out of range [0..128]: " + prefix_string); + } + } + + common::ipv6_prefix_t source_ipv6_prefix(prefix_string); + if (!source_ipv6_prefix.isValid()) + { + throw error_result_t(eResult::invalidConfigurationFile, "nat64stateful: source_ipv6_prefix has host bits set: " + prefix_string); + } + + source_ipv6_prefixes.emplace_back(source_ipv6_prefix); + } + + /// assign only after full validation succeeds; this replaces the default ::/0 + nat64stateful.source_ipv6_prefixes = std::move(source_ipv6_prefixes); + } + for (const auto& prefix_json : moduleJson["ipv4_prefixes"]) { common::ipv4_prefix_t ipv4_prefix(prefix_json.get());