From a27303094ab8bedb0a3b3bfe771daeed10a2e0a9 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Sun, 23 Mar 2014 18:08:26 +0100 Subject: sgsn/ctrl: Add ctrl interface, implement listing subscribers Add the control interface with no hierachy right now and implement the first command to list IMSI + Context Address of active sessions. sgsn_cmd_handle could share more code with bsc variant. Fixes: SYS#264, SYS#265 --- openbsc/include/openbsc/gprs_sgsn.h | 10 +++ openbsc/src/gprs/Makefile.am | 9 ++- openbsc/src/gprs/sgsn_ctrl.c | 138 ++++++++++++++++++++++++++++++++++++ openbsc/src/gprs/sgsn_main.c | 13 ++++ openbsc/src/gprs/sgsn_vty.c | 2 +- openbsc/tests/ctrl_test_runner.py | 23 ++++++ 6 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 openbsc/src/gprs/sgsn_ctrl.c diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h index 8074d8f9b..df9b82621 100644 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -15,6 +15,7 @@ #define GSM_EXTENSION_LENGTH 15 struct gprs_llc_lle; +struct ctrl_handle; /* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ enum gprs_mm_state { @@ -219,4 +220,13 @@ uint32_t sgsn_alloc_ptmsi(void); * ottherwise lost state (recovery procedure) */ int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn); +char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len); + +/* + * ctrl interface related work + */ +struct gsm_network; +struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *, uint16_t port); +int sgsn_ctrl_cmds_install(void); + #endif /* _GPRS_SGSN_H */ diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index f4b135467..90c376c79 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -19,6 +19,9 @@ osmo_gbproxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ - gprs_llc.c gprs_llc_vty.c crc24.c -osmo_sgsn_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ - -lgtp $(OSMO_LIBS) + gprs_llc.c gprs_llc_vty.c crc24.c \ + sgsn_ctrl.c +osmo_sgsn_LDADD = \ + $(top_builddir)/src/libctrl/libctrl.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -lgtp $(OSMO_LIBS) $(LIBOSMOABIS_LIBS) diff --git a/openbsc/src/gprs/sgsn_ctrl.c b/openbsc/src/gprs/sgsn_ctrl.c new file mode 100644 index 000000000..789c91e05 --- /dev/null +++ b/openbsc/src/gprs/sgsn_ctrl.c @@ -0,0 +1,138 @@ +/* Control Interface Implementation for the SGSN */ +/* + * (C) 2014 by Holger Hans Peter Freyther + * (C) 2014 by sysmocom s.f.m.c. GmbH + * 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 . + * + */ + +#include +#include +#include +#include +#include + +#include + +extern vector ctrl_node_vec; + +static int verify_subscriber_list(struct ctrl_cmd *cmd, const char *v, void *d) +{ + return 1; +} + +static int set_subscriber_list(struct ctrl_cmd *cmd, void *d) +{ + cmd->reply = "Get only attribute"; + return CTRL_CMD_ERROR; +} + +static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) +{ + struct sgsn_mm_ctx *mm; + + cmd->reply = talloc_strdup(cmd, ""); + llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { + char *addr = NULL; + struct sgsn_pdp_ctx *pdp; + + if (strlen(mm->imsi) == 0) + continue; + + llist_for_each_entry(pdp, &mm->pdp_list, list) + addr = gprs_pdpaddr2str(pdp->lib->eua.v, + pdp->lib->eua.l); + + cmd->reply = talloc_asprintf_append( + cmd->reply, + "%s,%s\n", mm->imsi, addr ? addr : ""); + } + + return CTRL_CMD_REPLY; +} +CTRL_CMD_DEFINE(subscriber_list, "subscriber-list-active-v1"); + +int sgsn_ctrl_cmds_install(void) +{ + int rc = 0; + rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); + return rc; +} + +static int sgsn_cmd_handle(struct ctrl_cmd *cmd, void *data) +{ + char *request; + cmd->reply = NULL; + cmd->node = NULL; + vector vline, cmdvec, cmds_vec; + int i, ret; + + ret = CTRL_CMD_ERROR; + + request = talloc_strdup(tall_bsc_ctx, cmd->variable); + if (!request) + goto err; + + for (i = 0; i < strlen(request); ++i) { + if (request[i] == '.') + request[i] = ' '; + } + + vline = cmd_make_strvec(request); + talloc_free(request); + if (!vline) { + cmd->reply = "cmd_make_strvec failed."; + goto err; + } + + /* If we're here the rest must be the command */ + cmdvec = vector_init(vector_active(vline)); + for (i = 0 ; i < vector_active(vline); ++i) { + vector_set(cmdvec, vector_slot(vline, i)); + } + + /* Get the command vector of the right node */ + cmds_vec = vector_lookup(ctrl_node_vec, CTRL_NODE_ROOT); + + if (!cmds_vec) { + cmd->reply = "Command not found."; + vector_free(cmdvec); + goto err; + } + + ret = ctrl_cmd_exec(cmdvec, cmd, cmds_vec, data); + + vector_free(cmdvec); + cmd_free_strvec(vline); + +err: + if (!cmd->reply) { + LOGP(DCTRL, LOGL_ERROR, "cmd->reply has not been set.\n"); + if (ret == CTRL_CMD_ERROR) + cmd->reply = "An error has occured."; + else + cmd->reply = "Command has been handled."; + } + + if (ret == CTRL_CMD_ERROR) + cmd->type = CTRL_TYPE_ERROR; + return ret; +} + +struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net, uint16_t port) +{ + return controlif_setup(net, port, sgsn_cmd_handle); +} diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c index 9d42648a4..f5a66e663 100644 --- a/openbsc/src/gprs/sgsn_main.c +++ b/openbsc/src/gprs/sgsn_main.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -288,6 +289,7 @@ static const struct log_info gprs_log_info = { int main(int argc, char **argv) { + struct ctrl_handle *ctrl; struct gsm_network dummy_network; int rc; @@ -314,6 +316,17 @@ int main(int argc, char **argv) if (rc < 0) exit(1); + ctrl = sgsn_controlif_setup(NULL, 4251); + if (!ctrl) { + LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n"); + exit(1); + } + + if (sgsn_ctrl_cmds_install() != 0) { + LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n"); + exit(1); + } + gprs_ns_set_log_ss(DNS); bssgp_set_log_ss(DBSSGP); diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index 5fb7145c1..31c94f796 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -71,7 +71,7 @@ static char *gprs_apn2str(uint8_t *apn, unsigned int len) return apnbuf+1; } -static char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) +char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) { static char str[INET6_ADDRSTRLEN + 10]; diff --git a/openbsc/tests/ctrl_test_runner.py b/openbsc/tests/ctrl_test_runner.py index 60d90a7c1..70ae0908a 100644 --- a/openbsc/tests/ctrl_test_runner.py +++ b/openbsc/tests/ctrl_test_runner.py @@ -383,6 +383,21 @@ class TestCtrlNAT(TestCtrlBase): self.assertEquals(r['var'], 'net') self.assertEquals(r['value'], None) +class TestCtrlSGSN(TestCtrlBase): + def ctrl_command(self): + return ["./src/gprs/osmo-sgsn", "-c", + "doc/examples/osmo-sgsn/osmo-sgsn.cfg"] + + def ctrl_app(self): + return (4251, "./src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn") + + def testListSubscribers(self): + # TODO. Add command to mark a subscriber as active + r = self.do_get('subscriber-list-active-v1') + self.assertEquals(r['mtype'], 'GET_REPLY') + self.assertEquals(r['var'], 'subscriber-list-active-v1') + self.assertEquals(r['value'], None) + def add_bsc_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): print("Skipping the BSC test") @@ -401,6 +416,13 @@ def add_nat_test(suite, workdir): test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlNAT) suite.addTest(test) +def add_sgsn_test(suite, workdir): + if not os.path.isfile(os.path.join(workdir, "src/gprs/osmo-sgsn")): + print("Skipping the SGSN test") + return + test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlSGSN) + suite.addTest(test) + if __name__ == '__main__': import argparse import sys @@ -434,5 +456,6 @@ if __name__ == '__main__': add_bsc_test(suite, workdir) add_nitb_test(suite, workdir) add_nat_test(suite, workdir) + add_sgsn_test(suite, workdir) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) -- cgit v1.2.3