Move out gbproxy to its own subdir
Change-Id: I2cc98d3a276d953609bbbbaa9782a0112687791e
This commit is contained in:
parent
658b222e6d
commit
1ddefb12e4
|
@ -247,6 +247,7 @@ AC_OUTPUT(
|
|||
include/osmocom/sgsn/Makefile
|
||||
src/Makefile
|
||||
src/gprs/Makefile
|
||||
src/gbproxy/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/gprs/Makefile
|
||||
|
|
|
@ -86,12 +86,12 @@ Files: include/osmocom/sgsn/a_reset.h
|
|||
src/gprs/gprs_gb_parse.c
|
||||
src/gprs/gprs_utils.c
|
||||
src/gprs/sgsn_ares.c
|
||||
src/gprs/gb_proxy.c
|
||||
src/gprs/gb_proxy_main.c
|
||||
src/gprs/gb_proxy_patch.c
|
||||
src/gprs/gb_proxy_peer.c
|
||||
src/gprs/gb_proxy_tlli.c
|
||||
src/gprs/gb_proxy_vty.c
|
||||
src/gbproxy/gb_proxy.c
|
||||
src/gbproxy/gb_proxy_main.c
|
||||
src/gbproxy/gb_proxy_patch.c
|
||||
src/gbproxy/gb_proxy_peer.c
|
||||
src/gbproxy/gb_proxy_tlli.c
|
||||
src/gbproxy/gb_proxy_vty.c
|
||||
src/gprs/gprs_gmm.c
|
||||
src/gprs/gprs_llc.c
|
||||
src/gprs/gprs_llc_vty.c
|
||||
|
|
|
@ -23,7 +23,7 @@ app_configs = {
|
|||
}
|
||||
|
||||
|
||||
apps = [(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
|
||||
apps = [(4246, "src/gbproxy/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
|
||||
(4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"),
|
||||
(4253, "src/gprs/osmo-gtphub", "OsmoGTPhub", "gtphub")
|
||||
]
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_builddir) \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-fno-strict-aliasing \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOGB_CFLAGS) \
|
||||
$(LIBOSMOGSUPCLIENT_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(LIBGTP_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
bin_PROGRAMS = \
|
||||
osmo-gbproxy \
|
||||
$(NULL)
|
||||
|
||||
osmo_gbproxy_SOURCES = \
|
||||
gb_proxy.c \
|
||||
gb_proxy_main.c \
|
||||
gb_proxy_vty.c \
|
||||
gb_proxy_ctrl.c \
|
||||
gb_proxy_patch.c \
|
||||
gb_proxy_tlli.c \
|
||||
gb_proxy_peer.c \
|
||||
$(NULL)
|
||||
osmo_gbproxy_LDADD = \
|
||||
$(top_builddir)/src/gprs/gprs_gb_parse.o \
|
||||
$(top_builddir)/src/gprs/gprs_llc_parse.o \
|
||||
$(top_builddir)/src/gprs/crc24.o \
|
||||
$(top_builddir)/src/gprs/gprs_utils.o \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBGTP_LIBS) \
|
||||
-lrt \
|
||||
$(NULL)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
|||
/* Control Interface Implementation for the Gb-proxy */
|
||||
/*
|
||||
* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Daniel Willmann
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gprs/gprs_ns.h>
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
extern vector ctrl_node_vec;
|
||||
|
||||
static int get_nsvc_state(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gbproxy_config *cfg = data;
|
||||
struct gprs_ns_inst *nsi = cfg->nsi;
|
||||
struct gprs_nsvc *nsvc;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, "");
|
||||
|
||||
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
|
||||
if (nsvc == nsi->unknown_nsvc)
|
||||
continue;
|
||||
|
||||
cmd->reply = gprs_nsvc_state_append(cmd->reply, nsvc);
|
||||
}
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(nsvc_state, "nsvc-state");
|
||||
|
||||
static int get_gbproxy_state(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gbproxy_config *cfg = data;
|
||||
struct gbproxy_peer *peer;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, "");
|
||||
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
struct gprs_ra_id raid;
|
||||
gsm48_parse_ra(&raid, peer->ra);
|
||||
|
||||
cmd->reply = talloc_asprintf_append(cmd->reply, "%u,%u,%u,%u,%u,%u,%s\n",
|
||||
peer->nsei, peer->bvci,
|
||||
raid.mcc, raid.mnc,
|
||||
raid.lac, raid.rac,
|
||||
peer->blocked ? "BLOCKED" : "UNBLOCKED");
|
||||
}
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(gbproxy_state, "gbproxy-state");
|
||||
|
||||
static int get_num_peers(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gbproxy_config *cfg = data;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, "");
|
||||
cmd->reply = talloc_asprintf_append(cmd->reply, "%u", llist_count(&cfg->bts_peers));
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
CTRL_CMD_DEFINE_RO(num_peers, "number-of-peers");
|
||||
|
||||
int gb_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc = 0;
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_nsvc_state);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_gbproxy_state);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_num_peers);
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,393 @@
|
|||
/* NS-over-IP proxy */
|
||||
|
||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
|
||||
#include <osmocom/gprs/gprs_ns.h>
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
|
||||
#include <osmocom/sgsn/signal.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/vty.h>
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
void *tall_sgsn_ctx;
|
||||
|
||||
const char *openbsc_copyright =
|
||||
"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
#define CONFIG_FILE_DEFAULT "osmo-gbproxy.cfg"
|
||||
#define CONFIG_FILE_LEGACY "osmo_gbproxy.cfg"
|
||||
|
||||
static char *config_file = NULL;
|
||||
struct gbproxy_config *gbcfg;
|
||||
static int daemonize = 0;
|
||||
|
||||
/* Pointer to the SGSN peer */
|
||||
extern struct gbprox_peer *gbprox_peer_sgsn;
|
||||
|
||||
/* call-back function for the NS protocol */
|
||||
static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
|
||||
struct msgb *msg, uint16_t bvci)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (event) {
|
||||
case GPRS_NS_EVT_UNIT_DATA:
|
||||
rc = gbprox_rcvmsg(gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
|
||||
break;
|
||||
default:
|
||||
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
|
||||
if (msg)
|
||||
msgb_free(msg);
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stdout, "signal %u received\n", signal);
|
||||
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
|
||||
sleep(1);
|
||||
exit(0);
|
||||
break;
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report
|
||||
* and then return to the caller, who will abort the process */
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_sgsn_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: bsc_hack\n");
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf(" Some useful help...\n");
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
|
||||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
||||
printf(" -c --config-file filename The config file to use [%s]\n", CONFIG_FILE_DEFAULT);
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -T --timestamp Prefix every log line with a timestamp\n");
|
||||
printf(" -V --version. Print the version.\n");
|
||||
printf(" -e --log-level number. Set a global loglevel.\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "debug", 1, 0, 'd' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "disable-color", 0, 0, 's' },
|
||||
{ "timestamp", 0, 0, 'T' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "log-level", 1, 0, 'e' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hd:Dc:sTVe:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
config_file = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||
break;
|
||||
case 'e':
|
||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int gbproxy_vty_is_config_node(struct vty *vty, int node)
|
||||
{
|
||||
switch (node) {
|
||||
/* add items that are not config */
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int gbproxy_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
case GBPROXY_NODE:
|
||||
default:
|
||||
if (gbproxy_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "OsmoGbProxy",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = gbproxy_vty_go_parent,
|
||||
.is_config_node = gbproxy_vty_is_config_node,
|
||||
};
|
||||
|
||||
/* default categories */
|
||||
static struct log_info_cat gprs_categories[] = {
|
||||
[DGPRS] = {
|
||||
.name = "DGPRS",
|
||||
.description = "GPRS Packet Service",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
[DNS] = {
|
||||
.name = "DNS",
|
||||
.description = "GPRS Network Service (NS)",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DBSSGP] = {
|
||||
.name = "DBSSGP",
|
||||
.description = "GPRS BSS Gateway Protocol (BSSGP)",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info gprs_log_info = {
|
||||
.filter_fn = gprs_log_filter_fn,
|
||||
.cat = gprs_categories,
|
||||
.num_cat = ARRAY_SIZE(gprs_categories),
|
||||
};
|
||||
|
||||
static bool file_exists(const char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
return stat(path, &sb) ? false : true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
tall_sgsn_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
|
||||
msgb_talloc_ctx_init(tall_sgsn_ctx, 0);
|
||||
vty_info.tall_ctx = tall_sgsn_ctx;
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGTERM, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
osmo_init_logging2(tall_sgsn_ctx, &gprs_log_info);
|
||||
|
||||
vty_info.copyright = openbsc_copyright;
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
gbproxy_vty_init();
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
/* Backwards compatibility: for years, the default config file name was
|
||||
* osmo_gbproxy.cfg. All other Osmocom programs use osmo-*.cfg with a
|
||||
* dash. To be able to use the new config file name without breaking
|
||||
* previous setups that might rely on the legacy default config file
|
||||
* name, we need to look for the old config file if no -c option was
|
||||
* passed AND no file exists with the new default file name. */
|
||||
if (!config_file) {
|
||||
/* No -c option was passed */
|
||||
if (file_exists(CONFIG_FILE_LEGACY)
|
||||
&& !file_exists(CONFIG_FILE_DEFAULT))
|
||||
config_file = CONFIG_FILE_LEGACY;
|
||||
else
|
||||
config_file = CONFIG_FILE_DEFAULT;
|
||||
}
|
||||
|
||||
rate_ctr_init(tall_sgsn_ctx);
|
||||
osmo_stats_init(tall_sgsn_ctx);
|
||||
|
||||
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_sgsn_ctx);
|
||||
if (!bssgp_nsi) {
|
||||
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
gbcfg = talloc_zero(tall_sgsn_ctx, struct gbproxy_config);
|
||||
if (!gbcfg) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Unable to allocate config\n");
|
||||
exit(1);
|
||||
}
|
||||
gbproxy_init_config(gbcfg);
|
||||
gbcfg->nsi = bssgp_nsi;
|
||||
gprs_ns_vty_init(bssgp_nsi);
|
||||
gprs_ns_set_log_ss(DNS);
|
||||
bssgp_set_log_ss(DBSSGP);
|
||||
osmo_signal_register_handler(SS_L_NS, &gbprox_signal, gbcfg);
|
||||
|
||||
rc = gbproxy_parse_config(config_file, gbcfg);
|
||||
if (rc < 0) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file '%s'\n", config_file);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
/* start telnet after reading config for vty_get_bind_addr() */
|
||||
rc = telnet_init_dynif(tall_sgsn_ctx, NULL,
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
/* Start control interface after getting config for
|
||||
* ctrl_vty_get_bind_addr() */
|
||||
ctrl = ctrl_interface_setup_dynip(gbcfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_GBPROXY, NULL);
|
||||
if (!ctrl) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Failed to create CTRL interface.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (gb_ctrl_cmds_install() != 0) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Failed to install CTRL commands.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!gprs_nsvc_by_nsei(gbcfg->nsi, gbcfg->nsip_sgsn_nsei)) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
|
||||
"without creating that NSEI before\n",
|
||||
gbcfg->nsip_sgsn_nsei);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rc = gprs_ns_nsip_listen(bssgp_nsi);
|
||||
if (rc < 0) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
rc = gprs_ns_frgre_listen(bssgp_nsi);
|
||||
if (rc < 0) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
|
||||
"socket. Do you have CAP_NET_RAW?\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset all the persistent NS-VCs that we've read from the config */
|
||||
gbprox_reset_persistent_nsvcs(bssgp_nsi);
|
||||
|
||||
while (1) {
|
||||
rc = osmo_select_main(0);
|
||||
if (rc < 0)
|
||||
exit(3);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -0,0 +1,465 @@
|
|||
/* Gb-proxy message patching */
|
||||
|
||||
/* (C) 2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_utils.h>
|
||||
#include <osmocom/sgsn/gprs_gb_parse.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
#include <osmocom/gprs/protocol/gsm_08_18.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
extern void *tall_sgsn_ctx;
|
||||
|
||||
/* patch RA identifier in place */
|
||||
static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_peer *peer,
|
||||
int to_bss, const char *log_text)
|
||||
{
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
struct osmo_plmn_id old_plmn;
|
||||
struct gprs_ra_id raid;
|
||||
enum gbproxy_peer_ctr counter =
|
||||
to_bss ?
|
||||
GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
|
||||
GBPROX_PEER_CTR_RAID_PATCHED_BSS;
|
||||
|
||||
if (!state->local_plmn.mcc || !state->local_plmn.mnc)
|
||||
return;
|
||||
|
||||
gsm48_parse_ra(&raid, (uint8_t *)raid_enc);
|
||||
|
||||
old_plmn = (struct osmo_plmn_id){
|
||||
.mcc = raid.mcc,
|
||||
.mnc = raid.mnc,
|
||||
.mnc_3_digits = raid.mnc_3_digits,
|
||||
};
|
||||
|
||||
if (!to_bss) {
|
||||
/* BSS -> SGSN */
|
||||
if (state->local_plmn.mcc)
|
||||
raid.mcc = peer->cfg->core_plmn.mcc;
|
||||
|
||||
if (state->local_plmn.mnc) {
|
||||
raid.mnc = peer->cfg->core_plmn.mnc;
|
||||
raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits;
|
||||
}
|
||||
} else {
|
||||
/* SGSN -> BSS */
|
||||
if (state->local_plmn.mcc)
|
||||
raid.mcc = state->local_plmn.mcc;
|
||||
|
||||
if (state->local_plmn.mnc) {
|
||||
raid.mnc = state->local_plmn.mnc;
|
||||
raid.mnc_3_digits = state->local_plmn.mnc_3_digits;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"Patching %s to %s: "
|
||||
"%s-%d-%d -> %s\n",
|
||||
log_text,
|
||||
to_bss ? "BSS" : "SGSN",
|
||||
osmo_plmn_name(&old_plmn), raid.lac, raid.rac,
|
||||
osmo_rai_name(&raid));
|
||||
|
||||
gsm48_encode_ra(raid_enc, &raid);
|
||||
rate_ctr_inc(&peer->ctrg->ctr[counter]);
|
||||
}
|
||||
|
||||
static void gbproxy_patch_apn_ie(struct msgb *msg,
|
||||
uint8_t *apn_ie, size_t apn_ie_len,
|
||||
struct gbproxy_peer *peer,
|
||||
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 (peer->cfg->core_apn_size == 0) {
|
||||
char str1[110];
|
||||
/* Remove the IE */
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"Patching %s to SGSN: Removing APN '%s'\n",
|
||||
log_text,
|
||||
osmo_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(peer->cfg->core_apn_size <= 100);
|
||||
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"Patching %s to SGSN: "
|
||||
"Replacing APN '%s' -> '%s'\n",
|
||||
log_text,
|
||||
osmo_apn_to_str(str1, apn, apn_len),
|
||||
osmo_apn_to_str(str2, peer->cfg->core_apn,
|
||||
peer->cfg->core_apn_size));
|
||||
|
||||
*new_apn_ie_len = peer->cfg->core_apn_size + 2;
|
||||
msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
|
||||
memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
|
||||
hdr->apn_len = peer->cfg->core_apn_size;
|
||||
}
|
||||
|
||||
rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
|
||||
}
|
||||
|
||||
static int gbproxy_patch_tlli(uint8_t *tlli_enc,
|
||||
struct gbproxy_peer *peer,
|
||||
uint32_t new_tlli,
|
||||
int to_bss, const char *log_text)
|
||||
{
|
||||
uint32_t tlli_be;
|
||||
uint32_t tlli;
|
||||
enum gbproxy_peer_ctr counter =
|
||||
to_bss ?
|
||||
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
|
||||
GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
|
||||
|
||||
memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
|
||||
tlli = ntohl(tlli_be);
|
||||
|
||||
if (tlli == new_tlli)
|
||||
return 0;
|
||||
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"Patching %ss: "
|
||||
"Replacing %08x -> %08x\n",
|
||||
log_text, tlli, new_tlli);
|
||||
|
||||
tlli_be = htonl(new_tlli);
|
||||
memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
|
||||
|
||||
rate_ctr_inc(&peer->ctrg->ctr[counter]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
|
||||
struct gbproxy_peer *peer,
|
||||
uint32_t new_ptmsi,
|
||||
int to_bss, const char *log_text)
|
||||
{
|
||||
uint32_t ptmsi_be;
|
||||
uint32_t ptmsi;
|
||||
enum gbproxy_peer_ctr counter =
|
||||
to_bss ?
|
||||
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
|
||||
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
|
||||
memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
|
||||
ptmsi = ntohl(ptmsi_be);
|
||||
|
||||
if (ptmsi == new_ptmsi)
|
||||
return 0;
|
||||
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"Patching %ss: "
|
||||
"Replacing %08x -> %08x\n",
|
||||
log_text, ptmsi, new_ptmsi);
|
||||
|
||||
ptmsi_be = htonl(new_ptmsi);
|
||||
memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
|
||||
|
||||
rate_ctr_inc(&peer->ctrg->ctr[counter]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
|
||||
struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info, int *len_change,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
|
||||
int have_patched = 0;
|
||||
int fcs;
|
||||
struct gbproxy_config *cfg = peer->cfg;
|
||||
|
||||
if (parse_ctx->ptmsi_enc && link_info &&
|
||||
!parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
|
||||
uint32_t ptmsi;
|
||||
if (parse_ctx->to_bss)
|
||||
ptmsi = link_info->tlli.ptmsi;
|
||||
else
|
||||
ptmsi = link_info->sgsn_tlli.ptmsi;
|
||||
|
||||
if (ptmsi != GSM_RESERVED_TMSI) {
|
||||
if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
|
||||
ptmsi, parse_ctx->to_bss, "P-TMSI"))
|
||||
have_patched = 1;
|
||||
} else {
|
||||
/* TODO: invalidate old RAI if present (see below) */
|
||||
}
|
||||
}
|
||||
|
||||
if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
|
||||
uint32_t ptmsi;
|
||||
if (parse_ctx->to_bss)
|
||||
ptmsi = link_info->tlli.ptmsi;
|
||||
else
|
||||
ptmsi = link_info->sgsn_tlli.ptmsi;
|
||||
|
||||
OSMO_ASSERT(ptmsi);
|
||||
if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
|
||||
ptmsi, parse_ctx->to_bss, "new P-TMSI"))
|
||||
have_patched = 1;
|
||||
}
|
||||
|
||||
if (parse_ctx->raid_enc) {
|
||||
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss,
|
||||
parse_ctx->llc_msg_name);
|
||||
have_patched = 1;
|
||||
}
|
||||
|
||||
if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
|
||||
/* TODO: Patch to invalid if P-TMSI unknown. */
|
||||
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
|
||||
parse_ctx->llc_msg_name);
|
||||
have_patched = 1;
|
||||
}
|
||||
|
||||
if (parse_ctx->apn_ie &&
|
||||
cfg->core_apn &&
|
||||
!parse_ctx->to_bss &&
|
||||
gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
|
||||
cfg->core_apn) {
|
||||
size_t new_len;
|
||||
gbproxy_patch_apn_ie(msg,
|
||||
parse_ctx->apn_ie, parse_ctx->apn_ie_len,
|
||||
peer, &new_len, parse_ctx->llc_msg_name);
|
||||
*len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
|
||||
|
||||
have_patched = 1;
|
||||
}
|
||||
|
||||
if (have_patched) {
|
||||
llc_len += *len_change;
|
||||
ghp->crc_length += *len_change;
|
||||
|
||||
/* Fix FCS */
|
||||
fcs = gprs_llc_fcs(llc, ghp->crc_length);
|
||||
LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
|
||||
ghp->fcs, fcs);
|
||||
|
||||
llc[llc_len - 3] = fcs & 0xff;
|
||||
llc[llc_len - 2] = (fcs >> 8) & 0xff;
|
||||
llc[llc_len - 1] = (fcs >> 16) & 0xff;
|
||||
}
|
||||
|
||||
return have_patched;
|
||||
}
|
||||
|
||||
/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */
|
||||
void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
|
||||
struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info, int *len_change,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
const char *err_info = NULL;
|
||||
int err_ctr = -1;
|
||||
|
||||
if (parse_ctx->bssgp_raid_enc)
|
||||
gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer,
|
||||
parse_ctx->to_bss, "BSSGP");
|
||||
|
||||
if (parse_ctx->need_decryption &&
|
||||
(peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
|
||||
/* Patching LLC messages has been requested
|
||||
* explicitly, but the message (including the
|
||||
* type) is encrypted, so we possibly fail to
|
||||
* patch the LLC part of the message. */
|
||||
err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
|
||||
err_info = "GMM message is encrypted";
|
||||
goto patch_error;
|
||||
}
|
||||
|
||||
if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
|
||||
/* Happens with unknown (not cached) TLLI coming from
|
||||
* the SGSN */
|
||||
/* TODO: What shall be done with the message in this case? */
|
||||
err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
|
||||
err_info = "TLLI sent by the SGSN is unknown";
|
||||
goto patch_error;
|
||||
}
|
||||
|
||||
if (!link_info)
|
||||
return;
|
||||
|
||||
if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
|
||||
uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
|
||||
link_info, parse_ctx->to_bss);
|
||||
|
||||
if (tlli) {
|
||||
gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
|
||||
parse_ctx->to_bss, "TLLI");
|
||||
parse_ctx->tlli = tlli;
|
||||
} else {
|
||||
/* Internal error */
|
||||
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
|
||||
err_info = "Replacement TLLI is 0";
|
||||
goto patch_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
|
||||
uint32_t ptmsi;
|
||||
if (parse_ctx->to_bss)
|
||||
ptmsi = link_info->tlli.ptmsi;
|
||||
else
|
||||
ptmsi = link_info->sgsn_tlli.ptmsi;
|
||||
|
||||
if (ptmsi != GSM_RESERVED_TMSI)
|
||||
gbproxy_patch_ptmsi(
|
||||
parse_ctx->bssgp_ptmsi_enc, peer,
|
||||
ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
|
||||
}
|
||||
|
||||
if (parse_ctx->llc) {
|
||||
uint8_t *llc = parse_ctx->llc;
|
||||
size_t llc_len = parse_ctx->llc_len;
|
||||
int llc_len_change = 0;
|
||||
|
||||
gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
|
||||
&llc_len_change, parse_ctx);
|
||||
/* Note that the APN might have been resized here, but no
|
||||
* pointer int the parse_ctx will refer to an adress after the
|
||||
* APN. So it's possible to patch first and do the TLLI
|
||||
* handling afterwards. */
|
||||
|
||||
if (llc_len_change) {
|
||||
llc_len += llc_len_change;
|
||||
|
||||
/* Fix LLC IE len */
|
||||
/* TODO: This is a kludge, but the a pointer to the
|
||||
* start of the IE is not available here */
|
||||
if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
|
||||
/* most probably a one byte length */
|
||||
if (llc_len > 127) {
|
||||
err_info = "Cannot increase size";
|
||||
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
|
||||
goto patch_error;
|
||||
}
|
||||
llc[-1] = llc_len | 0x80;
|
||||
} else {
|
||||
llc[-2] = (llc_len >> 8) & 0x7f;
|
||||
llc[-1] = llc_len & 0xff;
|
||||
}
|
||||
*len_change += llc_len_change;
|
||||
}
|
||||
/* Note that the tp struct might contain invalid pointers here
|
||||
* if the LLC field has changed its size */
|
||||
parse_ctx->llc_len = llc_len;
|
||||
}
|
||||
return;
|
||||
|
||||
patch_error:
|
||||
OSMO_ASSERT(err_ctr >= 0);
|
||||
rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
|
||||
LOGP(DGPRS, LOGL_ERROR,
|
||||
"NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
|
||||
msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
|
||||
err_info);
|
||||
}
|
||||
|
||||
void gbproxy_clear_patch_filter(struct gbproxy_match *match)
|
||||
{
|
||||
if (match->enable) {
|
||||
regfree(&match->re_comp);
|
||||
match->enable = false;
|
||||
}
|
||||
talloc_free(match->re_str);
|
||||
match->re_str = NULL;
|
||||
}
|
||||
|
||||
int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
|
||||
const char **err_msg)
|
||||
{
|
||||
static char err_buf[300];
|
||||
int rc;
|
||||
|
||||
gbproxy_clear_patch_filter(match);
|
||||
|
||||
if (!filter)
|
||||
return 0;
|
||||
|
||||
rc = regcomp(&match->re_comp, filter,
|
||||
REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
||||
|
||||
if (rc == 0) {
|
||||
match->enable = true;
|
||||
match->re_str = talloc_strdup(tall_sgsn_ctx, filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err_msg) {
|
||||
regerror(rc, &match->re_comp,
|
||||
err_buf, sizeof(err_buf));
|
||||
*err_msg = err_buf;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gbproxy_check_imsi(struct gbproxy_match *match,
|
||||
const uint8_t *imsi, size_t imsi_len)
|
||||
{
|
||||
char mi_buf[200];
|
||||
int rc;
|
||||
|
||||
if (!match->enable)
|
||||
return 1;
|
||||
|
||||
rc = gprs_is_mi_imsi(imsi, imsi_len);
|
||||
if (rc > 0)
|
||||
rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
|
||||
if (rc <= 0) {
|
||||
LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
|
||||
osmo_hexdump(imsi, imsi_len));
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
|
||||
|
||||
rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
|
||||
if (rc == REG_NOMATCH) {
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"IMSI '%s' doesn't match pattern '%s'\n",
|
||||
mi_buf, match->re_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
/* Gb proxy peer handling */
|
||||
|
||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010-2013 by On-Waves
|
||||
* (C) 2013 by Holger Hans Peter Freyther
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
#include <osmocom/gprs/protocol/gsm_08_18.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
extern void *tall_sgsn_ctx;
|
||||
|
||||
static const struct rate_ctr_desc peer_ctr_description[] = {
|
||||
{ "blocked", "BVC Block " },
|
||||
{ "unblocked", "BVC Unblock " },
|
||||
{ "dropped", "BVC blocked, dropped packet " },
|
||||
{ "inv-nsei", "NSEI mismatch " },
|
||||
{ "tx-err", "NS Transmission error " },
|
||||
{ "raid-mod:bss", "RAID patched (BSS )" },
|
||||
{ "raid-mod:sgsn", "RAID patched (SGSN)" },
|
||||
{ "apn-mod:sgsn", "APN patched " },
|
||||
{ "tlli-mod:bss", "TLLI patched (BSS )" },
|
||||
{ "tlli-mod:sgsn", "TLLI patched (SGSN)" },
|
||||
{ "ptmsi-mod:bss", "P-TMSI patched (BSS )" },
|
||||
{ "ptmsi-mod:sgsn","P-TMSI patched (SGSN)" },
|
||||
{ "mod-crypt-err", "Patch error: encrypted " },
|
||||
{ "mod-err", "Patch error: other " },
|
||||
{ "attach-reqs", "Attach Request count " },
|
||||
{ "attach-rejs", "Attach Reject count " },
|
||||
{ "attach-acks", "Attach Accept count " },
|
||||
{ "attach-cpls", "Attach Completed count " },
|
||||
{ "ra-upd-reqs", "RoutingArea Update Request count" },
|
||||
{ "ra-upd-rejs", "RoutingArea Update Reject count " },
|
||||
{ "ra-upd-acks", "RoutingArea Update Accept count " },
|
||||
{ "ra-upd-cpls", "RoutingArea Update Compltd count" },
|
||||
{ "gmm-status", "GMM Status count (BSS)" },
|
||||
{ "gmm-status", "GMM Status count (SGSN)" },
|
||||
{ "detach-reqs", "Detach Request count " },
|
||||
{ "detach-acks", "Detach Accept count " },
|
||||
{ "pdp-act-reqs", "PDP Activation Request count " },
|
||||
{ "pdp-act-rejs", "PDP Activation Reject count " },
|
||||
{ "pdp-act-acks", "PDP Activation Accept count " },
|
||||
{ "pdp-deact-reqs","PDP Deactivation Request count " },
|
||||
{ "pdp-deact-acks","PDP Deactivation Accept count " },
|
||||
{ "tlli-unknown", "TLLI from SGSN unknown " },
|
||||
{ "tlli-cache", "TLLI cache size " },
|
||||
};
|
||||
|
||||
osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
|
||||
|
||||
static const struct rate_ctr_group_desc peer_ctrg_desc = {
|
||||
.group_name_prefix = "gbproxy:peer",
|
||||
.group_description = "GBProxy Peer Statistics",
|
||||
.num_ctr = ARRAY_SIZE(peer_ctr_description),
|
||||
.ctr_desc = peer_ctr_description,
|
||||
.class_id = OSMO_STATS_CLASS_PEER,
|
||||
};
|
||||
|
||||
|
||||
/* Find the gbprox_peer by its BVCI */
|
||||
struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
if (peer->bvci == bvci)
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find the gbprox_peer by its NSEI */
|
||||
struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg,
|
||||
uint16_t nsei)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
if (peer->nsei == nsei)
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up a peer by its Routeing Area Identification (RAI) */
|
||||
struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg,
|
||||
const uint8_t *ra)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
if (!memcmp(peer->ra, ra, 6))
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up a peer by its Location Area Identification (LAI) */
|
||||
struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg,
|
||||
const uint8_t *la)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
if (!memcmp(peer->ra, la, 5))
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up a peer by its Location Area Code (LAC) */
|
||||
struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg,
|
||||
const uint8_t *la)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
llist_for_each_entry(peer, &cfg->bts_peers, list) {
|
||||
if (!memcmp(peer->ra + 3, la + 3, 2))
|
||||
return peer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg,
|
||||
struct tlv_parsed *tp)
|
||||
{
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
|
||||
uint16_t bvci;
|
||||
|
||||
bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
|
||||
if (bvci >= 2)
|
||||
return gbproxy_peer_by_bvci(cfg, bvci);
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
|
||||
uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
|
||||
/* Only compare LAC part, since MCC/MNC are possibly patched.
|
||||
* Since the LAC of different BSS must be different when
|
||||
* MCC/MNC are patched, collisions shouldn't happen. */
|
||||
return gbproxy_peer_by_lac(cfg, rai);
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
|
||||
uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
|
||||
return gbproxy_peer_by_lac(cfg, lai);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void clean_stale_timer_cb(void *data)
|
||||
{
|
||||
time_t now;
|
||||
struct timespec ts = {0,};
|
||||
struct gbproxy_peer *peer = (struct gbproxy_peer *) data;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
now = ts.tv_sec;
|
||||
gbproxy_remove_stale_link_infos(peer, now);
|
||||
if (peer->cfg->clean_stale_timer_freq != 0)
|
||||
osmo_timer_schedule(&peer->clean_stale_timer,
|
||||
peer->cfg->clean_stale_timer_freq, 0);
|
||||
}
|
||||
|
||||
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci)
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
|
||||
peer = talloc_zero(tall_sgsn_ctx, struct gbproxy_peer);
|
||||
if (!peer)
|
||||
return NULL;
|
||||
|
||||
peer->bvci = bvci;
|
||||
peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
|
||||
if (!peer->ctrg) {
|
||||
talloc_free(peer);
|
||||
return NULL;
|
||||
}
|
||||
peer->cfg = cfg;
|
||||
|
||||
llist_add(&peer->list, &cfg->bts_peers);
|
||||
|
||||
INIT_LLIST_HEAD(&peer->patch_state.logical_links);
|
||||
|
||||
osmo_timer_setup(&peer->clean_stale_timer, clean_stale_timer_cb, peer);
|
||||
if (peer->cfg->clean_stale_timer_freq != 0)
|
||||
osmo_timer_schedule(&peer->clean_stale_timer,
|
||||
peer->cfg->clean_stale_timer_freq, 0);
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
void gbproxy_peer_free(struct gbproxy_peer *peer)
|
||||
{
|
||||
llist_del(&peer->list);
|
||||
osmo_timer_del(&peer->clean_stale_timer);
|
||||
gbproxy_delete_link_infos(peer);
|
||||
|
||||
rate_ctr_group_free(peer->ctrg);
|
||||
peer->ctrg = NULL;
|
||||
|
||||
talloc_free(peer);
|
||||
}
|
||||
|
||||
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
|
||||
{
|
||||
int counter = 0;
|
||||
struct gbproxy_peer *peer, *tmp;
|
||||
|
||||
llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) {
|
||||
if (peer->nsei != nsei)
|
||||
continue;
|
||||
if (bvci && peer->bvci != bvci)
|
||||
continue;
|
||||
|
||||
gbproxy_peer_free(peer);
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
|
@ -0,0 +1,723 @@
|
|||
/* Gb-proxy TLLI state handling */
|
||||
|
||||
/* (C) 2014 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_utils.h>
|
||||
#include <osmocom/sgsn/gprs_gb_parse.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
|
||||
uint32_t tlli)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
if (!tlli)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(link_info, &state->logical_links, list)
|
||||
if (link_info->tlli.current == tlli ||
|
||||
link_info->tlli.assigned == tlli)
|
||||
return link_info;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
|
||||
struct gbproxy_peer *peer,
|
||||
uint32_t ptmsi)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
if (ptmsi == GSM_RESERVED_TMSI)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(link_info, &state->logical_links, list)
|
||||
if (link_info->tlli.ptmsi == ptmsi)
|
||||
return link_info;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
|
||||
struct gbproxy_peer *peer,
|
||||
uint32_t tlli)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
if (!tlli)
|
||||
return NULL;
|
||||
|
||||
/* Don't care about the NSEI */
|
||||
llist_for_each_entry(link_info, &state->logical_links, list)
|
||||
if (link_info->sgsn_tlli.current == tlli ||
|
||||
link_info->sgsn_tlli.assigned == tlli)
|
||||
return link_info;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
|
||||
struct gbproxy_peer *peer,
|
||||
uint32_t tlli, uint32_t sgsn_nsei)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
if (!tlli)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(link_info, &state->logical_links, list)
|
||||
if ((link_info->sgsn_tlli.current == tlli ||
|
||||
link_info->sgsn_tlli.assigned == tlli) &&
|
||||
link_info->sgsn_nsei == sgsn_nsei)
|
||||
return link_info;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
|
||||
struct gbproxy_peer *peer,
|
||||
const uint8_t *imsi,
|
||||
size_t imsi_len)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
if (!gprs_is_mi_imsi(imsi, imsi_len))
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(link_info, &state->logical_links, list) {
|
||||
if (link_info->imsi_len != imsi_len)
|
||||
continue;
|
||||
if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
|
||||
continue;
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct msgb *msg, *nxt;
|
||||
|
||||
llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
|
||||
llist_del(&msg->list);
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
gbproxy_link_info_discard_messages(link_info);
|
||||
|
||||
llist_del(&link_info->list);
|
||||
talloc_free(link_info);
|
||||
state->logical_link_count -= 1;
|
||||
|
||||
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
|
||||
state->logical_link_count;
|
||||
}
|
||||
|
||||
void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
|
||||
{
|
||||
struct gbproxy_link_info *link_info, *nxt;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
|
||||
OSMO_ASSERT(state->logical_link_count == 0);
|
||||
OSMO_ASSERT(llist_empty(&state->logical_links));
|
||||
}
|
||||
|
||||
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
|
||||
struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
link_info->timestamp = now;
|
||||
llist_add(&link_info->list, &state->logical_links);
|
||||
state->logical_link_count += 1;
|
||||
|
||||
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
|
||||
state->logical_link_count;
|
||||
}
|
||||
|
||||
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
|
||||
{
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
int exceeded_max_len = 0;
|
||||
int deleted_count = 0;
|
||||
int check_for_age;
|
||||
|
||||
if (peer->cfg->tlli_max_len > 0)
|
||||
exceeded_max_len =
|
||||
state->logical_link_count - peer->cfg->tlli_max_len;
|
||||
|
||||
check_for_age = peer->cfg->tlli_max_age > 0;
|
||||
|
||||
for (; exceeded_max_len > 0; exceeded_max_len--) {
|
||||
struct gbproxy_link_info *link_info;
|
||||
OSMO_ASSERT(!llist_empty(&state->logical_links));
|
||||
link_info = llist_entry(state->logical_links.prev,
|
||||
struct gbproxy_link_info,
|
||||
list);
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Removing TLLI %08x from list "
|
||||
"(stale, length %d, max_len exceeded)\n",
|
||||
link_info->tlli.current, state->logical_link_count);
|
||||
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
deleted_count += 1;
|
||||
}
|
||||
|
||||
while (check_for_age && !llist_empty(&state->logical_links)) {
|
||||
time_t age;
|
||||
struct gbproxy_link_info *link_info;
|
||||
link_info = llist_entry(state->logical_links.prev,
|
||||
struct gbproxy_link_info,
|
||||
list);
|
||||
age = now - link_info->timestamp;
|
||||
/* age < 0 only happens after system time jumps, discard entry */
|
||||
if (age <= peer->cfg->tlli_max_age && age >= 0) {
|
||||
check_for_age = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Removing TLLI %08x from list "
|
||||
"(stale, age %d, max_age exceeded)\n",
|
||||
link_info->tlli.current, (int)age);
|
||||
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
deleted_count += 1;
|
||||
}
|
||||
|
||||
return deleted_count;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
|
||||
link_info = talloc_zero(peer, struct gbproxy_link_info);
|
||||
link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
|
||||
link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
|
||||
|
||||
link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
|
||||
|
||||
INIT_LLIST_HEAD(&link_info->stored_msgs);
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
void gbproxy_detach_link_info(
|
||||
struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
llist_del(&link_info->list);
|
||||
OSMO_ASSERT(state->logical_link_count > 0);
|
||||
state->logical_link_count -= 1;
|
||||
|
||||
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
|
||||
state->logical_link_count;
|
||||
}
|
||||
|
||||
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
|
||||
const uint8_t *imsi, size_t imsi_len)
|
||||
{
|
||||
if (!gprs_is_mi_imsi(imsi, imsi_len))
|
||||
return;
|
||||
|
||||
link_info->imsi_len = imsi_len;
|
||||
link_info->imsi =
|
||||
talloc_realloc_size(link_info, link_info->imsi, imsi_len);
|
||||
OSMO_ASSERT(link_info->imsi != NULL);
|
||||
memcpy(link_info->imsi, imsi, imsi_len);
|
||||
}
|
||||
|
||||
void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
|
||||
struct gbproxy_peer *peer, uint32_t new_tlli)
|
||||
{
|
||||
if (new_tlli == tlli_state->current)
|
||||
return;
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"The TLLI has been reassigned from %08x to %08x\n",
|
||||
tlli_state->current, new_tlli);
|
||||
|
||||
/* Remember assigned TLLI */
|
||||
tlli_state->assigned = new_tlli;
|
||||
tlli_state->bss_validated = false;
|
||||
tlli_state->net_validated = false;
|
||||
}
|
||||
|
||||
uint32_t gbproxy_map_tlli(uint32_t other_tlli,
|
||||
struct gbproxy_link_info *link_info, int to_bss)
|
||||
{
|
||||
uint32_t tlli = 0;
|
||||
struct gbproxy_tlli_state *src, *dst;
|
||||
if (to_bss) {
|
||||
src = &link_info->sgsn_tlli;
|
||||
dst = &link_info->tlli;
|
||||
} else {
|
||||
src = &link_info->tlli;
|
||||
dst = &link_info->sgsn_tlli;
|
||||
}
|
||||
if (src->current == other_tlli)
|
||||
tlli = dst->current;
|
||||
else if (src->assigned == other_tlli)
|
||||
tlli = dst->assigned;
|
||||
|
||||
return tlli;
|
||||
}
|
||||
|
||||
static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
|
||||
uint32_t tlli, int to_bss)
|
||||
{
|
||||
LOGP(DGPRS, LOGL_DEBUG,
|
||||
"%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
|
||||
__func__, tlli_state->current, tlli_state->assigned,
|
||||
tlli_state->net_validated, tlli_state->bss_validated, tlli);
|
||||
|
||||
if (!tlli_state->assigned || tlli_state->assigned != tlli)
|
||||
return;
|
||||
|
||||
/* TODO: Is this ok? Check spec */
|
||||
if (gprs_tlli_type(tlli) != TLLI_LOCAL)
|
||||
return;
|
||||
|
||||
/* See GSM 04.08, 4.7.1.5 */
|
||||
if (to_bss)
|
||||
tlli_state->net_validated = true;
|
||||
else
|
||||
tlli_state->bss_validated = true;
|
||||
|
||||
if (!tlli_state->bss_validated || !tlli_state->net_validated)
|
||||
return;
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"The TLLI %08x has been validated (was %08x)\n",
|
||||
tlli_state->assigned, tlli_state->current);
|
||||
|
||||
tlli_state->current = tlli;
|
||||
tlli_state->assigned = 0;
|
||||
}
|
||||
|
||||
static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info,
|
||||
time_t now)
|
||||
{
|
||||
gbproxy_detach_link_info(peer, link_info);
|
||||
gbproxy_attach_link_info(peer, now, link_info);
|
||||
}
|
||||
|
||||
static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info)
|
||||
{
|
||||
if (!link_info)
|
||||
return 1;
|
||||
|
||||
if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
|
||||
link_info->tlli.current);
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
link_info->tlli.current = 0;
|
||||
link_info->tlli.assigned = 0;
|
||||
link_info->sgsn_tlli.current = 0;
|
||||
link_info->sgsn_tlli.assigned = 0;
|
||||
|
||||
link_info->is_deregistered = true;
|
||||
|
||||
gbproxy_reset_link(link_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gbproxy_imsi_matches(struct gbproxy_config *cfg,
|
||||
enum gbproxy_match_id match_id,
|
||||
struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct gbproxy_match *match;
|
||||
OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
|
||||
|
||||
match = &cfg->matches[match_id];
|
||||
if (!match->enable)
|
||||
return 1;
|
||||
|
||||
return link_info != NULL && link_info->is_matching[match_id];
|
||||
}
|
||||
|
||||
static void gbproxy_assign_imsi(struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
int imsi_matches;
|
||||
struct gbproxy_link_info *other_link_info;
|
||||
enum gbproxy_match_id match_id;
|
||||
|
||||
/* Make sure that there is a second entry with the same IMSI */
|
||||
other_link_info = gbproxy_link_info_by_imsi(
|
||||
peer, parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
|
||||
if (other_link_info && other_link_info != link_info) {
|
||||
char mi_buf[200];
|
||||
mi_buf[0] = '\0';
|
||||
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
|
||||
parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Removing TLLI %08x from list (IMSI %s re-used)\n",
|
||||
other_link_info->tlli.current, mi_buf);
|
||||
gbproxy_delete_link_info(peer, other_link_info);
|
||||
}
|
||||
|
||||
/* Update the IMSI field */
|
||||
gbproxy_update_link_info(link_info,
|
||||
parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
|
||||
/* Check, whether the IMSI matches */
|
||||
OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
|
||||
ARRAY_SIZE(peer->cfg->matches));
|
||||
for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
|
||||
++match_id) {
|
||||
imsi_matches = gbproxy_check_imsi(
|
||||
&peer->cfg->matches[match_id],
|
||||
parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
if (imsi_matches >= 0)
|
||||
link_info->is_matching[match_id] = imsi_matches ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
|
||||
const struct gbproxy_tlli_state *b)
|
||||
{
|
||||
if (a->current && a->current == b->current)
|
||||
return 1;
|
||||
|
||||
if (a->assigned && a->assigned == b->assigned)
|
||||
return 1;
|
||||
|
||||
if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gbproxy_remove_matching_link_infos(
|
||||
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
|
||||
{
|
||||
struct gbproxy_link_info *info, *nxt;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
/* Make sure that there is no second entry with the same P-TMSI or TLLI */
|
||||
llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
|
||||
if (info == link_info)
|
||||
continue;
|
||||
|
||||
if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
|
||||
(link_info->sgsn_nsei != info->sgsn_nsei ||
|
||||
!gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
|
||||
continue;
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
|
||||
info->tlli.current);
|
||||
gbproxy_delete_link_info(peer, info);
|
||||
}
|
||||
}
|
||||
|
||||
static struct gbproxy_link_info *gbproxy_get_link_info_ul(
|
||||
struct gbproxy_peer *peer,
|
||||
int *tlli_is_valid,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
struct gbproxy_link_info *link_info = NULL;
|
||||
|
||||
if (parse_ctx->tlli_enc) {
|
||||
link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
|
||||
|
||||
if (link_info) {
|
||||
*tlli_is_valid = 1;
|
||||
return link_info;
|
||||
}
|
||||
}
|
||||
|
||||
*tlli_is_valid = 0;
|
||||
|
||||
if (!link_info && parse_ctx->imsi) {
|
||||
link_info = gbproxy_link_info_by_imsi(
|
||||
peer, parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
}
|
||||
|
||||
if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
|
||||
uint32_t bss_ptmsi;
|
||||
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
|
||||
link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
|
||||
}
|
||||
|
||||
if (!link_info)
|
||||
return NULL;
|
||||
|
||||
link_info->is_deregistered = false;
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_update_link_state_ul(
|
||||
struct gbproxy_peer *peer,
|
||||
time_t now,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
struct gbproxy_link_info *link_info;
|
||||
int tlli_is_valid;
|
||||
|
||||
link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
|
||||
|
||||
if (parse_ctx->tlli_enc && parse_ctx->llc) {
|
||||
uint32_t sgsn_tlli;
|
||||
|
||||
if (!link_info) {
|
||||
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
|
||||
parse_ctx->tlli);
|
||||
link_info = gbproxy_link_info_alloc(peer);
|
||||
gbproxy_attach_link_info(peer, now, link_info);
|
||||
|
||||
/* Setup TLLIs */
|
||||
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
|
||||
parse_ctx->tlli);
|
||||
link_info->sgsn_tlli.current = sgsn_tlli;
|
||||
link_info->tlli.current = parse_ctx->tlli;
|
||||
} else if (!tlli_is_valid) {
|
||||
/* New TLLI (info found by IMSI or P-TMSI) */
|
||||
link_info->tlli.current = parse_ctx->tlli;
|
||||
link_info->tlli.assigned = 0;
|
||||
link_info->sgsn_tlli.current =
|
||||
gbproxy_make_sgsn_tlli(peer, link_info,
|
||||
parse_ctx->tlli);
|
||||
link_info->sgsn_tlli.assigned = 0;
|
||||
gbproxy_touch_link_info(peer, link_info, now);
|
||||
} else {
|
||||
sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
|
||||
if (!sgsn_tlli)
|
||||
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
|
||||
parse_ctx->tlli);
|
||||
|
||||
gbproxy_validate_tlli(&link_info->tlli,
|
||||
parse_ctx->tlli, 0);
|
||||
gbproxy_validate_tlli(&link_info->sgsn_tlli,
|
||||
sgsn_tlli, 0);
|
||||
gbproxy_touch_link_info(peer, link_info, now);
|
||||
}
|
||||
} else if (link_info) {
|
||||
gbproxy_touch_link_info(peer, link_info, now);
|
||||
}
|
||||
|
||||
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
|
||||
gbproxy_assign_imsi(peer, link_info, parse_ctx);
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
static struct gbproxy_link_info *gbproxy_get_link_info_dl(
|
||||
struct gbproxy_peer *peer,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
struct gbproxy_link_info *link_info = NULL;
|
||||
|
||||
/* Which key to use depends on its availability only, if that fails, do
|
||||
* not retry it with another key (e.g. IMSI). */
|
||||
if (parse_ctx->tlli_enc)
|
||||
link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
|
||||
parse_ctx->peer_nsei);
|
||||
|
||||
/* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
|
||||
* GSM 08.18, 7.2) instead of using the IMSI as key. */
|
||||
else if (parse_ctx->imsi)
|
||||
link_info = gbproxy_link_info_by_imsi(
|
||||
peer, parse_ctx->imsi, parse_ctx->imsi_len);
|
||||
|
||||
if (link_info)
|
||||
link_info->is_deregistered = false;
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
struct gbproxy_link_info *gbproxy_update_link_state_dl(
|
||||
struct gbproxy_peer *peer,
|
||||
time_t now,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
struct gbproxy_link_info *link_info = NULL;
|
||||
|
||||
link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
|
||||
|
||||
if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
|
||||
/* A new P-TMSI has been signalled in the message,
|
||||
* register new TLLI */
|
||||
uint32_t new_sgsn_ptmsi;
|
||||
uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
|
||||
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
|
||||
|
||||
if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
|
||||
new_bss_ptmsi = link_info->tlli.ptmsi;
|
||||
|
||||
if (new_bss_ptmsi == GSM_RESERVED_TMSI)
|
||||
new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Got new PTMSI %08x from SGSN, using %08x for BSS\n",
|
||||
new_sgsn_ptmsi, new_bss_ptmsi);
|
||||
/* Setup PTMSIs */
|
||||
link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
|
||||
link_info->tlli.ptmsi = new_bss_ptmsi;
|
||||
} else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
|
||||
!peer->cfg->patch_ptmsi) {
|
||||
/* A new P-TMSI has been signalled in the message with an unknown
|
||||
* TLLI, create a new link_info */
|
||||
/* TODO: Add a test case for this branch */
|
||||
uint32_t new_ptmsi;
|
||||
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
|
||||
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
|
||||
parse_ctx->tlli, new_ptmsi);
|
||||
|
||||
link_info = gbproxy_link_info_alloc(peer);
|
||||
link_info->sgsn_tlli.current = parse_ctx->tlli;
|
||||
link_info->tlli.current = parse_ctx->tlli;
|
||||
link_info->sgsn_tlli.ptmsi = new_ptmsi;
|
||||
link_info->tlli.ptmsi = new_ptmsi;
|
||||
gbproxy_attach_link_info(peer, now, link_info);
|
||||
} else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
|
||||
!peer->cfg->patch_ptmsi) {
|
||||
/* Unknown SGSN TLLI, create a new link_info */
|
||||
uint32_t new_ptmsi;
|
||||
link_info = gbproxy_link_info_alloc(peer);
|
||||
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
|
||||
parse_ctx->tlli);
|
||||
|
||||
gbproxy_attach_link_info(peer, now, link_info);
|
||||
|
||||
/* Setup TLLIs */
|
||||
link_info->sgsn_tlli.current = parse_ctx->tlli;
|
||||
link_info->tlli.current = parse_ctx->tlli;
|
||||
|
||||
if (!parse_ctx->new_ptmsi_enc)
|
||||
return link_info;
|
||||
/* A new P-TMSI has been signalled in the message */
|
||||
|
||||
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Assigning new P-TMSI %08x\n", new_ptmsi);
|
||||
/* Setup P-TMSIs */
|
||||
link_info->sgsn_tlli.ptmsi = new_ptmsi;
|
||||
link_info->tlli.ptmsi = new_ptmsi;
|
||||
} else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
|
||||
uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
|
||||
link_info, 1);
|
||||
gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
|
||||
gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
|
||||
gbproxy_touch_link_info(peer, link_info, now);
|
||||
} else if (link_info) {
|
||||
gbproxy_touch_link_info(peer, link_info, now);
|
||||
}
|
||||
|
||||
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
|
||||
gbproxy_assign_imsi(peer, link_info, parse_ctx);
|
||||
|
||||
return link_info;
|
||||
}
|
||||
|
||||
int gbproxy_update_link_state_after(
|
||||
struct gbproxy_peer *peer,
|
||||
struct gbproxy_link_info *link_info,
|
||||
time_t now,
|
||||
struct gprs_gb_parse_context *parse_ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
if (parse_ctx->invalidate_tlli && link_info) {
|
||||
int keep_info =
|
||||
peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
|
||||
(peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
|
||||
parse_ctx->await_reattach) ||
|
||||
(peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
|
||||
link_info->imsi_len > 0);
|
||||
if (keep_info) {
|
||||
LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
|
||||
link_info->tlli.current);
|
||||
rc = gbproxy_unregister_link_info(peer, link_info);
|
||||
} else {
|
||||
LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
|
||||
link_info->tlli.current);
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
rc = 1;
|
||||
}
|
||||
} else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
|
||||
parse_ctx->new_ptmsi_enc && link_info) {
|
||||
/* A new PTMSI has been signaled in the message,
|
||||
* register new TLLI */
|
||||
uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
|
||||
uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
|
||||
uint32_t new_sgsn_tlli;
|
||||
uint32_t new_bss_tlli = 0;
|
||||
|
||||
new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
|
||||
if (new_bss_ptmsi != GSM_RESERVED_TMSI)
|
||||
new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
|
||||
LOGP(DGPRS, LOGL_INFO,
|
||||
"Assigning new TLLI %08x to SGSN, %08x to BSS\n",
|
||||
new_sgsn_tlli, new_bss_tlli);
|
||||
|
||||
gbproxy_reassign_tlli(&link_info->sgsn_tlli,
|
||||
peer, new_sgsn_tlli);
|
||||
gbproxy_reassign_tlli(&link_info->tlli,
|
||||
peer, new_bss_tlli);
|
||||
gbproxy_remove_matching_link_infos(peer, link_info);
|
||||
}
|
||||
|
||||
gbproxy_remove_stale_link_infos(peer, now);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,926 @@
|
|||
/*
|
||||
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/gprs/gprs_ns.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gb_proxy.h>
|
||||
#include <osmocom/sgsn/gprs_utils.h>
|
||||
#include <osmocom/sgsn/vty.h>
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
static struct gbproxy_config *g_cfg = NULL;
|
||||
|
||||
/*
|
||||
* vty code for gbproxy below
|
||||
*/
|
||||
static struct cmd_node gbproxy_node = {
|
||||
GBPROXY_NODE,
|
||||
"%s(config-gbproxy)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static const struct value_string keep_modes[] = {
|
||||
{GBPROX_KEEP_NEVER, "never"},
|
||||
{GBPROX_KEEP_REATTACH, "re-attach"},
|
||||
{GBPROX_KEEP_IDENTIFIED, "identified"},
|
||||
{GBPROX_KEEP_ALWAYS, "always"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static const struct value_string match_ids[] = {
|
||||
{GBPROX_MATCH_PATCHING, "patching"},
|
||||
{GBPROX_MATCH_ROUTING, "routing"},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
|
||||
{
|
||||
struct gprs_ra_id raid;
|
||||
gsm48_parse_ra(&raid, peer->ra);
|
||||
|
||||
vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
|
||||
"RAI %s", peer->nsei, peer->bvci, osmo_rai_name(&raid));
|
||||
if (peer->blocked)
|
||||
vty_out(vty, " [BVC-BLOCKED]");
|
||||
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_gbproxy(struct vty *vty)
|
||||
{
|
||||
enum gbproxy_match_id match_id;
|
||||
|
||||
vty_out(vty, "gbproxy%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (g_cfg->core_plmn.mcc > 0)
|
||||
vty_out(vty, " core-mobile-country-code %s%s",
|
||||
osmo_mcc_name(g_cfg->core_plmn.mcc), VTY_NEWLINE);
|
||||
if (g_cfg->core_plmn.mnc > 0)
|
||||
vty_out(vty, " core-mobile-network-code %s%s",
|
||||
osmo_mnc_name(g_cfg->core_plmn.mnc, g_cfg->core_plmn.mnc_3_digits), VTY_NEWLINE);
|
||||
|
||||
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
|
||||
struct gbproxy_match *match = &g_cfg->matches[match_id];
|
||||
if (match->re_str)
|
||||
vty_out(vty, " match-imsi %s %s%s",
|
||||
get_value_string(match_ids, match_id),
|
||||
match->re_str, 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",
|
||||
osmo_apn_to_str(str, g_cfg->core_apn,
|
||||
g_cfg->core_apn_size),
|
||||
VTY_NEWLINE);
|
||||
} else {
|
||||
vty_out(vty, " core-access-point-name none%s",
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_cfg->route_to_sgsn2)
|
||||
vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (g_cfg->clean_stale_timer_freq > 0)
|
||||
vty_out(vty, " link-list clean-stale-timer %u%s",
|
||||
g_cfg->clean_stale_timer_freq, VTY_NEWLINE);
|
||||
if (g_cfg->tlli_max_age > 0)
|
||||
vty_out(vty, " link-list max-age %d%s",
|
||||
g_cfg->tlli_max_age, VTY_NEWLINE);
|
||||
if (g_cfg->tlli_max_len > 0)
|
||||
vty_out(vty, " link-list max-length %d%s",
|
||||
g_cfg->tlli_max_len, VTY_NEWLINE);
|
||||
vty_out(vty, " link-list keep-mode %s%s",
|
||||
get_value_string(keep_modes, g_cfg->keep_link_infos),
|
||||
VTY_NEWLINE);
|
||||
if (g_cfg->stored_msgs_max_len > 0)
|
||||
vty_out(vty, " link stored-msgs-max-length %"PRIu32"%s",
|
||||
g_cfg->stored_msgs_max_len, VTY_NEWLINE);
|
||||
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy,
|
||||
cfg_gbproxy_cmd,
|
||||
"gbproxy",
|
||||
"Configure the Gb proxy")
|
||||
{
|
||||
vty->node = GBPROXY_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nsip_sgsn_nsei,
|
||||
cfg_nsip_sgsn_nsei_cmd,
|
||||
"sgsn nsei <0-65534>",
|
||||
"SGSN information\n"
|
||||
"NSEI to be used in the connection with the SGSN\n"
|
||||
"The NSEI\n")
|
||||
{
|
||||
unsigned int nsei = atoi(argv[0]);
|
||||
|
||||
if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
|
||||
vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
|
||||
nsei, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_cfg->nsip_sgsn_nsei = nsei;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_core_mnc,
|
||||
cfg_gbproxy_core_mnc_cmd,
|
||||
"core-mobile-network-code <1-999>",
|
||||
GBPROXY_CORE_MNC_STR "NCC value\n")
|
||||
{
|
||||
uint16_t mnc;
|
||||
bool mnc_3_digits;
|
||||
if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) {
|
||||
vty_out(vty, "%% Invalid MNC: %s%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
g_cfg->core_plmn.mnc = mnc;
|
||||
g_cfg->core_plmn.mnc_3_digits = mnc_3_digits;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_core_mnc,
|
||||
cfg_gbproxy_no_core_mnc_cmd,
|
||||
"no core-mobile-network-code",
|
||||
NO_STR GBPROXY_CORE_MNC_STR)
|
||||
{
|
||||
g_cfg->core_plmn.mnc = 0;
|
||||
g_cfg->core_plmn.mnc_3_digits = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_core_mcc,
|
||||
cfg_gbproxy_core_mcc_cmd,
|
||||
"core-mobile-country-code <1-999>",
|
||||
GBPROXY_CORE_MCC_STR "MCC value\n")
|
||||
{
|
||||
g_cfg->core_plmn.mcc = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_core_mcc,
|
||||
cfg_gbproxy_no_core_mcc_cmd,
|
||||
"no core-mobile-country-code",
|
||||
NO_STR GBPROXY_CORE_MCC_STR)
|
||||
{
|
||||
g_cfg->core_plmn.mcc = 0;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_match_imsi,
|
||||
cfg_gbproxy_match_imsi_cmd,
|
||||
"match-imsi (patching|routing) .REGEXP",
|
||||
GBPROXY_MATCH_IMSI_STR
|
||||
"Patch MS related information elements on match only\n"
|
||||
"Route to the secondary SGSN on match only\n"
|
||||
"Regular expression for the IMSI match\n")
|
||||
{
|
||||
const char *filter = argv[1];
|
||||
const char *err_msg = NULL;
|
||||
struct gbproxy_match *match;
|
||||
enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
|
||||
|
||||
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
|
||||
match_id < GBPROX_MATCH_LAST);
|
||||
match = &g_cfg->matches[match_id];
|
||||
|
||||
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
|
||||
vty_out(vty, "Match expression invalid: %s%s",
|
||||
err_msg, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_cfg->acquire_imsi = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_match_imsi,
|
||||
cfg_gbproxy_no_match_imsi_cmd,
|
||||
"no match-imsi",
|
||||
NO_STR GBPROXY_MATCH_IMSI_STR)
|
||||
{
|
||||
enum gbproxy_match_id match_id;
|
||||
|
||||
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
|
||||
gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
|
||||
|
||||
g_cfg->acquire_imsi = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
|
||||
#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
|
||||
|
||||
static int set_core_apn(struct vty *vty, const char *apn)
|
||||
{
|
||||
int apn_len;
|
||||
|
||||
if (!apn) {
|
||||
talloc_free(g_cfg->core_apn);
|
||||
g_cfg->core_apn = NULL;
|
||||
g_cfg->core_apn_size = 0;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
apn_len = strlen(apn);
|
||||
|
||||
if (apn_len >= 100) {
|
||||
vty_out(vty, "APN string too long (max 99 chars)%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (apn_len == 0) {
|
||||
talloc_free(g_cfg->core_apn);
|
||||
/* TODO: replace NULL */
|
||||
g_cfg->core_apn = talloc_zero_size(NULL, 2);
|
||||
g_cfg->core_apn_size = 0;
|
||||
} else {
|
||||
/* TODO: replace NULL */
|
||||
g_cfg->core_apn =
|
||||
talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
|
||||
g_cfg->core_apn_size =
|
||||
gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_core_apn,
|
||||
cfg_gbproxy_core_apn_cmd,
|
||||
"core-access-point-name (APN|none)",
|
||||
GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
|
||||
{
|
||||
if (strcmp(argv[0], "none") == 0)
|
||||
return set_core_apn(vty, "");
|
||||
else
|
||||
return set_core_apn(vty, argv[0]);
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_core_apn,
|
||||
cfg_gbproxy_no_core_apn_cmd,
|
||||
"no core-access-point-name",
|
||||
NO_STR GBPROXY_CORE_APN_STR)
|
||||
{
|
||||
return set_core_apn(vty, NULL);
|
||||
}
|
||||
|
||||
/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
|
||||
* automatically when needed. This command is only left for manual testing
|
||||
* (e.g. doing P-TMSI patching without using a secondary SGSN)
|
||||
*/
|
||||
#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_patch_ptmsi,
|
||||
cfg_gbproxy_patch_ptmsi_cmd,
|
||||
"patch-ptmsi",
|
||||
GBPROXY_PATCH_PTMSI_STR)
|
||||
{
|
||||
g_cfg->patch_ptmsi = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_patch_ptmsi,
|
||||
cfg_gbproxy_no_patch_ptmsi_cmd,
|
||||
"no patch-ptmsi",
|
||||
NO_STR GBPROXY_PATCH_PTMSI_STR)
|
||||
{
|
||||
g_cfg->patch_ptmsi = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* TODO: Remove the acquire-imsi command, since that feature is enabled
|
||||
* automatically when IMSI matching is enabled. This command is only left for
|
||||
* manual testing (e.g. doing IMSI acquisition without IMSI based patching)
|
||||
*/
|
||||
#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_acquire_imsi,
|
||||
cfg_gbproxy_acquire_imsi_cmd,
|
||||
"acquire-imsi",
|
||||
GBPROXY_ACQUIRE_IMSI_STR)
|
||||
{
|
||||
g_cfg->acquire_imsi = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_acquire_imsi,
|
||||
cfg_gbproxy_no_acquire_imsi_cmd,
|
||||
"no acquire-imsi",
|
||||
NO_STR GBPROXY_ACQUIRE_IMSI_STR)
|
||||
{
|
||||
g_cfg->acquire_imsi = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_secondary_sgsn,
|
||||
cfg_gbproxy_secondary_sgsn_cmd,
|
||||
"secondary-sgsn nsei <0-65534>",
|
||||
GBPROXY_SECOND_SGSN_STR
|
||||
"NSEI to be used in the connection with the SGSN\n"
|
||||
"The NSEI\n")
|
||||
{
|
||||
unsigned int nsei = atoi(argv[0]);
|
||||
|
||||
if (g_cfg->nsip_sgsn_nsei == nsei) {
|
||||
vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
|
||||
nsei, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_cfg->route_to_sgsn2 = true;
|
||||
g_cfg->nsip_sgsn2_nsei = nsei;
|
||||
|
||||
g_cfg->patch_ptmsi = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_no_secondary_sgsn,
|
||||
cfg_gbproxy_no_secondary_sgsn_cmd,
|
||||
"no secondary-sgsn",
|
||||
NO_STR GBPROXY_SECOND_SGSN_STR)
|
||||
{
|
||||
g_cfg->route_to_sgsn2 = false;
|
||||
g_cfg->nsip_sgsn2_nsei = 0xFFFF;
|
||||
|
||||
g_cfg->patch_ptmsi = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
|
||||
#define GBPROXY_LINK_STR "Set TLLI parameters\n"
|
||||
|
||||
#define GBPROXY_CLEAN_STALE_TIMER_STR "Periodic timer to clean stale links\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_clean_stale_timer,
|
||||
cfg_gbproxy_link_list_clean_stale_timer_cmd,
|
||||
"link-list clean-stale-timer <1-999999>",
|
||||
GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR
|
||||
"Frequency at which the periodic timer is fired (in seconds)\n")
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
|
||||
|
||||
/* Re-schedule running timers soon in case prev frequency was really big
|
||||
and new frequency is desired to be lower. After initial run, periodic
|
||||
time is used. Use random() to avoid firing timers for all peers at
|
||||
the same time */
|
||||
llist_for_each_entry(peer, &g_cfg->bts_peers, list)
|
||||
osmo_timer_schedule(&peer->clean_stale_timer,
|
||||
random() % 5, random() % 1000000);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_no_clean_stale_timer,
|
||||
cfg_gbproxy_link_list_no_clean_stale_timer_cmd,
|
||||
"no link-list clean-stale-timer",
|
||||
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_CLEAN_STALE_TIMER_STR)
|
||||
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
g_cfg->clean_stale_timer_freq = 0;
|
||||
|
||||
llist_for_each_entry(peer, &g_cfg->bts_peers, list)
|
||||
osmo_timer_del(&peer->clean_stale_timer);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_max_age,
|
||||
cfg_gbproxy_link_list_max_age_cmd,
|
||||
"link-list max-age <1-999999>",
|
||||
GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
|
||||
"Maximum age in seconds\n")
|
||||
{
|
||||
g_cfg->tlli_max_age = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_no_max_age,
|
||||
cfg_gbproxy_link_list_no_max_age_cmd,
|
||||
"no link-list max-age",
|
||||
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
|
||||
{
|
||||
g_cfg->tlli_max_age = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_max_len,
|
||||
cfg_gbproxy_link_list_max_len_cmd,
|
||||
"link-list max-length <1-99999>",
|
||||
GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
|
||||
"Maximum number of logical links in the list\n")
|
||||
{
|
||||
g_cfg->tlli_max_len = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_no_max_len,
|
||||
cfg_gbproxy_link_list_no_max_len_cmd,
|
||||
"no link-list max-length",
|
||||
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
|
||||
{
|
||||
g_cfg->tlli_max_len = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_list_keep_mode,
|
||||
cfg_gbproxy_link_list_keep_mode_cmd,
|
||||
"link-list keep-mode (never|re-attach|identified|always)",
|
||||
GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
|
||||
"Discard entry immediately after detachment\n"
|
||||
"Keep entry if a re-attachment has be requested\n"
|
||||
"Keep entry if it associated with an IMSI\n"
|
||||
"Don't discard entries after detachment\n")
|
||||
{
|
||||
int val = get_string_value(keep_modes, argv[0]);
|
||||
OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
|
||||
g_cfg->keep_link_infos = val;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_stored_msgs_max_len,
|
||||
cfg_gbproxy_link_stored_msgs_max_len_cmd,
|
||||
"link stored-msgs-max-length <1-99999>",
|
||||
GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR
|
||||
"Maximum number of msgb stored in the logical link waiting to acquire its IMSI\n")
|
||||
{
|
||||
g_cfg->stored_msgs_max_len = (uint32_t) atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_gbproxy_link_no_stored_msgs_max_len,
|
||||
cfg_gbproxy_link_no_stored_msgs_max_len_cmd,
|
||||
"no link stored-msgs-max-length",
|
||||
NO_STR GBPROXY_LINK_STR GBPROXY_MAX_LEN_STR)
|
||||
{
|
||||
g_cfg->stored_msgs_max_len = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
|
||||
SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
int show_stats = argc >= 1;
|
||||
|
||||
if (show_stats)
|
||||
vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
|
||||
|
||||
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
|
||||
gbprox_vty_print_peer(vty, peer);
|
||||
|
||||
if (show_stats)
|
||||
vty_out_rate_ctr_group(vty, " ", peer->ctrg);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
|
||||
SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
|
||||
{
|
||||
struct gbproxy_peer *peer;
|
||||
char mi_buf[200];
|
||||
time_t now;
|
||||
struct timespec ts = {0,};
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
now = ts.tv_sec;
|
||||
|
||||
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
|
||||
struct gbproxy_link_info *link_info;
|
||||
struct gbproxy_patch_state *state = &peer->patch_state;
|
||||
|
||||
gbprox_vty_print_peer(vty, peer);
|
||||
|
||||
llist_for_each_entry(link_info, &state->logical_links, list) {
|
||||
time_t age = now - link_info->timestamp;
|
||||
|
||||
if (link_info->imsi > 0) {
|
||||
snprintf(mi_buf, sizeof(mi_buf), "(invalid)");
|
||||
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
|
||||
link_info->imsi,
|
||||
link_info->imsi_len);
|
||||
} else {
|
||||
snprintf(mi_buf, sizeof(mi_buf), "(none)");
|
||||
}
|
||||
vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
|
||||
link_info->tlli.current, mi_buf, (int)age);
|
||||
|
||||
if (link_info->stored_msgs_len)
|
||||
vty_out(vty, ", STORED %"PRIu32"/%"PRIu32,
|
||||
link_info->stored_msgs_len,
|
||||
g_cfg->stored_msgs_max_len);
|
||||
|
||||
if (g_cfg->route_to_sgsn2)
|
||||
vty_out(vty, ", SGSN NSEI %d",
|
||||
link_info->sgsn_nsei);
|
||||
|
||||
if (link_info->is_deregistered)
|
||||
vty_out(vty, ", DE-REGISTERED");
|
||||
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
|
||||
"delete-gbproxy-peer <0-65534> bvci <2-65534>",
|
||||
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
|
||||
"NSEI number\n"
|
||||
"Only delete peer with a matching BVCI\n"
|
||||
"BVCI number\n")
|
||||
{
|
||||
const uint16_t nsei = atoi(argv[0]);
|
||||
const uint16_t bvci = atoi(argv[1]);
|
||||
int counter;
|
||||
|
||||
counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
|
||||
|
||||
if (counter == 0) {
|
||||
vty_out(vty, "BVC not found%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
|
||||
"delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
|
||||
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
|
||||
"NSEI number\n"
|
||||
"Only delete BSSGP connections (BVC)\n"
|
||||
"Only delete dynamic NS connections (NS-VC)\n"
|
||||
"Delete BVC and dynamic NS connections\n"
|
||||
"Show what would be deleted instead of actually deleting\n"
|
||||
)
|
||||
{
|
||||
const uint16_t nsei = atoi(argv[0]);
|
||||
const char *mode = argv[1];
|
||||
int dry_run = argc > 2;
|
||||
int delete_bvc = 0;
|
||||
int delete_nsvc = 0;
|
||||
int counter;
|
||||
|
||||
if (strcmp(mode, "only-bvc") == 0)
|
||||
delete_bvc = 1;
|
||||
else if (strcmp(mode, "only-nsvc") == 0)
|
||||
delete_nsvc = 1;
|
||||
else
|
||||
delete_bvc = delete_nsvc = 1;
|
||||
|
||||
if (delete_bvc) {
|
||||
if (!dry_run)
|
||||
counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
|
||||
else {
|
||||
struct gbproxy_peer *peer;
|
||||
counter = 0;
|
||||
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
|
||||
if (peer->nsei != nsei)
|
||||
continue;
|
||||
|
||||
vty_out(vty, "BVC: ");
|
||||
gbprox_vty_print_peer(vty, peer);
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
vty_out(vty, "%sDeleted %d BVC%s",
|
||||
dry_run ? "Not " : "", counter, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
if (delete_nsvc) {
|
||||
struct gprs_ns_inst *nsi = g_cfg->nsi;
|
||||
struct gprs_nsvc *nsvc, *nsvc2;
|
||||
|
||||
counter = 0;
|
||||
llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
|
||||
if (nsvc->nsei != nsei)
|
||||
continue;
|
||||
if (nsvc->persistent)
|
||||
continue;
|
||||
|
||||
if (!dry_run)
|
||||
gprs_nsvc_delete(nsvc);
|
||||
else
|
||||
vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
|
||||
"remote %s%s",
|
||||
nsvc->nsei, nsvc->nsvci,
|
||||
gprs_ns_ll_str(nsvc), VTY_NEWLINE);
|
||||
counter += 1;
|
||||
}
|
||||
vty_out(vty, "%sDeleted %d NS-VC%s",
|
||||
dry_run ? "Not " : "", counter, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_DELETE_LINK_STR \
|
||||
"Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
|
||||
|
||||
DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
|
||||
"delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
|
||||
GBPROXY_DELETE_LINK_STR
|
||||
"Delete entries with a matching TLLI (hex)\n"
|
||||
"Delete entries with a matching IMSI\n"
|
||||
"Delete entries with a matching SGSN NSEI\n"
|
||||
"Identification to match\n")
|
||||
{
|
||||
const uint16_t nsei = atoi(argv[0]);
|
||||
enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
|
||||
uint32_t ident = 0;
|
||||
const char *imsi = NULL;
|
||||
struct gbproxy_peer *peer = 0;
|
||||
struct gbproxy_link_info *link_info, *nxt;
|
||||
struct gbproxy_patch_state *state;
|
||||
char mi_buf[200];
|
||||
int found = 0;
|
||||
|
||||
match = argv[1][0];
|
||||
|
||||
switch (match) {
|
||||
case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
|
||||
case MATCH_IMSI: imsi = argv[2]; break;
|
||||
case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
|
||||
};
|
||||
|
||||
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
|
||||
if (!peer) {
|
||||
vty_out(vty, "Didn't find peer with NSEI %d%s",
|
||||
nsei, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
state = &peer->patch_state;
|
||||
|
||||
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
|
||||
switch (match) {
|
||||
case MATCH_TLLI:
|
||||
if (link_info->tlli.current != ident)
|
||||
continue;
|
||||
break;
|
||||
case MATCH_SGSN:
|
||||
if (link_info->sgsn_nsei != ident)
|
||||
continue;
|
||||
break;
|
||||
case MATCH_IMSI:
|
||||
if (!link_info->imsi)
|
||||
continue;
|
||||
mi_buf[0] = '\0';
|
||||
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
|
||||
link_info->imsi,
|
||||
link_info->imsi_len);
|
||||
|
||||
if (strcmp(mi_buf, imsi) != 0)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
|
||||
VTY_NEWLINE);
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
found += 1;
|
||||
}
|
||||
|
||||
if (!found && argc >= 2) {
|
||||
vty_out(vty, "Didn't find link entry with %s %s%s",
|
||||
argv[1], argv[2], VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(delete_gb_link, delete_gb_link_cmd,
|
||||
"delete-gbproxy-link <0-65534> (stale|de-registered)",
|
||||
GBPROXY_DELETE_LINK_STR
|
||||
"Delete stale entries\n"
|
||||
"Delete de-registered entries\n")
|
||||
{
|
||||
const uint16_t nsei = atoi(argv[0]);
|
||||
enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
|
||||
struct gbproxy_peer *peer = 0;
|
||||
struct gbproxy_link_info *link_info, *nxt;
|
||||
struct gbproxy_patch_state *state;
|
||||
time_t now;
|
||||
struct timespec ts = {0,};
|
||||
|
||||
int found = 0;
|
||||
|
||||
match = argv[1][0];
|
||||
|
||||
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
|
||||
if (!peer) {
|
||||
vty_out(vty, "Didn't find peer with NSEI %d%s",
|
||||
nsei, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
state = &peer->patch_state;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
now = ts.tv_sec;
|
||||
|
||||
if (match == MATCH_STALE) {
|
||||
found = gbproxy_remove_stale_link_infos(peer, now);
|
||||
if (found)
|
||||
vty_out(vty, "Deleted %d stale logical link%s%s",
|
||||
found, found == 1 ? "" : "s", VTY_NEWLINE);
|
||||
} else {
|
||||
llist_for_each_entry_safe(link_info, nxt,
|
||||
&state->logical_links, list) {
|
||||
if (!link_info->is_deregistered)
|
||||
continue;
|
||||
|
||||
gbproxy_delete_link_info(peer, link_info);
|
||||
found += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
vty_out(vty, "Deleted %d %s logical link%s%s",
|
||||
found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* legacy commands to provide an upgrade path from "broken" releases
|
||||
* or pre-releases
|
||||
*/
|
||||
DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
|
||||
cfg_gbproxy_broken_apn_match_cmd,
|
||||
"core-access-point-name none match-imsi .REGEXP",
|
||||
GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
|
||||
"Patch MS related information elements on match only\n"
|
||||
"Route to the secondary SGSN on match only\n"
|
||||
"Regular expression for the IMSI match\n")
|
||||
{
|
||||
const char *filter = argv[0];
|
||||
const char *err_msg = NULL;
|
||||
struct gbproxy_match *match;
|
||||
enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
|
||||
|
||||
/* apply APN none */
|
||||
set_core_apn(vty, "");
|
||||
|
||||
/* do the matching... with copy and paste */
|
||||
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
|
||||
match_id < GBPROX_MATCH_LAST);
|
||||
match = &g_cfg->matches[match_id];
|
||||
|
||||
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
|
||||
vty_out(vty, "Match expression invalid: %s%s",
|
||||
err_msg, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
g_cfg->acquire_imsi = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
|
||||
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
|
||||
DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
|
||||
cfg_gbproxy_depr_tlli_list_max_len_cmd,
|
||||
"tlli-list max-length <1-99999>",
|
||||
GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
|
||||
"Maximum number of TLLIs in the list\n")
|
||||
{
|
||||
g_cfg->tlli_max_len = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int gbproxy_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_gbproxy_cmd);
|
||||
install_element_ve(&show_gbproxy_links_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
|
||||
install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
|
||||
install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
|
||||
install_element(ENABLE_NODE, &delete_gb_link_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
|
||||
install_node(&gbproxy_node, config_write_gbproxy);
|
||||
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_match_imsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_clean_stale_timer_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_stored_msgs_max_len_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_match_imsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_clean_stale_timer_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_link_no_stored_msgs_max_len_cmd);
|
||||
|
||||
/* broken or deprecated to allow an upgrade path */
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
|
||||
install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
g_cfg = cfg;
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -32,10 +32,10 @@ gbproxy_test_LDFLAGS = \
|
|||
$(NULL)
|
||||
|
||||
gbproxy_test_LDADD = \
|
||||
$(top_builddir)/src/gprs/gb_proxy.o \
|
||||
$(top_builddir)/src/gprs/gb_proxy_patch.o \
|
||||
$(top_builddir)/src/gprs/gb_proxy_peer.o \
|
||||
$(top_builddir)/src/gprs/gb_proxy_tlli.o \
|
||||
$(top_builddir)/src/gbproxy/gb_proxy.o \
|
||||
$(top_builddir)/src/gbproxy/gb_proxy_patch.o \
|
||||
$(top_builddir)/src/gbproxy/gb_proxy_peer.o \
|
||||
$(top_builddir)/src/gbproxy/gb_proxy_tlli.o \
|
||||
$(top_builddir)/src/gprs/gprs_gb_parse.o \
|
||||
$(top_builddir)/src/gprs/gprs_llc_parse.o \
|
||||
$(top_builddir)/src/gprs/crc24.o \
|
||||
|
|
|
@ -71,11 +71,11 @@ class TestVTYBase(unittest.TestCase):
|
|||
class TestVTYGbproxy(TestVTYBase):
|
||||
|
||||
def vty_command(self):
|
||||
return ["./src/gprs/osmo-gbproxy", "-c",
|
||||
return ["./src/gbproxy/osmo-gbproxy", "-c",
|
||||
"doc/examples/osmo-gbproxy/osmo-gbproxy.cfg"]
|
||||
|
||||
def vty_app(self):
|
||||
return (4246, "./src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy")
|
||||
return (4246, "./src/gbproxy/osmo-gbproxy", "OsmoGbProxy", "gbproxy")
|
||||
|
||||
def testVtyTree(self):
|
||||
self.vty.enable()
|
||||
|
@ -277,7 +277,7 @@ class TestVTYSGSN(TestVTYBase):
|
|||
self.assertTrue(self.vty.verify('timer t%d 10' % t, ['']))
|
||||
|
||||
def add_gbproxy_test(suite, workdir):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-gbproxy")):
|
||||
if not os.path.isfile(os.path.join(workdir, "src/gbproxy/osmo-gbproxy")):
|
||||
print("Skipping the Gb-Proxy test")
|
||||
return
|
||||
test = unittest.TestLoader().loadTestsFromTestCase(TestVTYGbproxy)
|
||||
|
|
Loading…
Reference in New Issue