lynxis lazus has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmo-hlr/+/32512 )
Change subject: Add support for multiple APN profiles for subscriber data
......................................................................
Add support for multiple APN profiles for subscriber data
Previous the HLR sent in the Insert Subscriber Data call only the
wildcard APN as a single entry.
This violates the spec because the first entry (with the lowest context_id) is
always the default APN, but it is forbidden to have a wildcard APN as default apn.
Introduce a default template/profile which can contain multiple APNs.
This profile is always sent out to the SGSN/MME as part of Insert-Subscriber-Data.
In the future a subscriber might have a profile template name written into the
database which will resolve to a "pdp-profile premium" in the configuration.
To be backward compatible, if the pdp-profile default section is missing,
the HLR will send out only a wildcard APN.
Config example:
hlr
ps
pdp-profile default
profile 1
apn internet
profile 2
apn *
TODO: check if SGSN is fine with this
TODO: check if we can inform all PS session on change of the config
Change-Id: I540132ee5dcfd09f4816e02e702927e1074ca50f
---
M doc/examples/osmo-hlr.cfg
M include/osmocom/hlr/hlr.h
A include/osmocom/hlr/hlr_ps.h
M include/osmocom/hlr/hlr_vty.h
M src/gsup_server.c
M src/hlr_vty.c
M tests/test_nodes.vty
7 files changed, 316 insertions(+), 5 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hlr refs/changes/12/32512/1
diff --git a/doc/examples/osmo-hlr.cfg b/doc/examples/osmo-hlr.cfg
index dabfc8e..dd5292e 100644
--- a/doc/examples/osmo-hlr.cfg
+++ b/doc/examples/osmo-hlr.cfg
@@ -24,3 +24,9 @@
bind ip 127.0.0.1
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi
+ ps
+ pdp-profiles default
+ profile 1
+ apn internet
+ profile 2
+ apn *
diff --git a/include/osmocom/hlr/hlr.h b/include/osmocom/hlr/hlr.h
index b27fb3d..dc0c77e 100644
--- a/include/osmocom/hlr/hlr.h
+++ b/include/osmocom/hlr/hlr.h
@@ -58,6 +58,14 @@
struct hlr_euse *euse_default;
enum gsm48_gmm_cause reject_cause;
enum gsm48_gmm_cause no_proxy_reject_cause;
+ /* PS: APN default configuration used by Subscription Data on ISR */
+ struct {
+ struct {
+ bool enabled;
+ struct osmo_gsup_pdp_info pdp_infos[OSMO_GSUP_MAX_NUM_PDP_INFO];
+ size_t num_pdp_infos;
+ } pdp_profile;
+ } ps;
/* NCSS (call independent) session guard timeout value */
int ncss_guard_timeout;
diff --git a/include/osmocom/hlr/hlr_ps.h b/include/osmocom/hlr/hlr_ps.h
new file mode 100644
index 0000000..d84a0af
--- /dev/null
+++ b/include/osmocom/hlr/hlr_ps.h
@@ -0,0 +1,37 @@
+/* OsmoHLR packet switched header */
+
+/* (C) 2023 sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Alexander Couzens <lynxis(a)fe80.eu>
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+struct hlr_pdp_profile {
+ struct llist_head list;
+ int context_id;
+ const char *apn;
+}
+
+
+/* PS: APN default configuration used by Subscription Data on ISR */
+struct {
+ struct {
+ bool enabled;
+ struct osmo_gsup_pdp_info pdp_infos[OSMO_GSUP_MAX_NUM_PDP_INFO];
+ size_t num_pdp_infos;
+ } pdp_profile;
+} ps;
diff --git a/include/osmocom/hlr/hlr_vty.h b/include/osmocom/hlr/hlr_vty.h
index 83691b8..1674075 100644
--- a/include/osmocom/hlr/hlr_vty.h
+++ b/include/osmocom/hlr/hlr_vty.h
@@ -35,6 +35,9 @@
MSLOOKUP_SERVER_NODE,
MSLOOKUP_SERVER_MSC_NODE,
MSLOOKUP_CLIENT_NODE,
+ PS_NODE,
+ PS_PDP_PROFILES_NODE,
+ PS_PDP_PROFILES_PROFILE_NODE,
};
diff --git a/src/gsup_server.c b/src/gsup_server.c
index b14a791..5230e69 100644
--- a/src/gsup_server.c
+++ b/src/gsup_server.c
@@ -32,6 +32,7 @@
#include <osmocom/hlr/gsup_server.h>
#include <osmocom/hlr/gsup_router.h>
+#include <osmocom/hlr/hlr.h>
#define LOG_GSUP_CONN(conn, level, fmt, args...) \
LOGP(DLGSUP, level, "GSUP peer %s: " fmt, \
@@ -455,7 +456,7 @@
void *talloc_ctx)
{
int len;
- char *msisdn_buf = talloc_size(talloc_ctx, OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN);
+ uint8_t *msisdn_buf = talloc_size(talloc_ctx, OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN);
OSMO_ASSERT(gsup);
*gsup = (struct osmo_gsup_message){
@@ -476,10 +477,15 @@
gsup->cn_domain = cn_domain;
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
- char *apn_buf = talloc_size(talloc_ctx, APN_MAXLEN);
- /* FIXME: PDP infos - use more fine-grained access control
- instead of wildcard APN */
- osmo_gsup_configure_wildcard_apn(gsup, apn_buf, APN_MAXLEN);
+ if (g_hlr->ps.pdp_profile.enabled) {
+ memcpy(gsup->pdp_infos,
+ g_hlr->ps.pdp_profile.pdp_infos,
+ sizeof(struct osmo_gsup_pdp_info) * g_hlr->ps.pdp_profile.num_pdp_infos);
+ gsup->num_pdp_infos = g_hlr->ps.pdp_profile.num_pdp_infos;
+ } else {
+ char *apn_buf = talloc_size(talloc_ctx, APN_MAXLEN);
+ osmo_gsup_configure_wildcard_apn(gsup, apn_buf, APN_MAXLEN);
+ }
}
return 0;
diff --git a/src/hlr_vty.c b/src/hlr_vty.c
index 02e0cde..b4bbec6 100644
--- a/src/hlr_vty.c
+++ b/src/hlr_vty.c
@@ -26,9 +26,12 @@
*/
#include <errno.h>
+#include <string.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+#include <osmocom/gsm/apn.h>
+
#include <osmocom/vty/vty.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/command.h>
@@ -103,6 +106,172 @@
return CMD_SUCCESS;
}
+struct cmd_node ps_node = {
+ PS_NODE,
+ "%s(config-hlr-ps)# ",
+ 1,
+};
+
+DEFUN(cfg_ps,
+ cfg_ps_cmd,
+ "ps",
+ "Configure the PS options")
+{
+ vty->node = PS_NODE;
+ return CMD_SUCCESS;
+}
+
+struct cmd_node ps_pdp_profiles_node = {
+ PS_PDP_PROFILES_NODE,
+ "%s(config-hlr-ps-pdp-profiles)# ",
+ 1,
+};
+
+DEFUN(cfg_ps_pdp_profiles,
+ cfg_ps_pdp_profiles_cmd,
+ "pdp-profiles default",
+ "Define a PDP profile set.\n"
+ "Unique identifier for this PDP profile set.\n")
+{
+ g_hlr->ps.pdp_profile.enabled = true;
+
+ vty->node = PS_PDP_PROFILES_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ps_pdp_profiles,
+ cfg_no_ps_pdp_profiles_cmd,
+ "no pdp-profiles default",
+ NO_STR
+ "Delete PDP profile.\n"
+ "Unique identifier for this PDP profile set.\n")
+{
+ g_hlr->ps.pdp_profile.enabled = false;
+ return CMD_SUCCESS;
+}
+
+
+
+struct cmd_node ps_pdp_profiles_profile_node = {
+ PS_PDP_PROFILES_PROFILE_NODE,
+ "%s(config-hlr-ps-pdp-profile)# ",
+ 1,
+};
+
+
+/* context_id == 0 means the slot is free */
+struct osmo_gsup_pdp_info *get_pdp_profile(uint8_t context_id)
+{
+ for (int i = 0; i < OSMO_GSUP_MAX_NUM_PDP_INFO; i++) {
+ struct osmo_gsup_pdp_info *info = &g_hlr->ps.pdp_profile.pdp_infos[i];
+ if (info->context_id == context_id) {
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+struct osmo_gsup_pdp_info *create_pdp_profile(uint8_t context_id)
+{
+ struct osmo_gsup_pdp_info *info = get_pdp_profile(0);
+ if (!info)
+ return NULL;
+
+ memset(info, 0, sizeof(*info));
+ info->context_id = context_id;
+ info->have_info = 1;
+
+ g_hlr->ps.pdp_profile.num_pdp_infos++;
+ return info;
+}
+
+void destroy_pdp_profile(struct osmo_gsup_pdp_info *info)
+{
+ info->context_id = 0;
+ if (info->apn_enc)
+ talloc_free(info->apn_enc);
+
+ g_hlr->ps.pdp_profile.num_pdp_infos--;
+ memset(info, 0, sizeof(*info));
+}
+
+DEFUN(cfg_ps_pdp_profiles_profile,
+ cfg_ps_pdp_profiles_profile_cmd,
+ "profile <1-10>",
+ "Configure a PDP profile\n"
+ "Unique PDP context identifier. The lowest profile will be used as default
context.\n")
+{
+ struct osmo_gsup_pdp_info *info;
+ uint8_t context_id = atoi(argv[0]);
+
+ info = get_pdp_profile(context_id);
+ if (!info) {
+ info = create_pdp_profile(context_id);
+ if (!info) {
+ vty_out(vty, "Failed to create profile %d!%s", context_id, VTY_NEWLINE);
+ return CMD_ERR_INCOMPLETE;
+ }
+ }
+
+ vty->node = PS_PDP_PROFILES_PROFILE_NODE;
+ vty->index = info;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ps_pdp_profiles_profile,
+ cfg_no_ps_pdp_profiles_profile_cmd,
+ "no profile <1-10>",
+ NO_STR
+ "Delete a PDP profile\n"
+ "Unique PDP context identifier. The lowest profile will be used as default
context.\n")
+{
+ struct osmo_gsup_pdp_info *info;
+ uint8_t context_id = atoi(argv[0]);
+
+ info = get_pdp_profile(context_id);
+ if (info)
+ destroy_pdp_profile(info);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ps_pdp_profile_apn, cfg_ps_pdp_profile_apn_cmd,
+ "apn ID",
+ "Configure the APN.\n"
+ "APN name or * for wildcard apn.\n")
+{
+ struct osmo_gsup_pdp_info *info = vty->index;
+ const char *apn_name = argv[0];
+ size_t apn = strlen(apn_name);
+
+ if (apn > APN_MAXLEN) {
+ vty_out(vty, "APN name is too long '%s'. Max is %d!%s", apn_name,
APN_MAXLEN, VTY_NEWLINE);
+ return CMD_ERR_INCOMPLETE;
+ }
+
+ info->apn_enc = (uint8_t *) talloc_strdup(g_hlr, apn_name);
+ info->apn_enc_len = strlen(apn_name);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ps_pdp_profile_apn, cfg_no_ps_pdp_profile_apn_cmd,
+ "no apn",
+ NO_STR
+ "Delete the APN.\n")
+{
+ struct osmo_gsup_pdp_info *info = vty->index;
+ if (info->apn_enc) {
+ talloc_free(info->apn_enc);
+ info->apn_enc = NULL;
+ info->apn_enc_len = 0;
+ }
+
+ return CMD_SUCCESS;
+}
+
+
static int config_write_hlr(struct vty *vty)
{
vty_out(vty, "hlr%s", VTY_NEWLINE);
@@ -149,6 +318,33 @@
return CMD_SUCCESS;
}
+static int config_write_hlr_ps(struct vty *vty)
+{
+ vty_out(vty, " ps%s", VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+static int config_write_hlr_ps_pdp_profiles(struct vty *vty)
+{
+ if (!g_hlr->ps.pdp_profile.enabled)
+ return CMD_SUCCESS;
+
+ vty_out(vty, " pdp-profiles default%s", VTY_NEWLINE);
+ for (int i = 0; i < g_hlr->ps.pdp_profile.num_pdp_infos; i++) {
+ struct osmo_gsup_pdp_info *pdp_info = &g_hlr->ps.pdp_profile.pdp_infos[i];
+ if (!pdp_info->context_id)
+ continue;
+
+ vty_out(vty, " profile %d%s", pdp_info->context_id, VTY_NEWLINE);
+ if (!pdp_info->have_info)
+ continue;
+
+ if (pdp_info->apn_enc && pdp_info->apn_enc_len)
+ vty_out(vty, " apn %s%s", pdp_info->apn_enc, VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
static void show_one_conn(struct vty *vty, const struct osmo_gsup_conn *conn)
{
const struct ipa_server_conn *isc = conn->conn;
@@ -538,6 +734,24 @@
install_element(GSUP_NODE, &cfg_hlr_gsup_bind_ip_cmd);
install_element(GSUP_NODE, &cfg_hlr_gsup_ipa_name_cmd);
+ /* PS */
+ install_node(&ps_node, config_write_hlr_ps);
+ install_element(HLR_NODE, &cfg_ps_cmd);
+
+ install_node(&ps_pdp_profiles_node, config_write_hlr_ps_pdp_profiles);
+ install_element(PS_NODE, &cfg_ps_pdp_profiles_cmd);
+ install_element(PS_NODE, &cfg_no_ps_pdp_profiles_cmd);
+
+ install_node(&ps_pdp_profiles_profile_node, NULL);
+ install_element(PS_PDP_PROFILES_NODE, &cfg_ps_pdp_profiles_profile_cmd);
+ install_element(PS_PDP_PROFILES_NODE, &cfg_no_ps_pdp_profiles_profile_cmd);
+ install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_ps_pdp_profile_apn_cmd);
+ install_element(PS_PDP_PROFILES_PROFILE_NODE, &cfg_no_ps_pdp_profile_apn_cmd);
+
+ /* TODO:
+ * no profiles default
+ */
+
install_element(HLR_NODE, &cfg_database_cmd);
install_element(HLR_NODE, &cfg_euse_cmd);
diff --git a/tests/test_nodes.vty b/tests/test_nodes.vty
index ac5d88d..8c8b6e5 100644
--- a/tests/test_nodes.vty
+++ b/tests/test_nodes.vty
@@ -53,6 +53,7 @@
OsmoHLR(config-hlr)# list
...
gsup
+ ps
database PATH
euse NAME
no euse NAME
@@ -112,6 +113,8 @@
ipa-name unnamed-HLR
ussd route prefix *#100# internal own-msisdn
ussd route prefix *#101# internal own-imsi
+ ps
+ pdp-profiles default
end
OsmoHLR# configure terminal
--
To view, visit
https://gerrit.osmocom.org/c/osmo-hlr/+/32512
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Change-Id: I540132ee5dcfd09f4816e02e702927e1074ca50f
Gerrit-Change-Number: 32512
Gerrit-PatchSet: 1
Gerrit-Owner: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-MessageType: newchange