From 41c6bb4681cff7ff510089857319cb71f59fdde2 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Mon, 5 Apr 2021 17:09:39 +0900 Subject: [PATCH] new: HSS Cx interface is added --- configs/volte.yaml.in | 4 +- lib/app/ogs-context.c | 4 + lib/app/ogs-context.h | 3 + lib/core/ogs-3gpp-types.h | 2 +- lib/core/ogs-conv.c | 14 + lib/core/ogs-conv.h | 2 + lib/diameter/common/message.c | 2 - lib/diameter/common/message.h | 18 +- lib/diameter/cx/dict.c | 567 ++++++++++++++ lib/diameter/cx/meson.build | 40 + lib/diameter/cx/message.c | 211 +++++ lib/diameter/cx/message.h | 186 +++++ lib/diameter/cx/ogs-diameter-cx.h | 41 + lib/diameter/meson.build | 1 + lib/diameter/rx/dict.c | 405 ---------- src/hss/hss-context.c | 266 ++++++- src/hss/hss-context.h | 36 +- src/hss/hss-cx-path.c | 999 ++++++++++++++++++++++++ src/hss/hss-fd-path.c | 825 +------------------ src/hss/hss-fd-path.h | 5 + src/hss/hss-s6a-path.c | 828 ++++++++++++++++++++ src/hss/meson.build | 9 +- src/mme/mme-fd-path.c | 82 +- src/pcrf/pcrf-context.c | 8 +- src/pcrf/pcrf-context.h | 10 +- src/pcrf/pcrf-fd-path.c | 34 +- src/pcrf/pcrf-gx-path.c | 20 +- src/pcrf/pcrf-rx-path.c | 20 +- src/smf/fd-path.c | 21 +- tests/common/context.c | 4 + tests/common/context.h | 3 + tests/common/meson.build | 6 +- tests/volte/abts-main.c | 2 + tests/volte/cx-test.c | 350 +++++++++ tests/volte/diameter-cx-path.c | 926 ++++++++++++++++++++++ tests/volte/meson.build | 2 + tests/volte/pcscf-fd-path.c | 53 +- tests/volte/pcscf-fd-path.h | 8 + webui/package-lock.json | 2 +- webui/package.json | 2 +- webui/server/models/profile.js | 2 + webui/server/models/subscriber.js | 2 + webui/src/components/Profile/Edit.js | 34 +- webui/src/components/Profile/View.js | 17 + webui/src/components/Shared/Form.js | 7 +- webui/src/components/Subscriber/Edit.js | 35 +- webui/src/components/Subscriber/View.js | 17 + 47 files changed, 4742 insertions(+), 1393 deletions(-) create mode 100644 lib/diameter/cx/dict.c create mode 100644 lib/diameter/cx/meson.build create mode 100644 lib/diameter/cx/message.c create mode 100644 lib/diameter/cx/message.h create mode 100644 lib/diameter/cx/ogs-diameter-cx.h create mode 100644 src/hss/hss-cx-path.c create mode 100644 src/hss/hss-s6a-path.c create mode 100644 tests/volte/cx-test.c create mode 100644 tests/volte/diameter-cx-path.c diff --git a/configs/volte.yaml.in b/configs/volte.yaml.in index 719306ba8..2fc0d5d49 100644 --- a/configs/volte.yaml.in +++ b/configs/volte.yaml.in @@ -167,6 +167,8 @@ hss: connect: - identity: mme.localdomain addr: 127.0.0.2 + - identity: ims.localdomain + addr: 127.0.0.1 pcrf: freeDiameter: identity: pcrf.localdomain @@ -184,7 +186,7 @@ pcrf: connect: - identity: smf.localdomain addr: 127.0.0.4 - - identity: pcscf.localdomain + - identity: ims.localdomain addr: 127.0.0.1 #nrf: diff --git a/lib/app/ogs-context.c b/lib/app/ogs-context.c index ee1f72404..83e389554 100644 --- a/lib/app/ogs-context.c +++ b/lib/app/ogs-context.c @@ -97,6 +97,10 @@ static void recalculate_pool_size(void) #define MAX_CSMAP_POOL 128 self.pool.csmap = MAX_CSMAP_POOL; /* Num of TAI-LAI Mapping Table */ + +#define MAX_NUM_OF_IMPU 8 + self.pool.impi = self.max.ue; + self.pool.impu = self.pool.impi * MAX_NUM_OF_IMPU; } static void regenerate_all_timer_duration(void) diff --git a/lib/app/ogs-context.h b/lib/app/ogs-context.h index 85deb5ed1..f6045a93a 100644 --- a/lib/app/ogs-context.h +++ b/lib/app/ogs-context.h @@ -126,6 +126,9 @@ typedef struct ogs_app_context_s { uint64_t gtp_node; uint64_t pfcp_xact; uint64_t pfcp_node; + + uint64_t impi; + uint64_t impu; } pool; struct { diff --git a/lib/core/ogs-3gpp-types.h b/lib/core/ogs-3gpp-types.h index 195499b0c..ba7c3d342 100644 --- a/lib/core/ogs-3gpp-types.h +++ b/lib/core/ogs-3gpp-types.h @@ -604,7 +604,7 @@ typedef struct ogs_subscription_data_s { int num_of_slice; ogs_slice_data_t slice[OGS_MAX_NUM_OF_SLICE]; -#define OGS_MAX_NUM_OF_MSISDN 4 +#define OGS_MAX_NUM_OF_MSISDN 2 int num_of_msisdn; struct { uint8_t buf[OGS_MAX_MSISDN_LEN]; diff --git a/lib/core/ogs-conv.c b/lib/core/ogs-conv.c index ef4638f9f..d79e007e6 100644 --- a/lib/core/ogs-conv.c +++ b/lib/core/ogs-conv.c @@ -230,3 +230,17 @@ uint64_t ogs_uint64_from_string(char *str) return x; } + +void ogs_extract_digit_from_string(char *digit, char *string) +{ + ogs_assert(string); + ogs_assert(digit); + + while(*string) { + if (*string >= '0' && *string <= '9') + *digit++ = *string; + string++; + } + + *digit = 0; +} diff --git a/lib/core/ogs-conv.h b/lib/core/ogs-conv.h index 68b96d6b7..ca516b92e 100644 --- a/lib/core/ogs-conv.h +++ b/lib/core/ogs-conv.h @@ -49,6 +49,8 @@ char *ogs_uint64_to_string(uint64_t x); ogs_uint24_t ogs_uint24_from_string(char *str); uint64_t ogs_uint64_from_string(char *str); +void ogs_extract_digit_from_string(char *digit, char *string); + #ifdef __cplusplus } #endif diff --git a/lib/diameter/common/message.c b/lib/diameter/common/message.c index 91cb6850d..4b6ee0901 100644 --- a/lib/diameter/common/message.c +++ b/lib/diameter/common/message.c @@ -22,8 +22,6 @@ #define CHECK_dict_search( _type, _criteria, _what, _result ) \ CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, (_type), (_criteria), (_what), (_result), ENOENT) ); -#define OGS_3GPP_VENDOR_ID 10415 - struct dict_object *ogs_diam_session_id = NULL; struct dict_object *ogs_diam_origin_host = NULL; struct dict_object *ogs_diam_origin_realm = NULL; diff --git a/lib/diameter/common/message.h b/lib/diameter/common/message.h index dea82bfaa..e99245586 100644 --- a/lib/diameter/common/message.h +++ b/lib/diameter/common/message.h @@ -28,16 +28,18 @@ extern "C" { #endif -#define OGS_DIAM_AVP_CODE_FRAME_IP_ADDRESS 8 -#define OGS_DIAM_AVP_CODE_FRAME_IPV6_PREFIX 97 +#define OGS_3GPP_VENDOR_ID 10415 + +#define OGS_DIAM_AVP_CODE_FRAME_IP_ADDRESS 8 +#define OGS_DIAM_AVP_CODE_FRAME_IPV6_PREFIX 97 /* Result-Code AVP */ -#define OGS_DIAM_UNKNOWN_PEER 3010 -#define OGS_DIAM_AVP_UNSUPPORTED 5001 -#define OGS_DIAM_UNKNOWN_SESSION_ID 5002 -#define OGS_DIAM_AUTHORIZATION_REJECTED 5003 -#define OGS_DIAM_MISSING_AVP 5004 -#define OGS_DIAM_INVALID_AVP_VALUE 5005 +#define OGS_DIAM_UNKNOWN_PEER 3010 +#define OGS_DIAM_AVP_UNSUPPORTED 5001 +#define OGS_DIAM_UNKNOWN_SESSION_ID 5002 +#define OGS_DIAM_AUTHORIZATION_REJECTED 5003 +#define OGS_DIAM_MISSING_AVP 5004 +#define OGS_DIAM_INVALID_AVP_VALUE 5005 extern struct dict_object *ogs_diam_session_id; extern struct dict_object *ogs_diam_origin_host; diff --git a/lib/diameter/cx/dict.c b/lib/diameter/cx/dict.c new file mode 100644 index 000000000..f4119d29d --- /dev/null +++ b/lib/diameter/cx/dict.c @@ -0,0 +1,567 @@ +/********************************************************************************************************* + * Software License Agreement (BSD License) * + * Author: Thomas Klausner * + * * + * Copyright (c) 2013, Thomas Klausner * + * All rights reserved. * + * * + * Written under contract by nfotex IT GmbH, http://nfotex.com/ * + * * + * Redistribution and use of this software in source and binary forms, with or without modification, are * + * permitted provided that the following conditions are met: * + * * + * * Redistributions of source code must retain the above * + * copyright notice, this list of conditions and the * + * following disclaimer. * + * * + * * Redistributions in binary form must reproduce the above * + * copyright notice, this list of conditions and the * + * following disclaimer in the documentation and/or other * + * materials provided with the distribution. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + *********************************************************************************************************/ + +/* + * Dictionary definitions for objects specified for DCCA by 3GPP. + * + * This extensions contains a lot of AVPs from various 3GPP standards + * documents, and some rules for the grouped AVPs described therein. + * + * This extension does not contain ALL AVPs described by 3GPP, but + * quite a big number of them. + * + * When extending the AVPs, please edit dict_rx.org instead and + * create pastable code with contrib/tools/org_to_fd.pl. + * + * Some points of consideration: + * 1. This dictionary could be split up per document. + * + * + pro: you can only load the AVPs/Rules you're interested in -> + * smaller memory size + * + * - con: the documents use AVPs from each other A LOT, so setting the + * dependencies correctly will be annoying + * + * - con: you need to load all of them as extensions + * + * 2. This dictionary contains ONE AVP in the "3GPP2" vendor space, + * since I found it wasteful to write a separate dictionary just for + * one AVP. Also, it is defined in a 3GPP document. + * + * 3. While there are quite a number of rules here already, many more + * are missing. I've only added rules for those grouped AVPs or + * commands in which I was concretely interested so far; many more + * will need to be added to make this complete. + * + * That being said, I hope this will be useful for you. + * + */ + + +/* + * Some comments on the 3GPP Standards documents themselves: + * + * 1. It would be good if 29.061 was reviewed to check for each AVP if + * it is Mandatory or not. The data currently in the document does not + * match what was in the previous version of the freeDiameter + * extension (the one that existedbefore I rewrote it) or what I saw + * so far. IIRC, even the table and the document contradict each + * other. The AVP table is also missing an entry for + * "External-Identifier", 28. + * + * 2. 29.140 has conflicting AVP names with other documents: + * - Sequence-Number is also in 32.329 + * - Recipient-Address is also in 32.299 + * - Status is also in 32.299 + * + * 3. 29.229 has name conflict with 29.329 about User-Data (different + * AVP code 702, instead of 606) -- the weird thing is, the latter + * uses some AVPs from the former, but not this one. +*/ +#include + + +/* The content of this file follows the same structure as dict_base_proto.c */ + +#define CHECK_dict_new( _type, _data, _parent, _ref ) \ + CHECK_FCT( fd_dict_new( fd_g_config->cnf_dict, (_type), (_data), (_parent), (_ref)) ); + +#define CHECK_dict_search( _type, _criteria, _what, _result ) \ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, (_type), (_criteria), (_what), (_result), ENOENT) ); + +struct local_rules_definition { + struct dict_avp_request avp_vendor_plus_name; + enum rule_position position; + int min; + int max; +}; + +#define RULE_ORDER( _position ) ((((_position) == RULE_FIXED_HEAD) || ((_position) == RULE_FIXED_TAIL)) ? 1 : 0 ) + +/* Attention! This version of the macro uses AVP_BY_NAME_AND_VENDOR, in contrast to most other copies! */ +#define PARSE_loc_rules( _rulearray, _parent) { \ + int __ar; \ + for (__ar=0; __ar < sizeof(_rulearray) / sizeof((_rulearray)[0]); __ar++) { \ + struct dict_rule_data __data = { NULL, \ + (_rulearray)[__ar].position, \ + 0, \ + (_rulearray)[__ar].min, \ + (_rulearray)[__ar].max}; \ + __data.rule_order = RULE_ORDER(__data.rule_position); \ + CHECK_FCT( fd_dict_search( \ + fd_g_config->cnf_dict, \ + DICT_AVP, \ + AVP_BY_NAME_AND_VENDOR, \ + &(_rulearray)[__ar].avp_vendor_plus_name, \ + &__data.rule_avp, 0 ) ); \ + if ( !__data.rule_avp ) { \ + TRACE_DEBUG(INFO, "AVP Not found: '%s'", (_rulearray)[__ar].avp_vendor_plus_name.avp_name); \ + return ENOENT; \ + } \ + CHECK_FCT_DO( fd_dict_new( fd_g_config->cnf_dict, DICT_RULE, &__data, _parent, NULL), \ + { \ + TRACE_DEBUG(INFO, "Error on rule with AVP '%s'", \ + (_rulearray)[__ar].avp_vendor_plus_name.avp_name); \ + return EINVAL; \ + } ); \ + } \ +} + +#define enumval_def_u32( _val_, _str_ ) \ + { _str_, { .u32 = _val_ }} + +#define enumval_def_os( _len_, _val_, _str_ ) \ + { _str_, { .os = { .data = (unsigned char *)_val_, .len = _len_ }}} + + +int ogs_dict_cx_entry(char *conffile) +{ + /* Applications section */ + { + /* Create the vendors */ + + { + struct dict_object * vendor; + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "3GPP", &vendor, ENOENT)); + struct dict_application_data app_data = { 16777216, "Cx" }; + CHECK_FCT(fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, &app_data, vendor, NULL)); + } + + } + + /* Command section */ + + { + struct dict_object *cx; + CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Cx", &cx, ENOENT)); + + /* User-Authorization-Request (UAR) Command */ + { + struct dict_object* cmd_uar; + struct dict_cmd_data data = { + 300, /* Code */ + "3GPP/User-Authorization-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Visited-Network-Identifier" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "User-Authorization-Type" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "UAR-Flags" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_uar); + PARSE_loc_rules(rules, cmd_uar); + } + + /* User-Authorization-Answer (UAA) Command */ + { + struct dict_object* cmd_uaa; + struct dict_cmd_data data = { + 300, /* Code */ + "3GPP/User-Authorization-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Capabilities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_uaa); + PARSE_loc_rules(rules, cmd_uaa); + } + + /* Location-Info-Request (LIR) Command */ + { + struct dict_object* cmd_lir; + struct dict_cmd_data data = { + 302, /* Code */ + "3GPP/Location-Info-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Originating-Request" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "User-Authorization-Type" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Session-Priority" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_lir); + PARSE_loc_rules(rules, cmd_lir); + } + + /* Location-Info-Answer (LIA) Command */ + { + struct dict_object* cmd_lia; + struct dict_cmd_data data = { + 302, /* Code */ + "3GPP/Location-Info-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Capabilities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "LIA-Flags" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_lia); + PARSE_loc_rules(rules, cmd_lia); + } + + /* Multimedia-Auth-Request (MAR) Command */ + { + struct dict_object* cmd_mar; + struct dict_cmd_data data = { + 303, /* Code */ + "3GPP/Multimedia-Auth-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SIP-Number-Auth-Items" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 }, + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_mar); + PARSE_loc_rules(rules, cmd_mar); + } + + /* Multimedia-Auth-Answer (MAA) Command */ + { + struct dict_object* cmd_maa; + struct dict_cmd_data data = { + 303, /* Code */ + "3GPP/Multimedia-Auth-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SIP-Number-Auth-Items" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_maa); + PARSE_loc_rules(rules, cmd_maa); + } + + /* Server-Assignment-Request (SAR) Command */ + { + struct dict_object* cmd_sar; + struct dict_cmd_data data = { + 301, /* Code */ + "3GPP/Server-Assignment-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Assignment-Type" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "User-Data-Already-Available" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SCSCF-Restoration-Info" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Multiple-Registration-Indication" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Session-Priority" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_sar); + PARSE_loc_rules(rules, cmd_sar); + } + + /* Server-Assignment-Answer (SAA) Command */ + { + struct dict_object* cmd_saa; + struct dict_cmd_data data = { + 301, /* Code */ + "3GPP/Server-Assignment-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "User-Data" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Charging-Information" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Loose-Route-Indication" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SCSCF-Restoration-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Associated-Registered-Identities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Priviledged-Sender-Indication" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_saa); + PARSE_loc_rules(rules, cmd_saa); + } + + /* Registration-Termination-Request (RTR) Command */ + { + struct dict_object* cmd_rtr; + struct dict_cmd_data data = { + 304, /* Code */ + "3GPP/Registration-Termination-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Deregistration-Reason" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_rtr); + PARSE_loc_rules(rules, cmd_rtr); + } + + /* Registration-Termination-Answer (RTA) Command */ + { + struct dict_object* cmd_rta; + struct dict_cmd_data data = { + 304, /* Code */ + "3GPP/Registration-Termination-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "Identity-with-Emergency-Registration" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_rta); + PARSE_loc_rules(rules, cmd_rta); + } + + /* Push-Profile-Request (PPR) Command */ + { + struct dict_object* cmd_ppr; + struct dict_cmd_data data = { + 305, /* Code */ + "3GPP/Push-Profile-Request", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_vendor = 10415, .avp_name = "User-Data" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Charging-Information" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_ppr); + PARSE_loc_rules(rules, cmd_ppr); + } + + /* Push-Profile-Answer (PPA) Command */ + { + struct dict_object* cmd_ppa; + struct dict_cmd_data data = { + 305, /* Code */ + "3GPP/Push-Profile-Answer", /* Name */ + CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ + CMD_FLAG_PROXIABLE /* Fixed flag values */ + }; + + struct local_rules_definition rules[] = + { + { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, + { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, + { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, + { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, + { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, + { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } + }; + + CHECK_dict_new(DICT_COMMAND, &data, cx, &cmd_ppa); + PARSE_loc_rules(rules, cmd_ppa); + } + } + + LOG_D( "Extension 'Dictionary definitions for DCCA 3GPP' initialized"); + return 0; +} + +#if 0 /* modified by acetcom */ +EXTENSION_ENTRY("dict_cx", ogs_dict_cx_entry, "dict_dcca_3gpp"); +#endif diff --git a/lib/diameter/cx/meson.build b/lib/diameter/cx/meson.build new file mode 100644 index 000000000..efb1a2f8b --- /dev/null +++ b/lib/diameter/cx/meson.build @@ -0,0 +1,40 @@ +# Copyright (C) 2019 by Sukchan Lee + +# This file is part of Open5GS. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +libdiameter_cx_sources = files(''' + ogs-diameter-cx.h + + message.h + + dict.c + message.c +'''.split()) + +libdiameter_cx_inc = include_directories('.') + +libdiameter_cx = library('ogsdiameter-cx', + sources : libdiameter_cx_sources, + version : libogslib_version, + c_args : libdiameter_common_cc_flags, + include_directories : libdiameter_cx_inc, + dependencies : libdiameter_common_dep, + install : true) + +libdiameter_cx_dep = declare_dependency( + link_with : libdiameter_cx, + include_directories : libdiameter_cx_inc, + dependencies : libdiameter_common_dep) diff --git a/lib/diameter/cx/message.c b/lib/diameter/cx/message.c new file mode 100644 index 000000000..d6d1c0dbe --- /dev/null +++ b/lib/diameter/cx/message.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ogs-diameter-cx.h" + +#define CHECK_dict_search( _type, _criteria, _what, _result ) \ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, (_type), (_criteria), (_what), (_result), ENOENT) ); + +struct dict_object *ogs_diam_cx_application = NULL; + +struct dict_object *ogs_diam_cx_cmd_uar = NULL; +struct dict_object *ogs_diam_cx_cmd_uaa = NULL; + +struct dict_object *ogs_diam_cx_cmd_mar = NULL; +struct dict_object *ogs_diam_cx_cmd_maa = NULL; + +struct dict_object *ogs_diam_cx_cmd_sar = NULL; +struct dict_object *ogs_diam_cx_cmd_saa = NULL; + +struct dict_object *ogs_diam_cx_public_identity = NULL; +struct dict_object *ogs_diam_cx_visited_network_identifier = NULL; +struct dict_object *ogs_diam_cx_server_name = NULL; + +struct dict_object *ogs_diam_cx_sip_number_auth_items = NULL; +struct dict_object *ogs_diam_cx_sip_item_number = NULL; +struct dict_object *ogs_diam_cx_sip_auth_data_item = NULL; +struct dict_object *ogs_diam_cx_sip_authentication_scheme = NULL; +struct dict_object *ogs_diam_cx_sip_authenticate = NULL; +struct dict_object *ogs_diam_cx_sip_authorization = NULL; +struct dict_object *ogs_diam_cx_confidentiality_key = NULL; +struct dict_object *ogs_diam_cx_integrity_key = NULL; +struct dict_object *ogs_diam_cx_server_assignment_type = NULL; +struct dict_object *ogs_diam_cx_user_data_already_available = NULL; +struct dict_object *ogs_diam_cx_user_data = NULL; + +struct dict_object *ogs_diam_cx_charging_information = NULL; +struct dict_object *ogs_diam_cx_primary_event_charging_function_name = NULL; +struct dict_object *ogs_diam_cx_secondary_event_charging_function_name = NULL; +struct dict_object + *ogs_diam_cx_primary_charging_collection_function_name = NULL; +struct dict_object + *ogs_diam_cx_secondary_charging_collection_function_name = NULL; + +const char *ogs_diam_cx_xml_version=""; +const char *ogs_diam_cx_xml_ims_subscription_s=""; +const char *ogs_diam_cx_xml_ims_subscription_e=""; +const char *ogs_diam_cx_xml_private_id_s=""; +const char *ogs_diam_cx_xml_private_id_e=""; +const char *ogs_diam_cx_xml_service_profile_s=""; +const char *ogs_diam_cx_xml_service_profile_e=""; +const char *ogs_diam_cx_xml_public_id_s=""; +const char *ogs_diam_cx_xml_public_id_e=""; +const char *ogs_diam_cx_xml_barring_indication_s=""; +const char *ogs_diam_cx_xml_barring_indication_e=""; +const char *ogs_diam_cx_xml_identity_s=""; +const char *ogs_diam_cx_xml_identity_e=""; +const char *ogs_diam_cx_xml_identity_type_s=""; +const char *ogs_diam_cx_xml_identity_type_e=""; +const char *ogs_diam_cx_xml_wildcarded_psi_s=""; +const char *ogs_diam_cx_xml_wildcarded_psi_e=""; +const char *ogs_diam_cx_xml_display_name_s=""; +const char *ogs_diam_cx_xml_display_name_e=""; + +const char *ogs_diam_cx_xml_ifc_s=""; +const char *ogs_diam_cx_xml_ifc_e=""; +const char *ogs_diam_cx_xml_priority_s=""; +const char *ogs_diam_cx_xml_priority_e=""; +const char *ogs_diam_cx_xml_tp_s=""; +const char *ogs_diam_cx_xml_tp_e=""; +const char *ogs_diam_cx_xml_cnf_s=""; +const char *ogs_diam_cx_xml_cnf_e=""; +const char *ogs_diam_cx_xml_spt_s=""; +const char *ogs_diam_cx_xml_spt_e=""; +const char *ogs_diam_cx_xml_condition_negated_s=""; +const char *ogs_diam_cx_xml_condition_negated_e=""; +const char *ogs_diam_cx_xml_group_s=""; +const char *ogs_diam_cx_xml_group_e=""; +const char *ogs_diam_cx_xml_req_uri_s=""; +const char *ogs_diam_cx_xml_req_uri_e=""; +const char *ogs_diam_cx_xml_method_s=""; +const char *ogs_diam_cx_xml_method_e=""; +const char *ogs_diam_cx_xml_sip_hdr_s=""; +const char *ogs_diam_cx_xml_sip_hdr_e=""; +const char *ogs_diam_cx_xml_session_case_s=""; +const char *ogs_diam_cx_xml_session_case_e=""; +const char *ogs_diam_cx_xml_session_desc_s=""; +const char *ogs_diam_cx_xml_session_desc_e=""; +const char *ogs_diam_cx_xml_registration_type_s=""; +const char *ogs_diam_cx_xml_registration_type_e=""; +const char *ogs_diam_cx_xml_header_s="
"; +const char *ogs_diam_cx_xml_header_e="
"; +const char *ogs_diam_cx_xml_content_s=""; +const char *ogs_diam_cx_xml_content_e=""; +const char *ogs_diam_cx_xml_line_s=""; +const char *ogs_diam_cx_xml_line_e=""; + +const char *ogs_diam_cx_xml_app_server_s=""; +const char *ogs_diam_cx_xml_app_server_e=""; +const char *ogs_diam_cx_xml_server_name_s=""; +const char *ogs_diam_cx_xml_server_name_e=""; +const char *ogs_diam_cx_xml_default_handling_s=""; +const char *ogs_diam_cx_xml_default_handling_e=""; +const char *ogs_diam_cx_xml_service_info_s=""; +const char *ogs_diam_cx_xml_service_info_e=""; +const char *ogs_diam_cx_xml_include_register_request=""; +const char *ogs_diam_cx_xml_include_register_response=""; + +const char *ogs_diam_cx_xml_profile_part_ind_s=""; +const char *ogs_diam_cx_xml_profile_part_ind_e=""; + +const char *ogs_diam_cx_xml_cn_services_auth_s=""; +const char *ogs_diam_cx_xml_cn_services_auth_e=""; +const char *ogs_diam_cx_xml_subs_media_profile_id_s=""; +const char *ogs_diam_cx_xml_subs_media_profile_id_e=""; +const char *ogs_diam_cx_xml_shared_ifc_set_id_s=""; +const char *ogs_diam_cx_xml_shared_ifc_set_id_e=""; + +const char *ogs_diam_cx_xml_extension_s=""; +const char *ogs_diam_cx_xml_extension_e=""; + +extern int ogs_dict_cx_entry(char *conffile); + +int ogs_diam_cx_init(void) +{ + application_id_t id = OGS_DIAM_CX_APPLICATION_ID; + + ogs_assert(ogs_dict_cx_entry(NULL) == 0); + + CHECK_dict_search(DICT_APPLICATION, APPLICATION_BY_ID, + (void *)&id, &ogs_diam_cx_application); + + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/User-Authorization-Request", &ogs_diam_cx_cmd_uar); + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/User-Authorization-Answer", &ogs_diam_cx_cmd_uaa); + + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/Multimedia-Auth-Request", &ogs_diam_cx_cmd_mar); + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/Multimedia-Auth-Answer", &ogs_diam_cx_cmd_maa); + + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/Server-Assignment-Request", &ogs_diam_cx_cmd_sar); + CHECK_dict_search(DICT_COMMAND, CMD_BY_NAME, + "3GPP/Server-Assignment-Answer", &ogs_diam_cx_cmd_saa); + + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Public-Identity", &ogs_diam_cx_public_identity); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Visited-Network-Identifier", + &ogs_diam_cx_visited_network_identifier); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Server-Name", &ogs_diam_cx_server_name); + + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Number-Auth-Items", &ogs_diam_cx_sip_number_auth_items); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Item-Number", &ogs_diam_cx_sip_item_number); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Auth-Data-Item", &ogs_diam_cx_sip_auth_data_item); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Authentication-Scheme", + &ogs_diam_cx_sip_authentication_scheme); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Authenticate", &ogs_diam_cx_sip_authenticate); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "SIP-Authorization", &ogs_diam_cx_sip_authorization); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Confidentiality-Key", &ogs_diam_cx_confidentiality_key); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Integrity-Key", &ogs_diam_cx_integrity_key); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Server-Assignment-Type", &ogs_diam_cx_server_assignment_type); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "User-Data-Already-Available", + &ogs_diam_cx_user_data_already_available); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "User-Data", &ogs_diam_cx_user_data); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Charging-Information", &ogs_diam_cx_charging_information); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Primary-Event-Charging-Function-Name", + &ogs_diam_cx_primary_event_charging_function_name); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Secondary-Event-Charging-Function-Name", + &ogs_diam_cx_secondary_event_charging_function_name); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Primary-Charging-Collection-Function-Name", + &ogs_diam_cx_primary_charging_collection_function_name); + CHECK_dict_search(DICT_AVP, AVP_BY_NAME_ALL_VENDORS, + "Secondary-Charging-Collection-Function-Name", + &ogs_diam_cx_secondary_charging_collection_function_name); + + return 0; +} diff --git a/lib/diameter/cx/message.h b/lib/diameter/cx/message.h new file mode 100644 index 000000000..012894533 --- /dev/null +++ b/lib/diameter/cx/message.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if !defined(OGS_DIAMETER_INSIDE) && !defined(OGS_DIAMETER_COMPILATION) +#error "This header cannot be included directly." +#endif + +#ifndef OGS_DIAM_CX_MESSAGE_H +#define OGS_DIAM_CX_MESSAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define OGS_DIAM_CX_APPLICATION_ID 16777216 + +extern struct dict_object *ogs_diam_cx_application; + +extern struct dict_object *ogs_diam_cx_cmd_uar; +extern struct dict_object *ogs_diam_cx_cmd_uaa; +extern struct dict_object *ogs_diam_cx_cmd_mar; +extern struct dict_object *ogs_diam_cx_cmd_maa; +extern struct dict_object *ogs_diam_cx_cmd_sar; +extern struct dict_object *ogs_diam_cx_cmd_saa; + +extern struct dict_object *ogs_diam_cx_public_identity; +extern struct dict_object *ogs_diam_cx_visited_network_identifier; +extern struct dict_object *ogs_diam_cx_server_name; + +extern struct dict_object *ogs_diam_cx_sip_number_auth_items; +extern struct dict_object *ogs_diam_cx_sip_item_number; +extern struct dict_object *ogs_diam_cx_sip_auth_data_item; +#define OGS_DIAM_CX_AUTH_SCHEME_IMS_AKA "Digest-AKAv1-MD5" +#define OGS_DIAM_CX_AUTH_SCHEME_SIP_DIGEST "SIP Digest" +#define OGS_DIAM_CX_AUTH_SCHEME_NASS_BUNDLED "NASS-Bundled" +#define OGS_DIAM_CX_AUTH_SCHEME_GPRS_IMS_BUNDLED "Early-IMS-Security" +#define OGS_DIAM_CX_AUTH_SCHEME_UNKNOWN "Unknown" +extern struct dict_object *ogs_diam_cx_sip_authentication_scheme; +extern struct dict_object *ogs_diam_cx_sip_authenticate; +extern struct dict_object *ogs_diam_cx_sip_authorization; +extern struct dict_object *ogs_diam_cx_confidentiality_key; +extern struct dict_object *ogs_diam_cx_integrity_key; +#define OGS_DIAM_CX_SERVER_NO_ASSIGNMENT 0 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_REGISTRATION 1 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_RE_REGISTRATION 2 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_UNREGISTERED_USER 3 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_TIMEOUT_DEREGISTRATION 4 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_USER_DEREGISTRATION 5 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME 6 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_USER_DEREGISTRATION_STORE_SERVER_NAME 7 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_ADMINISTRATIVE_DEREGISTRATION 8 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_AUTHENTICATION_FAILURE 9 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_AUTHENTICATION_TIMEOUT 10 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_DEREGISTRATION_TOO_MUCH_DATA 11 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_AAA_USER_DATA_REQUEST 12 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_PGW_UPDATE 13 +#define OGS_DIAM_CX_SERVER_ASSIGNMENT_RESTORATION 14 +extern struct dict_object *ogs_diam_cx_server_assignment_type; +#define OGS_DIAM_CX_USER_DATA_NOT_AVAILABLE 0 +#define OGS_DIAM_CX_USER_DATA_ALREADY_AVAILABLE 1 +extern struct dict_object *ogs_diam_cx_user_data_already_available; +extern struct dict_object *ogs_diam_cx_user_data; +extern struct dict_object *ogs_diam_cx_charging_information; +extern struct dict_object *ogs_diam_cx_primary_event_charging_function_name; +extern struct dict_object *ogs_diam_cx_secondary_event_charging_function_name; +extern struct dict_object + *ogs_diam_cx_primary_charging_collection_function_name; +extern struct dict_object + *ogs_diam_cx_secondary_charging_collection_function_name; + +#define OGS_DIAM_CX_FIRST_REGISTRATION 2001 +#define OGS_DIAM_CX_SUBSEQUENT_REGISTRATION 2002 +#define OGS_DIAM_CX_UNREGISTERED_SERVICE 2003 +#define OGS_DIAM_CX_SERVER_NAME_NOT_STORED 2004 +#define OGS_DIAM_CX_ERROR_USER_UNKNOWN 5001 +#define OGS_DIAM_CX_ERROR_IDENTITIES_DONT_MATCH 5002 +#define OGS_DIAM_CX_ERROR_IDENTITY_NOT_REGISTERED 5003 +#define OGS_DIAM_CX_ERROR_ROAMING_NOT_ALLOWED 5004 +#define OGS_DIAM_CX_ERROR_IDENTITY_ALREADY_REGISTERED 5005 +#define OGS_DIAM_CX_ERROR_AUTH_SCHEME_NOT_SUPPORTED 5006 +#define OGS_DIAM_CX_ERROR_IN_ASSIGNMENT_TYPE 5007 +#define OGS_DIAM_CX_ERROR_TOO_MUCH_DATA 5008 +#define OGS_DIAM_CX_ERROR_NOT_SUPPORTED_USER_DATA 5009 +#define OGS_DIAM_CX_ERROR_FEATURE_UNSUPPORTED 5011 +#define OGS_DIAM_CX_ERROR_SERVING_NODE_FEATURE_UNSUPPORTED 5012 + +int ogs_diam_cx_init(void); + +extern const char *ogs_diam_cx_xml_version; +extern const char *ogs_diam_cx_xml_ims_subscription_s; +extern const char *ogs_diam_cx_xml_ims_subscription_e; +extern const char *ogs_diam_cx_xml_private_id_s; +extern const char *ogs_diam_cx_xml_private_id_e; +extern const char *ogs_diam_cx_xml_service_profile_s; +extern const char *ogs_diam_cx_xml_service_profile_e; +extern const char *ogs_diam_cx_xml_public_id_s; +extern const char *ogs_diam_cx_xml_public_id_e; +extern const char *ogs_diam_cx_xml_barring_indication_s; +extern const char *ogs_diam_cx_xml_barring_indication_e; +extern const char *ogs_diam_cx_xml_identity_s; +extern const char *ogs_diam_cx_xml_identity_e; +extern const char *ogs_diam_cx_xml_identity_type_s; +extern const char *ogs_diam_cx_xml_identity_type_e; +extern const char *ogs_diam_cx_xml_wildcarded_psi_s; +extern const char *ogs_diam_cx_xml_wildcarded_psi_e; +extern const char *ogs_diam_cx_xml_display_name_s; +extern const char *ogs_diam_cx_xml_display_name_e; + +extern const char *ogs_diam_cx_xml_ifc_s; +extern const char *ogs_diam_cx_xml_ifc_e; +extern const char *ogs_diam_cx_xml_priority_s; +extern const char *ogs_diam_cx_xml_priority_e; +extern const char *ogs_diam_cx_xml_tp_s; +extern const char *ogs_diam_cx_xml_tp_e; +extern const char *ogs_diam_cx_xml_cnf_s; +extern const char *ogs_diam_cx_xml_cnf_e; +extern const char *ogs_diam_cx_xml_spt_s; +extern const char *ogs_diam_cx_xml_spt_e; +extern const char *ogs_diam_cx_xml_condition_negated_s; +extern const char *ogs_diam_cx_xml_condition_negated_e; +extern const char *ogs_diam_cx_xml_group_s; +extern const char *ogs_diam_cx_xml_group_e; +extern const char *ogs_diam_cx_xml_req_uri_s; +extern const char *ogs_diam_cx_xml_req_uri_e; +extern const char *ogs_diam_cx_xml_method_s; +extern const char *ogs_diam_cx_xml_method_e; +extern const char *ogs_diam_cx_xml_sip_hdr_s; +extern const char *ogs_diam_cx_xml_sip_hdr_e; +extern const char *ogs_diam_cx_xml_session_case_s; +extern const char *ogs_diam_cx_xml_session_case_e; +extern const char *ogs_diam_cx_xml_session_desc_s; +extern const char *ogs_diam_cx_xml_session_desc_e; +extern const char *ogs_diam_cx_xml_registration_type_s; +extern const char *ogs_diam_cx_xml_registration_type_e; +extern const char *ogs_diam_cx_xml_header_s; +extern const char *ogs_diam_cx_xml_header_e; +extern const char *ogs_diam_cx_xml_content_s; +extern const char *ogs_diam_cx_xml_content_e; +extern const char *ogs_diam_cx_xml_line_s; +extern const char *ogs_diam_cx_xml_line_e; + +extern const char *ogs_diam_cx_xml_app_server_s; +extern const char *ogs_diam_cx_xml_app_server_e; +extern const char *ogs_diam_cx_xml_server_name_s; +extern const char *ogs_diam_cx_xml_server_name_e; +extern const char *ogs_diam_cx_xml_default_handling_s; +extern const char *ogs_diam_cx_xml_default_handling_e; +extern const char *ogs_diam_cx_xml_service_info_s; +extern const char *ogs_diam_cx_xml_service_info_e; +extern const char *ogs_diam_cx_xml_include_register_request; +extern const char *ogs_diam_cx_xml_include_register_response; + +extern const char *ogs_diam_cx_xml_profile_part_ind_s; +extern const char *ogs_diam_cx_xml_profile_part_ind_e; + +extern const char *ogs_diam_cx_xml_cn_services_auth_s; +extern const char *ogs_diam_cx_xml_cn_services_auth_e; +extern const char *ogs_diam_cx_xml_subs_media_profile_id_s; +extern const char *ogs_diam_cx_xml_subs_media_profile_id_e; +extern const char *ogs_diam_cx_xml_shared_ifc_set_id_s; +extern const char *ogs_diam_cx_xml_shared_ifc_set_id_e; + +extern const char *ogs_diam_cx_xml_extension_s; +extern const char *ogs_diam_cx_xml_extension_e; + +#ifdef __cplusplus +} +#endif + +#endif /* OGS_DIAM_OGS_DIAM_CX_MESSAGE_H */ diff --git a/lib/diameter/cx/ogs-diameter-cx.h b/lib/diameter/cx/ogs-diameter-cx.h new file mode 100644 index 000000000..5e6222f70 --- /dev/null +++ b/lib/diameter/cx/ogs-diameter-cx.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef OGS_DIAMETER_CX_H +#define OGS_DIAMETER_CX_H + +#include "ogs-diameter-common.h" + +#define OGS_DIAMETER_INSIDE + +#include "diameter/cx/message.h" + +#undef OGS_DIAMETER_INSIDE + +#ifdef __cplusplus +extern "C" { +#endif + +/* Nothing */ + +#ifdef __cplusplus +} +#endif + +#endif /* OGS_DIAMETER_CX_H */ diff --git a/lib/diameter/meson.build b/lib/diameter/meson.build index befcc7099..5d31907b6 100644 --- a/lib/diameter/meson.build +++ b/lib/diameter/meson.build @@ -19,3 +19,4 @@ subdir('common') subdir('gx') subdir('rx') subdir('s6a') +subdir('cx') diff --git a/lib/diameter/rx/dict.c b/lib/diameter/rx/dict.c index 54372e50b..95b788a97 100644 --- a/lib/diameter/rx/dict.c +++ b/lib/diameter/rx/dict.c @@ -148,13 +148,6 @@ int ogs_dict_rx_entry(char *conffile) { /* Create the vendors */ - { - struct dict_object * vendor; - CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "3GPP", &vendor, ENOENT)); - struct dict_application_data app_data = { 16777216, "Cx" }; - CHECK_FCT(fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, &app_data, vendor, NULL)); - } - { struct dict_object * vendor; CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_VENDOR, VENDOR_BY_NAME, "3GPP", &vendor, ENOENT)); @@ -165,404 +158,6 @@ int ogs_dict_rx_entry(char *conffile) } /* Command section */ - { - struct dict_object* app; - CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Cx", &app, ENOENT)); - - /* User-Authorization-Request (UAR) Command */ - { - struct dict_object* cmd_uar; - struct dict_cmd_data data = { - 300, /* Code */ - "3GPP/User-Authorization-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Visited-Network-Identifier" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "User-Authorization-Type" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "UAR-Flags" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_uar); - PARSE_loc_rules(rules, cmd_uar); - } - - /* User-Authorization-Answer (UAA) Command */ - { - struct dict_object* cmd_uaa; - struct dict_cmd_data data = { - 300, /* Code */ - "3GPP/User-Authorization-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Capabilities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_uaa); - PARSE_loc_rules(rules, cmd_uaa); - } - - /* Location-Info-Request (LIR) Command */ - { - struct dict_object* cmd_lir; - struct dict_cmd_data data = { - 302, /* Code */ - "3GPP/Location-Info-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Originating-Request" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "User-Authorization-Type" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Session-Priority" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_lir); - PARSE_loc_rules(rules, cmd_lir); - } - - /* Location-Info-Answer (LIA) Command */ - { - struct dict_object* cmd_lia; - struct dict_cmd_data data = { - 302, /* Code */ - "3GPP/Location-Info-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Capabilities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "LIA-Flags" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_lia); - PARSE_loc_rules(rules, cmd_lia); - } - - /* Multimedia-Auth-Request (MAR) Command */ - { - struct dict_object* cmd_mar; - struct dict_cmd_data data = { - 303, /* Code */ - "3GPP/Multimedia-Auth-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SIP-Number-Auth-Items" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 }, - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_mar); - PARSE_loc_rules(rules, cmd_mar); - } - - /* Multimedia-Auth-Answer (MAA) Command */ - { - struct dict_object* cmd_maa; - struct dict_cmd_data data = { - 303, /* Code */ - "3GPP/Multimedia-Auth-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SIP-Number-Auth-Items" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_maa); - PARSE_loc_rules(rules, cmd_maa); - } - - /* Server-Assignment-Request (SAR) Command */ - { - struct dict_object* cmd_sar; - struct dict_cmd_data data = { - 301, /* Code */ - "3GPP/Server-Assignment-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Assignment-Type" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "User-Data-Already-Available" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SCSCF-Restoration-Info" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Multiple-Registration-Indication" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Session-Priority" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_sar); - PARSE_loc_rules(rules, cmd_sar); - } - - /* Server-Assignment-Answer (SAA) Command */ - { - struct dict_object* cmd_saa; - struct dict_cmd_data data = { - 301, /* Code */ - "3GPP/Server-Assignment-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "User-Data" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Charging-Information" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Loose-Route-Indication" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SCSCF-Restoration-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Associated-Registered-Identities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Server-Name" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Wildcarded-Public-Identity" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Priviledged-Sender-Indication" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_saa); - PARSE_loc_rules(rules, cmd_saa); - } - - /* Registration-Termination-Request (RTR) Command */ - { - struct dict_object* cmd_rtr; - struct dict_cmd_data data = { - 304, /* Code */ - "3GPP/Registration-Termination-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Public-Identity" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Deregistration-Reason" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_rtr); - PARSE_loc_rules(rules, cmd_rtr); - } - - /* Registration-Termination-Answer (RTA) Command */ - { - struct dict_object* cmd_rta; - struct dict_cmd_data data = { - 304, /* Code */ - "3GPP/Registration-Termination-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Associated-Identities" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "Identity-with-Emergency-Registration" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_rta); - PARSE_loc_rules(rules, cmd_rta); - } - - /* Push-Profile-Request (PPR) Command */ - { - struct dict_object* cmd_ppr; - struct dict_cmd_data data = { - 305, /* Code */ - "3GPP/Push-Profile-Request", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Destination-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "User-Name" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_vendor = 10415, .avp_name = "User-Data" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Charging-Information" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "SIP-Auth-Data-Item" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_ppr); - PARSE_loc_rules(rules, cmd_ppr); - } - - /* Push-Profile-Answer (PPA) Command */ - { - struct dict_object* cmd_ppa; - struct dict_cmd_data data = { - 305, /* Code */ - "3GPP/Push-Profile-Answer", /* Name */ - CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE | CMD_FLAG_ERROR, /* Fixed flags */ - CMD_FLAG_PROXIABLE /* Fixed flag values */ - }; - - struct local_rules_definition rules[] = - { - { { .avp_name = "Session-Id" }, RULE_FIXED_HEAD, -1, 1 }, - { { .avp_name = "Vendor-Specific-Application-Id" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Result-Code" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Experimental-Result" }, RULE_OPTIONAL, -1, 1 }, - { { .avp_name = "Auth-Session-State" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Host" }, RULE_REQUIRED, -1, 1 }, - { { .avp_name = "Origin-Realm" }, RULE_REQUIRED, -1, 1 }, - { { .avp_vendor = 10415, .avp_name = "Supported-Features" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Failed-AVP" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Proxy-Info" }, RULE_OPTIONAL, -1, -1 }, - { { .avp_name = "Route-Record" }, RULE_OPTIONAL, -1, -1 } - }; - - CHECK_dict_new(DICT_COMMAND, &data, app, &cmd_ppa); - PARSE_loc_rules(rules, cmd_ppa); - } - } struct dict_object* rx; CHECK_FCT(fd_dict_search(fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Rx", &rx, ENOENT)); diff --git a/src/hss/hss-context.c b/src/hss/hss-context.c index 919f78809..525d6927c 100644 --- a/src/hss/hss-context.c +++ b/src/hss/hss-context.c @@ -27,6 +27,21 @@ int __hss_log_domain; static int context_initialized = 0; +static OGS_POOL(impi_pool, hss_impi_t); +static OGS_POOL(impu_pool, hss_impu_t); + +static hss_impi_t *impi_add(char *id); +static void impi_remove(hss_impi_t *impi); +static void impi_remove_all(void); + +static hss_impi_t *impi_find_by_id(char *id); +static char *impi_get_server_name(hss_impi_t *impi); + +static hss_impu_t *impu_add(hss_impi_t *impi, char *id); +static void impu_remove(hss_impu_t *impu); +static void impu_remove_all(hss_impi_t *impi); +static hss_impu_t *impu_find_by_id(hss_impi_t *impi, char *id); + hss_context_t* hss_self(void) { return &self; @@ -47,7 +62,13 @@ void hss_context_init(void) ogs_log_install_domain(&__ogs_dbi_domain, "dbi", ogs_core()->log.level); ogs_log_install_domain(&__hss_log_domain, "hss", ogs_core()->log.level); + ogs_pool_init(&impi_pool, ogs_app()->pool.impi); + ogs_pool_init(&impu_pool, ogs_app()->pool.impu); + + self.impi_hash = ogs_hash_make(); + ogs_thread_mutex_init(&self.db_lock); + ogs_thread_mutex_init(&self.cx_lock); context_initialized = 1; } @@ -56,7 +77,16 @@ void hss_context_final(void) { ogs_assert(context_initialized == 1); + impi_remove_all(); + + ogs_assert(self.impi_hash); + ogs_hash_destroy(self.impi_hash); + + ogs_pool_final(&impi_pool); + ogs_pool_final(&impu_pool); + ogs_thread_mutex_destroy(&self.db_lock); + ogs_thread_mutex_destroy(&self.cx_lock); context_initialized = 0; } @@ -276,8 +306,7 @@ int hss_db_auth_info(char *imsi_bcd, ogs_dbi_auth_info_t *auth_info) return rv; } -int hss_db_update_rand_and_sqn( - char *imsi_bcd, uint8_t *rand, uint64_t sqn) +int hss_db_update_sqn(char *imsi_bcd, uint8_t *rand, uint64_t sqn) { int rv; char *supi = NULL; @@ -335,3 +364,236 @@ int hss_db_subscription_data( return rv; } + +static hss_impi_t *impi_add(char *id) +{ + hss_impi_t *impi = NULL; + + ogs_assert(id); + + ogs_pool_alloc(&impi_pool, &impi); + ogs_assert(impi); + memset(impi, 0, sizeof *impi); + + impi->id = ogs_strdup(id); + ogs_assert(impi->id); + + ogs_hash_set(self.impi_hash, impi->id, strlen(impi->id), impi); + + ogs_list_add(&self.impi_list, impi); + + return impi; +} + +static void impi_remove(hss_impi_t *impi) +{ + ogs_assert(impi); + + ogs_list_remove(&self.impi_list, impi); + + impu_remove_all(impi); + + ogs_assert(impi->id); + ogs_hash_set(self.impi_hash, impi->id, strlen(impi->id), NULL); + ogs_free(impi->id); + + ogs_pool_free(&impi_pool, impi); +} + +static void impi_remove_all(void) +{ + hss_impi_t *impi = NULL, *next = NULL; + + ogs_list_for_each_safe(&self.impi_list, next, impi) + impi_remove(impi); +} + +static hss_impi_t *impi_find_by_id(char *id) +{ + ogs_assert(id); + return (hss_impi_t *)ogs_hash_get(self.impi_hash, id, strlen(id)); +} + +static char *impi_get_server_name(hss_impi_t *impi) +{ + hss_impu_t *impu = NULL; + + ogs_assert(impi); + + ogs_list_for_each(&impi->impu_list, impu) { + if (impu->server_name) + return impu->server_name; + } + + return NULL; +} + +static hss_impu_t *impu_add(hss_impi_t *impi, char *id) +{ + hss_impu_t *impu = NULL; + + ogs_assert(impi); + ogs_assert(id); + + ogs_pool_alloc(&impu_pool, &impu); + ogs_assert(impu); + memset(impu, 0, sizeof *impu); + + impu->id = ogs_strdup(id); + ogs_assert(impu->id); + + impu->impi = impi; + ogs_list_add(&impi->impu_list, impu); + + return impu; +} + +static void impu_remove(hss_impu_t *impu) +{ + hss_impi_t *impi = NULL; + + ogs_assert(impu); + impi = impu->impi; + ogs_assert(impi); + + ogs_list_remove(&impi->impu_list, impu); + + ogs_assert(impu->id); + ogs_free(impu->id); + + if (impu->server_name) + ogs_free(impu->server_name); + + ogs_pool_free(&impu_pool, impu); +} + +static void impu_remove_all(hss_impi_t *impi) +{ + hss_impu_t *impu = NULL, *next = NULL; + + ogs_list_for_each_safe(&impi->impu_list, next, impu) + impu_remove(impu); +} + +static hss_impu_t *impu_find_by_id(hss_impi_t *impi, char *id) +{ + hss_impu_t *impu = NULL; + + ogs_assert(impi); + ogs_assert(id); + + ogs_list_for_each(&impi->impu_list, impu) { + if (!strcmp(impu->id, id)) + return impu; + } + + return NULL; +} + +void hss_cx_associate_identity(char *user_name, char *public_identity) +{ + hss_impi_t *impi = NULL; + hss_impu_t *impu = NULL; + + ogs_assert(user_name); + ogs_assert(public_identity); + + ogs_thread_mutex_lock(&self.cx_lock); + + impi = impi_find_by_id(user_name); + if (!impi) { + impi = impi_add(user_name); + ogs_assert(impi); + } + + impu = impu_find_by_id(impi, public_identity); + if (!impu) { + impu = impu_add(impi, public_identity); + ogs_assert(impu); + } + + ogs_thread_mutex_unlock(&self.cx_lock); +} + +bool hss_cx_identity_is_associated(char *user_name, char *public_identity) +{ + bool match_result = false; + + hss_impi_t *impi = NULL; + hss_impu_t *impu = NULL; + + ogs_thread_mutex_lock(&self.cx_lock); + + impi = impi_find_by_id(user_name); + if (impi) { + impu = impu_find_by_id(impi, public_identity); + if (impu) { + match_result = true; + } + } + + ogs_thread_mutex_unlock(&self.cx_lock); + + return match_result; +} + +char *hss_cx_get_server_name(char *user_name, char *public_identity) +{ + char *server_name = NULL; + + hss_impi_t *impi = NULL; + hss_impu_t *impu = NULL; + + ogs_thread_mutex_lock(&self.cx_lock); + + impi = impi_find_by_id(user_name); + ogs_assert(impi); + + impu = impu_find_by_id(impi, public_identity); + ogs_assert(impu); + + server_name = impu->server_name; + if (!server_name) { + server_name = impi_get_server_name(impi); + } + + ogs_thread_mutex_unlock(&self.cx_lock); + + return server_name; +} + +void hss_cx_set_server_name( + char *user_name, char *public_identity, + char *server_name, bool overwrite) +{ + hss_impi_t *impi = NULL; + hss_impu_t *impu = NULL; + + ogs_thread_mutex_lock(&self.cx_lock); + + impi = impi_find_by_id(user_name); + ogs_assert(impi); + + impu = impu_find_by_id(impi, public_identity); + ogs_assert(impu); + + if (!impu->server_name) { + impu->server_name = ogs_strdup(server_name); + ogs_assert(impu->server_name); + } else { + if (strcmp(impu->server_name, server_name) != 0) { + if (overwrite == true) { + ogs_warn("S-CSCF reassignment[%s->%s]", + impu->server_name, server_name); + ogs_free(impu->server_name); + impu->server_name = ogs_strdup(server_name); + ogs_assert(impu->server_name); + } else { + ogs_error("Use Old S-CSCF[%s!=%s]", + server_name, impu->server_name); + } + } + } + + ogs_thread_mutex_unlock(&self.cx_lock); +} diff --git a/src/hss/hss-context.h b/src/hss/hss-context.h index 59638a1e7..912107209 100644 --- a/src/hss/hss-context.h +++ b/src/hss/hss-context.h @@ -21,6 +21,7 @@ #define HSS_CONTEXT_H #include "ogs-diameter-s6a.h" +#include "ogs-diameter-cx.h" #include "ogs-dbi.h" #include "ogs-app.h" @@ -34,12 +35,33 @@ extern int __hss_log_domain; #define OGS_LOG_DOMAIN __hss_log_domain typedef struct _hss_context_t { - const char *diam_conf_path; /* HSS Diameter conf path */ - ogs_diam_config_t *diam_config; /* HSS Diameter config */ + const char *diam_conf_path;/* HSS Diameter conf path */ + ogs_diam_config_t *diam_config; /* HSS Diameter config */ ogs_thread_mutex_t db_lock; + ogs_thread_mutex_t cx_lock; + + ogs_list_t impi_list; + ogs_hash_t *impi_hash; /* hash table (IMPI) */ } hss_context_t; +typedef struct hss_impi_s { + ogs_lnode_t lnode; + + char *id; + + ogs_list_t impu_list; +} hss_impi_t; + +typedef struct hss_impu_s { + ogs_lnode_t lnode; + + char *id; + char *server_name; + + hss_impi_t *impi; +} hss_impu_t; + void hss_context_init(void); void hss_context_final(void); hss_context_t *hss_self(void); @@ -47,12 +69,20 @@ hss_context_t *hss_self(void); int hss_context_parse_config(void); int hss_db_auth_info(char *imsi_bcd, ogs_dbi_auth_info_t *auth_info); -int hss_db_update_rand_and_sqn(char *imsi_bcd, uint8_t *rand, uint64_t sqn); +int hss_db_update_sqn(char *imsi_bcd, uint8_t *rand, uint64_t sqn); int hss_db_increment_sqn(char *imsi_bcd); int hss_db_subscription_data( char *imsi_bcd, ogs_subscription_data_t *subscription_data); +void hss_cx_associate_identity(char *user_name, char *public_identity); +bool hss_cx_identity_is_associated(char *user_name, char *public_identity); + +char *hss_cx_get_server_name(char *user_name, char *public_identity); +void hss_cx_set_server_name( + char *user_name, char *public_identity, + char *server_name, bool overwrite); + #ifdef __cplusplus } #endif diff --git a/src/hss/hss-cx-path.c b/src/hss/hss-cx-path.c new file mode 100644 index 000000000..bca14b330 --- /dev/null +++ b/src/hss/hss-cx-path.c @@ -0,0 +1,999 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ogs-crypt.h" + +#include "hss-context.h" +#include "hss-fd-path.h" + +/* handler for fallback cb */ +static struct disp_hdl *hdl_cx_fb = NULL; +/* handler for User-Authorization-Request cb */ +static struct disp_hdl *hdl_cx_uar = NULL; +/* handler for Multimedia-Auth-Request cb */ +static struct disp_hdl *hdl_cx_mar = NULL; +/* handler for Server-Assignment-Request cb */ +static struct disp_hdl *hdl_cx_sar = NULL; + +static char *download_user_data( + char *user_name, ogs_subscription_data_t *subscription_data); + +/* Default callback for the application. */ +static int hss_ogs_diam_cx_fb_cb(struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + /* This CB should never be called */ + ogs_warn("Unexpected message received!"); + + return ENOTSUP; +} + +/* Callback for incoming User-Authorization-Request messages */ +static int hss_ogs_diam_cx_uar_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int rv, ret; + uint32_t result_code = 0; + + struct msg *ans, *qry; + + struct avp_hdr *hdr; + union avp_value val; + + char *user_name = NULL; + char *public_identity = NULL; + char *server_name = NULL; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + char msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1]; + + ogs_subscription_data_t subscription_data; + + ogs_assert(msg); + + ogs_debug("User-Authorization-Request"); + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + /* Get User-Name AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + user_name = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(user_name); + + ogs_extract_digit_from_string(imsi_bcd, user_name); + + /* Get Public-Identity AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_public_identity, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + public_identity = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(public_identity); + + ogs_extract_digit_from_string(msisdn_bcd, public_identity); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + rv = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_CX_ERROR_USER_UNKNOWN; + goto out; + } + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Associate IMPI(User-Name) + IMPU(Public-Identity) */ + hss_cx_associate_identity(user_name, public_identity); + + /* Get Server-Name for IMPI(User-Name) + IMPU(Public-Identity) */ + server_name = hss_cx_get_server_name(user_name, public_identity); + if (!server_name) + result_code = OGS_DIAM_CX_FIRST_REGISTRATION; + else + result_code = OGS_DIAM_CX_SUBSEQUENT_REGISTRATION; + + /* Set the Experimental-Result, Origin-Host and Origin-Realm AVPs */ + ret = ogs_diam_message_experimental_rescode_set( + ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Server-Name AVP */ + if (server_name) { + ret = fd_msg_avp_new(ogs_diam_cx_server_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)server_name; + val.os.len = strlen(server_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("User-Authorization-Answer"); + + /* Add this value to the stats */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + ogs_subscription_data_free(&subscription_data); + + ogs_free(user_name); + ogs_free(public_identity); + + return 0; + +out: + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Experimental-Result, Origin-Host and Origin-Realm AVPs */ + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_subscription_data_free(&subscription_data); + + ogs_free(user_name); + ogs_free(public_identity); + + return 0; +} + +/* Callback for incoming Multimedia-Auth-Request messages */ +static int hss_ogs_diam_cx_mar_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int rv, ret; + uint32_t result_code = 0; + + struct msg *ans, *qry; + + struct avp *sip_auth_data_item_avp = NULL; + struct avp *authentication_scheme_avp = NULL; + struct avp *sip_authorization_avp = NULL; + + struct avp *avpch = NULL; + struct avp_hdr *hdr; + union avp_value val; + + char *user_name = NULL; + char *public_identity = NULL; + char *server_name = NULL; + char *authentication_scheme = NULL; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + char msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1]; + + ogs_dbi_auth_info_t auth_info; + uint8_t zero[OGS_RAND_LEN]; + + uint8_t authenticate[OGS_KEY_LEN*2]; + + uint8_t opc[OGS_KEY_LEN]; + uint8_t sqn[OGS_SQN_LEN]; + + uint8_t autn[OGS_AUTN_LEN]; + uint8_t ik[OGS_KEY_LEN]; + uint8_t ck[OGS_KEY_LEN]; + uint8_t ak[OGS_AK_LEN]; + uint8_t xres[OGS_MAX_RES_LEN]; + size_t xres_len = 8; + + uint8_t mac_s[OGS_MAC_S_LEN]; + + ogs_assert(msg); + + ogs_debug("Multimedia-Auth-Request"); + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + /* Get User-Name AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + user_name = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(user_name); + + ogs_extract_digit_from_string(imsi_bcd, user_name); + + /* Get Public-Identity AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_public_identity, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + public_identity = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(public_identity); + + ogs_extract_digit_from_string(msisdn_bcd, public_identity); + + /* Get Server-Name AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_server_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + server_name = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(server_name); + + /* Check if IMPI(User-Name) + IMPU(Public-Identity) is associated */ + bool matched = hss_cx_identity_is_associated(user_name, public_identity); + if (!matched) { + ogs_error("User-Name[%s] Public-Identity[%s] is not assocated", + user_name, public_identity); + result_code = OGS_DIAM_CX_ERROR_IDENTITIES_DONT_MATCH; + goto out; + } + + /* Get the SIP-Auth-Data-Item AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp( + qry, ogs_diam_cx_sip_auth_data_item, &sip_auth_data_item_avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(sip_auth_data_item_avp, &hdr); + ogs_assert(ret == 0); + + /* Get the Authentication-Scheme AVP */ + ret = fd_msg_search_avp(sip_auth_data_item_avp, + ogs_diam_cx_sip_authentication_scheme, &authentication_scheme_avp); + ogs_assert(ret == 0); + if (authentication_scheme_avp) { + ret = fd_msg_avp_hdr(authentication_scheme_avp, &hdr); + ogs_assert(ret == 0); + + authentication_scheme = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(authentication_scheme); + } + + /* IMS_AKA is only supported */ + if (authentication_scheme && + strcmp(authentication_scheme, OGS_DIAM_CX_AUTH_SCHEME_IMS_AKA) != 0 && + strcmp(authentication_scheme, OGS_DIAM_CX_AUTH_SCHEME_UNKNOWN) != 0) { + ogs_error("Authentication-Scheme[%s] is not supported", + authentication_scheme); + result_code = OGS_DIAM_CX_ERROR_AUTH_SCHEME_NOT_SUPPORTED; + goto out; + } + + rv = hss_db_auth_info(imsi_bcd, &auth_info); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_CX_ERROR_USER_UNKNOWN; + goto out; + } + + /* Overwrite Server-Name for IMPI(User-Name) + IMPU(Public-Identity) */ + hss_cx_set_server_name(user_name, public_identity, server_name, true); + + memset(zero, 0, sizeof(zero)); + if (memcmp(auth_info.rand, zero, OGS_RAND_LEN) == 0) { + ogs_random(auth_info.rand, OGS_RAND_LEN); + } + + if (auth_info.use_opc) + memcpy(opc, auth_info.opc, sizeof(opc)); + else + milenage_opc(auth_info.k, auth_info.op, opc); + + /* Get the SIP-Authorization AVP */ + ret = fd_msg_search_avp(sip_auth_data_item_avp, + ogs_diam_cx_sip_authorization, &sip_authorization_avp); + ogs_assert(ret == 0); + if (sip_authorization_avp) { + ret = fd_msg_avp_hdr(sip_authorization_avp, &hdr); + ogs_assert(ret == 0); + + ogs_auc_sqn(opc, auth_info.k, + hdr->avp_value->os.data, + hdr->avp_value->os.data + OGS_RAND_LEN, + sqn, mac_s); + if (memcmp(mac_s, hdr->avp_value->os.data + + OGS_RAND_LEN + OGS_SQN_LEN, OGS_MAC_S_LEN) == 0) { + ogs_random(auth_info.rand, OGS_RAND_LEN); + auth_info.sqn = ogs_buffer_to_uint64(sqn, OGS_SQN_LEN); + /* 33.102 C.3.4 Guide : IND + 1 */ + auth_info.sqn = (auth_info.sqn + 32 + 1) & OGS_MAX_SQN; + } else { + ogs_error("Re-synch MAC failed for IMSI:`%s`", imsi_bcd); + ogs_log_print(OGS_LOG_ERROR, "MAC_S: "); + ogs_log_hexdump(OGS_LOG_ERROR, mac_s, OGS_MAC_S_LEN); + ogs_log_hexdump(OGS_LOG_ERROR, + (void*)(hdr->avp_value->os.data + + OGS_RAND_LEN + OGS_SQN_LEN), + OGS_MAC_S_LEN); + ogs_log_print(OGS_LOG_ERROR, "SQN: "); + ogs_log_hexdump(OGS_LOG_ERROR, sqn, OGS_SQN_LEN); + result_code = OGS_DIAM_CX_ERROR_AUTH_SCHEME_NOT_SUPPORTED; + goto out; + } + } + + rv = hss_db_update_sqn(imsi_bcd, auth_info.rand, auth_info.sqn); + if (rv != OGS_OK) { + ogs_error("Cannot update rand and sqn for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_CX_ERROR_IN_ASSIGNMENT_TYPE; + goto out; + } + + rv = hss_db_increment_sqn(imsi_bcd); + if (rv != OGS_OK) { + ogs_error("Cannot increment sqn for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_CX_ERROR_IN_ASSIGNMENT_TYPE; + goto out; + } + +#if 0 /* Test Vector for ipsec_reg.pcapng */ + /* + * Ki : 8baf473f2f8fd09487cccbd7097c6862 + * OP : 11111111111111111111111111111111 + * OPc : 8E27B6AF0E692E750F32667A3B14605D + * open5gs SQN : 44254 (dec) + * Fhoss SQN : 00000000ad25 (hex) + * RAND : a0944c75ff3c4f0853a2f910aa1f104f + * AMF : c6e2f46c8f4280007f3bb9b84b7c3ff6 + * + * New SQN : 000017242898 (hex) + */ + + auth_info.sqn = 0x17242898; +#define RAND "a0944c75ff3c4f0853a2f910aa1f104f" + OGS_HEX(RAND, strlen(RAND), auth_info.rand); +#endif + + milenage_generate(opc, auth_info.amf, auth_info.k, + ogs_uint64_to_buffer(auth_info.sqn, OGS_SQN_LEN, sqn), auth_info.rand, + autn, ik, ck, ak, xres, &xres_len); + + memcpy(authenticate, auth_info.rand, OGS_RAND_LEN); + memcpy(authenticate + OGS_RAND_LEN, autn, OGS_AUTN_LEN); + + ogs_log_print(OGS_LOG_DEBUG, "K - "); + ogs_log_hexdump(OGS_LOG_DEBUG, auth_info.k, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "AMF - "); + ogs_log_hexdump(OGS_LOG_DEBUG, auth_info.amf, OGS_AMF_LEN); + ogs_log_print(OGS_LOG_DEBUG, "OPc - "); + ogs_log_hexdump(OGS_LOG_DEBUG, opc, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "RAND - "); + ogs_log_hexdump(OGS_LOG_DEBUG, auth_info.rand, OGS_RAND_LEN); + ogs_log_print(OGS_LOG_DEBUG, "SQN - "); + ogs_log_hexdump(OGS_LOG_DEBUG, sqn, OGS_SQN_LEN); + + ogs_log_print(OGS_LOG_DEBUG, "AUTN - "); + ogs_log_hexdump(OGS_LOG_DEBUG, autn, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "ck - "); + ogs_log_hexdump(OGS_LOG_DEBUG, ck, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "ik - "); + ogs_log_hexdump(OGS_LOG_DEBUG, ik, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "ak - "); + ogs_log_hexdump(OGS_LOG_DEBUG, ak, OGS_KEY_LEN); + ogs_log_print(OGS_LOG_DEBUG, "xles - "); + ogs_log_hexdump(OGS_LOG_DEBUG, xres, xres_len); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)user_name; + val.os.len = strlen(user_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Public-Identity AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_public_identity, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)public_identity; + val.os.len = strlen(public_identity); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the SIP-Number-Auth-Items AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_number_auth_items, 0, &avp); + ogs_assert(ret == 0); + val.u32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the SIP-Auth-Data-Item AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_auth_data_item, 0, &avp); + ogs_assert(ret == 0); + + /* Set the SIP-Item-Number AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_item_number, 0, &avpch); + ogs_assert(ret == 0); + val.u32 = 1; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + /* Set the SIP-Authentication-Scheme AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_authentication_scheme, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)OGS_DIAM_CX_AUTH_SCHEME_IMS_AKA; + val.os.len = strlen(OGS_DIAM_CX_AUTH_SCHEME_IMS_AKA); + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + /* Set the SIP-Authenticatie AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_authenticate, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = authenticate; + val.os.len = OGS_KEY_LEN * 2; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + /* Set the SIP-Authorization AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_authorization, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = xres; + val.os.len = xres_len; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + /* Set the Confidentiality-Key AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_confidentiality_key, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = ck; + val.os.len = OGS_KEY_LEN; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + /* Set the Integirty-Key AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_integrity_key, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = ik; + val.os.len = OGS_KEY_LEN; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("Multimedia-Auth-Answer"); + + /* Add this value to the stats */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + if (authentication_scheme) + ogs_free(authentication_scheme); + + ogs_free(user_name); + ogs_free(public_identity); + ogs_free(server_name); + + return 0; + +out: + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Experimental-Result, Origin-Host and Origin-Realm AVPs */ + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + if (authentication_scheme) + ogs_free(authentication_scheme); + + ogs_free(user_name); + ogs_free(public_identity); + ogs_free(server_name); + + return 0; +} + +/* Callback for incoming Server-Assignment-Request messages */ +static int hss_ogs_diam_cx_sar_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int rv, ret; + uint32_t result_code = 0; + + struct msg *ans, *qry; + + struct avp *avpch = NULL; + struct avp_hdr *hdr; + union avp_value val; + + char *user_name = NULL; + char *public_identity = NULL; + char *server_name = NULL; + char *user_data = NULL; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + char msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1]; + + ogs_subscription_data_t subscription_data; + + ogs_assert(msg); + + ogs_debug("Server-Assignment-Request"); + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + /* Get User-Name AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + user_name = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(user_name); + + ogs_extract_digit_from_string(imsi_bcd, user_name); + + /* Get Public-Identity AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_public_identity, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + public_identity = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(public_identity); + + ogs_extract_digit_from_string(msisdn_bcd, public_identity); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + rv = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_CX_ERROR_USER_UNKNOWN; + goto out; + } + + /* Get Server-Name AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_server_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + server_name = ogs_strndup( + (char*)hdr->avp_value->os.data, hdr->avp_value->os.len); + ogs_assert(server_name); + + /* Check if IMPI(User-Name) + IMPU(Public-Identity) is associated */ + bool matched = hss_cx_identity_is_associated(user_name, public_identity); + if (!matched) { + ogs_error("User-Name[%s] Public-Identity[%s] is not assocated", + user_name, public_identity); + result_code = OGS_DIAM_CX_ERROR_IDENTITIES_DONT_MATCH; + goto out; + } + + /* Overwrite Server-Name for IMPI(User-Name) + IMPU(Public-Identity) */ + hss_cx_set_server_name(user_name, public_identity, server_name, true); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)user_name; + val.os.len = strlen(user_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Get User-Data-Already-Available AVP (Mandatory has already been checked) */ + ret = fd_msg_search_avp(qry, ogs_diam_cx_user_data_already_available, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + + if (hdr->avp_value->i32 == OGS_DIAM_CX_USER_DATA_ALREADY_AVAILABLE) { + /* Nothing to do */ + } else { + /* Set the User-Data AVP */ + user_data = download_user_data(user_name, &subscription_data); + ogs_assert(user_data); + + ret = fd_msg_avp_new(ogs_diam_cx_user_data, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)user_data; + val.os.len = strlen(user_data); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Charging-Information AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_charging_information, 0, &avp); + ogs_assert(ret == 0); + + /* Set the Charging-Information AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_charging_information, 0, &avp); + ogs_assert(ret == 0); + + /* Set the Primary-Charging-Collection-Function-Name AVP */ + ret = fd_msg_avp_new( + ogs_diam_cx_primary_charging_collection_function_name, 0, + &avpch); + ogs_assert(ret == 0); +#define PRIMARY_CHARGING_COLLECTION_FUNCTION_NAME "pcrf" + val.os.data = (uint8_t *)PRIMARY_CHARGING_COLLECTION_FUNCTION_NAME; + val.os.len = strlen(PRIMARY_CHARGING_COLLECTION_FUNCTION_NAME); + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("Server-Assignment-Answer"); + + /* Add this value to the stats */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + ogs_subscription_data_free(&subscription_data); + + if (user_data) + ogs_free(user_data); + ogs_free(user_name); + ogs_free(public_identity); + ogs_free(server_name); + + return 0; + +out: + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Experimental-Result, Origin-Host and Origin-Realm AVPs */ + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_subscription_data_free(&subscription_data); + + if (user_data) + ogs_free(user_data); + ogs_free(user_name); + ogs_free(public_identity); + ogs_free(server_name); + + return 0; +} + +static char *download_user_data( + char *user_name, ogs_subscription_data_t *subscription_data) +{ + char *user_data = NULL; + int i; + + ogs_assert(user_name); + ogs_assert(subscription_data); + + user_data = ogs_strdup(ogs_diam_cx_xml_version); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_ims_subscription_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_private_id_s, + user_name, + ogs_diam_cx_xml_private_id_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_service_profile_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_barring_indication_s, + "1", + ogs_diam_cx_xml_barring_indication_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%ssip:%s%s", + ogs_diam_cx_xml_identity_s, + user_name, + ogs_diam_cx_xml_identity_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_identity_type_s, + "0", + ogs_diam_cx_xml_identity_type_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_e); + ogs_assert(user_data); + + for (i = 0; i < subscription_data->num_of_msisdn; i++) { + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%stel:%s%s", + ogs_diam_cx_xml_identity_s, + subscription_data->msisdn[i].bcd, + ogs_diam_cx_xml_identity_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_identity_type_s, + "0", + ogs_diam_cx_xml_identity_type_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_e); + ogs_assert(user_data); + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%ssip:%s%s", + ogs_diam_cx_xml_identity_s, + subscription_data->msisdn[i].bcd, + ogs_diam_cx_xml_identity_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_s); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s%s%s", + ogs_diam_cx_xml_identity_type_s, + "0", + ogs_diam_cx_xml_identity_type_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_extension_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_public_id_e); + ogs_assert(user_data); + } + + user_data = ogs_mstrcatf(user_data, "%s", + ogs_diam_cx_xml_service_profile_e); + ogs_assert(user_data); + + user_data = ogs_mstrcatf(user_data, + "%s", ogs_diam_cx_xml_ims_subscription_e); + ogs_assert(user_data); + + return user_data; +} + +int hss_cx_init(void) +{ + int ret; + struct disp_when data; + + /* Install objects definitions for this application */ + ret = ogs_diam_cx_init(); + ogs_assert(ret == 0); + + memset(&data, 0, sizeof(data)); + data.app = ogs_diam_cx_application; + + /* Fallback CB if command != unexpected message received */ + ret = fd_disp_register(hss_ogs_diam_cx_fb_cb, DISP_HOW_APPID, + &data, NULL, &hdl_cx_fb); + ogs_assert(ret == 0); + + /* Specific handler for User-Authorization-Request */ + data.command = ogs_diam_cx_cmd_uar; + ret = fd_disp_register(hss_ogs_diam_cx_uar_cb, DISP_HOW_CC, &data, NULL, + &hdl_cx_uar); + ogs_assert(ret == 0); + + /* Specific handler for Multimedia-Auth-Request */ + data.command = ogs_diam_cx_cmd_mar; + ret = fd_disp_register(hss_ogs_diam_cx_mar_cb, DISP_HOW_CC, &data, NULL, + &hdl_cx_mar); + ogs_assert(ret == 0); + + /* Specific handler for Server-Assignment-Request */ + data.command = ogs_diam_cx_cmd_sar; + ret = fd_disp_register(hss_ogs_diam_cx_sar_cb, DISP_HOW_CC, &data, NULL, + &hdl_cx_sar); + ogs_assert(ret == 0); + + /* Advertise the support for the application in the peer */ + ret = fd_disp_app_support(ogs_diam_cx_application, ogs_diam_vendor, 1, 0); + ogs_assert(ret == 0); + + return OGS_OK; +} + +void hss_cx_final(void) +{ + if (hdl_cx_fb) + (void) fd_disp_unregister(&hdl_cx_fb, NULL); + if (hdl_cx_uar) + (void) fd_disp_unregister(&hdl_cx_uar, NULL); + if (hdl_cx_mar) + (void) fd_disp_unregister(&hdl_cx_mar, NULL); + if (hdl_cx_sar) + (void) fd_disp_unregister(&hdl_cx_sar, NULL); +} diff --git a/src/hss/hss-fd-path.c b/src/hss/hss-fd-path.c index 688ff5c92..0c66d5ac4 100644 --- a/src/hss/hss-fd-path.c +++ b/src/hss/hss-fd-path.c @@ -22,833 +22,26 @@ #include "hss-context.h" #include "hss-fd-path.h" -/* handler for fallback cb */ -static struct disp_hdl *hdl_s6a_fb = NULL; -/* handler for Authentication-Information-Request cb */ -static struct disp_hdl *hdl_s6a_air = NULL; -/* handler for Update-Location-Request cb */ -static struct disp_hdl *hdl_s6a_ulr = NULL; - -/* Default callback for the application. */ -static int hss_ogs_diam_s6a_fb_cb(struct msg **msg, struct avp *avp, - struct session *session, void *opaque, enum disp_action *act) -{ - /* This CB should never be called */ - ogs_warn("Unexpected message received!"); - - return ENOTSUP; -} - -/* Callback for incoming Authentication-Information-Request messages */ -static int hss_ogs_diam_s6a_air_cb( struct msg **msg, struct avp *avp, - struct session *session, void *opaque, enum disp_action *act) -{ - int ret; - - struct msg *ans, *qry; - struct avp *avpch; - struct avp *avp_e_utran_vector, *avp_xres, *avp_kasme, *avp_rand, *avp_autn; - struct avp_hdr *hdr; - union avp_value val; - - char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; - uint8_t opc[OGS_KEY_LEN]; - uint8_t sqn[OGS_SQN_LEN]; - uint8_t autn[OGS_AUTN_LEN]; - uint8_t ik[OGS_KEY_LEN]; - uint8_t ck[OGS_KEY_LEN]; - uint8_t ak[OGS_AK_LEN]; - uint8_t xres[OGS_MAX_RES_LEN]; - uint8_t kasme[OGS_SHA256_DIGEST_SIZE]; - size_t xres_len = 8; - - uint8_t mac_s[OGS_MAC_S_LEN]; - - ogs_dbi_auth_info_t auth_info; - uint8_t zero[OGS_RAND_LEN]; - int rv; - uint32_t result_code = 0; - - ogs_assert(msg); - - ogs_debug("[HSS] Authentication-Information-Request\n"); - - /* Create answer header */ - qry = *msg; - ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); - ogs_assert(ret == 0); - ans = *msg; - - ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); - ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data, - ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1); - - rv = hss_db_auth_info(imsi_bcd, &auth_info); - if (rv != OGS_OK) { - result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; - goto out; - } - - memset(zero, 0, sizeof(zero)); - if (memcmp(auth_info.rand, zero, OGS_RAND_LEN) == 0) { - ogs_random(auth_info.rand, OGS_RAND_LEN); - } - - if (auth_info.use_opc) - memcpy(opc, auth_info.opc, sizeof(opc)); - else - milenage_opc(auth_info.k, auth_info.op, opc); - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_req_eutran_auth_info, &avp); - ogs_assert(ret == 0); - if (avp) { - ret = fd_avp_search_avp( - avp, ogs_diam_s6a_re_synchronization_info, &avpch); - ogs_assert(ret == 0); - if (avpch) { - ret = fd_msg_avp_hdr(avpch, &hdr); - ogs_assert(ret == 0); - ogs_auc_sqn(opc, auth_info.k, - hdr->avp_value->os.data, - hdr->avp_value->os.data + OGS_RAND_LEN, - sqn, mac_s); - if (memcmp(mac_s, hdr->avp_value->os.data + - OGS_RAND_LEN + OGS_SQN_LEN, OGS_MAC_S_LEN) == 0) { - ogs_random(auth_info.rand, OGS_RAND_LEN); - auth_info.sqn = ogs_buffer_to_uint64(sqn, OGS_SQN_LEN); - /* 33.102 C.3.4 Guide : IND + 1 */ - auth_info.sqn = (auth_info.sqn + 32 + 1) & OGS_MAX_SQN; - } else { - ogs_error("Re-synch MAC failed for IMSI:`%s`", imsi_bcd); - ogs_log_print(OGS_LOG_ERROR, "MAC_S: "); - ogs_log_hexdump(OGS_LOG_ERROR, mac_s, OGS_MAC_S_LEN); - ogs_log_hexdump(OGS_LOG_ERROR, - (void*)(hdr->avp_value->os.data + - OGS_RAND_LEN + OGS_SQN_LEN), - OGS_MAC_S_LEN); - ogs_log_print(OGS_LOG_ERROR, "SQN: "); - ogs_log_hexdump(OGS_LOG_ERROR, sqn, OGS_SQN_LEN); - result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; - goto out; - } - } - } - - rv = hss_db_update_rand_and_sqn(imsi_bcd, auth_info.rand, auth_info.sqn); - if (rv != OGS_OK) { - ogs_error("Cannot update rand and sqn for IMSI:'%s'", imsi_bcd); - result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; - goto out; - } - - rv = hss_db_increment_sqn(imsi_bcd); - if (rv != OGS_OK) { - ogs_error("Cannot increment sqn for IMSI:'%s'", imsi_bcd); - result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; - goto out; - } - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_visited_plmn_id, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); -#if 0 // TODO : check visited_plmn_id - memcpy(visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); -#endif - - milenage_generate(opc, auth_info.amf, auth_info.k, - ogs_uint64_to_buffer(auth_info.sqn, OGS_SQN_LEN, sqn), auth_info.rand, - autn, ik, ck, ak, xres, &xres_len); - ogs_auc_kasme(ck, ik, hdr->avp_value->os.data, sqn, ak, kasme); - - /* Set the Authentication-Info */ - ret = fd_msg_avp_new(ogs_diam_s6a_authentication_info, 0, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_new(ogs_diam_s6a_e_utran_vector, 0, &avp_e_utran_vector); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_rand, 0, &avp_rand); - ogs_assert(ret == 0); - val.os.data = auth_info.rand; - val.os.len = OGS_KEY_LEN; - ret = fd_msg_avp_setvalue(avp_rand, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_rand); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_xres, 0, &avp_xres); - ogs_assert(ret == 0); - val.os.data = xres; - val.os.len = xres_len; - ret = fd_msg_avp_setvalue(avp_xres, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_xres); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_autn, 0, &avp_autn); - ogs_assert(ret == 0); - val.os.data = autn; - val.os.len = OGS_AUTN_LEN; - ret = fd_msg_avp_setvalue(avp_autn, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_autn); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_kasme, 0, &avp_kasme); - ogs_assert(ret == 0); - val.os.data = kasme; - val.os.len = OGS_SHA256_DIGEST_SIZE; - ret = fd_msg_avp_setvalue(avp_kasme, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_kasme); - ogs_assert(ret == 0); - - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_e_utran_vector); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ - ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); - ogs_assert(ret == 0); - - /* Set the Auth-Session-State AVP */ - ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); - ogs_assert(ret == 0); - val.i32 = 1; - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - /* Set Vendor-Specific-Application-Id AVP */ - ret = ogs_diam_message_vendor_specific_appid_set( - ans, OGS_DIAM_S6A_APPLICATION_ID); - ogs_assert(ret == 0); - - /* Send the answer */ - ret = fd_msg_send(msg, NULL, NULL); - ogs_assert(ret == 0); - - ogs_debug("[HSS] Authentication-Information-Answer\n"); - - /* Add this value to the stats */ - ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); - ogs_diam_logger_self()->stats.nb_echoed++; - ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); - - return 0; - -out: - ret = ogs_diam_message_experimental_rescode_set(ans, result_code); - ogs_assert(ret == 0); - - /* Set the Auth-Session-State AVP */ - ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); - ogs_assert(ret == 0); - val.i32 = 1; - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - /* Set Vendor-Specific-Application-Id AVP */ - ret = ogs_diam_message_vendor_specific_appid_set( - ans, OGS_DIAM_S6A_APPLICATION_ID); - ogs_assert(ret == 0); - - ret = fd_msg_send(msg, NULL, NULL); - ogs_assert(ret == 0); - - return 0; -} - -/* Callback for incoming Update-Location-Request messages */ -static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, - struct session *session, void *opaque, enum disp_action *act) -{ - int ret; - struct msg *ans, *qry; - - struct avp_hdr *hdr; - union avp_value val; - - char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; - ogs_s_nssai_t s_nssai; - - int rv; - uint32_t result_code = 0; - ogs_subscription_data_t subscription_data; - ogs_slice_data_t *slice_data = NULL; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - - ogs_assert(msg); - - ogs_debug("[HSS] Update-Location-Request\n"); - - memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); - - /* Create answer header */ - qry = *msg; - ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); - ogs_assert(ret == 0); - ans = *msg; - - ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); - ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data, - ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1); - - rv = hss_db_subscription_data(imsi_bcd, &subscription_data); - if (rv != OGS_OK) { - ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; - goto out; - } - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_visited_plmn_id, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); -#if 0 // TODO : check visited_plmn_id - memcpy(visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); -#endif - - /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ - ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); - ogs_assert(ret == 0); - - /* Set the Auth-Session-State AVP */ - ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); - ogs_assert(ret == 0); - val.i32 = 1; - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - /* Set the ULA Flags */ - ret = fd_msg_avp_new(ogs_diam_s6a_ula_flags, 0, &avp); - ogs_assert(ret == 0); - val.i32 = OGS_DIAM_S6A_ULA_FLAGS_MME_REGISTERED_FOR_SMS; - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - ret = fd_msg_search_avp(qry, ogs_diam_s6a_ulr_flags, &avp); - ogs_assert(ret == 0); - ret = fd_msg_avp_hdr(avp, &hdr); - ogs_assert(ret == 0); - if (!(hdr->avp_value->u32 & OGS_DIAM_S6A_ULR_SKIP_SUBSCRIBER_DATA)) { - struct avp *avp_msisdn, *avp_a_msisdn; - struct avp *avp_access_restriction_data; - struct avp *avp_subscriber_status, *avp_network_access_mode; - struct avp *avp_ambr, *avp_max_bandwidth_ul, *avp_max_bandwidth_dl; - struct avp *avp_rau_tau_timer; - - /* Set the APN Configuration Profile */ - struct avp *apn_configuration_profile; - struct avp *context_identifier; - struct avp *all_apn_configuration_included_indicator; - - int i; - - /* Set the Subscription Data */ - - ret = fd_msg_avp_new(ogs_diam_s6a_subscription_data, 0, &avp); - ogs_assert(ret == 0); - - /* - * TS29.328 - * 6.3.2 MSISDN AVP - * - * The MSISDN AVP is of type OctetString. - * This AVP contains an MSISDN, in international number format - * as described in ITU-T Rec E.164 [8], encoded as a TBCD-string, - * i.e. digits from 0 through 9 are encoded 0000 to 1001; - * 1111 is used as a filler when there is an odd number of digits; - * bits 8 to 5 of octet n encode digit 2n; - * bits 4 to 1 of octet n encode digit 2(n-1)+1. - */ - if (subscription_data.num_of_msisdn >= 1) { - ret = fd_msg_avp_new(ogs_diam_s6a_msisdn, 0, &avp_msisdn); - ogs_assert(ret == 0); - val.os.data = subscription_data.msisdn[0].buf; - val.os.len = subscription_data.msisdn[0].len; - ret = fd_msg_avp_setvalue(avp_msisdn, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_msisdn); - ogs_assert(ret == 0); - } - - if (subscription_data.num_of_msisdn >= 2) { - ret = fd_msg_avp_new(ogs_diam_s6a_a_msisdn, 0, &avp_a_msisdn); - ogs_assert(ret == 0); - val.os.data = subscription_data.msisdn[1].buf; - val.os.len = subscription_data.msisdn[1].len; - ret = fd_msg_avp_setvalue(avp_a_msisdn, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_a_msisdn); - ogs_assert(ret == 0); - } - - if (subscription_data.access_restriction_data) { - ret = fd_msg_avp_new(ogs_diam_s6a_access_restriction_data, 0, - &avp_access_restriction_data); - ogs_assert(ret == 0); - val.i32 = subscription_data.access_restriction_data; - ret = fd_msg_avp_setvalue( avp_access_restriction_data, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, - avp_access_restriction_data); - ogs_assert(ret == 0); - } - - ret = fd_msg_avp_new( - ogs_diam_s6a_subscriber_status, 0, &avp_subscriber_status); - ogs_assert(ret == 0); - val.i32 = subscription_data.subscriber_status; - ret = fd_msg_avp_setvalue(avp_subscriber_status, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_subscriber_status); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_network_access_mode, 0, - &avp_network_access_mode); - ogs_assert(ret == 0); - val.i32 = subscription_data.network_access_mode; - ret = fd_msg_avp_setvalue(avp_network_access_mode, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_network_access_mode); - ogs_assert(ret == 0); - - /* Set the AMBR */ - ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); - ogs_assert(ret == 0); - ret = fd_msg_avp_new( - ogs_diam_s6a_max_bandwidth_ul, 0, &avp_max_bandwidth_ul); - ogs_assert(ret == 0); - val.u32 = subscription_data.ambr.uplink; - ret = fd_msg_avp_setvalue(avp_max_bandwidth_ul, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add( - avp_ambr, MSG_BRW_LAST_CHILD, avp_max_bandwidth_ul); - ogs_assert(ret == 0); - ret = fd_msg_avp_new( - ogs_diam_s6a_max_bandwidth_dl, 0, &avp_max_bandwidth_dl); - ogs_assert(ret == 0); - val.u32 = subscription_data.ambr.downlink; - ret = fd_msg_avp_setvalue(avp_max_bandwidth_dl, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add( - avp_ambr, MSG_BRW_LAST_CHILD, avp_max_bandwidth_dl); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_ambr); - ogs_assert(ret == 0); - - /* Set the Subscribed RAU TAU Timer */ - ret = fd_msg_avp_new( - ogs_diam_s6a_subscribed_rau_tau_timer, 0, &avp_rau_tau_timer); - ogs_assert(ret == 0); - val.i32 = subscription_data.subscribed_rau_tau_timer * 60; /* seconds */ - ret = fd_msg_avp_setvalue(avp_rau_tau_timer, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_rau_tau_timer); - ogs_assert(ret == 0); - - /* For EPC, we'll use SST:1 */ - s_nssai.sst = 1; - s_nssai.sd.v = OGS_S_NSSAI_NO_SD_VALUE; - - slice_data = ogs_slice_find_by_s_nssai( - subscription_data.slice, subscription_data.num_of_slice, - &s_nssai); - if (!slice_data) { - ogs_error("[%s] Cannot find S-NSSAI", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; - goto out; - } - - if (!slice_data->num_of_session) { - ogs_error("[%s] No PDN", imsi_bcd); - result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; - goto out; - } - - ret = fd_msg_avp_new(ogs_diam_s6a_apn_configuration_profile, 0, - &apn_configuration_profile); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_context_identifier, 0, - &context_identifier); - ogs_assert(ret == 0); - val.i32 = 1; /* Context Identifier : 1 */ - ret = fd_msg_avp_setvalue(context_identifier, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration_profile, - MSG_BRW_LAST_CHILD, context_identifier); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new( - ogs_diam_s6a_all_apn_configuration_included_indicator, 0, - &all_apn_configuration_included_indicator); - ogs_assert(ret == 0); - val.i32 = 0; - ret = fd_msg_avp_setvalue( - all_apn_configuration_included_indicator, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration_profile, MSG_BRW_LAST_CHILD, - all_apn_configuration_included_indicator); - ogs_assert(ret == 0); - - for (i = 0; i < slice_data->num_of_session; i++) { - /* Set the APN Configuration */ - struct avp *apn_configuration, *context_identifier, *pdn_type; - struct avp *served_party_ip_address, *service_selection; - struct avp *eps_subscribed_qos_profile, *qos_class_identifier; - struct avp *allocation_retention_priority, *priority_level; - struct avp *pre_emption_capability, *pre_emption_vulnerability; - struct avp *mip6_agent_info, *mip_home_agent_address; - - ogs_session_t *session = &slice_data->session[i]; - ogs_assert(session); - session->context_identifier = i+1; - - ret = fd_msg_avp_new(ogs_diam_s6a_apn_configuration, 0, - &apn_configuration); - ogs_assert(ret == 0); - - /* Set Context-Identifier */ - ret = fd_msg_avp_new(ogs_diam_s6a_context_identifier, 0, - &context_identifier); - ogs_assert(ret == 0); - val.i32 = session->context_identifier; - ret = fd_msg_avp_setvalue(context_identifier, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, context_identifier); - ogs_assert(ret == 0); - - /* Set PDN-Type */ - ret = fd_msg_avp_new(ogs_diam_s6a_pdn_type, 0, &pdn_type); - ogs_assert(ret == 0); - val.i32 = OGS_PDU_SESSION_TYPE_TO_DIAMETER(session->session_type); - ret = fd_msg_avp_setvalue(pdn_type, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, pdn_type); - ogs_assert(ret == 0); - - /* Set Served-Party-IP-Address */ - if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV4 || - session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && - session->ue_ip.ipv4) { - ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, - 0, &served_party_ip_address); - ogs_assert(ret == 0); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = session->ue_ip.addr; - ret = fd_msg_avp_value_encode(&sin, served_party_ip_address); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, - served_party_ip_address); - ogs_assert(ret == 0); - } - - if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV6 || - session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && - session->ue_ip.ipv6) { - ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, - 0, &served_party_ip_address); - ogs_assert(ret == 0); - sin6.sin6_family = AF_INET6; - memcpy(sin6.sin6_addr.s6_addr, - session->ue_ip.addr6, OGS_IPV6_LEN); - ret = fd_msg_avp_value_encode(&sin6, served_party_ip_address); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, - served_party_ip_address); - ogs_assert(ret == 0); - } - - /* Set Service-Selection */ - ret = fd_msg_avp_new(ogs_diam_s6a_service_selection, 0, - &service_selection); - ogs_assert(ret == 0); - ogs_assert(session->name); - val.os.data = (uint8_t *)session->name; - val.os.len = strlen(session->name); - ret = fd_msg_avp_setvalue(service_selection, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, service_selection); - ogs_assert(ret == 0); - - /* Set the EPS Subscribed QoS Profile */ - ret = fd_msg_avp_new(ogs_diam_s6a_eps_subscribed_qos_profile, 0, - &eps_subscribed_qos_profile); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_qos_class_identifier, 0, - &qos_class_identifier); - ogs_assert(ret == 0); - val.i32 = session->qos.index; - ret = fd_msg_avp_setvalue(qos_class_identifier, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(eps_subscribed_qos_profile, - MSG_BRW_LAST_CHILD, qos_class_identifier); - ogs_assert(ret == 0); - - /* Set Allocation retention priority */ - ret = fd_msg_avp_new(ogs_diam_s6a_allocation_retention_priority, 0, - &allocation_retention_priority); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new( - ogs_diam_s6a_priority_level, 0, &priority_level); - ogs_assert(ret == 0); - val.u32 = session->qos.arp.priority_level; - ret = fd_msg_avp_setvalue(priority_level, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(allocation_retention_priority, - MSG_BRW_LAST_CHILD, priority_level); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_pre_emption_capability, 0, - &pre_emption_capability); - ogs_assert(ret == 0); - val.u32 = OGS_EPC_PRE_EMPTION_DISABLED; - if (session->qos.arp.pre_emption_capability == - OGS_5GC_PRE_EMPTION_ENABLED) - val.u32 = OGS_EPC_PRE_EMPTION_ENABLED; - ret = fd_msg_avp_setvalue(pre_emption_capability, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(allocation_retention_priority, - MSG_BRW_LAST_CHILD, pre_emption_capability); - ogs_assert(ret == 0); - - ret = fd_msg_avp_new(ogs_diam_s6a_pre_emption_vulnerability, 0, - &pre_emption_vulnerability); - ogs_assert(ret == 0); - val.u32 = OGS_EPC_PRE_EMPTION_DISABLED; - if (session->qos.arp.pre_emption_vulnerability == - OGS_5GC_PRE_EMPTION_ENABLED) - val.u32 = OGS_EPC_PRE_EMPTION_ENABLED; - ret = fd_msg_avp_setvalue(pre_emption_vulnerability, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(allocation_retention_priority, - MSG_BRW_LAST_CHILD, pre_emption_vulnerability); - ogs_assert(ret == 0); - - ret = fd_msg_avp_add(eps_subscribed_qos_profile, - MSG_BRW_LAST_CHILD, allocation_retention_priority); - ogs_assert(ret == 0); - - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, eps_subscribed_qos_profile); - ogs_assert(ret == 0); - - /* Set MIP6-Agent-Info */ - if (session->smf_ip.ipv4 || session->smf_ip.ipv6) { - ret = fd_msg_avp_new(ogs_diam_mip6_agent_info, 0, - &mip6_agent_info); - ogs_assert(ret == 0); - - if (session->smf_ip.ipv4) { - ret = fd_msg_avp_new(ogs_diam_mip_home_agent_address, 0, - &mip_home_agent_address); - ogs_assert(ret == 0); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = session->smf_ip.addr; - ret = fd_msg_avp_value_encode ( - &sin, mip_home_agent_address ); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(mip6_agent_info, - MSG_BRW_LAST_CHILD, mip_home_agent_address); - ogs_assert(ret == 0); - } - - if (session->smf_ip.ipv6) { - ret = fd_msg_avp_new(ogs_diam_mip_home_agent_address, 0, - &mip_home_agent_address); - ogs_assert(ret == 0); - sin6.sin6_family = AF_INET6; - memcpy(sin6.sin6_addr.s6_addr, session->smf_ip.addr6, - sizeof session->smf_ip.addr6); - ret = fd_msg_avp_value_encode ( - &sin6, mip_home_agent_address ); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(mip6_agent_info, - MSG_BRW_LAST_CHILD, mip_home_agent_address); - ogs_assert(ret == 0); - } - - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, mip6_agent_info); - ogs_assert(ret == 0); - } - - /* Set AMBR */ - if (session->ambr.downlink || session->ambr.uplink) { - ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); - ogs_assert(ret == 0); - ret = fd_msg_avp_new(ogs_diam_s6a_max_bandwidth_ul, 0, - &avp_max_bandwidth_ul); - ogs_assert(ret == 0); - val.u32 = session->ambr.uplink; - ret = fd_msg_avp_setvalue(avp_max_bandwidth_ul, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_ambr, MSG_BRW_LAST_CHILD, - avp_max_bandwidth_ul); - ogs_assert(ret == 0); - ret = fd_msg_avp_new(ogs_diam_s6a_max_bandwidth_dl, 0, - &avp_max_bandwidth_dl); - ogs_assert(ret == 0); - val.u32 = session->ambr.downlink; - ret = fd_msg_avp_setvalue(avp_max_bandwidth_dl, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(avp_ambr, MSG_BRW_LAST_CHILD, - avp_max_bandwidth_dl); - ogs_assert(ret == 0); - - ret = fd_msg_avp_add(apn_configuration, - MSG_BRW_LAST_CHILD, avp_ambr); - ogs_assert(ret == 0); - } - - ret = fd_msg_avp_add(apn_configuration_profile, - MSG_BRW_LAST_CHILD, apn_configuration); - ogs_assert(ret == 0); - } - ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, - apn_configuration_profile); - ogs_assert(ret == 0); - - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - } - - /* Set Vendor-Specific-Application-Id AVP */ - ret = ogs_diam_message_vendor_specific_appid_set( - ans, OGS_DIAM_S6A_APPLICATION_ID); - ogs_assert(ret == 0); - - /* Send the answer */ - ret = fd_msg_send(msg, NULL, NULL); - ogs_assert(ret == 0); - - ogs_debug("[HSS] Update-Location-Answer\n"); - - /* Add this value to the stats */ - ogs_assert( pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); - ogs_diam_logger_self()->stats.nb_echoed++; - ogs_assert( pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); - - ogs_subscription_data_free(&subscription_data); - - return 0; - -out: - ret = ogs_diam_message_experimental_rescode_set(ans, result_code); - ogs_assert(ret == 0); - - /* Set the Auth-Session-State AVP */ - ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); - ogs_assert(ret == 0); - val.i32 = 1; - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); - - /* Set Vendor-Specific-Application-Id AVP */ - ret = ogs_diam_message_vendor_specific_appid_set( - ans, OGS_DIAM_S6A_APPLICATION_ID); - ogs_assert(ret == 0); - - ret = fd_msg_send(msg, NULL, NULL); - ogs_assert(ret == 0); - - ogs_subscription_data_free(&subscription_data); - - return 0; -} - int hss_fd_init(void) { - int ret; - struct disp_when data; - struct dict_object *s6a_app, *vnd; - struct dict_vendor_data vnd_data; - struct dict_application_data s6a_app_data; + int rv; - ret = ogs_diam_init(FD_MODE_SERVER, + rv = ogs_diam_init(FD_MODE_SERVER, hss_self()->diam_conf_path, hss_self()->diam_config); - ogs_assert(ret == 0); + ogs_assert(rv == 0); - vnd_data.vendor_id = 10415; - vnd_data.vendor_name = (char *) "3GPP"; - - ret = fd_dict_new(fd_g_config->cnf_dict, - DICT_VENDOR, &vnd_data, NULL, &vnd); - ogs_assert(ret == 0); - - s6a_app_data.application_id = 16777251; - s6a_app_data.application_name = (char *) "S6A"; - - ret = fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, - &s6a_app_data, NULL, &s6a_app); - ogs_assert(ret == 0); - - ret = fd_disp_app_support(s6a_app, vnd, 1, 0); - ogs_assert(ret == 0); - - /* Install objects definitions for this application */ - ret = ogs_diam_s6a_init(); - ogs_assert(ret == 0); - - memset(&data, 0, sizeof(data)); - data.app = ogs_diam_s6a_application; - - /* Fallback CB if command != unexpected message received */ - ret = fd_disp_register(hss_ogs_diam_s6a_fb_cb, DISP_HOW_APPID, &data, NULL, - &hdl_s6a_fb); - ogs_assert(ret == 0); - - /* Specific handler for Authentication-Information-Request */ - data.command = ogs_diam_s6a_cmd_air; - ret = fd_disp_register(hss_ogs_diam_s6a_air_cb, DISP_HOW_CC, &data, NULL, - &hdl_s6a_air); - ogs_assert(ret == 0); - - /* Specific handler for Location-Update-Request */ - data.command = ogs_diam_s6a_cmd_ulr; - ret = fd_disp_register(hss_ogs_diam_s6a_ulr_cb, DISP_HOW_CC, &data, NULL, - &hdl_s6a_ulr); - ogs_assert(ret == 0); - - /* Advertise the support for the application in the peer */ - ret = fd_disp_app_support(ogs_diam_s6a_application, ogs_diam_vendor, 1, 0); - ogs_assert(ret == 0); + rv = hss_s6a_init(); + ogs_assert(rv == OGS_OK); + rv = hss_cx_init(); + ogs_assert(rv == OGS_OK); return OGS_OK; } void hss_fd_final(void) { - if (hdl_s6a_fb) - (void) fd_disp_unregister(&hdl_s6a_fb, NULL); - if (hdl_s6a_air) - (void) fd_disp_unregister(&hdl_s6a_air, NULL); - if (hdl_s6a_ulr) - (void) fd_disp_unregister(&hdl_s6a_ulr, NULL); + hss_s6a_final(); + hss_cx_final(); ogs_diam_final(); } diff --git a/src/hss/hss-fd-path.h b/src/hss/hss-fd-path.h index 40202dfbf..4e08b11ef 100644 --- a/src/hss/hss-fd-path.h +++ b/src/hss/hss-fd-path.h @@ -27,6 +27,11 @@ extern "C" { int hss_fd_init(void); void hss_fd_final(void); +int hss_s6a_init(void); +void hss_s6a_final(void); +int hss_cx_init(void); +void hss_cx_final(void); + #ifdef __cplusplus } #endif diff --git a/src/hss/hss-s6a-path.c b/src/hss/hss-s6a-path.c new file mode 100644 index 000000000..550593e17 --- /dev/null +++ b/src/hss/hss-s6a-path.c @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ogs-crypt.h" + +#include "hss-context.h" +#include "hss-fd-path.h" + +/* handler for fallback cb */ +static struct disp_hdl *hdl_s6a_fb = NULL; +/* handler for Authentication-Information-Request cb */ +static struct disp_hdl *hdl_s6a_air = NULL; +/* handler for Update-Location-Request cb */ +static struct disp_hdl *hdl_s6a_ulr = NULL; + +/* Default callback for the application. */ +static int hss_ogs_diam_s6a_fb_cb(struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + /* This CB should never be called */ + ogs_warn("Unexpected message received!"); + + return ENOTSUP; +} + +/* Callback for incoming Authentication-Information-Request messages */ +static int hss_ogs_diam_s6a_air_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int ret; + + struct msg *ans, *qry; + struct avp *avpch; + struct avp *avp_e_utran_vector, *avp_xres, *avp_kasme, *avp_rand, *avp_autn; + struct avp_hdr *hdr; + union avp_value val; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + uint8_t opc[OGS_KEY_LEN]; + uint8_t sqn[OGS_SQN_LEN]; + uint8_t autn[OGS_AUTN_LEN]; + uint8_t ik[OGS_KEY_LEN]; + uint8_t ck[OGS_KEY_LEN]; + uint8_t ak[OGS_AK_LEN]; + uint8_t xres[OGS_MAX_RES_LEN]; + uint8_t kasme[OGS_SHA256_DIGEST_SIZE]; + size_t xres_len = 8; + + uint8_t mac_s[OGS_MAC_S_LEN]; + + ogs_dbi_auth_info_t auth_info; + uint8_t zero[OGS_RAND_LEN]; + int rv; + uint32_t result_code = 0; + + ogs_assert(msg); + + ogs_debug("Authentication-Information-Request"); + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1); + + rv = hss_db_auth_info(imsi_bcd, &auth_info); + if (rv != OGS_OK) { + result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; + goto out; + } + + memset(zero, 0, sizeof(zero)); + if (memcmp(auth_info.rand, zero, OGS_RAND_LEN) == 0) { + ogs_random(auth_info.rand, OGS_RAND_LEN); + } + + if (auth_info.use_opc) + memcpy(opc, auth_info.opc, sizeof(opc)); + else + milenage_opc(auth_info.k, auth_info.op, opc); + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_req_eutran_auth_info, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_avp_search_avp( + avp, ogs_diam_s6a_re_synchronization_info, &avpch); + ogs_assert(ret == 0); + if (avpch) { + ret = fd_msg_avp_hdr(avpch, &hdr); + ogs_assert(ret == 0); + ogs_auc_sqn(opc, auth_info.k, + hdr->avp_value->os.data, + hdr->avp_value->os.data + OGS_RAND_LEN, + sqn, mac_s); + if (memcmp(mac_s, hdr->avp_value->os.data + + OGS_RAND_LEN + OGS_SQN_LEN, OGS_MAC_S_LEN) == 0) { + ogs_random(auth_info.rand, OGS_RAND_LEN); + auth_info.sqn = ogs_buffer_to_uint64(sqn, OGS_SQN_LEN); + /* 33.102 C.3.4 Guide : IND + 1 */ + auth_info.sqn = (auth_info.sqn + 32 + 1) & OGS_MAX_SQN; + } else { + ogs_error("Re-synch MAC failed for IMSI:`%s`", imsi_bcd); + ogs_log_print(OGS_LOG_ERROR, "MAC_S: "); + ogs_log_hexdump(OGS_LOG_ERROR, mac_s, OGS_MAC_S_LEN); + ogs_log_hexdump(OGS_LOG_ERROR, + (void*)(hdr->avp_value->os.data + + OGS_RAND_LEN + OGS_SQN_LEN), + OGS_MAC_S_LEN); + ogs_log_print(OGS_LOG_ERROR, "SQN: "); + ogs_log_hexdump(OGS_LOG_ERROR, sqn, OGS_SQN_LEN); + result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; + goto out; + } + } + } + + rv = hss_db_update_sqn(imsi_bcd, auth_info.rand, auth_info.sqn); + if (rv != OGS_OK) { + ogs_error("Cannot update rand and sqn for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; + goto out; + } + + rv = hss_db_increment_sqn(imsi_bcd); + if (rv != OGS_OK) { + ogs_error("Cannot increment sqn for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_S6A_AUTHENTICATION_DATA_UNAVAILABLE; + goto out; + } + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_visited_plmn_id, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); +#if 0 // TODO : check visited_plmn_id + memcpy(visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); +#endif + + milenage_generate(opc, auth_info.amf, auth_info.k, + ogs_uint64_to_buffer(auth_info.sqn, OGS_SQN_LEN, sqn), auth_info.rand, + autn, ik, ck, ak, xres, &xres_len); + ogs_auc_kasme(ck, ik, hdr->avp_value->os.data, sqn, ak, kasme); + + /* Set the Authentication-Info */ + ret = fd_msg_avp_new(ogs_diam_s6a_authentication_info, 0, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_new(ogs_diam_s6a_e_utran_vector, 0, &avp_e_utran_vector); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_rand, 0, &avp_rand); + ogs_assert(ret == 0); + val.os.data = auth_info.rand; + val.os.len = OGS_KEY_LEN; + ret = fd_msg_avp_setvalue(avp_rand, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_rand); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_xres, 0, &avp_xres); + ogs_assert(ret == 0); + val.os.data = xres; + val.os.len = xres_len; + ret = fd_msg_avp_setvalue(avp_xres, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_xres); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_autn, 0, &avp_autn); + ogs_assert(ret == 0); + val.os.data = autn; + val.os.len = OGS_AUTN_LEN; + ret = fd_msg_avp_setvalue(avp_autn, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_autn); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_kasme, 0, &avp_kasme); + ogs_assert(ret == 0); + val.os.data = kasme; + val.os.len = OGS_SHA256_DIGEST_SIZE; + ret = fd_msg_avp_setvalue(avp_kasme, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_e_utran_vector, MSG_BRW_LAST_CHILD, avp_kasme); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_e_utran_vector); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("Authentication-Information-Answer"); + + /* Add this value to the stats */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + return 0; + +out: + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + return 0; +} + +/* Callback for incoming Update-Location-Request messages */ +static int hss_ogs_diam_s6a_ulr_cb( struct msg **msg, struct avp *avp, + struct session *session, void *opaque, enum disp_action *act) +{ + int ret; + struct msg *ans, *qry; + + struct avp_hdr *hdr; + union avp_value val; + + char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1]; + ogs_s_nssai_t s_nssai; + + int rv; + uint32_t result_code = 0; + ogs_subscription_data_t subscription_data; + ogs_slice_data_t *slice_data = NULL; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + + ogs_assert(msg); + + ogs_debug("Update-Location-Request"); + + memset(&subscription_data, 0, sizeof(ogs_subscription_data_t)); + + /* Create answer header */ + qry = *msg; + ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0); + ogs_assert(ret == 0); + ans = *msg; + + ret = fd_msg_search_avp(qry, ogs_diam_user_name, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data, + ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1); + + rv = hss_db_subscription_data(imsi_bcd, &subscription_data); + if (rv != OGS_OK) { + ogs_error("Cannot get Subscription-Data for IMSI:'%s'", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_USER_UNKNOWN; + goto out; + } + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_visited_plmn_id, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); +#if 0 // TODO : check visited_plmn_id + memcpy(visited_plmn_id, hdr->avp_value->os.data, hdr->avp_value->os.len); +#endif + + /* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */ + ret = fd_msg_rescode_set(ans, (char*)"DIAMETER_SUCCESS", NULL, NULL, 1); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the ULA Flags */ + ret = fd_msg_avp_new(ogs_diam_s6a_ula_flags, 0, &avp); + ogs_assert(ret == 0); + val.i32 = OGS_DIAM_S6A_ULA_FLAGS_MME_REGISTERED_FOR_SMS; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = fd_msg_search_avp(qry, ogs_diam_s6a_ulr_flags, &avp); + ogs_assert(ret == 0); + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + if (!(hdr->avp_value->u32 & OGS_DIAM_S6A_ULR_SKIP_SUBSCRIBER_DATA)) { + struct avp *avp_msisdn, *avp_a_msisdn; + struct avp *avp_access_restriction_data; + struct avp *avp_subscriber_status, *avp_network_access_mode; + struct avp *avp_ambr, *avp_max_bandwidth_ul, *avp_max_bandwidth_dl; + struct avp *avp_rau_tau_timer; + + /* Set the APN Configuration Profile */ + struct avp *apn_configuration_profile; + struct avp *context_identifier; + struct avp *all_apn_configuration_included_indicator; + + int i; + + /* Set the Subscription Data */ + + ret = fd_msg_avp_new(ogs_diam_s6a_subscription_data, 0, &avp); + ogs_assert(ret == 0); + + /* + * TS29.328 + * 6.3.2 MSISDN AVP + * + * The MSISDN AVP is of type OctetString. + * This AVP contains an MSISDN, in international number format + * as described in ITU-T Rec E.164 [8], encoded as a TBCD-string, + * i.e. digits from 0 through 9 are encoded 0000 to 1001; + * 1111 is used as a filler when there is an odd number of digits; + * bits 8 to 5 of octet n encode digit 2n; + * bits 4 to 1 of octet n encode digit 2(n-1)+1. + */ + if (subscription_data.num_of_msisdn >= 1) { + ret = fd_msg_avp_new(ogs_diam_s6a_msisdn, 0, &avp_msisdn); + ogs_assert(ret == 0); + val.os.data = subscription_data.msisdn[0].buf; + val.os.len = subscription_data.msisdn[0].len; + ret = fd_msg_avp_setvalue(avp_msisdn, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_msisdn); + ogs_assert(ret == 0); + } + + if (subscription_data.num_of_msisdn >= 2) { + ret = fd_msg_avp_new(ogs_diam_s6a_a_msisdn, 0, &avp_a_msisdn); + ogs_assert(ret == 0); + val.os.data = subscription_data.msisdn[1].buf; + val.os.len = subscription_data.msisdn[1].len; + ret = fd_msg_avp_setvalue(avp_a_msisdn, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_a_msisdn); + ogs_assert(ret == 0); + } + + if (subscription_data.access_restriction_data) { + ret = fd_msg_avp_new(ogs_diam_s6a_access_restriction_data, 0, + &avp_access_restriction_data); + ogs_assert(ret == 0); + val.i32 = subscription_data.access_restriction_data; + ret = fd_msg_avp_setvalue( avp_access_restriction_data, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, + avp_access_restriction_data); + ogs_assert(ret == 0); + } + + ret = fd_msg_avp_new( + ogs_diam_s6a_subscriber_status, 0, &avp_subscriber_status); + ogs_assert(ret == 0); + val.i32 = subscription_data.subscriber_status; + ret = fd_msg_avp_setvalue(avp_subscriber_status, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_subscriber_status); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_network_access_mode, 0, + &avp_network_access_mode); + ogs_assert(ret == 0); + val.i32 = subscription_data.network_access_mode; + ret = fd_msg_avp_setvalue(avp_network_access_mode, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_network_access_mode); + ogs_assert(ret == 0); + + /* Set the AMBR */ + ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); + ogs_assert(ret == 0); + ret = fd_msg_avp_new( + ogs_diam_s6a_max_bandwidth_ul, 0, &avp_max_bandwidth_ul); + ogs_assert(ret == 0); + val.u32 = subscription_data.ambr.uplink; + ret = fd_msg_avp_setvalue(avp_max_bandwidth_ul, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add( + avp_ambr, MSG_BRW_LAST_CHILD, avp_max_bandwidth_ul); + ogs_assert(ret == 0); + ret = fd_msg_avp_new( + ogs_diam_s6a_max_bandwidth_dl, 0, &avp_max_bandwidth_dl); + ogs_assert(ret == 0); + val.u32 = subscription_data.ambr.downlink; + ret = fd_msg_avp_setvalue(avp_max_bandwidth_dl, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add( + avp_ambr, MSG_BRW_LAST_CHILD, avp_max_bandwidth_dl); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_ambr); + ogs_assert(ret == 0); + + /* Set the Subscribed RAU TAU Timer */ + ret = fd_msg_avp_new( + ogs_diam_s6a_subscribed_rau_tau_timer, 0, &avp_rau_tau_timer); + ogs_assert(ret == 0); + val.i32 = subscription_data.subscribed_rau_tau_timer * 60; /* seconds */ + ret = fd_msg_avp_setvalue(avp_rau_tau_timer, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avp_rau_tau_timer); + ogs_assert(ret == 0); + + /* For EPC, we'll use SST:1 */ + s_nssai.sst = 1; + s_nssai.sd.v = OGS_S_NSSAI_NO_SD_VALUE; + + slice_data = ogs_slice_find_by_s_nssai( + subscription_data.slice, subscription_data.num_of_slice, + &s_nssai); + if (!slice_data) { + ogs_error("[%s] Cannot find S-NSSAI", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; + goto out; + } + + if (!slice_data->num_of_session) { + ogs_error("[%s] No PDN", imsi_bcd); + result_code = OGS_DIAM_S6A_ERROR_UNKNOWN_EPS_SUBSCRIPTION; + goto out; + } + + ret = fd_msg_avp_new(ogs_diam_s6a_apn_configuration_profile, 0, + &apn_configuration_profile); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_context_identifier, 0, + &context_identifier); + ogs_assert(ret == 0); + val.i32 = 1; /* Context Identifier : 1 */ + ret = fd_msg_avp_setvalue(context_identifier, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration_profile, + MSG_BRW_LAST_CHILD, context_identifier); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new( + ogs_diam_s6a_all_apn_configuration_included_indicator, 0, + &all_apn_configuration_included_indicator); + ogs_assert(ret == 0); + val.i32 = 0; + ret = fd_msg_avp_setvalue( + all_apn_configuration_included_indicator, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration_profile, MSG_BRW_LAST_CHILD, + all_apn_configuration_included_indicator); + ogs_assert(ret == 0); + + for (i = 0; i < slice_data->num_of_session; i++) { + /* Set the APN Configuration */ + struct avp *apn_configuration, *context_identifier, *pdn_type; + struct avp *served_party_ip_address, *service_selection; + struct avp *eps_subscribed_qos_profile, *qos_class_identifier; + struct avp *allocation_retention_priority, *priority_level; + struct avp *pre_emption_capability, *pre_emption_vulnerability; + struct avp *mip6_agent_info, *mip_home_agent_address; + + ogs_session_t *session = &slice_data->session[i]; + ogs_assert(session); + session->context_identifier = i+1; + + ret = fd_msg_avp_new(ogs_diam_s6a_apn_configuration, 0, + &apn_configuration); + ogs_assert(ret == 0); + + /* Set Context-Identifier */ + ret = fd_msg_avp_new(ogs_diam_s6a_context_identifier, 0, + &context_identifier); + ogs_assert(ret == 0); + val.i32 = session->context_identifier; + ret = fd_msg_avp_setvalue(context_identifier, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, context_identifier); + ogs_assert(ret == 0); + + /* Set PDN-Type */ + ret = fd_msg_avp_new(ogs_diam_s6a_pdn_type, 0, &pdn_type); + ogs_assert(ret == 0); + val.i32 = OGS_PDU_SESSION_TYPE_TO_DIAMETER(session->session_type); + ret = fd_msg_avp_setvalue(pdn_type, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, pdn_type); + ogs_assert(ret == 0); + + /* Set Served-Party-IP-Address */ + if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV4 || + session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && + session->ue_ip.ipv4) { + ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, + 0, &served_party_ip_address); + ogs_assert(ret == 0); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = session->ue_ip.addr; + ret = fd_msg_avp_value_encode(&sin, served_party_ip_address); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, + served_party_ip_address); + ogs_assert(ret == 0); + } + + if ((session->session_type == OGS_PDU_SESSION_TYPE_IPV6 || + session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) && + session->ue_ip.ipv6) { + ret = fd_msg_avp_new(ogs_diam_s6a_served_party_ip_address, + 0, &served_party_ip_address); + ogs_assert(ret == 0); + sin6.sin6_family = AF_INET6; + memcpy(sin6.sin6_addr.s6_addr, + session->ue_ip.addr6, OGS_IPV6_LEN); + ret = fd_msg_avp_value_encode(&sin6, served_party_ip_address); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration, MSG_BRW_LAST_CHILD, + served_party_ip_address); + ogs_assert(ret == 0); + } + + /* Set Service-Selection */ + ret = fd_msg_avp_new(ogs_diam_s6a_service_selection, 0, + &service_selection); + ogs_assert(ret == 0); + ogs_assert(session->name); + val.os.data = (uint8_t *)session->name; + val.os.len = strlen(session->name); + ret = fd_msg_avp_setvalue(service_selection, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, service_selection); + ogs_assert(ret == 0); + + /* Set the EPS Subscribed QoS Profile */ + ret = fd_msg_avp_new(ogs_diam_s6a_eps_subscribed_qos_profile, 0, + &eps_subscribed_qos_profile); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_qos_class_identifier, 0, + &qos_class_identifier); + ogs_assert(ret == 0); + val.i32 = session->qos.index; + ret = fd_msg_avp_setvalue(qos_class_identifier, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(eps_subscribed_qos_profile, + MSG_BRW_LAST_CHILD, qos_class_identifier); + ogs_assert(ret == 0); + + /* Set Allocation retention priority */ + ret = fd_msg_avp_new(ogs_diam_s6a_allocation_retention_priority, 0, + &allocation_retention_priority); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new( + ogs_diam_s6a_priority_level, 0, &priority_level); + ogs_assert(ret == 0); + val.u32 = session->qos.arp.priority_level; + ret = fd_msg_avp_setvalue(priority_level, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(allocation_retention_priority, + MSG_BRW_LAST_CHILD, priority_level); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_pre_emption_capability, 0, + &pre_emption_capability); + ogs_assert(ret == 0); + val.u32 = OGS_EPC_PRE_EMPTION_DISABLED; + if (session->qos.arp.pre_emption_capability == + OGS_5GC_PRE_EMPTION_ENABLED) + val.u32 = OGS_EPC_PRE_EMPTION_ENABLED; + ret = fd_msg_avp_setvalue(pre_emption_capability, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(allocation_retention_priority, + MSG_BRW_LAST_CHILD, pre_emption_capability); + ogs_assert(ret == 0); + + ret = fd_msg_avp_new(ogs_diam_s6a_pre_emption_vulnerability, 0, + &pre_emption_vulnerability); + ogs_assert(ret == 0); + val.u32 = OGS_EPC_PRE_EMPTION_DISABLED; + if (session->qos.arp.pre_emption_vulnerability == + OGS_5GC_PRE_EMPTION_ENABLED) + val.u32 = OGS_EPC_PRE_EMPTION_ENABLED; + ret = fd_msg_avp_setvalue(pre_emption_vulnerability, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(allocation_retention_priority, + MSG_BRW_LAST_CHILD, pre_emption_vulnerability); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(eps_subscribed_qos_profile, + MSG_BRW_LAST_CHILD, allocation_retention_priority); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, eps_subscribed_qos_profile); + ogs_assert(ret == 0); + + /* Set MIP6-Agent-Info */ + if (session->smf_ip.ipv4 || session->smf_ip.ipv6) { + ret = fd_msg_avp_new(ogs_diam_mip6_agent_info, 0, + &mip6_agent_info); + ogs_assert(ret == 0); + + if (session->smf_ip.ipv4) { + ret = fd_msg_avp_new(ogs_diam_mip_home_agent_address, 0, + &mip_home_agent_address); + ogs_assert(ret == 0); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = session->smf_ip.addr; + ret = fd_msg_avp_value_encode ( + &sin, mip_home_agent_address ); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(mip6_agent_info, + MSG_BRW_LAST_CHILD, mip_home_agent_address); + ogs_assert(ret == 0); + } + + if (session->smf_ip.ipv6) { + ret = fd_msg_avp_new(ogs_diam_mip_home_agent_address, 0, + &mip_home_agent_address); + ogs_assert(ret == 0); + sin6.sin6_family = AF_INET6; + memcpy(sin6.sin6_addr.s6_addr, session->smf_ip.addr6, + sizeof session->smf_ip.addr6); + ret = fd_msg_avp_value_encode ( + &sin6, mip_home_agent_address ); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(mip6_agent_info, + MSG_BRW_LAST_CHILD, mip_home_agent_address); + ogs_assert(ret == 0); + } + + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, mip6_agent_info); + ogs_assert(ret == 0); + } + + /* Set AMBR */ + if (session->ambr.downlink || session->ambr.uplink) { + ret = fd_msg_avp_new(ogs_diam_s6a_ambr, 0, &avp_ambr); + ogs_assert(ret == 0); + ret = fd_msg_avp_new(ogs_diam_s6a_max_bandwidth_ul, 0, + &avp_max_bandwidth_ul); + ogs_assert(ret == 0); + val.u32 = session->ambr.uplink; + ret = fd_msg_avp_setvalue(avp_max_bandwidth_ul, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_ambr, MSG_BRW_LAST_CHILD, + avp_max_bandwidth_ul); + ogs_assert(ret == 0); + ret = fd_msg_avp_new(ogs_diam_s6a_max_bandwidth_dl, 0, + &avp_max_bandwidth_dl); + ogs_assert(ret == 0); + val.u32 = session->ambr.downlink; + ret = fd_msg_avp_setvalue(avp_max_bandwidth_dl, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp_ambr, MSG_BRW_LAST_CHILD, + avp_max_bandwidth_dl); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(apn_configuration, + MSG_BRW_LAST_CHILD, avp_ambr); + ogs_assert(ret == 0); + } + + ret = fd_msg_avp_add(apn_configuration_profile, + MSG_BRW_LAST_CHILD, apn_configuration); + ogs_assert(ret == 0); + } + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, + apn_configuration_profile); + ogs_assert(ret == 0); + + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Send the answer */ + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_debug("Update-Location-Answer"); + + /* Add this value to the stats */ + ogs_assert( pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_echoed++; + ogs_assert( pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + ogs_subscription_data_free(&subscription_data); + + return 0; + +out: + ret = ogs_diam_message_experimental_rescode_set(ans, result_code); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + ans, OGS_DIAM_S6A_APPLICATION_ID); + ogs_assert(ret == 0); + + ret = fd_msg_send(msg, NULL, NULL); + ogs_assert(ret == 0); + + ogs_subscription_data_free(&subscription_data); + + return 0; +} + +int hss_s6a_init(void) +{ + int ret; + struct disp_when data; + + /* Install objects definitions for this application */ + ret = ogs_diam_s6a_init(); + ogs_assert(ret == 0); + + memset(&data, 0, sizeof(data)); + data.app = ogs_diam_s6a_application; + + /* Fallback CB if command != unexpected message received */ + ret = fd_disp_register(hss_ogs_diam_s6a_fb_cb, DISP_HOW_APPID, &data, NULL, + &hdl_s6a_fb); + ogs_assert(ret == 0); + + /* Specific handler for Authentication-Information-Request */ + data.command = ogs_diam_s6a_cmd_air; + ret = fd_disp_register(hss_ogs_diam_s6a_air_cb, DISP_HOW_CC, &data, NULL, + &hdl_s6a_air); + ogs_assert(ret == 0); + + /* Specific handler for Location-Update-Request */ + data.command = ogs_diam_s6a_cmd_ulr; + ret = fd_disp_register(hss_ogs_diam_s6a_ulr_cb, DISP_HOW_CC, &data, NULL, + &hdl_s6a_ulr); + ogs_assert(ret == 0); + + /* Advertise the support for the application in the peer */ + ret = fd_disp_app_support(ogs_diam_s6a_application, ogs_diam_vendor, 1, 0); + ogs_assert(ret == 0); + + return OGS_OK; +} + +void hss_s6a_final(void) +{ + if (hdl_s6a_fb) + (void) fd_disp_unregister(&hdl_s6a_fb, NULL); + if (hdl_s6a_air) + (void) fd_disp_unregister(&hdl_s6a_air, NULL); + if (hdl_s6a_ulr) + (void) fd_disp_unregister(&hdl_s6a_ulr, NULL); +} diff --git a/src/hss/meson.build b/src/hss/meson.build index 57c31ebad..f6977926d 100644 --- a/src/hss/meson.build +++ b/src/hss/meson.build @@ -18,19 +18,24 @@ libhss_sources = files(''' hss-context.h hss-fd-path.h + hss-init.c hss-context.c + hss-s6a-path.c + hss-cx-path.c hss-fd-path.c '''.split()) libhss = static_library('hss', sources : libhss_sources, - dependencies : [libapp_dep, libcrypt_dep, libdbi_dep, libdiameter_s6a_dep], + dependencies : [libapp_dep, libcrypt_dep, libdbi_dep, + libdiameter_s6a_dep, libdiameter_cx_dep], install : false) libhss_dep = declare_dependency( link_with : libhss, - dependencies : [libapp_dep, libcrypt_dep, libdbi_dep, libdiameter_s6a_dep]) + dependencies : [libapp_dep, libcrypt_dep, libdbi_dep, + libdiameter_s6a_dep, libdiameter_cx_dep]) hss_sources = files(''' app-init.c diff --git a/src/mme/mme-fd-path.c b/src/mme/mme-fd-path.c index 1bd165a11..cf27f46ee 100644 --- a/src/mme/mme-fd-path.c +++ b/src/mme/mme-fd-path.c @@ -58,17 +58,17 @@ void mme_s6a_send_air(mme_ue_t *mme_ue, /* Clear Security Context */ CLEAR_SECURITY_CONTEXT(mme_ue); - + /* Create the random value to store with the session */ sess_data = ogs_calloc(1, sizeof (*sess_data)); ogs_assert(sess_data); - + sess_data->mme_ue = mme_ue; - + /* Create the request */ ret = fd_msg_new(ogs_diam_s6a_cmd_air, MSGFL_ALLOC_ETEID, &req); ogs_assert(ret == 0); - + /* Create a new session */ #define OGS_DIAM_S6A_APP_SID_OPT "app_s6a" ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_S6A_APP_SID_OPT, @@ -89,7 +89,7 @@ void mme_s6a_send_air(mme_ue_t *mme_ue, /* Set Origin-Host & Origin-Realm */ ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); - + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -99,12 +99,12 @@ void mme_s6a_send_air(mme_ue_t *mme_ue, ogs_assert(ret == 0); ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); - + /* Set the User-Name AVP */ ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); ogs_assert(ret == 0); val.os.data = (uint8_t *)mme_ue->imsi_bcd; - val.os.len = strlen(mme_ue->imsi_bcd); + val.os.len = strlen(mme_ue->imsi_bcd); ret = fd_msg_avp_setvalue(avp, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); @@ -163,16 +163,16 @@ void mme_s6a_send_air(mme_ue_t *mme_ue, ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts); ogs_assert(ret == 0); - - /* Keep a pointer to the session data for debug purpose, + + /* Keep a pointer to the session data for debug purpose, * in real life we would not need it */ svg = sess_data; - + /* Store this value in the session */ ret = fd_sess_state_store(mme_s6a_reg, session, &sess_data); ogs_assert(ret == 0); ogs_assert(sess_data == 0); - + /* Send the request */ ret = fd_msg_send(&req, mme_s6a_aia_cb, svg); ogs_assert(ret == 0); @@ -455,15 +455,16 @@ void mme_s6a_send_ulr(mme_ue_t *mme_ue) ogs_assert(mme_ue); ogs_debug("[MME] Update-Location-Request"); - + /* Create the random value to store with the session */ sess_data = ogs_calloc(1, sizeof(*sess_data)); + ogs_assert(sess_data); sess_data->mme_ue = mme_ue; - + /* Create the request */ ret = fd_msg_new(ogs_diam_s6a_cmd_ulr, MSGFL_ALLOC_ETEID, &req); ogs_assert(ret == 0); - + /* Create a new session */ #define OGS_DIAM_S6A_APP_SID_OPT "app_s6a" ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_S6A_APP_SID_OPT, @@ -484,7 +485,7 @@ void mme_s6a_send_ulr(mme_ue_t *mme_ue) /* Set Origin-Host & Origin-Realm */ ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); - + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -494,7 +495,7 @@ void mme_s6a_send_ulr(mme_ue_t *mme_ue) ogs_assert(ret == 0); ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); - + /* Set the User-Name AVP */ ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); ogs_assert(ret == 0); @@ -576,16 +577,16 @@ void mme_s6a_send_ulr(mme_ue_t *mme_ue) ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts); ogs_assert(ret == 0); - - /* Keep a pointer to the session data for debug purpose, + + /* Keep a pointer to the session data for debug purpose, * in real life we would not need it */ svg = sess_data; - + /* Store this value in the session */ ret = fd_sess_state_store(mme_s6a_reg, session, &sess_data); ogs_assert(ret == 0); ogs_assert(sess_data == 0); - + /* Send the request */ ret = fd_msg_send(&req, mme_s6a_ula_cb, svg); ogs_assert(ret == 0); @@ -1113,12 +1114,12 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) /* Free the message */ ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); - dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) + + dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) + ((ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); if (ogs_diam_logger_self()->stats.nb_recv) { /* Ponderate in the avg */ - ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg * - ogs_diam_logger_self()->stats.nb_recv + dur) / + ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg * + ogs_diam_logger_self()->stats.nb_recv + dur) / (ogs_diam_logger_self()->stats.nb_recv + 1); /* Min, max */ if (dur < ogs_diam_logger_self()->stats.shortest) @@ -1136,17 +1137,17 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) ogs_diam_logger_self()->stats.nb_recv++; ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); - + /* Display how long it took */ if (ts.tv_nsec > sess_data->ts.tv_nsec) - ogs_trace("in %d.%06ld sec", + ogs_trace("in %d.%06ld sec", (int)(ts.tv_sec - sess_data->ts.tv_sec), (long)(ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); else - ogs_trace("in %d.%06ld sec", + ogs_trace("in %d.%06ld sec", (int)(ts.tv_sec + 1 - sess_data->ts.tv_sec), (long)(1000000000 + ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); - + ret = fd_msg_free(*msg); ogs_assert(ret == 0); *msg = NULL; @@ -1159,29 +1160,9 @@ static void mme_s6a_ula_cb(void *data, struct msg **msg) int mme_fd_init(void) { int ret; - struct dict_object *s6a_app, *vnd; - struct dict_vendor_data vnd_data; - struct dict_application_data s6a_app_data; ret = ogs_diam_init(FD_MODE_CLIENT, mme_self()->diam_conf_path, mme_self()->diam_config); - ogs_assert(ret == OGS_OK); - - vnd_data.vendor_id = 10415; - vnd_data.vendor_name = (char *) "3GPP"; - - ret = fd_dict_new(fd_g_config->cnf_dict, - DICT_VENDOR, &vnd_data, NULL, &vnd); - ogs_assert(ret == 0); - - s6a_app_data.application_id = 16777251; - s6a_app_data.application_name = (char *) "S6A"; - - ret = fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, - &s6a_app_data, NULL, &s6a_app); - ogs_assert(ret == 0); - - ret = fd_disp_app_support(s6a_app, vnd, 1, 0); ogs_assert(ret == 0); /* Install objects definitions for this application */ @@ -1189,13 +1170,12 @@ int mme_fd_init(void) ogs_assert(ret == OGS_OK); /* Create handler for sessions */ - ret = fd_sess_handler_create(&mme_s6a_reg, &state_cleanup, - NULL, NULL); - ogs_assert(ret == OGS_OK); + ret = fd_sess_handler_create(&mme_s6a_reg, &state_cleanup, NULL, NULL); + ogs_assert(ret == 0); /* Advertise the support for the application in the peer */ ret = fd_disp_app_support(ogs_diam_s6a_application, ogs_diam_vendor, 1, 0); - ogs_assert(ret == OGS_OK); + ogs_assert(ret == 0); return 0; } diff --git a/src/pcrf/pcrf-context.c b/src/pcrf/pcrf-context.c index 80d4a9b53..92f5ef430 100644 --- a/src/pcrf/pcrf-context.c +++ b/src/pcrf/pcrf-context.c @@ -289,7 +289,7 @@ int pcrf_db_qos_data( return rv; } -int pcrf_sess_set_ipv4(const void *key, uint8_t *sid) +void pcrf_sess_set_ipv4(const void *key, uint8_t *sid) { ogs_assert(self.ip_hash); @@ -298,10 +298,8 @@ int pcrf_sess_set_ipv4(const void *key, uint8_t *sid) ogs_hash_set(self.ip_hash, key, OGS_IPV4_LEN, sid); ogs_thread_mutex_unlock(&self.hash_lock); - - return OGS_OK; } -int pcrf_sess_set_ipv6(const void *key, uint8_t *sid) +void pcrf_sess_set_ipv6(const void *key, uint8_t *sid) { ogs_assert(self.ip_hash); @@ -310,8 +308,6 @@ int pcrf_sess_set_ipv6(const void *key, uint8_t *sid) ogs_hash_set(self.ip_hash, key, OGS_IPV6_DEFAULT_PREFIX_LEN >> 3, sid); ogs_thread_mutex_unlock(&self.hash_lock); - - return OGS_OK; } uint8_t *pcrf_sess_find_by_ipv4(const void *key) diff --git a/src/pcrf/pcrf-context.h b/src/pcrf/pcrf-context.h index 1a0feca3f..3ee31c36a 100644 --- a/src/pcrf/pcrf-context.h +++ b/src/pcrf/pcrf-context.h @@ -41,10 +41,10 @@ typedef struct pcrf_context_s { const char *diam_conf_path; /* PCRF Diameter conf path */ ogs_diam_config_t *diam_config; /* PCRF Diameter config */ - ogs_thread_mutex_t db_lock; + ogs_thread_mutex_t db_lock; - ogs_hash_t *ip_hash; /* hash table for Gx Frame IPv4/IPv6 */ - ogs_thread_mutex_t hash_lock; + ogs_hash_t *ip_hash; /* hash table for Gx Frame IPv4/IPv6 */ + ogs_thread_mutex_t hash_lock; } pcrf_context_t; void pcrf_context_init(void); @@ -56,8 +56,8 @@ int pcrf_context_parse_config(void); int pcrf_db_qos_data(char *imsi_bcd, char *apn, ogs_session_data_t *session_data); -int pcrf_sess_set_ipv4(const void *key, uint8_t *sid); -int pcrf_sess_set_ipv6(const void *key, uint8_t *sid); +void pcrf_sess_set_ipv4(const void *key, uint8_t *sid); +void pcrf_sess_set_ipv6(const void *key, uint8_t *sid); uint8_t *pcrf_sess_find_by_ipv4(const void *key); uint8_t *pcrf_sess_find_by_ipv6(const void *key); diff --git a/src/pcrf/pcrf-fd-path.c b/src/pcrf/pcrf-fd-path.c index 1a7d34962..419e72289 100644 --- a/src/pcrf/pcrf-fd-path.c +++ b/src/pcrf/pcrf-fd-path.c @@ -22,39 +22,11 @@ int pcrf_fd_init(void) { - int rv, ret; - struct dict_object *gx_app, *rx_app, *vnd; - struct dict_vendor_data vnd_data; - struct dict_application_data gx_app_data, rx_app_data; + int rv; - ret = ogs_diam_init(FD_MODE_CLIENT|FD_MODE_SERVER, + rv = ogs_diam_init(FD_MODE_CLIENT|FD_MODE_SERVER, pcrf_self()->diam_conf_path, pcrf_self()->diam_config); - ogs_assert(ret == 0); - - vnd_data.vendor_id = 10415; - vnd_data.vendor_name = (char *) "3GPP"; - - ret = fd_dict_new(fd_g_config->cnf_dict, - DICT_VENDOR, &vnd_data, NULL, &vnd); - ogs_assert(ret == 0); - - gx_app_data.application_id = 16777238; - gx_app_data.application_name = (char *) "Gx"; - - rx_app_data.application_id = 16777236; - rx_app_data.application_name = (char *) "Rx"; - - ret = fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, - &gx_app_data, NULL, &gx_app); - ogs_assert(ret == 0); - ret = fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, - &rx_app_data, NULL, &rx_app); - ogs_assert(ret == 0); - - ret = fd_disp_app_support(gx_app, vnd, 1, 0); - ogs_assert(ret == 0); - ret = fd_disp_app_support(rx_app, vnd, 1, 0); - ogs_assert(ret == 0); + ogs_assert(rv == 0); rv = pcrf_gx_init(); ogs_assert(rv == OGS_OK); diff --git a/src/pcrf/pcrf-gx-path.c b/src/pcrf/pcrf-gx-path.c index a43fc82a5..9f27e5bbf 100644 --- a/src/pcrf/pcrf-gx-path.c +++ b/src/pcrf/pcrf-gx-path.c @@ -931,16 +931,6 @@ int pcrf_gx_send_rar( /* Set Origin-Host & Origin-Realm */ ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); - - /* Set the Destination-Realm AVP */ - ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); - ogs_assert(ret == 0); - val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); - val.os.len = strlen(fd_g_config->cnf_diamrlm); - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); /* Set the Destination-Host AVP */ ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); @@ -952,6 +942,16 @@ int pcrf_gx_send_rar( ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Auth-Application-Id AVP */ ret = fd_msg_avp_new(ogs_diam_auth_application_id, 0, &avp); ogs_assert(ret == 0); diff --git a/src/pcrf/pcrf-rx-path.c b/src/pcrf/pcrf-rx-path.c index f67bdaf37..5d07bc1d3 100644 --- a/src/pcrf/pcrf-rx-path.c +++ b/src/pcrf/pcrf-rx-path.c @@ -501,16 +501,6 @@ int pcrf_rx_send_asr(uint8_t *rx_sid, uint32_t abort_cause) /* Set Origin-Host & Origin-Realm */ ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); - - /* Set the Destination-Realm AVP */ - ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); - ogs_assert(ret == 0); - val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); - val.os.len = strlen(fd_g_config->cnf_diamrlm); - ret = fd_msg_avp_setvalue(avp, &val); - ogs_assert(ret == 0); - ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); - ogs_assert(ret == 0); /* Set the Destination-Host AVP */ ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); @@ -522,6 +512,16 @@ int pcrf_rx_send_asr(uint8_t *rx_sid, uint32_t abort_cause) ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Auth-Application-Id AVP */ ret = fd_msg_avp_new(ogs_diam_auth_application_id, 0, &avp); ogs_assert(ret == 0); diff --git a/src/smf/fd-path.c b/src/smf/fd-path.c index 9a4692831..322da4909 100644 --- a/src/smf/fd-path.c +++ b/src/smf/fd-path.c @@ -1056,9 +1056,6 @@ int smf_fd_init(void) { int ret; struct disp_when data; - struct dict_object *gx_app, *vnd; - struct dict_vendor_data vnd_data; - struct dict_application_data gx_app_data; if (smf_self()->diam_conf_path == NULL && (smf_self()->diam_config->cnf_diamid == NULL || @@ -1075,23 +1072,6 @@ int smf_fd_init(void) smf_self()->diam_conf_path, smf_self()->diam_config); ogs_assert(ret == 0); - vnd_data.vendor_id = 10415; - vnd_data.vendor_name = (char *) "3GPP"; - - ret = fd_dict_new(fd_g_config->cnf_dict, - DICT_VENDOR, &vnd_data, NULL, &vnd); - ogs_assert(ret == 0); - - gx_app_data.application_id = 16777238; - gx_app_data.application_name = (char *) "Gx"; - - ret = fd_dict_new(fd_g_config->cnf_dict, DICT_APPLICATION, - &gx_app_data, NULL, &gx_app); - ogs_assert(ret == 0); - - ret = fd_disp_app_support(gx_app, vnd, 1, 0); - ogs_assert(ret == 0); - /* Install objects definitions for this application */ ret = ogs_diam_gx_init(); ogs_assert(ret == 0); @@ -1114,6 +1094,7 @@ int smf_fd_init(void) /* Advertise the support for the application in the peer */ ret = fd_disp_app_support(ogs_diam_gx_application, ogs_diam_vendor, 1, 0); + ogs_assert(ret == 0); return OGS_OK; } diff --git a/tests/common/context.c b/tests/common/context.c index bcd5e9247..968a380e4 100644 --- a/tests/common/context.c +++ b/tests/common/context.c @@ -1535,6 +1535,10 @@ bson_t *test_db_new_ims(test_ue_t *test_ue) doc = BCON_NEW( "imsi", BCON_UTF8(test_ue->imsi), + "msisdn", "[", + BCON_UTF8(TEST_MSISDN), + BCON_UTF8(TEST_ADDITIONAL_MSISDN), + "]", "ambr", "{", "downlink", "{", "value", BCON_INT32(1), diff --git a/tests/common/context.h b/tests/common/context.h index a4d3bfb9b..7c600cb3e 100644 --- a/tests/common/context.h +++ b/tests/common/context.h @@ -40,6 +40,9 @@ extern "C" { #define MAX_NUM_OF_SERVED_GUAMI 8 +#define TEST_MSISDN "491725670014" +#define TEST_ADDITIONAL_MSISDN "491725670015" + typedef struct test_context_s { uint16_t ngap_port; /* Default NGAP Port */ ogs_list_t ngap_list; /* AMF NGAP IPv4 Server List */ diff --git a/tests/common/meson.build b/tests/common/meson.build index a2c04a495..c88aa62e7 100644 --- a/tests/common/meson.build +++ b/tests/common/meson.build @@ -67,7 +67,8 @@ libtestcommon = static_library('testcomon', libngap_dep, libnas_eps_dep, libnas_5gs_dep, - libdiameter_rx_dep], + libdiameter_rx_dep, + libdiameter_cx_dep], install : false) libtestcommon_dep = declare_dependency( @@ -83,4 +84,5 @@ libtestcommon_dep = declare_dependency( libngap_dep, libnas_eps_dep, libnas_5gs_dep, - libdiameter_rx_dep]) + libdiameter_rx_dep, + libdiameter_cx_dep]) diff --git a/tests/volte/abts-main.c b/tests/volte/abts-main.c index a9f151ce6..10bf4051a 100644 --- a/tests/volte/abts-main.c +++ b/tests/volte/abts-main.c @@ -25,6 +25,7 @@ abts_suite *test_bearer(abts_suite *suite); abts_suite *test_session(abts_suite *suite); abts_suite *test_rx(abts_suite *suite); abts_suite *test_video(abts_suite *suite); +abts_suite *test_cx(abts_suite *suite); const struct testlist { abts_suite *(*func)(abts_suite *suite); @@ -33,6 +34,7 @@ const struct testlist { {test_session}, {test_rx}, {test_video}, + {test_cx}, {NULL}, }; diff --git a/tests/volte/cx-test.c b/tests/volte/cx-test.c new file mode 100644 index 000000000..bce550667 --- /dev/null +++ b/tests/volte/cx-test.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "test-common.h" +#include "pcscf-fd-path.h" + +static void test1_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *s1ap; + ogs_socknode_t *gtpu; + ogs_pkbuf_t *emmbuf; + ogs_pkbuf_t *esmbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_s1ap_message_t message; + + uint8_t *rx_sid = NULL; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *bearer = NULL; + + uint32_t enb_ue_s1ap_id; + uint64_t mme_ue_s1ap_id; + + bson_t *doc = NULL; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_NAS_5GS_NULL_SCHEME; + mobile_identity_suci.home_network_pki_value = 0; + mobile_identity_suci.scheme_output[0] = 0x10; + mobile_identity_suci.scheme_output[1] = 0x32; + mobile_identity_suci.scheme_output[2] = 0x54; + mobile_identity_suci.scheme_output[3] = 0x86; + mobile_identity_suci.scheme_output[4] = 0x91; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, 13); + ogs_assert(test_ue); + + test_ue->e_cgi.cell_id = 0x1079baf; + test_ue->nas.ksi = 0; + test_ue->nas.value = OGS_NAS_ATTACH_TYPE_COMBINED_EPS_IMSI_ATTACH; + +#if 0 /* ipsec_reg.pcapng */ + test_ue->k_string = "8baf473f2f8fd09487cccbd7097c6862"; + test_ue->opc_string = "8E27B6AF0E692E750F32667A3B14605D"; +#else + test_ue->k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + test_ue->opc_string = "e8ed289deba952e4283b54e88e6183ca"; +#endif + + sess = test_sess_add_by_apn(test_ue, "internet"); + ogs_assert(sess); + + /* eNB connects to MME */ + s1ap = tests1ap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, s1ap); + + /* eNB connects to SGW */ + gtpu = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu); + + /* Send S1-Setup Reqeust */ + sendbuf = test_s1ap_build_s1_setup_request( + S1AP_ENB_ID_PR_macroENB_ID, 0x54f64); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Setup Response */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(NULL, recvbuf); + + /********** Insert Subscriber in Database */ + doc = test_db_new_ims(test_ue); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_INT_EQUAL(tc, OGS_OK, test_db_insert_ue(test_ue, doc)); + + /* Send Attach Request */ + memset(&sess->pdn_connectivity_param, + 0, sizeof(sess->pdn_connectivity_param)); + sess->pdn_connectivity_param.eit = 1; + esmbuf = testesm_build_pdn_connectivity_request(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + + memset(&test_ue->attach_request_param, + 0, sizeof(test_ue->attach_request_param)); + test_ue->attach_request_param.integrity_protected = 1; + test_ue->attach_request_param.drx_parameter = 1; + test_ue->attach_request_param.ms_network_capability = 1; + test_ue->attach_request_param.tmsi_status = 1; + test_ue->attach_request_param.mobile_station_classmark_2 = 1; + test_ue->attach_request_param.ue_usage_setting = 1; + emmbuf = testemm_build_attach_request(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, emmbuf); + + memset(&test_ue->initial_ue_param, 0, sizeof(test_ue->initial_ue_param)); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, false); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + emmbuf = testemm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + test_ue->mobile_identity_imeisv_presence = true; + emmbuf = testemm_build_security_mode_complete(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive ESM Information Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send ESM Information Response */ + sess->esm_information_param.pco = 1; + esmbuf = testesm_build_esm_information_response(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request + + * Attach Accept + + * Activate Default Bearer Context Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Capability Info Indication */ + sendbuf = tests1ap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Attach Complete + Activate default EPS bearer cotext accept */ + test_ue->nr_cgi.cell_id = 0x1234502; + bearer = test_bearer_find_by_ue_ebi(test_ue, 5); + ogs_assert(bearer); + esmbuf = testesm_build_activate_default_eps_bearer_context_accept( + bearer, false); + ABTS_PTR_NOTNULL(tc, esmbuf); + emmbuf = testemm_build_attach_complete(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive EMM information */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send PDN Connectivity Request */ + sess = test_sess_add_by_apn(test_ue, "ims"); + ogs_assert(sess); + sess->pti = 5; + + sess->pdn_connectivity_param.integrity_protected = 1; + sess->pdn_connectivity_param.ciphered = 1; + sess->pdn_connectivity_param.apn = 1; + sess->pdn_connectivity_param.pco = 1; + esmbuf = testesm_build_pdn_connectivity_request(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive E-RAB Setup Request + + * Activate default EPS bearer context request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send E-RAB Setup Response */ + bearer = test_bearer_find_by_ue_ebi(test_ue, 6); + ogs_assert(bearer); + sendbuf = test_s1ap_build_e_rab_setup_response(bearer); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Activate default EPS bearer context accept */ + esmbuf = testesm_build_activate_default_eps_bearer_context_accept( + bearer, true); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send AA-Request */ + pcscf_rx_send_aar_audio(&rx_sid, sess, + OGS_DIAM_RX_SUBSCRIPTION_ID_TYPE_END_USER_IMSI, 1, 1); + + /* Receive E-RAB Setup Request + + * Activate dedicated EPS bearer context request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send E-RAB Setup Response */ + bearer = test_bearer_find_by_ue_ebi(test_ue, 7); + ogs_assert(bearer); + sendbuf = test_s1ap_build_e_rab_setup_response(bearer); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Activate dedicated EPS bearer context accept */ + esmbuf = testesm_build_activate_dedicated_eps_bearer_context_accept(bearer); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* DELAY is needed in dedicated EPS bearer */ + ogs_msleep(100); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send User-Authorization-Request */ + test_cx_send_uar(test_ue, 0); + + /* DELAY for setup IMS */ + ogs_msleep(100); + + /* Send UEContextReleaseRequest */ + sendbuf = test_s1ap_build_ue_context_release_request(test_ue, + S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UEContextReleaseCommand */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UEContextReleaseComplete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + ABTS_INT_EQUAL(tc, OGS_OK, test_db_remove_ue(test_ue)); + + /* eNB disonncect from MME */ + testenb_s1ap_close(s1ap); + + /* eNB disonncect from SGW */ + test_gtpu_close(gtpu); + + test_ue_remove(test_ue); +} + +abts_suite *test_cx(abts_suite *suite) +{ + suite = ADD_SUITE(suite) + + abts_run_test(suite, test1_func, NULL); + + return suite; +} diff --git a/tests/volte/diameter-cx-path.c b/tests/volte/diameter-cx-path.c new file mode 100644 index 000000000..4183f973a --- /dev/null +++ b/tests/volte/diameter-cx-path.c @@ -0,0 +1,926 @@ +/* + * Copyright (C) 2019 by Sukchan Lee + * + * This file is part of Open5GS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ogs-diameter-cx.h" + +#include "test-common.h" +#include "pcscf-fd-path.h" + +static struct session_handler *test_cx_reg = NULL; + +struct sess_state { + test_ue_t *test_ue; + + char *user_name; + char *public_identity; + + bool resync; + + struct timespec ts; /* Time of sending the message */ +}; + +static struct disp_hdl *hdl_cx_fb = NULL; + +static void test_cx_uaa_cb(void *data, struct msg **msg); + +static void test_cx_send_mar(struct sess_state *sess_data); +static void test_cx_maa_cb(void *data, struct msg **msg); + +static void test_cx_send_sar(struct sess_state *sess_data); +static void test_cx_saa_cb(void *data, struct msg **msg); + +static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque) +{ + if (sess_data->user_name) + ogs_free(sess_data->user_name); + if (sess_data->public_identity) + ogs_free(sess_data->public_identity); + + ogs_free(sess_data); +} + +static int test_cx_fb_cb(struct msg **msg, struct avp *avp, + struct session *sess, void *opaque, enum disp_action *act) +{ + /* This CB should never be called */ + ogs_warn("Unexpected message received!"); + + return ENOTSUP; +} + +void test_cx_send_uar(test_ue_t *test_ue, int id_type) +{ + int ret; + + struct msg *req = NULL; + + struct msg_hdr *msg_header = NULL; + + struct session *session = NULL; + struct sess_state *sess_data = NULL, *svg; + + struct avp *avp = NULL; + union avp_value val; + + ogs_assert(test_ue); + + ogs_debug("User-Authroization-Request"); + + /* Create the random value to store with the session */ + sess_data = ogs_calloc(1, sizeof (*sess_data)); + ogs_assert(sess_data); + + sess_data->test_ue = test_ue; + + /* Create the request */ + ret = fd_msg_new(ogs_diam_cx_cmd_uar, MSGFL_ALLOC_ETEID, &req); + ogs_assert(ret == 0); + + ret = fd_msg_hdr(req, &msg_header); + ogs_assert(ret == 0); + msg_header->msg_appl = OGS_DIAM_CX_APPLICATION_ID; + + #define OGS_DIAM_CX_APP_SID_OPT "app_cx" + ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_CX_APP_SID_OPT, + CONSTSTRLEN(OGS_DIAM_CX_APP_SID_OPT)); + ogs_assert(ret == 0); + ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + req, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Origin-Host & Origin-Realm */ + ret = fd_msg_add_origin(req, 0); + ogs_assert(ret == 0); + + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_HSS_IDENTITY; + val.os.len = strlen(TEST_HSS_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + if (sess_data->user_name) + ogs_free(sess_data->user_name); + sess_data->user_name = + ogs_msprintf("%s@%s", test_ue->imsi, fd_g_config->cnf_diamrlm); + ogs_assert(sess_data->user_name); + + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->user_name; + val.os.len = strlen(sess_data->user_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Public-Identity AVP */ + if (sess_data->public_identity) + ogs_free(sess_data->public_identity); + sess_data->public_identity = ogs_msprintf("sip:%s@%s", + test_ue->imsi, fd_g_config->cnf_diamrlm); + ogs_assert(sess_data->public_identity); + + ret = fd_msg_avp_new(ogs_diam_cx_public_identity, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->public_identity; + val.os.len = strlen(sess_data->public_identity); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Visited-Network-Identifier AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_visited_network_identifier, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts); + ogs_assert(ret == 0); + + /* Keep a pointer to the session data for debug purpose, + * in real life we would not need it */ + svg = sess_data; + + /* Store this value in the session */ + ret = fd_sess_state_store(test_cx_reg, session, &sess_data); + ogs_assert(ret == 0); + ogs_assert(sess_data == 0); + + /* Send the request */ + ret = fd_msg_send(&req, test_cx_uaa_cb, svg); + ogs_assert(ret == 0); + + /* Increment the counter */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_sent++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); +} + +/* Callback for incoming User-Authorization-Answer messages */ +static void test_cx_uaa_cb(void *data, struct msg **msg) +{ + int ret, new; + + struct avp *avp, *avpch; + struct avp_hdr *hdr; + + unsigned long dur; + int error = 0; + + struct sess_state *sess_data = NULL; + struct timespec ts; + struct session *session; + + test_ue_t *test_ue = NULL; + + uint32_t result_code; + uint32_t *err = NULL, *exp_err = NULL; + + ogs_debug("User-Authroization-Answer"); + + ret = clock_gettime(CLOCK_REALTIME, &ts); + ogs_assert(ret == 0); + + /* Search the session, retrieve its data */ + ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(new == 0); + + ret = fd_sess_state_retrieve(test_cx_reg, session, &sess_data); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(sess_data); + ogs_expect_or_return((void *)sess_data == data); + + test_ue = sess_data->test_ue; + ogs_assert(test_ue); + + /* Value of Result Code */ + ret = fd_msg_search_avp(*msg, ogs_diam_result_code, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + err = &result_code; + ogs_debug(" Result Code: %d", hdr->avp_value->i32); + } else { + ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_avp_search_avp(avp, + ogs_diam_experimental_result_code, &avpch); + ogs_assert(ret == 0); + if (avpch) { + ret = fd_msg_avp_hdr(avpch, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + exp_err = &result_code; + ogs_debug(" Experimental Result Code: %d", result_code); + } + } else { + ogs_error("no Result-Code"); + error++; + } + } + + ogs_assert(!err && exp_err && + result_code == OGS_DIAM_CX_FIRST_REGISTRATION); + + /* Free the message */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) + + ((ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + if (ogs_diam_logger_self()->stats.nb_recv) { + /* Ponderate in the avg */ + ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg * + ogs_diam_logger_self()->stats.nb_recv + dur) / + (ogs_diam_logger_self()->stats.nb_recv + 1); + /* Min, max */ + if (dur < ogs_diam_logger_self()->stats.shortest) + ogs_diam_logger_self()->stats.shortest = dur; + if (dur > ogs_diam_logger_self()->stats.longest) + ogs_diam_logger_self()->stats.longest = dur; + } else { + ogs_diam_logger_self()->stats.shortest = dur; + ogs_diam_logger_self()->stats.longest = dur; + ogs_diam_logger_self()->stats.avg = dur; + } + if (error) + ogs_diam_logger_self()->stats.nb_errs++; + else + ogs_diam_logger_self()->stats.nb_recv++; + + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + /* Display how long it took */ + if (ts.tv_nsec > sess_data->ts.tv_nsec) + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec - sess_data->ts.tv_sec), + (long)(ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + else + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec + 1 - sess_data->ts.tv_sec), + (long)(1000000000 + ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + + ret = fd_msg_free(*msg); + ogs_assert(ret == 0); + *msg = NULL; + + test_cx_send_mar(sess_data); + return; +} + +static void test_cx_send_mar(struct sess_state *sess_data) +{ + int ret; + + struct msg *req = NULL; + + struct msg_hdr *msg_header = NULL; + + struct session *session = NULL; + struct sess_state *svg; + + struct avp *avp = NULL, *avpch = NULL; + union avp_value val; + + test_ue_t *test_ue = NULL; + + uint8_t resync[OGS_AUTS_LEN + OGS_RAND_LEN]; + + ogs_assert(sess_data); + test_ue = sess_data->test_ue; + ogs_assert(test_ue); + + ogs_debug("Multimedia-Auth-Request"); + + /* Create the request */ + ret = fd_msg_new(ogs_diam_cx_cmd_mar, MSGFL_ALLOC_ETEID, &req); + ogs_assert(ret == 0); + + ret = fd_msg_hdr(req, &msg_header); + ogs_assert(ret == 0); + msg_header->msg_appl = OGS_DIAM_CX_APPLICATION_ID; + + #define OGS_DIAM_CX_APP_SID_OPT "app_cx" + ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_CX_APP_SID_OPT, + CONSTSTRLEN(OGS_DIAM_CX_APP_SID_OPT)); + ogs_assert(ret == 0); + ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + req, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Origin-Host & Origin-Realm */ + ret = fd_msg_add_origin(req, 0); + ogs_assert(ret == 0); + + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_HSS_IDENTITY; + val.os.len = strlen(TEST_HSS_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->user_name; + val.os.len = strlen(sess_data->user_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Public-Identity AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_public_identity, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->public_identity; + val.os.len = strlen(sess_data->public_identity); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the SIP-Auth-Data-Item AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_auth_data_item, 0, &avp); + ogs_assert(ret == 0); + + if (sess_data->resync == true) { + uint8_t ak[OGS_AK_LEN]; + uint8_t sqn[OGS_SQN_LEN]; + uint8_t mac_s[OGS_MAC_S_LEN]; + uint8_t amf[2] = { 0, 0 }; + uint8_t auts[OGS_AUTS_LEN]; + + uint64_t sqn_ms; + int i; + + OGS_HEX(test_ue->k_string, strlen(test_ue->k_string), test_ue->k); + OGS_HEX(test_ue->opc_string, strlen(test_ue->opc_string), test_ue->opc); + + milenage_f2345(test_ue->opc, test_ue->k, test_ue->rand, + NULL, NULL, NULL, NULL, ak); + + sqn_ms = 0x11223344; + ogs_uint64_to_buffer(sqn_ms, 6, sqn); + milenage_f1(test_ue->opc, test_ue->k, test_ue->rand, + sqn, amf, NULL, auts + OGS_SQN_LEN); + for (i = 0; i < OGS_SQN_LEN; i++) + auts[i] = sqn[i] ^ ak[i]; + + memset(resync, 0, sizeof resync); + memcpy(resync, test_ue->rand, OGS_RAND_LEN); + memcpy(resync+OGS_RAND_LEN, auts, OGS_AUTS_LEN); + + ret = fd_msg_avp_new(ogs_diam_cx_sip_authorization, 0, &avpch); + ogs_assert(ret == 0); + val.os.len = OGS_RAND_LEN+OGS_AUTS_LEN; + val.os.data = resync; + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + } else { + ret = fd_msg_avp_new(ogs_diam_cx_sip_authentication_scheme, 0, &avpch); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)OGS_DIAM_CX_AUTH_SCHEME_UNKNOWN; + val.os.len = strlen(OGS_DIAM_CX_AUTH_SCHEME_UNKNOWN); + ret = fd_msg_avp_setvalue(avpch, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(avp, MSG_BRW_LAST_CHILD, avpch); + ogs_assert(ret == 0); + } + + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the SIP-Number-Auth-Items AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_sip_number_auth_items, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Server-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_server_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (os0_t)fd_g_config->cnf_diamid; + val.os.len = fd_g_config->cnf_diamid_len; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts); + ogs_assert(ret == 0); + + /* Keep a pointer to the session data for debug purpose, + * in real life we would not need it */ + svg = sess_data; + + /* Store this value in the session */ + ret = fd_sess_state_store(test_cx_reg, session, &sess_data); + ogs_assert(ret == 0); + ogs_assert(sess_data == 0); + + /* Send the request */ + ret = fd_msg_send(&req, test_cx_maa_cb, svg); + ogs_assert(ret == 0); + + /* Increment the counter */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_sent++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); +} + +/* Callback for incoming Multimedia-Auth-Answer messages */ +static void test_cx_maa_cb(void *data, struct msg **msg) +{ + int ret, new; + + struct avp *avp, *avpch; + struct avp_hdr *hdr; + + unsigned long dur; + int error = 0; + + struct sess_state *sess_data = NULL; + struct timespec ts; + struct session *session; + + test_ue_t *test_ue = NULL; + + uint32_t result_code; + uint32_t *err = NULL, *exp_err = NULL; + + ogs_debug("Multimedia-Auth-Answer"); + + ret = clock_gettime(CLOCK_REALTIME, &ts); + ogs_assert(ret == 0); + + /* Search the session, retrieve its data */ + ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(new == 0); + + ret = fd_sess_state_retrieve(test_cx_reg, session, &sess_data); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(sess_data); + ogs_expect_or_return((void *)sess_data == data); + + test_ue = sess_data->test_ue; + ogs_assert(test_ue); + + /* Value of Result Code */ + ret = fd_msg_search_avp(*msg, ogs_diam_result_code, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + err = &result_code; + ogs_debug(" Result Code: %d", hdr->avp_value->i32); + } else { + ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_avp_search_avp(avp, + ogs_diam_experimental_result_code, &avpch); + ogs_assert(ret == 0); + if (avpch) { + ret = fd_msg_avp_hdr(avpch, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + exp_err = &result_code; + ogs_debug(" Experimental Result Code: %d", result_code); + } + } else { + ogs_error("no Result-Code"); + error++; + } + } + + ogs_assert(err && !exp_err && result_code == ER_DIAMETER_SUCCESS); + + /* Free the message */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) + + ((ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + if (ogs_diam_logger_self()->stats.nb_recv) { + /* Ponderate in the avg */ + ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg * + ogs_diam_logger_self()->stats.nb_recv + dur) / + (ogs_diam_logger_self()->stats.nb_recv + 1); + /* Min, max */ + if (dur < ogs_diam_logger_self()->stats.shortest) + ogs_diam_logger_self()->stats.shortest = dur; + if (dur > ogs_diam_logger_self()->stats.longest) + ogs_diam_logger_self()->stats.longest = dur; + } else { + ogs_diam_logger_self()->stats.shortest = dur; + ogs_diam_logger_self()->stats.longest = dur; + ogs_diam_logger_self()->stats.avg = dur; + } + if (error) + ogs_diam_logger_self()->stats.nb_errs++; + else + ogs_diam_logger_self()->stats.nb_recv++; + + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + /* Display how long it took */ + if (ts.tv_nsec > sess_data->ts.tv_nsec) + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec - sess_data->ts.tv_sec), + (long)(ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + else + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec + 1 - sess_data->ts.tv_sec), + (long)(1000000000 + ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + + ret = fd_msg_free(*msg); + ogs_assert(ret == 0); + *msg = NULL; + + if (sess_data->resync == true) { + test_cx_send_sar(sess_data); + } else { + sess_data->resync = true; + test_cx_send_mar(sess_data); + } + return; +} + +static void test_cx_send_sar(struct sess_state *sess_data) +{ + int ret; + + struct msg *req = NULL; + + struct msg_hdr *msg_header = NULL; + + struct session *session = NULL; + struct sess_state *svg; + + struct avp *avp = NULL, *avpch = NULL; + union avp_value val; + + test_ue_t *test_ue = NULL; + + ogs_assert(sess_data); + test_ue = sess_data->test_ue; + ogs_assert(test_ue); + + ogs_debug("Server-Assignment-Request"); + + /* Create the request */ + ret = fd_msg_new(ogs_diam_cx_cmd_sar, MSGFL_ALLOC_ETEID, &req); + ogs_assert(ret == 0); + + ret = fd_msg_hdr(req, &msg_header); + ogs_assert(ret == 0); + msg_header->msg_appl = OGS_DIAM_CX_APPLICATION_ID; + + #define OGS_DIAM_CX_APP_SID_OPT "app_cx" + ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_CX_APP_SID_OPT, + CONSTSTRLEN(OGS_DIAM_CX_APP_SID_OPT)); + ogs_assert(ret == 0); + ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL); + ogs_assert(ret == 0); + + /* Set Vendor-Specific-Application-Id AVP */ + ret = ogs_diam_message_vendor_specific_appid_set( + req, OGS_DIAM_CX_APPLICATION_ID); + ogs_assert(ret == 0); + + /* Set the Auth-Session-State AVP */ + ret = fd_msg_avp_new(ogs_diam_auth_session_state, 0, &avp); + ogs_assert(ret == 0); + val.i32 = 1; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set Origin-Host & Origin-Realm */ + ret = fd_msg_add_origin(req, 0); + ogs_assert(ret == 0); + + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_HSS_IDENTITY; + val.os.len = strlen(TEST_HSS_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Destination-Realm AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_user_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->user_name; + val.os.len = strlen(sess_data->user_name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Public-Identity AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_public_identity, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (uint8_t *)sess_data->public_identity; + val.os.len = strlen(sess_data->public_identity); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Server-Name AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_server_name, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (os0_t)fd_g_config->cnf_diamid; + val.os.len = fd_g_config->cnf_diamid_len; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the Server-Assignment-Type AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_server_assignment_type, 0, &avp); + ogs_assert(ret == 0); + val.i32 = OGS_DIAM_CX_SERVER_ASSIGNMENT_REGISTRATION; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + /* Set the User-Data-Already-Avaiable AVP */ + ret = fd_msg_avp_new(ogs_diam_cx_user_data_already_available, 0, &avp); + ogs_assert(ret == 0); + val.i32 = OGS_DIAM_CX_USER_DATA_NOT_AVAILABLE; + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + + ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts); + ogs_assert(ret == 0); + + /* Keep a pointer to the session data for debug purpose, + * in real life we would not need it */ + svg = sess_data; + + /* Store this value in the session */ + ret = fd_sess_state_store(test_cx_reg, session, &sess_data); + ogs_assert(ret == 0); + ogs_assert(sess_data == 0); + + /* Send the request */ + ret = fd_msg_send(&req, test_cx_saa_cb, svg); + ogs_assert(ret == 0); + + /* Increment the counter */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + ogs_diam_logger_self()->stats.nb_sent++; + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); +} + +/* Callback for incoming Server-Assignment-Answer messages */ +static void test_cx_saa_cb(void *data, struct msg **msg) +{ + int ret, new; + + struct avp *avp, *avpch; + struct avp_hdr *hdr; + + unsigned long dur; + int error = 0; + + struct sess_state *sess_data = NULL; + struct timespec ts; + struct session *session; + + test_ue_t *test_ue = NULL; + + uint32_t result_code; + uint32_t *err = NULL, *exp_err = NULL; + + ogs_debug("Server-Assignment-Answer"); + + ret = clock_gettime(CLOCK_REALTIME, &ts); + ogs_assert(ret == 0); + + /* Search the session, retrieve its data */ + ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(new == 0); + + ret = fd_sess_state_retrieve(test_cx_reg, session, &sess_data); + ogs_expect_or_return(ret == 0); + ogs_expect_or_return(sess_data); + ogs_expect_or_return((void *)sess_data == data); + + test_ue = sess_data->test_ue; + ogs_assert(test_ue); + + /* Value of Result Code */ + ret = fd_msg_search_avp(*msg, ogs_diam_result_code, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_msg_avp_hdr(avp, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + err = &result_code; + ogs_debug(" Result Code: %d", hdr->avp_value->i32); + } else { + ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp); + ogs_assert(ret == 0); + if (avp) { + ret = fd_avp_search_avp(avp, + ogs_diam_experimental_result_code, &avpch); + ogs_assert(ret == 0); + if (avpch) { + ret = fd_msg_avp_hdr(avpch, &hdr); + ogs_assert(ret == 0); + result_code = hdr->avp_value->i32; + exp_err = &result_code; + ogs_debug(" Experimental Result Code: %d", result_code); + } + } else { + ogs_error("no Result-Code"); + error++; + } + } + + ogs_assert(err && !exp_err && result_code == ER_DIAMETER_SUCCESS); + + /* Free the message */ + ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0); + dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) + + ((ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + if (ogs_diam_logger_self()->stats.nb_recv) { + /* Ponderate in the avg */ + ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg * + ogs_diam_logger_self()->stats.nb_recv + dur) / + (ogs_diam_logger_self()->stats.nb_recv + 1); + /* Min, max */ + if (dur < ogs_diam_logger_self()->stats.shortest) + ogs_diam_logger_self()->stats.shortest = dur; + if (dur > ogs_diam_logger_self()->stats.longest) + ogs_diam_logger_self()->stats.longest = dur; + } else { + ogs_diam_logger_self()->stats.shortest = dur; + ogs_diam_logger_self()->stats.longest = dur; + ogs_diam_logger_self()->stats.avg = dur; + } + if (error) + ogs_diam_logger_self()->stats.nb_errs++; + else + ogs_diam_logger_self()->stats.nb_recv++; + + ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0); + + /* Display how long it took */ + if (ts.tv_nsec > sess_data->ts.tv_nsec) + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec - sess_data->ts.tv_sec), + (long)(ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + else + ogs_trace("in %d.%06ld sec", + (int)(ts.tv_sec + 1 - sess_data->ts.tv_sec), + (long)(1000000000 + ts.tv_nsec - sess_data->ts.tv_nsec) / 1000); + + ret = fd_msg_free(*msg); + ogs_assert(ret == 0); + *msg = NULL; + + state_cleanup(sess_data, NULL, NULL); + return; +} + +int test_cx_init(void) +{ + int ret; + struct disp_when data; + + /* Install objects definitions for this application */ + ret = ogs_diam_cx_init(); + ogs_assert(ret == 0); + + /* Create handler for sessions */ + ret = fd_sess_handler_create(&test_cx_reg, &state_cleanup, NULL, NULL); + ogs_assert(ret == 0); + + /* Fallback CB if command != unexpected message received */ + memset(&data, 0, sizeof(data)); + data.app = ogs_diam_cx_application; + + ret = fd_disp_register(test_cx_fb_cb, DISP_HOW_APPID, &data, NULL, + &hdl_cx_fb); + ogs_assert(ret == 0); + + /* Advertise the support for the application in the peer */ + ret = fd_disp_app_support(ogs_diam_cx_application, ogs_diam_vendor, 1, 0); + ogs_assert(ret == 0); + + return 0; +} + +void test_cx_final(void) +{ + int ret; + + ret = fd_sess_handler_destroy(&test_cx_reg, NULL); + ogs_assert(ret == OGS_OK); + + if (hdl_cx_fb) + (void) fd_disp_unregister(&hdl_cx_fb, NULL); +} diff --git a/tests/volte/meson.build b/tests/volte/meson.build index a45e9b8ff..c014a9b4d 100644 --- a/tests/volte/meson.build +++ b/tests/volte/meson.build @@ -18,12 +18,14 @@ testapp_volte_sources = files(''' pcscf-fd-path.h pcscf-fd-path.c + diameter-cx-path.c abts-main.c bearer-test.c session-test.c rx-test.c video-test.c + cx-test.c '''.split()) testapp_volte_exe = executable('volte', diff --git a/tests/volte/pcscf-fd-path.c b/tests/volte/pcscf-fd-path.c index 5152a4d32..be424f828 100644 --- a/tests/volte/pcscf-fd-path.c +++ b/tests/volte/pcscf-fd-path.c @@ -58,7 +58,7 @@ static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque) ogs_free(sess_data); } -static int pcscf_rx_fb_cb(struct msg **msg, struct avp *avp, +static int pcscf_rx_fb_cb(struct msg **msg, struct avp *avp, struct session *sess, void *opaque, enum disp_action *act) { /* This CB should never be called */ @@ -156,6 +156,16 @@ void pcscf_rx_send_aar_audio(uint8_t **rx_sid, ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_PCRF_IDENTITY; + val.os.len = strlen(TEST_PCRF_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -643,6 +653,16 @@ void pcscf_rx_send_aar_video(uint8_t **rx_sid, test_sess_t *sess, int id_type) ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_PCRF_IDENTITY; + val.os.len = strlen(TEST_PCRF_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -1311,6 +1331,16 @@ void pcscf_rx_send_aar_ctrl(uint8_t **rx_sid, test_sess_t *sess, int id_type) ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_PCRF_IDENTITY; + val.os.len = strlen(TEST_PCRF_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -1881,6 +1911,16 @@ void pcscf_rx_send_str(uint8_t *rx_sid) ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); + /* Set the Destination-Host AVP */ + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = TEST_PCRF_IDENTITY; + val.os.len = strlen(TEST_PCRF_IDENTITY); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); @@ -2063,7 +2103,7 @@ void pcscf_diam_config(void) { memset(&diam_config, 0, sizeof(ogs_diam_config_t)); - diam_config.cnf_diamid = "pcscf.localdomain"; + diam_config.cnf_diamid = "ims.localdomain"; diam_config.cnf_diamrlm = "localdomain"; diam_config.cnf_port = DIAMETER_PORT; diam_config.cnf_port_tls = DIAMETER_SECURE_PORT; @@ -2093,7 +2133,10 @@ void pcscf_diam_config(void) FD_EXT_DIR OGS_DIR_SEPARATOR_S "dict_dcca_3gpp.fdx"; diam_config.num_of_ext++; - diam_config.conn[diam_config.num_of_conn].identity = "pcrf.localdomain"; + diam_config.conn[diam_config.num_of_conn].identity = TEST_HSS_IDENTITY; + diam_config.conn[diam_config.num_of_conn].addr = "127.0.0.8"; + diam_config.num_of_conn++; + diam_config.conn[diam_config.num_of_conn].identity = TEST_PCRF_IDENTITY; diam_config.conn[diam_config.num_of_conn].addr = "127.0.0.9"; diam_config.num_of_conn++; } @@ -2108,6 +2151,8 @@ int pcscf_fd_init(void) ret = ogs_diam_init(FD_MODE_CLIENT, NULL, &diam_config); ogs_assert(ret == 0); + test_cx_init(); + /* Install objects definitions for this application */ ret = ogs_diam_rx_init(); ogs_assert(ret == 0); @@ -2148,5 +2193,7 @@ void pcscf_fd_final(void) if (hdl_rx_asr) (void) fd_disp_unregister(&hdl_rx_asr, NULL); + test_cx_final(); + ogs_diam_final(); } diff --git a/tests/volte/pcscf-fd-path.h b/tests/volte/pcscf-fd-path.h index 640a28c6e..d342fa789 100644 --- a/tests/volte/pcscf-fd-path.h +++ b/tests/volte/pcscf-fd-path.h @@ -26,15 +26,23 @@ extern "C" { #include "ogs-diameter-rx.h" +#define TEST_HSS_IDENTITY "hss.localdomain" +#define TEST_PCRF_IDENTITY "pcrf.localdomain" + int pcscf_fd_init(void); void pcscf_fd_final(void); +int test_cx_init(void); +void test_cx_final(void); + void pcscf_rx_send_aar_audio(uint8_t **rx_sid, test_sess_t *sess, int id_type, int qos_type, int flow_type); void pcscf_rx_send_aar_video(uint8_t **rx_sid, test_sess_t *sess, int id_type); void pcscf_rx_send_aar_ctrl(uint8_t **rx_sid, test_sess_t *sess, int id_type); void pcscf_rx_send_str(uint8_t *rx_sid); +void test_cx_send_uar(test_ue_t *test_ue, int id_type); + #ifdef __cplusplus } #endif diff --git a/webui/package-lock.json b/webui/package-lock.json index 51ce1ee71..afa385e6c 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -1,6 +1,6 @@ { "name": "open5gs", - "version": "2.2.1", + "version": "2.2.6", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/webui/package.json b/webui/package.json index 8b7f0be8c..6560bc67f 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "open5gs", - "version": "2.2.1", + "version": "2.2.6", "description": "Open5gs", "main": "index.js", "repository": "https://github.com/open5gs/open5gs/webui", diff --git a/webui/server/models/profile.js b/webui/server/models/profile.js index d5a496a4a..93750da83 100644 --- a/webui/server/models/profile.js +++ b/webui/server/models/profile.js @@ -11,6 +11,8 @@ const Profile = new Schema({ title: { $type: String, required: true }, + msisdn: [ String ], + security: { k: String, op: String, diff --git a/webui/server/models/subscriber.js b/webui/server/models/subscriber.js index 3f03d2473..5cb6425c7 100644 --- a/webui/server/models/subscriber.js +++ b/webui/server/models/subscriber.js @@ -11,6 +11,8 @@ const Subscriber = new Schema({ imsi: { $type: String, unique: true, required: true }, + msisdn: [ String ], + security: { k: String, op: String, diff --git a/webui/src/components/Profile/Edit.js b/webui/src/components/Profile/Edit.js index 282495317..ab7d41dac 100644 --- a/webui/src/components/Profile/Edit.js +++ b/webui/src/components/Profile/Edit.js @@ -9,11 +9,29 @@ const schema = { "type": "object", "properties": { "title": { - "type": "string", + "type": "string", "title": "Title*", "required": true, "maxLength": 24 }, + "msisdn": { + "type": "array", + "title": "", + "maxItems": 2, + "messages": { + "maxItems": "2 MSISDN are supported" + }, + "items": { + "type": "string", + "title": "MSISDN", + "maxLength": 15, + "required": true, + "pattern": "^\\d+$", + "messages": { + "pattern": "Only digits are allowed" + } + } + }, "security": { "title": "", "type": "object", @@ -440,7 +458,11 @@ const uiSchema = { "title" : { classNames: "col-xs-12", }, + "msisdn" : { + classNames: "col-xs-7", + }, "security" : { + classNames: "col-xs-12", "k" : { classNames: "col-xs-7", }, @@ -455,6 +477,7 @@ const uiSchema = { }, }, "ambr" : { + classNames: "col-xs-12", "downlink": { classNames: "col-xs-6", "value": { @@ -475,6 +498,7 @@ const uiSchema = { } }, "slice": { + classNames: "col-xs-12", "items": { "sst": { classNames: "col-xs-3", @@ -621,12 +645,12 @@ const uiSchema = { class Edit extends Component { static propTypes = { - visible: PropTypes.bool, - action: PropTypes.string, + visible: PropTypes.bool, + action: PropTypes.string, formData: PropTypes.object, isLoading: PropTypes.bool, - validate: PropTypes.func, - onHide: PropTypes.func, + validate: PropTypes.func, + onHide: PropTypes.func, onSubmit: PropTypes.func, onError: PropTypes.func } diff --git a/webui/src/components/Profile/View.js b/webui/src/components/Profile/View.js index 38e423a33..feb80ab95 100644 --- a/webui/src/components/Profile/View.js +++ b/webui/src/components/Profile/View.js @@ -8,6 +8,7 @@ import EditIcon from 'react-icons/lib/md/edit'; import DeleteIcon from 'react-icons/lib/md/delete'; import CloseIcon from 'react-icons/lib/md/close'; +import PhoneIcon from 'react-icons/lib/md/phone'; import SecurityIcon from 'react-icons/lib/md/security'; import PdnIcon from 'react-icons/lib/md/cast'; import KeyboardControlIcon from 'react-icons/lib/md/keyboard-control'; @@ -161,6 +162,7 @@ const Pdn = styled.div` const View = ({ visible, disableOnClickOutside, profile, onEdit, onDelete, onHide }) => { const _id = (profile || {})._id; const title = (profile || {}).title; + const msisdn_list = ((profile || {}).msisdn || []); const security = ((profile || {}).security || {}); const ambr = ((profile || {}).ambr || {}); const slice_list = ((profile || {}).slice || []); @@ -191,6 +193,21 @@ const View = ({ visible, disableOnClickOutside, profile, onEdit, onDelete, onHid
Profile Configuration
+ {msisdn_list.length !== 0 && +
+
+ +
+
+ {msisdn_list.map((msisdn, index) => +
+ {msisdn} + MSISDN +
+ )} +
+
+ }
diff --git a/webui/src/components/Shared/Form.js b/webui/src/components/Shared/Form.js index 9087c2e7f..971f13978 100644 --- a/webui/src/components/Shared/Form.js +++ b/webui/src/components/Shared/Form.js @@ -69,7 +69,10 @@ const fields = { }; function Label(props) { - const { label, required, id } = props; +// modified by acetcom +// const { label, required, id } = props; + const { label, id } = props; + const required = 0; if (!label) { // See #312: Ensure compatibility with old versions of React. return
; @@ -304,4 +307,4 @@ class Form extends Component { } } -export default Form; \ No newline at end of file +export default Form; diff --git a/webui/src/components/Subscriber/Edit.js b/webui/src/components/Subscriber/Edit.js index 19bb8a810..ccde6b6a4 100644 --- a/webui/src/components/Subscriber/Edit.js +++ b/webui/src/components/Subscriber/Edit.js @@ -11,7 +11,7 @@ const schema = { "type": "object", "properties": { "imsi": { - "type": "string", + "type": "string", "title": "IMSI*", "required": true, "pattern": "^\\d+$", @@ -20,6 +20,24 @@ const schema = { "pattern": "Only digits are allowed" } }, + "msisdn": { + "type": "array", + "title": "", + "maxItems": 2, + "messages": { + "maxItems": "2 MSISDN are supported" + }, + "items": { + "type": "string", + "title": "MSISDN", + "maxLength": 15, + "required": true, + "pattern": "^\\d+$", + "messages": { + "pattern": "Only digits are allowed" + } + } + }, "security": { "title": "", "type": "object", @@ -446,7 +464,11 @@ const uiSchema = { "imsi" : { classNames: "col-xs-12", }, + "msisdn" : { + classNames: "col-xs-7", + }, "security" : { + classNames: "col-xs-12", "k" : { classNames: "col-xs-7", }, @@ -461,6 +483,7 @@ const uiSchema = { }, }, "ambr" : { + classNames: "col-xs-12", "downlink": { classNames: "col-xs-6", "value": { @@ -628,12 +651,12 @@ const uiSchema = { class Edit extends Component { static propTypes = { - visible: PropTypes.bool, - action: PropTypes.string, + visible: PropTypes.bool, + action: PropTypes.string, formData: PropTypes.object, isLoading: PropTypes.bool, - validate: PropTypes.func, - onHide: PropTypes.func, + validate: PropTypes.func, + onHide: PropTypes.func, onSubmit: PropTypes.func, onError: PropTypes.func } @@ -679,7 +702,7 @@ class Edit extends Component { ...schema, "properties": { profile: { - type: "string", + type: "string", title: "Profile*", enum: profiles.map(profile => profile._id), enumNames: profiles.map(profile => profile.title), diff --git a/webui/src/components/Subscriber/View.js b/webui/src/components/Subscriber/View.js index 9e156835b..de1d61e49 100644 --- a/webui/src/components/Subscriber/View.js +++ b/webui/src/components/Subscriber/View.js @@ -8,6 +8,7 @@ import EditIcon from 'react-icons/lib/md/edit'; import DeleteIcon from 'react-icons/lib/md/delete'; import CloseIcon from 'react-icons/lib/md/close'; +import PhoneIcon from 'react-icons/lib/md/phone'; import SecurityIcon from 'react-icons/lib/md/security'; import PdnIcon from 'react-icons/lib/md/cast'; import KeyboardControlIcon from 'react-icons/lib/md/keyboard-control'; @@ -160,6 +161,7 @@ const Pdn = styled.div` ` const View = ({ visible, disableOnClickOutside, subscriber, onEdit, onDelete, onHide }) => { const imsi = (subscriber || {}).imsi; + const msisdn_list = ((subscriber || {}).msisdn || []); const security = ((subscriber || {}).security || {}); const ambr = ((subscriber || {}).ambr || {}); const slice_list = ((subscriber || {}).slice || []); @@ -190,6 +192,21 @@ const View = ({ visible, disableOnClickOutside, subscriber, onEdit, onDelete, on
Subscriber Configuration
+ {msisdn_list.length !== 0 && +
+
+ +
+
+ {msisdn_list.map((msisdn, index) => +
+ {msisdn} + MSISDN +
+ )} +
+
+ }