aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2014-05-23 20:48:07 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2014-07-22 16:07:01 +0200
commit736852825ab1e7f344a65e532bcb0cca54f5b630 (patch)
treebdf9c908a63dd153888dcfd7009eda778a2d74c4 /openbsc/src
parent91fb6802360608a0d2757d4898bfd2387a2ebdcc (diff)
gprs: Add APN patch support for LLC/GSM messages
Patch the APN in every 'Activate PDP Context Request' message to the value given by the 'core-access-point-name' command. If the command is given without an APN, the whole APN IE will be removed. If the command is being prefixed by a 'no', the APN IE remains unmodified. The patch mode 'llc-gsm' is added to selectively enable the patching of LLC session management messages. This is enabled implicitely by the patch mode 'llc'. Note that the patch mode should not be set to a value not enabling the patching of LLC GSM messages ('llc-gsm', 'llc', and 'default' are sufficient to patch 'Activate PDP Context Request' messages). Ticket: OW#1192 Sponsored-by: On-Waves ehf
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/gprs/gb_proxy.c198
-rw-r--r--openbsc/src/gprs/gb_proxy_vty.c70
2 files changed, 264 insertions, 4 deletions
diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c
index 6daf45077..5d508e767 100644
--- a/openbsc/src/gprs/gb_proxy.c
+++ b/openbsc/src/gprs/gb_proxy.c
@@ -61,6 +61,7 @@ enum gbprox_global_ctr {
GBPROX_GLOB_CTR_OTHER_ERR,
GBPROX_GLOB_CTR_RAID_PATCHED_BSS,
GBPROX_GLOB_CTR_RAID_PATCHED_SGSN,
+ GBPROX_GLOB_CTR_APN_PATCHED,
GBPROX_GLOB_CTR_PATCH_CRYPT_ERR,
GBPROX_GLOB_CTR_PATCH_ERR,
};
@@ -79,6 +80,7 @@ static const struct rate_ctr_desc global_ctr_description[] = {
{ "error", "Other error " },
{ "raid-mod.bss", "RAID patched (BSS )" },
{ "raid-mod.sgsn", "RAID patched (SGSN)" },
+ { "apn-mod.sgsn", "APN patched " },
{ "mod-crypt-err", "Patch error: encrypted " },
{ "mod-err", "Patch error: other " },
};
@@ -277,6 +279,89 @@ static void strip_ns_hdr(struct msgb *msg)
msgb_pull(msg, strip_len);
}
+/* TODO: Move this to libosmocore/msgb.c */
+static int msgb_resize_area(struct msgb *msg, uint8_t *area,
+ size_t old_size, size_t new_size)
+{
+ int rc;
+ uint8_t *rest = area + old_size;
+ int rest_len = msg->len - old_size - (area - msg->data);
+ int delta_size = (int)new_size - (int)old_size;
+
+ if (delta_size == 0)
+ return 0;
+
+ if (delta_size > 0) {
+ rc = msgb_trim(msg, msg->len + delta_size);
+ if (rc < 0)
+ return rc;
+ }
+
+ memmove(area + new_size, area + old_size, rest_len);
+
+ if (msg->l1h >= rest)
+ msg->l1h += delta_size;
+ if (msg->l2h >= rest)
+ msg->l2h += delta_size;
+ if (msg->l3h >= rest)
+ msg->l3h += delta_size;
+ if (msg->l4h >= rest)
+ msg->l4h += delta_size;
+
+ if (delta_size < 0)
+ msgb_trim(msg, msg->len + delta_size);
+
+ return 0;
+}
+
+/* TODO: Move these conversion functions to a utils file. */
+char * gbprox_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars)
+{
+ char *str = out_str;
+
+ while (rest_chars > 0 && apn_enc[0]) {
+ size_t label_size = apn_enc[0];
+ if (label_size + 1 > rest_chars)
+ return NULL;
+
+ memmove(str, apn_enc + 1, label_size);
+ str += label_size;
+ rest_chars -= label_size + 1;
+ apn_enc += label_size + 1;
+
+ if (rest_chars)
+ *(str++) = '.';
+ }
+ str[0] = '\0';
+
+ return out_str;
+}
+
+int gbprox_str_to_apn(uint8_t *apn_enc, const char *str, size_t max_chars)
+{
+ uint8_t *last_len_field = apn_enc;
+ int len = 1;
+ apn_enc += 1;
+
+ while (str[0]) {
+ if (str[0] == '.') {
+ *last_len_field = (apn_enc - last_len_field) - 1;
+ last_len_field = apn_enc;
+ } else {
+ *apn_enc = str[0];
+ }
+ apn_enc += 1;
+ str += 1;
+ len += 1;
+ if (len > max_chars)
+ return -1;
+ }
+
+ *last_len_field = (apn_enc - last_len_field) - 1;
+
+ return len;
+}
+
/* check whether patching is enabled at this level */
static int patching_is_enabled(enum gbproxy_patch_mode need_at_least)
{
@@ -364,6 +449,56 @@ static void gbprox_patch_raid(uint8_t *raid_enc, struct gbprox_patch_state *stat
}
}
+static void gbprox_patch_apn_ie(struct msgb *msg,
+ uint8_t *apn_ie, size_t apn_ie_len,
+ size_t *new_apn_ie_len, const char *log_text)
+{
+ struct apn_ie_hdr {
+ uint8_t iei;
+ uint8_t apn_len;
+ uint8_t apn[0];
+ } *hdr = (void *)apn_ie;
+
+ size_t apn_len = hdr->apn_len;
+ uint8_t *apn = hdr->apn;
+
+ OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
+ OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
+
+ if (gbcfg.core_apn_size == 0) {
+ char str1[110];
+ /* Remove the IE */
+ LOGP(DGPRS, LOGL_DEBUG,
+ "Patching %s to SGSN: Removing APN '%s'\n",
+ log_text,
+ gbprox_apn_to_str(str1, apn, apn_len));
+
+ *new_apn_ie_len = 0;
+ msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
+ } else {
+ /* Resize the IE */
+ char str1[110];
+ char str2[110];
+
+ OSMO_ASSERT(gbcfg.core_apn_size <= 100);
+
+ LOGP(DGPRS, LOGL_DEBUG,
+ "Patching %s to SGSN: "
+ "Replacing APN '%s' -> '%s'\n",
+ log_text,
+ gbprox_apn_to_str(str1, apn, apn_len),
+ gbprox_apn_to_str(str2, gbcfg.core_apn,
+ gbcfg.core_apn_size));
+
+ *new_apn_ie_len = gbcfg.core_apn_size + 2;
+ msgb_resize_area(msg, apn, apn_len, gbcfg.core_apn_size);
+ memcpy(apn, gbcfg.core_apn, gbcfg.core_apn_size);
+ hdr->apn_len = gbcfg.core_apn_size;
+ }
+
+ rate_ctr_inc(&get_global_ctrg()->ctr[GBPROX_GLOB_CTR_APN_PATCHED]);
+}
+
static int gbprox_patch_gmm_attach_req(struct msgb *msg,
uint8_t *data, size_t data_len,
struct gbprox_patch_state *state,
@@ -480,6 +615,60 @@ static int gbprox_patch_gmm_ptmsi_reall_cmd(struct msgb *msg,
return 1;
}
+static int gbprox_patch_gsm_act_pdp_req(struct msgb *msg,
+ uint8_t *data, size_t data_len,
+ struct gbprox_patch_state *state,
+ int to_bss, int *len_change)
+{
+ size_t new_len, old_len;
+
+ /* Check minimum length, always contains length field of
+ * Requested QoS */
+ if (data_len < 9)
+ return 0;
+
+ /* Skip Requested NSAPI */
+ /* Skip Requested LLC SAPI */
+ data_len -= 2;
+ data += 2;
+
+ /* Skip Requested QoS (support 04.08 and 24.008) */
+ if (data[0] < 4 || data[0] > 14 ||
+ data_len - (data[0] + 1) < 0)
+ /* invalid */
+ return 0;
+ data_len -= data[0] + 1;
+ data += data[0] + 1;
+
+ /* Skip Requested PDP address */
+ if (data_len < 1 ||
+ data[0] < 2 || data[0] > 18 ||
+ data_len - (data[0] + 1) < 0)
+ /* invalid */
+ return 0;
+ data_len -= data[0] + 1;
+ data += data[0] + 1;
+
+ /* Access point name */
+ if (data_len < 2 || data[0] != GSM48_IE_GSM_APN)
+ return 0;
+
+ if (data[1] < 1 || data[1] > 100 ||
+ data_len - (data[1] + 2) < 0)
+ /* invalid */
+ return 0;
+
+ old_len = data[1] + 2;
+
+ gbprox_patch_apn_ie(msg, data, old_len, &new_len, "LLC/ACT_PDP_REQ");
+
+ *len_change += (int)new_len - (int)old_len;
+ data_len -= old_len;
+ data += new_len;
+
+ return 1;
+}
+
static int gbprox_patch_dtap(struct msgb *msg, uint8_t *data, size_t data_len,
struct gbprox_patch_state *state, int to_bss,
int *len_change)
@@ -529,6 +718,13 @@ static int gbprox_patch_dtap(struct msgb *msg, uint8_t *data, size_t data_len,
return gbprox_patch_gmm_ptmsi_reall_cmd(msg, data, data_len,
state, to_bss, len_change);
+ case GSM48_MT_GSM_ACT_PDP_REQ:
+ if (!patching_is_enabled(GBPROX_PATCH_LLC_GSM))
+ break;
+ if (gbcfg.core_apn == NULL)
+ break;
+ return gbprox_patch_gsm_act_pdp_req(msg, data, data_len,
+ state, to_bss, len_change);
default:
break;
};
@@ -640,7 +836,7 @@ static void gbprox_patch_bssgp_message(struct msgb *msg, int to_bss)
uint8_t *data;
size_t data_len;
- if (!gbcfg.core_mcc && !gbcfg.core_mnc)
+ if (!gbcfg.core_mcc && !gbcfg.core_mnc && !gbcfg.core_apn)
return;
bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c
index 48033473f..6d241653d 100644
--- a/openbsc/src/gprs/gb_proxy_vty.c
+++ b/openbsc/src/gprs/gb_proxy_vty.c
@@ -21,6 +21,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <string.h>
#include <osmocom/core/talloc.h>
@@ -50,6 +51,7 @@ static const struct value_string patch_modes[] = {
{GBPROX_PATCH_LLC_ATTACH_REQ, "llc-attach-req"},
{GBPROX_PATCH_LLC_ATTACH, "llc-attach"},
{GBPROX_PATCH_LLC_GMM, "llc-gmm"},
+ {GBPROX_PATCH_LLC_GSM, "llc-gsm"},
{GBPROX_PATCH_LLC, "llc"},
{0, NULL}
};
@@ -67,6 +69,18 @@ static int config_write_gbproxy(struct vty *vty)
if (g_cfg->core_mnc > 0)
vty_out(vty, " core-mobile-network-code %d%s",
g_cfg->core_mnc, VTY_NEWLINE);
+ if (g_cfg->core_apn != NULL) {
+ if (g_cfg->core_apn_size > 0) {
+ char str[500] = {0};
+ vty_out(vty, " core-access-point-name %s%s",
+ gbprox_apn_to_str(str, g_cfg->core_apn,
+ g_cfg->core_apn_size),
+ VTY_NEWLINE);
+ } else {
+ vty_out(vty, " core-access-point-name%s",
+ VTY_NEWLINE);
+ }
+ }
if (g_cfg->patch_mode != GBPROX_PATCH_DEFAULT)
vty_out(vty, " patch-mode %s%s",
@@ -138,15 +152,62 @@ DEFUN(cfg_gbproxy_no_core_mcc,
return CMD_SUCCESS;
}
+#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
+
+DEFUN(cfg_gbproxy_core_apn_remove,
+ cfg_gbproxy_core_apn_remove_cmd,
+ "core-access-point-name",
+ GBPROXY_CORE_APN_STR)
+{
+ talloc_free(g_cfg->core_apn);
+ /* TODO: replace NULL */
+ g_cfg->core_apn = talloc_zero_size(NULL, 2);
+ g_cfg->core_apn_size = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy_core_apn,
+ cfg_gbproxy_core_apn_cmd,
+ "core-access-point-name APN",
+ GBPROXY_CORE_APN_STR "Replacement APN\n")
+{
+ int apn_len = strlen(argv[0]) + 1;
+
+ if (apn_len > 100) {
+ vty_out(vty, "APN string too long (max 99 chars)%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* TODO: replace NULL */
+ g_cfg->core_apn = talloc_realloc_size(NULL, g_cfg->core_apn, apn_len);
+ g_cfg->core_apn_size = gbprox_str_to_apn(g_cfg->core_apn, argv[0], apn_len);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_gbproxy_no_core_apn,
+ cfg_gbproxy_no_core_apn_cmd,
+ "no core-access-point-name",
+ NO_STR GBPROXY_CORE_APN_STR)
+{
+ talloc_free(g_cfg->core_apn);
+ g_cfg->core_apn = NULL;
+ g_cfg->core_apn_size = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_gbproxy_patch_mode,
cfg_gbproxy_patch_mode_cmd,
- "patch-mode (default|bssgp|llc-attach-req|llc-attach|llc)",
+ "patch-mode (default|bssgp|llc-attach-req|llc-attach|llc-gmm|llc-gsm|llc)",
"Set patch mode\n"
- "Use build-in default (at least llc-attach-req)\n"
+ "Use build-in default (best effort, try to patch everything)\n"
"Only patch BSSGP headers\n"
"Patch BSSGP headers and LLC Attach Request messages\n"
"Patch BSSGP headers and LLC Attach Request/Accept messages\n"
- "Patch BSSGP headers and all supported GMM LLC messages\n"
+ "Patch BSSGP headers and LLC GMM messages\n"
+ "Patch BSSGP headers, LLC GMM, and LLC GSM messages\n"
+ "Patch BSSGP headers and all supported LLC messages\n"
)
{
int val = get_string_value(patch_modes, argv[0]);
@@ -170,8 +231,11 @@ int gbproxy_vty_init(void)
install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_remove_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
+ install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_mode_cmd);
return 0;