diff options
Diffstat (limited to 'openbsc/src')
-rw-r--r-- | openbsc/src/gprs/gb_proxy.c | 198 | ||||
-rw-r--r-- | openbsc/src/gprs/gb_proxy_vty.c | 70 |
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; |