diff options
author | Alexander Couzens <lynxis@fe80.eu> | 2023-04-11 19:28:36 +0200 |
---|---|---|
committer | pespin <pespin@sysmocom.de> | 2023-09-12 09:48:02 +0000 |
commit | 37f0b3a8f37a2dd615e4c269d429b21b8f1dcbed (patch) | |
tree | db39290c9e856d5e0de61b36089a6c6508657ac7 | |
parent | 7a763aa0128b7a171b1ca56eea664c66dad4bf4c (diff) |
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
-rw-r--r-- | doc/examples/osmo-hlr.cfg | 6 | ||||
-rw-r--r-- | include/osmocom/hlr/hlr.h | 8 | ||||
-rw-r--r-- | include/osmocom/hlr/hlr_vty.h | 3 | ||||
-rw-r--r-- | src/gsup_server.c | 16 | ||||
-rw-r--r-- | src/hlr_vty.c | 224 | ||||
-rw-r--r-- | tests/test_nodes.vty | 48 | ||||
-rw-r--r-- | tests/test_subscriber_errors.ctrl | 20 |
7 files changed, 311 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 @@ hlr 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 { 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 @@ enum hlr_vty_node { 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 @@ int osmo_gsup_create_insert_subscriber_data_msg(struct osmo_gsup_message *gsup, 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 @@ DEFUN(cfg_gsup, 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 @@ static int config_write_hlr_gsup(struct vty *vty) 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 @@ void hlr_vty_init(void *hlr_ctx) 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)# ? OsmoHLR(config-hlr)# list ... gsup + ps database PATH euse NAME no euse NAME @@ -112,6 +113,12 @@ hlr 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 @@ mslookup 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 @@ ERROR 57 Value failed verification. 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 -ERROR 61 Invalid format. +SET 60 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,AAA +ERROR 60 Invalid format. -SET 62 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC -ERROR 62 Invalid format. +SET 61 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC +ERROR 61 Invalid format. -SET 63 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,zzz -ERROR 63 Invalid OP/OPC. +SET 62 subscriber.by-imsi-901990000000003.aud3g milenage,c01ffedc1cadaeac1d1f1edacac1ab0a,OPC,zzz +ERROR 62 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. |