Commit 97ccc2ed authored by Tien-Thinh Nguyen's avatar Tien-Thinh Nguyen

First version for AMF re-allocation procedure

parent 8a310228
......@@ -772,4 +772,11 @@ int amf_config::load_interface(
return RETURNok;
}
//------------------------------------------------------------------------------
std::string amf_config::get_nrf_nf_discovery_service_uri() {
return std::string(inet_ntoa(*((struct in_addr*) &nrf_addr.ipv4_addr))) +
":" + std::to_string(nrf_addr.port) + "/nnrf-disc/" +
nrf_addr.api_version + "/nf-instances";
}
} // namespace config
......@@ -205,6 +205,7 @@ class amf_config {
~amf_config();
int load(const std::string& config_file);
int load_interface(const Setting& if_cfg, interface_cfg_t& cfg);
std::string get_nrf_nf_discovery_service_uri();
void display();
unsigned int instance;
......@@ -236,31 +237,7 @@ class amf_config {
bool use_fqdn_dns;
bool use_http2;
} support_features;
/*
struct {
struct in_addr ipv4_addr;
unsigned int port;
std::string api_version;
} nrf_addr;
struct {
struct in_addr ipv4_addr;
unsigned int port;
std::string api_version;
} ausf_addr;
struct {
struct in_addr ipv4_addr;
unsigned int port;
std::string api_version;
} udm_addr;
struct {
struct in_addr ipv4_addr;
unsigned int port;
std::string api_version;
} nssf_addr;
*/
nf_addr_t nrf_addr;
nf_addr_t ausf_addr;
nf_addr_t udm_addr;
......
......@@ -939,6 +939,8 @@ void amf_n1::registration_request_handle(
std::make_unique<RegistrationRequest>();
regReq->decodefrombuffer(nullptr, (uint8_t*) bdata(reg), blength(reg));
nc->registration_request = blk2bstr((uint8_t*) bdata(reg), blength(reg));
nc->registration_request_is_set = true;
bdestroy(reg); // free buffer
// Find UE context
......@@ -3993,14 +3995,142 @@ bool amf_n1::get_target_amf(
authorized_network_slice_info) {
// Get Target AMF from AuthorizedNetworkSliceInfo
std::string target_amf_set = {};
std::string nrf_amf_set = {}; // The URI of NRF NFDiscovery Service to query
// the list of AMF candidates
if (authorized_network_slice_info.targetAmfSetIsSet()) {
target_amf_set = authorized_network_slice_info.getTargetAmfSet();
if (authorized_network_slice_info.nrfAmfSetIsSet()) {
nrf_amf_set = authorized_network_slice_info.getNrfAmfSet();
}
} else {
return false;
}
std::vector<std::string> candidate_amf_list;
if (authorized_network_slice_info.candidateAmfListIsSet()) {
candidate_amf_list = authorized_network_slice_info.getCandidateAmfList();
// TODO:
}
if (amf_cfg.support_features
.enable_smf_selection) { // TODO: define new option for external NRF
// use NRF's URI from conf file if not available
if (nrf_amf_set.empty()) {
nrf_amf_set = amf_cfg.get_nrf_nf_discovery_service_uri();
}
// Get list of AMF candidates from NRF
std::shared_ptr<itti_n11_nf_instance_discovery> itti_msg =
std::make_shared<itti_n11_nf_instance_discovery>(
TASK_AMF_N1, TASK_AMF_N11);
// Generate a promise and associate this promise to the ITTI message
uint32_t promise_id = amf_app_inst->generate_promise_id();
Logger::amf_n1().debug("Promise ID generated %d", promise_id);
boost::shared_ptr<boost::promise<nlohmann::json>> p =
boost::make_shared<boost::promise<nlohmann::json>>();
boost::shared_future<nlohmann::json> f = p->get_future();
amf_app_inst->add_promise(promise_id, p);
itti_msg->http_version = 1;
itti_msg->target_amf_set = target_amf_set;
itti_msg->target_amf_set_is_set = true;
itti_msg->promise_id = promise_id;
itti_msg->target_nf_type = "AMF";
int ret = itti_inst->send_msg(itti_msg);
if (0 != ret) {
Logger::ngap().error(
"Could not send ITTI message %s to task TASK_AMF_N11",
itti_msg->get_msg_name());
}
bool result = false;
boost::future_status status = {};
// wait for timeout or ready
status = f.wait_for(boost::chrono::milliseconds(FUTURE_STATUS_TIMEOUT_MS));
if (status == boost::future_status::ready) {
assert(f.is_ready());
assert(f.has_value());
assert(!f.has_exception());
// Wait for the result from NRF
nlohmann::json amf_candidate_list = f.get();
if (!amf_candidate_list.empty()) {
Logger::ngap().debug(
"Got List of AMF candidates from NRF: %s",
amf_candidate_list.dump().c_str());
// TODO: Select an AMF from the candidate list
if (!select_target_amf(nc, target_amf, amf_candidate_list)) {
Logger::ngap().debug(
"Could not select an appropriate AMF from the AMF candidates");
return false;
} else {
Logger::ngap().debug("Candidate AMF: %s", target_amf.c_str());
return true;
}
} else {
Logger::ngap().debug("Could not get List of AMF candidates from NRF");
return false;
}
} else {
Logger::ngap().debug("Could not get List of AMF candidates from NRF");
return false;
}
}
return true;
}
//------------------------------------------------------------------------------
bool amf_n1::select_target_amf(
const std::shared_ptr<nas_context>& nc, std::string& target_amf,
const nlohmann::json& amf_candidate_list) {
bool result = false;
// Process data to obtain the target AMF
if (amf_candidate_list.find("nfInstances") != amf_candidate_list.end()) {
for (auto& it : amf_candidate_list["nfInstances"].items()) {
nlohmann::json instance_json = it.value();
// TODO: do we need to check with sNSSAI?
if (instance_json.find("sNssais") != instance_json.end()) {
// Each S-NSSAI in the Default Single NSSAIs must be in the AMF's Slice
// List
for (auto& s : instance_json["sNssais"].items()) {
nlohmann::json Snssai = s.value(); // TODO: validate NSSAIs
}
}
// for now, just IP addr of AMF of the first NF instance
if (instance_json.find("ipv4Addresses") != instance_json.end()) {
if (instance_json["ipv4Addresses"].size() > 0)
target_amf = instance_json["ipv4Addresses"].at(0).get<std::string>();
result = true;
break;
}
}
}
return result;
}
//------------------------------------------------------------------------------
void amf_n1::send_n1_message_notity(
const std::shared_ptr<nas_context>& nc,
const std::string& target_amf) const {}
const std::string& target_amf) const {
std::shared_ptr<itti_n11_n1_message_notify> itti_msg =
std::make_shared<itti_n11_n1_message_notify>(TASK_AMF_N1, TASK_AMF_N11);
itti_msg->http_version = 1;
if (nc->registration_request_is_set) {
itti_msg->registration_request = nc->registration_request;
}
itti_msg->target_amf_uri = target_amf;
itti_msg->supi = nc->imsi;
int ret = itti_inst->send_msg(itti_msg);
if (0 != ret) {
Logger::ngap().error(
"Could not send ITTI message %s to task TASK_AMF_N11",
itti_msg->get_msg_name());
}
}
......@@ -238,11 +238,13 @@ class amf_n1 {
const oai::amf::model::SliceInfoForRegistration& slice_info,
oai::amf::model::AuthorizedNetworkSliceInfo&
authorized_network_slice_info) const;
bool get_target_amf(
const std::shared_ptr<nas_context>& nc, std::string& target_amf,
const oai::amf::model::AuthorizedNetworkSliceInfo&
authorized_network_slice_info);
bool select_target_amf(
const std::shared_ptr<nas_context>& nc, std::string& target_amf,
const nlohmann::json& amf_candidate_list);
void send_n1_message_notity(
const std::shared_ptr<nas_context>& nc,
......
......@@ -148,6 +148,15 @@ void amf_n11_task(void*) {
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case N11_N1_MESSAGE_NOTIFY: {
Logger::amf_n11().info(
"Receive N1 Message Notify message, "
"handling ...");
itti_n11_n1_message_notify* m =
dynamic_cast<itti_n11_n1_message_notify*>(msg);
amf_n11_inst->handle_itti_message(ref(*m));
} break;
case TERMINATE: {
if (itti_msg_terminate* terminate =
dynamic_cast<itti_msg_terminate*>(msg)) {
......@@ -661,6 +670,38 @@ void amf_n11::handle_itti_message(
return;
}
//------------------------------------------------------------------------------
void amf_n11::handle_itti_message(itti_n11_n1_message_notify& itti_msg) {
Logger::amf_n11().debug(
"Send N1 Message Notify to the target AMF (HTTP version "
"%d)",
itti_msg.http_version);
std::string url = itti_msg.target_amf_uri + "/ue-contexts/" + itti_msg.supi +
"/n1-message-notify";
Logger::amf_n11().debug("Target AMF URI: %s", url.c_str());
nlohmann::json json_data = {};
json_data["n1SmMsg"]["contentId"] = "n1SmMsg";
std::string json_part = json_data.dump();
std::string n1sm_msg = {};
octet_stream_2_hex_stream(
(uint8_t*) bdata(itti_msg.registration_request),
blength(itti_msg.registration_request), n1sm_msg);
uint8_t http_version = 1;
uint32_t response_code = 0;
std::string n2sm_msg = {};
curl_http_client(
url, json_part, n1sm_msg, n2sm_msg, http_version, response_code);
// TODO: handle response
return;
}
//------------------------------------------------------------------------------
bool amf_n11::smf_selection_from_configuration(
std::string& smf_addr, std::string& smf_api_version) {
......@@ -836,7 +877,8 @@ bool amf_n11::discover_smf_from_nsi_info(
curl_global_cleanup();
if (!result) return result;
Logger::amf_n11().debug("NSI Inforation is successfully retrieved from NSSF");
Logger::amf_n11().debug(
"NSI Information is successfully retrieved from NSSF");
if (!discover_smf(
smf_addr, smf_api_version, snssai, plmn, dnn, nrf_addr, nrf_port,
nrf_api_version))
......@@ -1446,6 +1488,172 @@ void amf_n11::curl_http_client(
free_wrapper((void**) &body_data);
}
//------------------------------------------------------------------------------
void amf_n11::curl_http_client(
std::string& remote_uri, std::string& json_data, std::string& n1sm_msg,
std::string& n2sm_msg, uint8_t http_version, uint32_t& response_code,
uint32_t promise_id) {
Logger::amf_n11().debug("Call SMF service: %s", remote_uri.c_str());
uint8_t number_parts = 0;
mime_parser parser = {};
std::string body = {};
bool is_multipart = true;
if ((n1sm_msg.size() > 0) and (n2sm_msg.size() > 0)) {
// prepare the body content for Curl
parser.create_multipart_related_content(
body, json_data, CURL_MIME_BOUNDARY, n1sm_msg, n2sm_msg);
} else if (n1sm_msg.size() > 0) { // only N1 content
// prepare the body content for Curl
parser.create_multipart_related_content(
body, json_data, CURL_MIME_BOUNDARY, n1sm_msg,
multipart_related_content_part_e::NAS);
} else if (n2sm_msg.size() > 0) { // only N2 content
// prepare the body content for Curl
parser.create_multipart_related_content(
body, json_data, CURL_MIME_BOUNDARY, n2sm_msg,
multipart_related_content_part_e::NGAP);
} else {
body = json_data;
is_multipart = false;
}
Logger::amf_n11().debug(
"Send HTTP message to SMF with body %s", body.c_str());
uint32_t str_len = body.length();
char* body_data = (char*) malloc(str_len + 1);
memset(body_data, 0, str_len + 1);
memcpy((void*) body_data, (void*) body.c_str(), str_len);
curl_global_init(CURL_GLOBAL_ALL);
CURL* curl = curl_easy_init();
if (curl) {
CURLcode res = {};
struct curl_slist* headers = nullptr;
std::string content_type = {};
if (is_multipart) {
content_type = "content-type: multipart/related; boundary=" +
std::string(CURL_MIME_BOUNDARY);
} else {
content_type = "content-type: application/json";
}
headers = curl_slist_append(headers, content_type.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, remote_uri.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT_MS);
curl_easy_setopt(curl, CURLOPT_INTERFACE, amf_cfg.n11.if_name.c_str());
if (http_version == 2) {
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
// we use a self-signed test server, skip verification during debugging
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(
curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE);
}
// Response information
long httpCode = {0};
std::unique_ptr<std::string> httpData(new std::string());
std::unique_ptr<std::string> httpHeaderData(new std::string());
// Hook up data handling function
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
curl_easy_setopt(curl, CURLOPT_HEADERDATA, httpHeaderData.get());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.length());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_data);
res = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
// get cause from the response
std::string response = *httpData.get();
std::string json_data_response = {};
std::string n1sm = {};
std::string n2sm = {};
nlohmann::json response_data = {};
bstring n1sm_hex, n2sm_hex;
// clear input
n1sm_msg = {};
n2sm_msg = {};
json_data = {};
Logger::amf_n11().info("Get response with HTTP code (%d)", httpCode);
Logger::amf_n11().info("Response body %s", response.c_str());
response_code = httpCode;
if (static_cast<http_response_codes_e>(httpCode) ==
http_response_codes_e::HTTP_RESPONSE_CODE_0) {
// TODO: should be removed
Logger::amf_n11().error(
"Cannot get response when calling %s", remote_uri.c_str());
// free curl before returning
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
free_wrapper((void**) &body_data);
return;
}
if (response.size() > 0) {
number_parts = parser.parse(response, json_data_response, n1sm, n2sm);
}
if (number_parts == 0) {
json_data_response = response;
}
Logger::amf_n11().info("JSON part %s", json_data_response.c_str());
if ((static_cast<http_response_codes_e>(httpCode) !=
http_response_codes_e::HTTP_RESPONSE_CODE_200_OK) &&
(static_cast<http_response_codes_e>(httpCode) !=
http_response_codes_e::HTTP_RESPONSE_CODE_201_CREATED) &&
(static_cast<http_response_codes_e>(httpCode) !=
http_response_codes_e::HTTP_RESPONSE_CODE_204_NO_CONTENT)) {
// TODO:
} else { // Response with success code
try {
response_data = nlohmann::json::parse(json_data_response);
} catch (nlohmann::json::exception& e) {
Logger::amf_n11().warn("Could not get JSON content from the response");
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
free_wrapper((void**) &body_data);
// TODO:
return;
}
// Transfer N1/N2 to gNB/UE if available
if (number_parts > 1) {
if (n1sm.size() > 0) {
n1sm_msg = n1sm;
}
if (n2sm.size() > 0) {
n2sm_msg = n2sm;
}
}
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
free_wrapper((void**) &body_data);
}
//-----------------------------------------------------------------------------------------------------
void amf_n11::curl_http_client(
std::string remote_uri, std::string method, std::string msg_body,
......
......@@ -64,6 +64,8 @@ class amf_n11 {
void handle_itti_message(
itti_n11_network_slice_selection_information& itti_msg);
void handle_itti_message(itti_n11_n1_message_notify& itti_msg);
void send_pdu_session_update_sm_context_request(
std::string supi, std::shared_ptr<pdu_session_context> psc,
std::string smf_addr, bstring sm_msg, std::string dnn);
......@@ -83,6 +85,11 @@ class amf_n11 {
std::string remote_uri, std::string method, std::string msg_body,
nlohmann::json& response_json, uint8_t http_version = 1);
void curl_http_client(
std::string& remote_uri, std::string& json_data, std::string& n1sm_msg,
std::string& n2sm_msg, uint8_t http_version, uint32_t& response_code,
uint32_t promise_id = 0);
bool discover_smf_from_nsi_info(
std::string& smf_addr, std::string& smf_api_version,
const snssai_t snssai, const plmn_t plmn, const std::string dnn);
......
......@@ -90,7 +90,16 @@ class nas_context {
uint8_t ueSecurityCapEEA;
uint8_t ueSecurityCapEIA;
std::vector<nas::SNSSAI_t> requestedNssai;
std::vector<nas::SNSSAI_t>
requestedNssai; // TODO: update with naming convention
std::vector<nas::SNSSAI_t> allowed_nssai; // in Registration Accept
std::vector<std::pair<bool, nas::SNSSAI_t>> subscribed_snssai;
std::vector<nas::SNSSAI_t> configured_nssai;
// std::vector<nas::SNSSAI_t> default_configured_nssai;
// std::vector<nas::SNSSAI_t> s_nssai; //for Network Slice selection
bstring registration_request; // for AMF re-allocation procedure
bool registration_request_is_set;
std::string serving_network;
bstring auts;
// NAS EP(s)
......
......@@ -88,6 +88,8 @@ typedef enum {
N11_DEREGISTER_NF_INSTANCE,
N11_SLICE_SELECTION_SUBSCRIPTION_DATA,
N11_NETWORK_SLICE_SELECTION_INFORMATION,
N11_NF_INSTANCE_DISCOVERY,
N11_N1_MESSAGE_NOTIFY,
SBI_EVENT_EXPOSURE_REQUEST,
SBI_NOTIFICATION_DATA,
SBI_NOTIFY_SUBSCRIBED_EVENT,
......
......@@ -227,4 +227,33 @@ class itti_n11_network_slice_selection_information : public itti_msg_n11 {
uint32_t promise_id;
};
//-----------------------------------------------------------------------------
class itti_n11_nf_instance_discovery : public itti_msg_n11 {
public:
itti_n11_nf_instance_discovery(const task_id_t orig, const task_id_t dest)
: itti_msg_n11(N11_NF_INSTANCE_DISCOVERY, orig, dest),
target_amf_set_is_set(false),
http_version(1) {}
const char* get_msg_name() { return "N11_NF_INSTANCE_DISCOVERY"; };
uint8_t http_version;
std::string target_amf_set;
bool target_amf_set_is_set;
std::string target_nf_type;
uint32_t promise_id;
};
//-----------------------------------------------------------------------------
class itti_n11_n1_message_notify : public itti_msg_n11 {
public:
itti_n11_n1_message_notify(const task_id_t orig, const task_id_t dest)
: itti_msg_n11(N11_N1_MESSAGE_NOTIFY, orig, dest), http_version(1) {}
const char* get_msg_name() { return "N11_N1_MESSAGE_NOTIFY"; };
uint8_t http_version;
std::string target_amf_uri;
std::string supi;
bstring registration_request;
};
#endif
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