Commit a2a8402b authored by Stefan Spettel's avatar Stefan Spettel

fix(smf): do not rely on pre-existing upf interfaces and handle snssai/dnn/dnai correctly

Signed-off-by: default avatarStefan Spettel <stefan.spettel@eurecom.fr>
parent 0594e14b
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include <unordered_set>
typedef uint64_t supi64_t; typedef uint64_t supi64_t;
#define SUPI_64_FMT "%" SCNu64 #define SUPI_64_FMT "%" SCNu64
...@@ -361,7 +362,7 @@ typedef struct nf_service_s { ...@@ -361,7 +362,7 @@ typedef struct nf_service_s {
typedef struct dnn_upf_info_item_s { typedef struct dnn_upf_info_item_s {
std::string dnn; std::string dnn;
std::vector<std::string> dnai_list; std::unordered_set<std::string> dnai_list;
// supported from R16.8 // supported from R16.8
std::map<std::string, std::string> dnai_nw_instance_list; std::map<std::string, std::string> dnai_nw_instance_list;
// std::vector<std::string> pdu_session_types // std::vector<std::string> pdu_session_types
...@@ -372,11 +373,19 @@ typedef struct dnn_upf_info_item_s { ...@@ -372,11 +373,19 @@ typedef struct dnn_upf_info_item_s {
dnai_nw_instance_list = d.dnai_nw_instance_list; dnai_nw_instance_list = d.dnai_nw_instance_list;
return *this; return *this;
} }
bool operator==(const dnn_upf_info_item_s& s) const { return dnn == s.dnn; }
size_t operator()(const dnn_upf_info_item_s&) const {
return std::hash<std::string>()(dnn);
}
} dnn_upf_info_item_t; } dnn_upf_info_item_t;
typedef struct snssai_upf_info_item_s { typedef struct snssai_upf_info_item_s {
snssai_t snssai; snssai_t snssai;
std::vector<dnn_upf_info_item_t> dnn_upf_info_list; std::unordered_set<dnn_upf_info_item_t, dnn_upf_info_item_t>
dnn_upf_info_list;
snssai_upf_info_item_s& operator=(const snssai_upf_info_item_s& s) { snssai_upf_info_item_s& operator=(const snssai_upf_info_item_s& s) {
snssai = s.snssai; snssai = s.snssai;
...@@ -384,6 +393,14 @@ typedef struct snssai_upf_info_item_s { ...@@ -384,6 +393,14 @@ typedef struct snssai_upf_info_item_s {
return *this; return *this;
} }
bool operator==(const snssai_upf_info_item_s& s) const {
return snssai == s.snssai;
}
size_t operator()(const snssai_upf_info_item_s&) const {
return snssai.operator()(snssai);
}
} snssai_upf_info_item_t; } snssai_upf_info_item_t;
typedef struct interface_upf_info_item_s { typedef struct interface_upf_info_item_s {
......
...@@ -44,53 +44,89 @@ extern itti_mw* itti_inst; ...@@ -44,53 +44,89 @@ extern itti_mw* itti_inst;
extern smf_n4* smf_n4_inst; extern smf_n4* smf_n4_inst;
extern smf_config smf_cfg; extern smf_config smf_cfg;
edge edge::from_upf_info(const upf_info_t& upf_info) {
edge e;
snssai_upf_info_item_s snssai_item;
for (const auto& snssai : upf_info.snssai_upf_info_list) {
snssai_item.snssai = snssai.snssai;
snssai_item.dnn_upf_info_list = snssai.dnn_upf_info_list;
e.snssai_dnns.insert(snssai);
}
return e;
}
edge edge::from_upf_info( edge edge::from_upf_info(
const upf_info_t& upf_info, const interface_upf_info_item_t& interface) { const upf_info_t& upf_info, const interface_upf_info_item_t& interface) {
edge e; edge e;
e.type = pfcp_association::iface_type_from_string(interface.interface_type); e.type = pfcp_association::iface_type_from_string(interface.interface_type);
e.nw_instance = interface.network_instance; e.nw_instance = interface.network_instance;
for (const auto& snssai : upf_info.snssai_upf_info_list) {
for (const auto& dnn : snssai.dnn_upf_info_list) {
// Stefan: I would say when a DNAI and a matching NW INSTANCE LIST is
// there, that means that for this interface only this specific SNSSAI and
// DNN combination counts still, to support non_DNAI option, just add all
// DNNs/SNSSAIs here in that case
if (dnn.dnai_list.empty()) {
e.dnns.insert(dnn.dnn);
e.snssais.insert(snssai.snssai);
}
for (const auto& dnai : dnn.dnai_list) {
auto dnai_nw_instance = dnn.dnai_nw_instance_list.find(dnai);
if (dnai_nw_instance != dnn.dnai_nw_instance_list.end()) { // we filter out the DNAIs which do not map to the given NW interface
if (dnai_nw_instance->second == e.nw_instance) { for (const auto& snssai_item : upf_info.snssai_upf_info_list) {
e.dnai = dnai; snssai_upf_info_item_s new_snssai_item;
e.dnns.insert(dnn.dnn); new_snssai_item.snssai = snssai_item.snssai;
e.snssais.insert(snssai.snssai);
for (const auto& dnn_item : snssai_item.dnn_upf_info_list) {
dnn_upf_info_item_s new_dnn_item;
new_dnn_item.dnn = dnn_item.dnn;
if (!dnn_item.dnai_list.empty() &&
!dnn_item.dnai_nw_instance_list.empty()) {
for (const auto& dnai : dnn_item.dnai_list) {
auto dnai_it = dnn_item.dnai_nw_instance_list.find(dnai);
if (dnai_it != dnn_item.dnai_nw_instance_list.end()) {
if (dnai_it->second == e.nw_instance) {
new_dnn_item.dnai_list.insert(dnai);
break;
}
} }
} }
} else {
Logger::smf_app().debug(
"DNAI List or DNAI NW Instance List is empty for this UPF.");
} }
new_snssai_item.dnn_upf_info_list.insert(new_dnn_item);
} }
e.snssai_dnns.insert(new_snssai_item);
} }
return e; return e;
} }
bool edge::serves_network( bool edge::serves_network(
const std::string& dnn, const snssai_t& snssai) const { const std::string& dnn, const snssai_t& snssai,
auto dnn_it = dnns.find(dnn); const std::unordered_set<std::string>& dnais,
if (dnn_it != dnns.end()) { std::string& matched_dnai) const {
auto snssai_it = snssais.find(snssai); // just create a snssai_upf_info_item for fast lookup
if (snssai_it != snssais.end()) { snssai_upf_info_item_s snssai_item;
snssai_item.snssai = snssai;
auto snssai_it = snssai_dnns.find(snssai_item);
if (snssai_it != snssai_dnns.end()) {
// create temp item for fast lookup
dnn_upf_info_item_s dnn_item;
dnn_item.dnn = dnn;
auto dnn_it = snssai_it->dnn_upf_info_list.find(dnn_item);
if (dnn_it != snssai_it->dnn_upf_info_list.end()) {
// should be only 1 DNAI
for (const auto& dnai : dnn_it->dnai_list) {
// O(1)
auto found_dnai = dnais.find(dnai);
if (found_dnai != dnais.end()) {
matched_dnai = dnai;
return true; return true;
} }
} }
}
}
return false; return false;
} }
bool edge::serves_network( bool edge::serves_network(
const std::string& dnn, const snssai_t& snssai, const std::string& dnn, const snssai_t& snssai) const {
const std::string& dnai_in) const { std::unordered_set<string> set;
return dnai == dnai_in && serves_network(dnn, snssai); std::string s;
return serves_network(dnn, snssai, set, s);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
...@@ -177,6 +213,17 @@ bool pfcp_association::find_interface_edge( ...@@ -177,6 +213,17 @@ bool pfcp_association::find_interface_edge(
edges.emplace_back(edge::from_upf_info(upf_info, iface)); edges.emplace_back(edge::from_upf_info(upf_info, iface));
} }
} }
// Because interfaceUpfInfoList is optional in TS 29.510 (why even?), we
// just guess that this UPF has a N3 or N6 interface
if (upf_info.interface_upf_info_list.empty()) {
Logger::smf_app().info(
"UPF Interface list ist empty: Assume that the UPF has a N3 and a N6 "
"interface.");
edge e = edge::from_upf_info(upf_info);
e.type = type_match;
edges.emplace_back(e);
}
return !edges.empty(); return !edges.empty();
} }
...@@ -444,8 +491,6 @@ bool pfcp_associations::get_association( ...@@ -444,8 +491,6 @@ bool pfcp_associations::get_association(
auto association = associations_graph.get_association(hash_node_id); auto association = associations_graph.get_association(hash_node_id);
if (!association) { if (!association) {
// TODO verify if this is still necessary with the graph
// We didn't find association, may be because hash map is made with // We didn't find association, may be because hash map is made with
// node_id_type FQDN // node_id_type FQDN
if (node_id.node_id_type == pfcp::NODE_ID_TYPE_IPV4_ADDRESS) { if (node_id.node_id_type == pfcp::NODE_ID_TYPE_IPV4_ADDRESS) {
...@@ -677,7 +722,7 @@ void upf_graph::insert_into_graph(const std::shared_ptr<pfcp_association>& sa) { ...@@ -677,7 +722,7 @@ void upf_graph::insert_into_graph(const std::shared_ptr<pfcp_association>& sa) {
"Cannot connect UPF to other nodes in the graph as it has no " "Cannot connect UPF to other nodes in the graph as it has no "
"profile, " "profile, "
"just add the node"); "just add the node");
Logger::smf_app().warn("Assume that the UPF has a N3 and a N6 interface."); Logger::smf_app().info("Assume that the UPF has a N3 and a N6 interface.");
edge n3_edge; edge n3_edge;
n3_edge.type = iface_type::N3; n3_edge.type = iface_type::N3;
...@@ -1225,13 +1270,13 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes( ...@@ -1225,13 +1270,13 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes(
std::unordered_set<uint32_t> precedences; std::unordered_set<uint32_t> precedences;
// std::shared_ptr<upf_graph> correct_sub_graph_ptr; // std::shared_ptr<upf_graph> correct_sub_graph_ptr;
std::set<std::string> dnais_from_all_rules; std::unordered_set<std::string> dnais_from_all_rules;
std::shared_ptr<upf_graph> sub_graph_ptr; std::shared_ptr<upf_graph> sub_graph_ptr;
// run DFS for each PCC rule, get different graphs and merge them // run DFS for each PCC rule, get different graphs and merge them
for (const auto& rule : pcc_rules) { for (const auto& rule : pcc_rules) {
std::set<std::string> dnais; std::unordered_set<std::string> dnais;
if (!rule.second.getRefTcData().empty()) { if (!rule.second.getRefTcData().empty()) {
// we just take the first traffic control, as defined in the standard // we just take the first traffic control, as defined in the standard
// see Note 1 in table 5.6.2.6-1 in TS29.512 // see Note 1 in table 5.6.2.6-1 in TS29.512
...@@ -1283,6 +1328,7 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes( ...@@ -1283,6 +1328,7 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes(
for (const auto& edge : node.second) { for (const auto& edge : node.second) {
if (edge.type == iface_type::N3) { if (edge.type == iface_type::N3) {
has_n3 = true; has_n3 = true;
break;
} }
} }
...@@ -1317,9 +1363,11 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes( ...@@ -1317,9 +1363,11 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes(
} }
// Now we verify the merged graph // Now we verify the merged graph
if (sub_graph_ptr) {
sub_graph_ptr->set_dfs_selection_criteria( sub_graph_ptr->set_dfs_selection_criteria(
dnais_from_all_rules, dfs_flow_description, dfs_precedence, snssai, dnn); dnais_from_all_rules, dfs_flow_description, dfs_precedence, snssai,
dnn);
}
if (sub_graph_ptr && sub_graph_ptr->verify()) { if (sub_graph_ptr && sub_graph_ptr->verify()) {
Logger::smf_app().info("Dynamic UPF selection successful."); Logger::smf_app().info("Dynamic UPF selection successful.");
sub_graph_ptr->print_graph(); sub_graph_ptr->print_graph();
...@@ -1342,7 +1390,7 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes( ...@@ -1342,7 +1390,7 @@ std::shared_ptr<upf_graph> upf_graph::select_upf_nodes(
bool upf_graph::verify() { bool upf_graph::verify() {
int access_count = 0; int access_count = 0;
bool has_exit = false; bool has_exit = false;
std::set<std::string> all_dnais_in_graph; std::unordered_set<std::string> all_dnais_in_graph;
for (const auto& node : adjacency_list) { for (const auto& node : adjacency_list) {
for (const auto& edge : node.second) { for (const auto& edge : node.second) {
if (edge.type == iface_type::N3) { if (edge.type == iface_type::N3) {
...@@ -1350,7 +1398,7 @@ bool upf_graph::verify() { ...@@ -1350,7 +1398,7 @@ bool upf_graph::verify() {
} else if (edge.type == iface_type::N6) { } else if (edge.type == iface_type::N6) {
has_exit = true; has_exit = true;
} }
all_dnais_in_graph.insert(edge.dnai); all_dnais_in_graph.insert(edge.used_dnai);
} }
} }
...@@ -1407,7 +1455,7 @@ bool upf_graph::verify() { ...@@ -1407,7 +1455,7 @@ bool upf_graph::verify() {
return false; return false;
} }
std::string upf_graph::get_dnai_list(const std::set<string>& dnais) { std::string upf_graph::get_dnai_list(const std::unordered_set<string>& dnais) {
std::string out; std::string out;
for (const auto& dnai : dnais) { for (const auto& dnai : dnais) {
...@@ -1420,8 +1468,9 @@ std::string upf_graph::get_dnai_list(const std::set<string>& dnais) { ...@@ -1420,8 +1468,9 @@ std::string upf_graph::get_dnai_list(const std::set<string>& dnais) {
} }
void upf_graph::set_dfs_selection_criteria( void upf_graph::set_dfs_selection_criteria(
const std::set<std::string>& all_dnais, const std::string& flow_description, const std::unordered_set<std::string>& all_dnais,
uint32_t precedence, const snssai_t& snssai, const std::string& dnn) { const std::string& flow_description, uint32_t precedence,
const snssai_t& snssai, const std::string& dnn) {
dfs_all_dnais = all_dnais; dfs_all_dnais = all_dnais;
dfs_flow_description = flow_description; dfs_flow_description = flow_description;
dfs_precedence = precedence; dfs_precedence = precedence;
...@@ -1459,13 +1508,13 @@ void upf_graph::create_subgraph_dfs( ...@@ -1459,13 +1508,13 @@ void upf_graph::create_subgraph_dfs(
// DFS: Go through all edges and check if the UPF serves one of the DNAIs // DFS: Go through all edges and check if the UPF serves one of the DNAIs
// from the PCC rule // from the PCC rule
for (auto edge_it : node_it->second) { for (auto edge_it : node_it->second) {
auto dnai_it = dfs_all_dnais.find(edge_it.dnai); std::string found_dnai;
if (dnai_it == dfs_all_dnais.end()) { if (!edge_it.serves_network(
continue; // do not consider this edge, it does not serve DNAI dfs_dnn, dfs_snssai, dfs_all_dnais, found_dnai)) {
} continue; // do not consider this edge, does not serve DNN or SNSSAI or
if (!edge_it.serves_network(dfs_dnn, dfs_snssai)) { // any DNAI
continue; // do not consider this edge, does not serve DNN or SNSSAI
} }
edge_it.used_dnai = found_dnai;
edge_it.flow_description = dfs_flow_description; edge_it.flow_description = dfs_flow_description;
// TODO move this precedence to the QOS FLOW? // TODO move this precedence to the QOS FLOW?
...@@ -1498,6 +1547,15 @@ void upf_graph::create_subgraph_dfs( ...@@ -1498,6 +1547,15 @@ void upf_graph::create_subgraph_dfs(
if (edge_edge.association == node_it->first) { if (edge_edge.association == node_it->first) {
dst_src = edge_edge; dst_src = edge_edge;
dst_src.uplink = false; dst_src.uplink = false;
std::string used_dnai;
if (!edge_edge.serves_network(
dfs_dnn, dfs_snssai, dfs_all_dnais, used_dnai)) {
Logger::smf_app().error(
"Back-Edge in DFS does not serve network. check your "
"configuration");
break;
}
dst_src.used_dnai = used_dnai;
} }
} }
sub_graph->add_upf_graph_edge( sub_graph->add_upf_graph_edge(
......
...@@ -264,9 +264,9 @@ const std::string DEFAULT_FLOW_DESCRIPTION = ...@@ -264,9 +264,9 @@ const std::string DEFAULT_FLOW_DESCRIPTION =
"permit out ip from any to assigned"; "permit out ip from any to assigned";
struct edge { struct edge {
std::string dnai;
// this might need to be replaced by FlowInformation model in the future, but // this might need to be replaced by FlowInformation model in the future, but
// for now we know this is always uplink // for now we know this is always uplink
std::string used_dnai;
std::string flow_description; std::string flow_description;
unsigned int precedence = 0; unsigned int precedence = 0;
std::string nw_instance; std::string nw_instance;
...@@ -275,22 +275,27 @@ struct edge { ...@@ -275,22 +275,27 @@ struct edge {
std::vector<smf_qos_flow> qos_flows; std::vector<smf_qos_flow> qos_flows;
bool n4_sent = false; bool n4_sent = false;
std::shared_ptr<pfcp_association> association; std::shared_ptr<pfcp_association> association;
std::unordered_set<std::string> dnns; // we use parts of the upf_interface here
std::unordered_set<snssai_t, snssai_t> snssais; // the reason why we do is that all the info is split up into several parts
// in the API. It is more time-efficient to have all in one place
std::unordered_set<snssai_upf_info_item_s, snssai_upf_info_item_s>
snssai_dnns;
static edge from_upf_info( static edge from_upf_info(
const upf_info_t& upf_info, const interface_upf_info_item_t& interface); const upf_info_t& upf_info, const interface_upf_info_item_t& interface);
static edge from_upf_info(const upf_info_t& upf_info);
bool serves_network(const std::string& dnn, const snssai_t& snssai) const; bool serves_network(const std::string& dnn, const snssai_t& snssai) const;
bool serves_network( bool serves_network(
const std::string& dnn, const snssai_t& snssai, const std::string& dnn, const snssai_t& snssai,
const std::string& dnai) const; const std::unordered_set<std::string>& dnais,
std::string& found_dnai) const;
bool operator==(const edge& other) const { bool operator==(const edge& other) const {
return dnai == other.dnai && nw_instance == other.nw_instance && return nw_instance == other.nw_instance && type == other.type &&
type == other.type && uplink == other.uplink && uplink == other.uplink && association == other.association;
association == other.association;
} }
[[nodiscard]] std::string to_string() const { [[nodiscard]] std::string to_string() const {
...@@ -330,7 +335,7 @@ class upf_graph { ...@@ -330,7 +335,7 @@ class upf_graph {
smf_qos_flow qos_flow_asynch; smf_qos_flow qos_flow_asynch;
// normal DFS temporary values // normal DFS temporary values
std::set<std::string> dfs_all_dnais; std::unordered_set<std::string> dfs_all_dnais;
std::string dfs_flow_description; std::string dfs_flow_description;
snssai_t dfs_snssai; snssai_t dfs_snssai;
std::string dfs_dnn; std::string dfs_dnn;
...@@ -376,7 +381,7 @@ class upf_graph { ...@@ -376,7 +381,7 @@ class upf_graph {
* @param dnn The DNN to match against * @param dnn The DNN to match against
*/ */
void set_dfs_selection_criteria( void set_dfs_selection_criteria(
const std::set<std::string>& all_dnais, const std::unordered_set<std::string>& all_dnais,
const std::string& flow_description, uint32_t precedence, const std::string& flow_description, uint32_t precedence,
const snssai_t& snssai, const std::string& dnn); const snssai_t& snssai, const std::string& dnn);
...@@ -409,7 +414,7 @@ class upf_graph { ...@@ -409,7 +414,7 @@ class upf_graph {
* @param dnais * @param dnais
* @return * @return
*/ */
static std::string get_dnai_list(const std::set<string>& dnais); static std::string get_dnai_list(const std::unordered_set<string>& dnais);
public: public:
upf_graph() : adjacency_list(), visited_asynch(){}; upf_graph() : adjacency_list(), visited_asynch(){};
......
...@@ -652,14 +652,14 @@ void upf_profile::from_json(const nlohmann::json& data) { ...@@ -652,14 +652,14 @@ void upf_profile::from_json(const nlohmann::json& data) {
} }
if (d.find("dnaiList") != d.end()) { if (d.find("dnaiList") != d.end()) {
dnn_item.dnai_list = dnn_item.dnai_list =
d["dnaiList"].get<std::vector<std::string>>(); d["dnaiList"].get<std::unordered_set<std::string>>();
} }
if (d.find("dnaiNwInstanceList") != d.end()) { if (d.find("dnaiNwInstanceList") != d.end()) {
dnn_item.dnai_nw_instance_list = dnn_item.dnai_nw_instance_list =
d["dnaiNwInstanceList"] d["dnaiNwInstanceList"]
.get<std::map<std::string, std::string>>(); .get<std::map<std::string, std::string>>();
} }
upf_info_item.dnn_upf_info_list.push_back(dnn_item); upf_info_item.dnn_upf_info_list.insert(dnn_item);
} }
} }
upf_info.snssai_upf_info_list.push_back(upf_info_item); upf_info.snssai_upf_info_list.push_back(upf_info_item);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment