pespin submitted this change.

View Change


Approvals: dexter: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved daniel: Looks good to me, but someone else must approve
Add support for multiple APN profiles for subscriber data

Previously 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 *

Changes to the apn list will be only handed out to subscribers
when the subscriber do a location update.

Related: SYS#6391
Change-Id: I540132ee5dcfd09f4816e02e702927e1074ca50f
---
M doc/examples/osmo-hlr.cfg
M include/osmocom/hlr/hlr.h
M include/osmocom/hlr/hlr_vty.h
M src/gsup_server.c
M src/hlr_vty.c
M tests/test_nodes.vty
M tests/test_subscriber_errors.ctrl
7 files changed, 347 insertions(+), 14 deletions(-)

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_vty.h b/include/osmocom/hlr/hlr_vty.h
index 771945d..43b6566 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 91110eb..20ea162 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, \
@@ -474,10 +475,17 @@

gsup->cn_domain = cn_domain;
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
- uint8_t *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) {
+ OSMO_ASSERT(g_hlr->ps.pdp_profile.num_pdp_infos <= ARRAY_SIZE(g_hlr->ps.pdp_profile.pdp_infos));
+ OSMO_ASSERT(g_hlr->ps.pdp_profile.num_pdp_infos <= ARRAY_SIZE(gsup->pdp_infos));
+ 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 {
+ uint8_t *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..af57159 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,182 @@
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"
+ "Define the global default profile.\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((void *) 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];
+
+ /* apn encoded takes one more byte than strlen() */
+ size_t apn_enc_len = strlen(apn_name) + 1;
+ uint8_t *apn_enc;
+ int ret;
+
+ if (apn_enc_len > 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 = apn_enc = (uint8_t *) talloc_zero_size(g_hlr, apn_enc_len);
+ ret = info->apn_enc_len = osmo_apn_from_str(apn_enc, apn_enc_len, apn_name);
+ if (ret < 0) {
+ talloc_free(apn_enc);
+ info->apn_enc = NULL;
+ info->apn_enc_len = 0;
+ vty_out(vty, "Invalid APN name %s!", apn_name);
+ return CMD_WARNING;
+ }
+
+ 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((void *) 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 +328,37 @@
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)
+{
+ char apn[APN_MAXLEN + 1] = {};
+
+ 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) {
+ osmo_apn_to_str(apn, pdp_info->apn_enc, pdp_info->apn_enc_len);
+ vty_out(vty, " apn %s%s", apn, 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 +748,20 @@
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);
+
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..8c7e95d 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,12 @@
ipa-name unnamed-HLR
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 *
end

OsmoHLR# configure terminal
@@ -448,3 +455,44 @@
client
mdns bind 239.192.23.42 4266
...
+OsmoHLR(config-mslookup-server)# end
+OsmoHLR# configure terminal
+
+OsmoHLR(config)# hlr
+OsmoHLR(config-hlr)# ps?
+ ps Configure the PS options
+
+OsmoHLR(config-hlr)# ps
+
+OsmoHLR(config-hlr-ps)# list
+...
+ pdp-profiles default
+ no pdp-profiles default
+...
+OsmoHLR(config-hlr-ps)# no pdp-profiles default
+
+
+OsmoHLR(config-hlr-ps)# pdp-profiles default
+OsmoHLR(config-hlr-ps-pdp-profiles)# ?
+...
+ profile Configure a PDP profile
+...
+OsmoHLR(config-hlr-ps-pdp-profiles)# profile 1
+
+OsmoHLR(config-hlr-ps-pdp-profile)# ?
+...
+ apn Configure the APN.
+...
+OsmoHLR(config-hlr-ps-pdp-profile)# apn internet
+OsmoHLR(config-hlr-ps-pdp-profile)# exit
+OsmoHLR(config-hlr-ps-pdp-profiles)# profile 2
+OsmoHLR(config-hlr-ps-pdp-profile)# apn *
+OsmoHLR(config-hlr-ps-pdp-profile)# show running-config
+...
+ ps
+ pdp-profiles default
+ profile 1
+ apn internet
+ profile 2
+ apn *
+...
diff --git a/tests/test_subscriber_errors.ctrl b/tests/test_subscriber_errors.ctrl
index 4603a77..2ae9b69 100644
--- a/tests/test_subscriber_errors.ctrl
+++ b/tests/test_subscriber_errors.ctrl
@@ -139,17 +139,17 @@
SET 58 subscriber.by-imsi-901990000000003.aud3g foobar,2134
ERROR 58 Unknown auth algorithm.

-SET 60 subscriber.by-imsi-901990000000003.aud3g milenage,2134
-ERROR 60 Invalid KI.
+SET 59 subscriber.by-imsi-901990000000003.aud3g milenage,2134
+ERROR 59 Invalid KI.

-SET 61 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,AAA
+SET 60 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,AAA
+ERROR 60 Invalid format.
+
+SET 61 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC
ERROR 61 Invalid format.

-SET 62 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC
-ERROR 62 Invalid format.
+SET 62 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,zzz
+ERROR 62 Invalid OP/OPC.

-SET 63 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,zzz
-ERROR 63 Invalid OP/OPC.
-
-SET 64 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,fb2a3d1b360f599abab99db8669f8308,
-ERROR 64 Invalid format.
+SET 63 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,fb2a3d1b360f599abab99db8669f8308,
+ERROR 63 Invalid format.

To view, visit change 32512. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-hlr
Gerrit-Branch: master
Gerrit-Change-Id: I540132ee5dcfd09f4816e02e702927e1074ca50f
Gerrit-Change-Number: 32512
Gerrit-PatchSet: 11
Gerrit-Owner: lynxis lazus <lynxis@fe80.eu>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann@sysmocom.de>
Gerrit-Reviewer: dexter <pmaier@sysmocom.de>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-CC: laforge <laforge@osmocom.org>
Gerrit-CC: neels <nhofmeyr@sysmocom.de>
Gerrit-MessageType: merged