fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/30870 )
Change subject: modem: passive decoding of SI{3,4,13} and IA Rest Octets ......................................................................
modem: passive decoding of SI{3,4,13} and IA Rest Octets
Change-Id: I8566a3cdc9f818bed7e28ea4b1957dce735c298b Related: SYS#5500 --- M src/host/layer23/src/modem/Makefile.am A src/host/layer23/src/modem/app_modem.c D src/host/layer23/src/modem/modem_main.c 3 files changed, 324 insertions(+), 170 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/70/30870/1
diff --git a/src/host/layer23/src/modem/Makefile.am b/src/host/layer23/src/modem/Makefile.am index 4fb3c11..522456a 100644 --- a/src/host/layer23/src/modem/Makefile.am +++ b/src/host/layer23/src/modem/Makefile.am @@ -13,7 +13,11 @@
bin_PROGRAMS = modem
-modem_SOURCES = modem_main.c +modem_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + $(top_srcdir)/src/misc/rslms.c \ + app_modem.c \ + $(NULL) modem_LDADD = \ $(top_builddir)/src/common/liblayer23.a \ $(LIBOSMOCORE_LIBS) \ diff --git a/src/host/layer23/src/modem/app_modem.c b/src/host/layer23/src/modem/app_modem.c new file mode 100644 index 0000000..c19bfda --- /dev/null +++ b/src/host/layer23/src/modem/app_modem.c @@ -0,0 +1,319 @@ +/* modem app (gprs) */ + +/* (C) 2022 by sysmocom - s.m.f.c. GmbH info@sysmocom.de + * 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/lienses/. + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/application.h> + +#include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/gsm48_ie.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gprs/rlcmac/gprs_rlcmac.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/misc/rslms.h> +#include <osmocom/bb/misc/layer3.h> + +#include <l1ctl_proto.h> + +static struct { + enum ccch_mode ccch_mode; + struct gsm48_sysinfo si; +} app_data; + +static int handle_si1(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&app_data.si.si1_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo1(&app_data.si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI1 message\n"); + return rc; + } + + return 0; +} + +static int handle_si3(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&app_data.si.si3_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo3(&app_data.si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI3 message\n"); + return rc; + } + + if (app_data.ccch_mode == CCCH_MODE_NONE) { + if (app_data.si.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) + app_data.ccch_mode = CCCH_MODE_COMBINED; + else + app_data.ccch_mode = CCCH_MODE_NON_COMBINED; + l1ctl_tx_ccch_mode_req(ms, app_data.ccch_mode); + } + + if (!app_data.si.gprs.supported) { + LOGP(DRR, LOGL_NOTICE, "SI3 Rest Octets IE contains no GPRS Indicator\n"); + return 0; + } + + LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n", + app_data.si.gprs.ra_colour, app_data.si.gprs.si13_pos ? "Ext" : "Norm"); + + return 0; +} + +static int handle_si4(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&app_data.si.si4_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo4(&app_data.si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI4 message\n"); + return rc; + } + + if (!app_data.si.gprs.supported) { + LOGP(DRR, LOGL_NOTICE, "SI4 Rest Octets IE contains no GPRS Indicator\n"); + return 0; + } + + LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n", + app_data.si.gprs.ra_colour, app_data.si.gprs.si13_pos ? "Ext" : "Norm"); + + return 0; +} + +static int handle_si13(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&app_data.si.si13_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo13(&app_data.si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) + return rc; + + return 0; +} + +int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms) +{ + const struct gsm48_system_information_type_header *si_hdr = msgb_l3(msg); + const uint8_t si_type = si_hdr->system_information; + + LOGP(DRR, LOGL_INFO, "BCCH message (type=0x%02x): %s\n", + si_type, gsm48_rr_msg_name(si_type)); + + switch (si_type) { + case GSM48_MT_RR_SYSINFO_1: + return handle_si1(ms, msg); + case GSM48_MT_RR_SYSINFO_3: + return handle_si3(ms, msg); + case GSM48_MT_RR_SYSINFO_4: + return handle_si4(ms, msg); + case GSM48_MT_RR_SYSINFO_13: + return handle_si13(ms, msg); + default: + return 0; + }; +} + +static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms) +{ + const struct gsm48_imm_ass *ia = msgb_l3(msg); + uint8_t ch_type, ch_subch, ch_ts; + int rc; + + /* Discard CS channel assignment */ + if ((ia->page_mode >> 4) == 0) + return 0; + + if (rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, ia->chan_desc.chan_nr); + return -EINVAL; + } + + if (!ia->chan_desc.h0.h) { + /* Non-hopping */ + uint16_t arfcn; + + arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8); + + LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, " + "ARFCN=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra, + ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch, + ia->chan_desc.h0.tsc); + } else { + /* Hopping */ + uint8_t maio, hsn; + + hsn = ia->chan_desc.h1.hsn; + maio = ia->chan_desc.h1.maio_low | (ia->chan_desc.h1.maio_high << 2); + + LOGP(DRR, LOGL_INFO, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, " + "HSN=%u, MAIO=%u, TS=%u, SS=%u, TSC=%u)\n", ia->req_ref.ra, + ia->chan_desc.chan_nr, hsn, maio, ch_ts, ch_subch, + ia->chan_desc.h1.tsc); + } + + const uint8_t *data = msgb_l3(msg) + sizeof(*ia) + ia->mob_alloc_len; + size_t data_len = msgb_l3len(msg) - (sizeof(*ia) + ia->mob_alloc_len); + IA_RestOctets_t iaro; + + rc = osmo_gprs_rlcmac_decode_imm_ass_ro(&iaro, data, data_len); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode IA Rest Octets IE\n"); + return rc; + } + + return 0; +} + +/* Dummy Paging Request 1 with "no identity" */ +static const uint8_t paging_fill[] = { + 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b, + /* The rest part may be randomized */ +}; + +/* LAPDm func=UI fill frame (for the BTS side) */ +static const uint8_t lapdm_fill[] = { + 0x03, 0x03, 0x01, 0x2b, + /* The rest part may be randomized */ +}; + +/* TODO: share / generalize this code */ +static bool is_fill_frame(const struct msgb *msg) +{ + const uint8_t *l2 = msgb_l3(msg); + + if (!memcmp(l2, paging_fill, sizeof(paging_fill))) + return true; + if (!memcmp(l2, lapdm_fill, sizeof(lapdm_fill))) + return true; + + return false; +} + +int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms) +{ + const struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + /* Skip frames with wrong length */ + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) { + LOGP(DRR, LOGL_ERROR, "Rx CCCH message with odd length=%u: %s\n", + msgb_l3len(msg), msgb_hexdump_l3(msg)); + return -EINVAL; + } + + /* Skip dummy (fill) frames */ + if (is_fill_frame(msg)) + return 0; + + if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) { + LOGP(DRR, LOGL_ERROR, "PCH pdisc (%s) != RR\n", + gsm48_pdisc_name(sih->rr_protocol_discriminator)); + } + + switch (sih->system_information) { + case GSM48_MT_RR_IMM_ASS: + return gsm48_rx_imm_ass(msg, ms); + default: + return 0; + } +} + +void layer3_app_reset(void) +{ + memset(&app_data, 0x00, sizeof(app_data)); +} + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + layer3_app_reset(); + return l1ctl_tx_fbsb_req(ms, ms->test_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_NONE, dbm2rxlev(-85)); + } + + return 0; +} + + +int l23_app_init(struct osmocom_ms *ms) +{ + log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG); + log_set_category_filter(osmo_stderr_target, DLCSN1, 1, LOGL_DEBUG); + log_set_category_filter(osmo_stderr_target, DRR, 1, LOGL_INFO); + + osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL); + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + + return layer3_init(ms); +} + +static struct l23_app_info info = { + .copyright = "Copyright (C) 2022 by sysmocom - s.m.f.c. GmbH info@sysmocom.de\n", +}; + +struct l23_app_info *l23_app_info(void) +{ + return &info; +} diff --git a/src/host/layer23/src/modem/modem_main.c b/src/host/layer23/src/modem/modem_main.c deleted file mode 100644 index 438c1fe..0000000 --- a/src/host/layer23/src/modem/modem_main.c +++ /dev/null @@ -1,169 +0,0 @@ -/* modem app (gprs) */ - -/* (C) 2022 by sysmocom - s.m.f.c. GmbH info@sysmocom.de - * 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/lienses/. - * - */ - -#include <osmocom/bb/common/osmocom_data.h> -#include <osmocom/bb/common/logging.h> -#include <osmocom/bb/modem/modem.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/signal.h> -#include <osmocom/core/application.h> - -#include <arpa/inet.h> - -#define _GNU_SOURCE -#include <getopt.h> - -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <unistd.h> - -void *tall_modem_ctx = NULL; -int daemonize = 0; - -static void print_usage(const char *app) -{ - printf("Usage: %s\n", app); -} - -static void print_help(void) -{ - printf(" Some help...\n"); - printf(" -h --help this text\n"); - printf(" -d --debug Change debug flags.\n"); - printf(" -D --daemonize Run as daemon\n"); -} - -static int 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'}, - {0, 0, 0, 0}, - }; - - c = getopt_long(argc, argv, "hi:u:c:v:d:Dm", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(argv[0]); - print_help(); - exit(0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - default: - /* Unknown parameter passed */ - return -EINVAL; - } - } - - return 0; -} - -void signal_handler(int signum) -{ - fprintf(stdout, "signal %u received\n", signum); - - switch (signum) { - case SIGINT: - case SIGTERM: - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report and - * then run default SIGABRT handler, who will generate coredump - * and abort the process. abort() should do this for us after we - * return, but program wouldn't exit if an external SIGABRT is - * received. - */ - talloc_report_full(tall_modem_ctx, stderr); - signal(SIGABRT, SIG_DFL); - raise(SIGABRT); - break; - case SIGUSR1: - talloc_report(tall_modem_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_modem_ctx, stderr); - break; - default: - break; - } -} - -int modem_start(void) -{ - printf("Nothing to be done yet\n"); - return 0; -} - -int main(int argc, char **argv) -{ - int rc; - - tall_modem_ctx = talloc_named_const(NULL, 1, "modem"); - msgb_talloc_ctx_init(tall_modem_ctx, 0); - osmo_signal_talloc_ctx_init(tall_modem_ctx); - - osmo_init_logging2(tall_modem_ctx, &log_info); - - rc = handle_options(argc, argv); - if (rc) { /* Abort in case of parsing errors */ - fprintf(stderr, "Error in command line options. Exiting.\n"); - return 1; - } - - signal(SIGINT, &signal_handler); - signal(SIGTERM, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - if (daemonize) { - printf("Running as daemon\n"); - rc = osmo_daemonize(); - if (rc) - fprintf(stderr, "Failed to run as daemon\n"); - } - - modem_start(); - - while (!osmo_select_shutdown_done()) { - osmo_select_main_ctx(0); - } - - talloc_report_full(tall_modem_ctx, stderr); - return 0; -}