pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-hlr/+/32512?usp=email )

Change subject: Add support for multiple APN profiles for subscriber data
......................................................................

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(-)

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




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 https://gerrit.osmocom.org/c/osmo-hlr/+/32512?usp=email
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: 11
Gerrit-Owner: lynxis lazus <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <[email protected]>
Gerrit-Reviewer: dexter <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-CC: laforge <[email protected]>
Gerrit-CC: neels <[email protected]>
Gerrit-MessageType: merged

Reply via email to