diff options
70 files changed, 3943 insertions, 467 deletions
@@ -67,6 +67,7 @@ tests/*/*_test utils/osmo-arfcn utils/osmo-auc-gen +utils/osmo-config-merge utils/osmo-sim-test doc/codec diff --git a/TODO-RELEASE b/TODO-RELEASE index 64833d23..00720f64 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -10,3 +10,9 @@ libosmogsm gsm0480_l3hdr_push() removed from gsm/gsm0480.h (was not exposed) libosmogsm gsm48_push_l3hdr() (re)introduced in gsm/gsm48.h (GSM 04.08 API) libosmogsm gsm48_push_l3hdr_tid() a wrapper around gsm48_push_l3hdr() +libosmocore msgb_l4len new symbol (exposed as 'static inline' in msgb.h) +libosmogsm gsm0808_cause_class_name() getter for class description +libosmogsm gsm0808_cause_class() getter for cause class +libosmogsm gsm0808_cause_ext() check for cause extended bit +libosmogsm gsm0808_cause_name() use enum as parameter +libosmogsm gsm0808_create_cipher_reject() use enum as parameter diff --git a/configure.ac b/configure.ac index dd5f15bc..bc3358a0 100644 --- a/configure.ac +++ b/configure.ac @@ -296,6 +296,21 @@ then CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" fi +AC_ARG_ENABLE([external_tests], + AC_HELP_STRING([--enable-external-tests], + [Include the VTY/CTRL tests in make check [default=no]]), + [enable_ext_tests="$enableval"],[enable_ext_tests="no"]) +if test "x$enable_ext_tests" = "xyes" ; then + AM_PATH_PYTHON + AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmo_verify_transcript_vty.py,yes) + if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then + AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.]) + fi +fi +AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) +AC_MSG_RESULT([$enable_ext_tests]) +AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") + CFLAGS="$CFLAGS -DBUILDING_LIBOSMOCORE -Wall" CPPFLAGS="$CPPFLAGS -DBUILDING_LIBOSMOCORE -Wall" diff --git a/contrib/jenkins_amd64.sh b/contrib/jenkins_amd64.sh index c3af378c..cd7b6661 100755 --- a/contrib/jenkins_amd64.sh +++ b/contrib/jenkins_amd64.sh @@ -15,7 +15,8 @@ build() { prep_build "$src_dir" "$build_dir" - "$src_dir"/configure --disable-silent-rules --enable-static $ENABLE_SANITIZE --enable-werror + "$src_dir"/configure --disable-silent-rules --enable-static $ENABLE_SANITIZE --enable-werror \ + --enable-external-tests run_make } diff --git a/contrib/jenkins_arch.sh b/contrib/jenkins_arch.sh index 99ce5c1a..93dd8bb4 100755 --- a/contrib/jenkins_arch.sh +++ b/contrib/jenkins_arch.sh @@ -14,7 +14,11 @@ if [ ! -d "./contrib" ]; then exit 1 fi -set -x -e +set -x + +gcc --version + +set -e case "$arch" in diff --git a/contrib/jenkins_arm.sh b/contrib/jenkins_arm.sh index 1d72f2f5..e3a6cd14 100755 --- a/contrib/jenkins_arm.sh +++ b/contrib/jenkins_arm.sh @@ -18,6 +18,7 @@ build() { --enable-embedded \ --disable-doxygen \ --disable-shared \ + --enable-external-tests \ CFLAGS="-Os -ffunction-sections -fdata-sections -nostartfiles -nodefaultlibs $WERROR_FLAGS" $MAKE $PARALLEL_MAKE diff --git a/contrib/struct_endianess.py b/contrib/struct_endianess.py new file mode 100755 index 00000000..be73fbe2 --- /dev/null +++ b/contrib/struct_endianess.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python3 + +'''Using mad regexes, automatically make sure that all structs with sub-byte +integers have matching big-endian definitions. The idea is to save a lot of +manual effort, and to automatically verify that there are no errors. +This script most certainly has numerous holes and shortcomings, but actually, +if you hit problems with it, rather adjust your coding style so that this +script can deal with it...''' + +import re +import sys +import codecs +import os.path + +re_struct_start = re.compile(r'^struct\s*[a-zA-Z_][a-zA-Z_0-9]*\s*{\s*$') +re_struct_end = re.compile(r'^}[^;]*;\s*$') + +re_substruct_start = re.compile(r'^\s+struct\s*{\s*$') +re_substruct_end = re.compile(r'^\s+}\s*([^;]*\s)[a-zA-Z_][a-zA-Z_0-9]*\s*;\s*$') + +re_int_def = re.compile(r'(^\s*((const|unsigned|signed|char|int|long|int[0-9]+_t|uint[0-9]_t)\s+)+\s*)([^;]*;)', + re.DOTALL | re.MULTILINE) +re_int_members = re.compile(r'([a-zA-Z_][a-zA-Z_0-9]*|[a-zA-Z_][a-zA-Z_0-9]*\s*:\s*[0-9]+)\s*[,;]\s*', re.DOTALL | re.MULTILINE) + +re_little_endian_ifdef = re.compile(r'#\s*(if|elif)\s+OSMO_IS_LITTLE_ENDIAN\s*(==\s*1\s*|)'); +re_big_endian_ifdef = re.compile(r'#\s*(if|elif)\s+OSMO_IS_BIG_ENDIAN\s*'); +re_else = re.compile(r'#\s*else\s*'); +re_endif = re.compile(r'#\s*endif\s*'); + +re_c_comment = re.compile(r'(/\*[^*]+\*/|//.?$)') + +def remove_c_comments(code_str): + return ''.join(re_c_comment.split(code_str)[::2]) + +def section_struct_body(struct_body_lines): + '''divide a top-level-struct body into sections of + ['arbitrary string', ['body;\n', 'lines;\n'], 'arbitrary string', ...] + Aim: handle each sub-struct on its own, and if there already are ifdefs for + little and big endian, keep just the little endian bit and derive big + endian from it. + An arbitrary string is anything other than struct member definitions, like + a 'struct {', '} sub_name;', ... + "body lines" are lines that define struct members (possibly with comments). + Return: list of alternate arbitrary strings and variable definitions. + ''' + + # these globals are needed so that end_def() can change them from inside + # the function. Not very nice style, but easiest implementation. + global struct_body_parts + global arbitrary_part + global def_part + + struct_body_parts = [] + arbitrary_part = [] + def_part = [] + + def end_def(): + '''if there is any content, flush out recorded parts (def_part, + arbitrary_part) and start a new part. In short, cut a section + boundary.''' + global struct_body_parts + global arbitrary_part + global def_part + + if def_part: + struct_body_parts.append(arbitrary_part) + arbitrary_part = [] + struct_body_parts.append(def_part) + def_part = [] + + j = 0 + while j < len(struct_body_lines): + line = struct_body_lines[j] + + if (re_substruct_start.fullmatch(line) + or re_substruct_end.fullmatch(line)): + end_def() + arbitrary_part.append(line) + j += 1 + continue + + if re_big_endian_ifdef.fullmatch(line): + end_def() + # discard big endian section + j += 1 + while j < len(struct_body_lines): + line = struct_body_lines[j] + if re_endif.fullmatch(line): + end_def() + j += 1 + break; + if re_little_endian_ifdef.fullmatch(line): + end_def() + # keep that start of little endian section, not j++ + break; + if re_else.fullmatch(line): + # there's an '#else' after big-endian. Shim a little-endian header in just for the loop. + struct_body_lines[j] = '#if OSMO_IS_LITTLE_ENDIAN\n' + break; + j += 1 + continue + + if re_little_endian_ifdef.fullmatch(line): + end_def() + j += 1 + while j < len(struct_body_lines): + line = struct_body_lines[j] + if re_endif.fullmatch(line): + end_def() + j += 1 + break; + if re_big_endian_ifdef.fullmatch(line): + end_def() + # keep that start of big endian section, not j++ + break; + if re_else.fullmatch(line): + # there's an '#else' after little-endian. Shim a big-endian header in just for the loop. + struct_body_lines[j] = '#if OSMO_IS_BIG_ENDIAN\n' + break; + def_part.append(line) + j += 1 + + continue + + def_part.append(line) + j += 1 + + # flush the last section remaining that didn't see an explicit end + end_def() + # end_def() only flushes arbitrary_part if there was a def_part, so: + if arbitrary_part: + struct_body_parts.append(arbitrary_part) + + return struct_body_parts + +def struct_body_to_big_endian(body_str): + '''Input: a multi-line string containing the body of a struct, i.e. without + sub-structs and without #if OSMO_IS_BIG_ENDIAN. like + + '\tconst char *foo;\n\tuint8_t moo:3, goo:2;\n\tuint8_t loo:3;\n\tvoid *baz;\n' + + Return None to indicate that there is no little/big endian split + required, or return a multi-line string of the big-endian version of this + same struct body, where sub-byte ints are reversed at byte boundaries, and + all others are copied 1:1. If there are no sub-byte integers, return None, + to indicate that there is no little/big endian split required.''' + + # kick comments out of the code analysis. They will end up being stripped + # from big-endian only. + body_str = remove_c_comments(body_str) + + def_strs = body_str.split(';') + def_strs = ('%s;' % def_str for def_str in def_strs if def_str.strip()) + + # classify defs as containing sub-byte members or not + # defs = [ (true, 'uint8_t ', ('foo:3', 'bar:5')), + # (false, 'int baz;'),...] + defs = [] + any_sub_byte_ints = False + for one_def in def_strs: + + # does it have sub-string integers? + int_def = re_int_def.fullmatch(one_def) + if not int_def: + # not even a number, same for big and little endian + defs.append((False, one_def)) + continue + + int_type = int_def.group(1) + members_str = int_def.groups()[-1] + has_sub_byte_ints = False + + members = [] + for int_member in re_int_members.finditer(members_str): + member = int_member.group(1) + members.append(member) + if ':' in member: + has_sub_byte_ints = True + + if not has_sub_byte_ints: + defs.append((False, one_def)) + else: + defs.append((True, one_def, int_type, members)) + any_sub_byte_ints = True + + if not any_sub_byte_ints: + return None + + # now the interesting part, go over the defs, and reverse the sub-byte ints + # at byte boundaries. + + i = 0 + got_bits = 0 + byte_type = None + members_within_a_byte = [] + big_endian_defs = [] + + big_defs = [] + for classified_def in defs: + has_sub_byte_ints = classified_def[0] + + # now the big endian part + if has_sub_byte_ints: + _, one_def, int_type, members = classified_def + + if byte_type and byte_type.strip() != int_type.strip(): + raise Exception('mismatching type continuation after incomplete byte: %r %r to %r' + % (byte_type, members_within_a_byte, int_type)) + byte_type = int_type + + for member in members: + member_name, bits_str = member.split(':') + member_name = member_name.strip() + bits = int(bits_str) + member = '%s:%d' % (member_name, bits) + members_within_a_byte.append(member) + got_bits += bits + + if got_bits == 8: + # reverse these. + big_endian_defs.append('%s%s;' % (byte_type, ', '.join(reversed(members_within_a_byte)))) + members_within_a_byte = [] + byte_type = None + got_bits = 0 + + elif got_bits > 8: + raise Exception('sub-byte int breaks clean byte bounds: %s -- %d + %d = %d bits' + % (member, got_bits - bits, bits, got_bits)) + + elif not has_sub_byte_ints: + if got_bits: + raise Exception('sub-byte members do not add up to clean byte bounds: %r' % members_within_a_byte) + + big_endian_defs.append(classified_def[1]) + + # strip empty lines + lines = [l for l in (''.join(big_endian_defs).split('\n')) if l.strip()] + # clean lines' whitespace errors we might have taken in with the type names + for i in range(len(lines)): + line = lines[i] + while len(line) and line[-1] in ' \t': + line = line[:-1] + lines[i] = line + return '\n'.join(lines) + +def handle_struct_body(body_str): + + big_endian_body_str = struct_body_to_big_endian(body_str) + + if big_endian_body_str: + new_lines = ['#if OSMO_IS_LITTLE_ENDIAN\n'] + new_lines.append(body_str) + new_lines.append('#elif OSMO_IS_BIG_ENDIAN\n' + '/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */\n') + new_lines.append(big_endian_body_str) + new_lines.append('\n#endif\n') + return ''.join(new_lines) + else: + return body_str + +def _check_file(f): + if not (f.endswith('.h') or f.endswith('.c') or f.endswith('.cpp')): + return + + # section the file into + # [ ["no struct def"], ["struct {...};"], ["no struct def"], ... ] + sections = [] + in_struct = False + buf = [] + for line in codecs.open(f, "r", "utf-8").readlines(): + + if not in_struct and re_struct_start.fullmatch(line): + # flush whatever might still be in buf from before + sections.append(buf) + # start an in_struct section + buf = [line] + in_struct = True + elif in_struct and re_struct_end.fullmatch(line): + # add this end to the in_struct section and then start a non-struct section + buf.append(line) + sections.append(buf) + in_struct = False + buf = [] + else: + buf.append(line) + # flush any leftovers in buf + if buf: + sections.append(buf) + + # examine each struct, i.e. every second item in 'sections' + for i in range(len(sections)): + if not (i & 1): + continue + + struct = sections[i] + + # If the struct isn't packed, we need not bother. + # The practical use of this: in some structs we have booleans in the + # form of + # integer flag:1; + # and these don't add up to bytes, and cause errors. So let's skip all + # non-packed structs, then all of those are out of the picture. + if not 'packed' in struct[-1]: + continue + + try: + + # assume the 'struct foo {' is on the first line, the closing brace + # '} __attribute...;' on the last, and the rest are individual + # definitions split by ';'. + struct_body_lines = struct[1:-1] + struct_body_parts = section_struct_body(struct_body_lines) + + new_struct_body_parts = [] + for j in range(len(struct_body_parts)): + part = ''.join(struct_body_parts[j]) + if not (j & 1): + new_struct_body_parts.append(part) + else: + new_struct_body_parts.append(handle_struct_body(part)) + + new_struct = [struct[0], ''.join(new_struct_body_parts), struct[-1]] + sections[i] = new_struct + except Exception as e: + raise Exception('ERROR in struct %r' % struct[0]) + + # phew. result. + result = ''.join((''.join(s) for s in sections)) + + # see if osmocom/core/endian.h is needed and included. + if (not f.endswith('endian.h') + and 'OSMO_IS_LITTLE_ENDIAN' in result + and '#include <osmocom/core/endian.h>' not in result): + # add the include after the last 'osmocom/core' include + last_include_start = result.rfind('#include <osmocom/core/') + if last_include_start < 0: + last_include_start = result.rfind('#include <osmocom/') + if last_include_start < 0: + last_include_start = result.rfind('#include') + + if last_include_start < 0: + raise Exception('do not know where to include osmocom/core/endian.h in %r' % f) + + insert_at = result.find('\n', last_include_start) + + result = result[:insert_at] + '\n#include <osmocom/core/endian.h>' + result[insert_at:] + + with codecs.open(f, "w", "utf-8") as fd: + fd.write(result) + +def check_file(f): + try: + _check_file(f) + except Exception as e: + raise Exception('ERROR IN FILE %r' % f) + +args = sys.argv[1:] +if not args: + args = ['.'] + +for f in args: + if os.path.isdir(f): + for parent_path, subdirs, files in os.walk(f, None, None): + for ff in files: + check_file(os.path.join(parent_path, ff)) + else: + check_file(f) + +# vim: tabstop=4 shiftwidth=4 expandtab diff --git a/debian/control b/debian/control index 540a8b5e..33ae2d42 100644 --- a/debian/control +++ b/debian/control @@ -315,7 +315,8 @@ Multi-Arch: same Description: Utilities for gsm This package contains a program for frequency calculation for GSM called 'osmo-arfcn' and a program called 'osmo-auc-gen' that is used for testing GSM - authentication. + authentication, as well as 'osmo-config-merge', a tool for merging Osmocom + configuration files. . They use the libosmocore library. The libosmocore library contain various utility functions that were originally developed as part of the OpenBSC diff --git a/debian/libosmocore-utils.install b/debian/libosmocore-utils.install index 9c3b8dce..d23cc73a 100644 --- a/debian/libosmocore-utils.install +++ b/debian/libosmocore-utils.install @@ -1,2 +1,3 @@ usr/bin/osmo-arfcn usr/bin/osmo-auc-gen +usr/bin/osmo-config-merge diff --git a/doc/vty/merge_doc.xsl b/doc/vty/merge_doc.xsl deleted file mode 100644 index d75c4994..00000000 --- a/doc/vty/merge_doc.xsl +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - xmlns:vty="urn:osmocom:xml:libosmocore:vty:doc:1.0"> - <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" /> - - - <xsl:template match="@*|node()"> - <xsl:copy> - <xsl:apply-templates select="@*|node()" /> - </xsl:copy> - </xsl:template> - - - <!-- Copy the name of the node --> - <xsl:template match="vty:node"> - <xsl:variable name="info" select="document($with)/vty:vtydoc/vty:node[@id=current()/@id]/." /> - <xsl:if test="not($info/vty:hide)"> - <xsl:copy> - <xsl:apply-templates select="@*|node()" /> - <xsl:for-each select="$info/*"> - <xsl:copy-of select="." /> - </xsl:for-each> - </xsl:copy> - </xsl:if> - </xsl:template> - - - <!-- Copy command and add nodes --> - <xsl:template match="vty:command"> - <xsl:variable name="info" select="document($with)/vty:vtydoc/vty:node[@id=current()/../@id]/vty:command[@id=current()/@id]/." /> - <xsl:variable name="info_generic" select="document($with)/vty:vtydoc/vty:common/vty:command[@id=current()/@id]/." /> - <xsl:copy> - <xsl:apply-templates select="@*|node()" /> - - <!-- Copy the specific issue... --> - <xsl:for-each select="$info/*"> - <xsl:copy-of select="." /> - </xsl:for-each> - - <xsl:if test="not($info)"> - <xsl:for-each select="$info_generic/*"> - <xsl:copy-of select="." /> - </xsl:for-each> - </xsl:if> - </xsl:copy> - </xsl:template> -</xsl:transform> - diff --git a/include/Makefile.am b/include/Makefile.am index ef8ec656..59a5feda 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -114,6 +114,7 @@ nobase_include_HEADERS = \ osmocom/gsm/protocol/gsm_09_02.h \ osmocom/gsm/protocol/gsm_12_21.h \ osmocom/gsm/protocol/gsm_23_003.h \ + osmocom/gsm/protocol/gsm_29_118.h \ osmocom/gsm/protocol/gsm_44_318.h \ osmocom/gsm/protocol/ipaccess.h \ osmocom/gsm/protocol/smpp34_osmocom.h \ @@ -152,7 +153,10 @@ nobase_include_HEADERS += \ endif noinst_HEADERS = \ - osmocom/gsm/kasumi.h osmocom/gsm/gea.h + osmocom/gsm/kasumi.h \ + osmocom/gsm/gea.h \ + osmocom/core/logging_internal.h \ + $(NULL) osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl $(AM_V_GEN)$(MKDIR_P) $(dir $@) diff --git a/include/osmocom/core/gsmtap.h b/include/osmocom/core/gsmtap.h index 9f5049f8..35ba71e5 100644 --- a/include/osmocom/core/gsmtap.h +++ b/include/osmocom/core/gsmtap.h @@ -245,20 +245,43 @@ enum { /* LTE RRC message types */ enum { - GSMTAP_LTE_RRC_SUB_DL_CCCH_Message = 0, - GSMTAP_LTE_RRC_SUB_DL_DCCH_Message, - GSMTAP_LTE_RRC_SUB_UL_CCCH_Message, - GSMTAP_LTE_RRC_SUB_UL_DCCH_Message, - GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message, - GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message, - GSMTAP_LTE_RRC_SUB_PCCH_Message, - GSMTAP_LTE_RRC_SUB_MCCH_Message, - - GSMTAP_LTE_RRC_SUB_MAX + GSMTAP_LTE_RRC_SUB_DL_CCCH_Message = 0, + GSMTAP_LTE_RRC_SUB_DL_DCCH_Message, + GSMTAP_LTE_RRC_SUB_UL_CCCH_Message, + GSMTAP_LTE_RRC_SUB_UL_DCCH_Message, + GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message, + GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message, + GSMTAP_LTE_RRC_SUB_PCCH_Message, + GSMTAP_LTE_RRC_SUB_MCCH_Message, + GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message_MBMS, + GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message_BR, + GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message_MBMS, + GSMTAP_LTE_RRC_SUB_SC_MCCH_Message, + GSMTAP_LTE_RRC_SUB_SBCCH_SL_BCH_Message, + GSMTAP_LTE_RRC_SUB_SBCCH_SL_BCH_Message_V2X, + GSMTAP_LTE_RRC_SUB_DL_CCCH_Message_NB, + GSMTAP_LTE_RRC_SUB_DL_DCCH_Message_NB, + GSMTAP_LTE_RRC_SUB_UL_CCCH_Message_NB, + GSMTAP_LTE_RRC_SUB_UL_DCCH_Message_NB, + GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message_NB, + GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message_TDD_NB, + GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message_NB, + GSMTAP_LTE_RRC_SUB_PCCH_Message_NB, + GSMTAP_LTE_RRC_SUB_SC_MCCH_Message_NB, + + GSMTAP_LTE_RRC_SUB_MAX +}; + +/* LTE NAS message types */ +enum { + GSMTAP_LTE_NAS_PLAIN = 0, + GSMTAP_LTE_NAS_SEC_HEADER, + + GSMTAP_LTE_NAS_SUB_MAX }; /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ -/*! Structure of the GTMTAP pseudo-header */ +/*! Structure of the GSMTAP pseudo-header */ struct gsmtap_hdr { uint8_t version; /*!< version, set to 0x01 currently */ uint8_t hdr_len; /*!< length in number of 32bit words */ @@ -278,7 +301,7 @@ struct gsmtap_hdr { } __attribute__((packed)); -/*! Structure of the GTMTAP libosmocore logging header */ +/*! Structure of the GSMTAP libosmocore logging header */ struct gsmtap_osmocore_log_hdr { struct { uint32_t sec; diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h index 8464043f..295e5a85 100644 --- a/include/osmocom/core/logging.h +++ b/include/osmocom/core/logging.h @@ -122,7 +122,8 @@ void logp(int subsys, const char *file, int line, int cont, const char *format, #define DLM3UA -16 /*!< Osmocom M3UA */ #define DLMGCP -17 /*!< Osmocom MGCP */ #define DLJIBUF -18 /*!< Osmocom Jitter Buffer */ -#define OSMO_NUM_DLIB 18 /*!< Number of logging sub-systems in libraries */ +#define DLRSPRO -19 /*!< Osmocom Remote SIM Protocol */ +#define OSMO_NUM_DLIB 19 /*!< Number of logging sub-systems in libraries */ /*! Configuration of single log category / sub-system */ struct log_category { @@ -373,10 +374,6 @@ int log_targets_reopen(void); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); -/* Generate command string for VTY use */ -const char *log_vty_command_string() OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE; -const char *log_vty_command_description() OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE; - struct log_target *log_target_find(int type, const char *fname); extern struct llist_head osmo_log_target_list; diff --git a/include/osmocom/core/logging_internal.h b/include/osmocom/core/logging_internal.h new file mode 100644 index 00000000..a510f83e --- /dev/null +++ b/include/osmocom/core/logging_internal.h @@ -0,0 +1,15 @@ +#pragma once + +/*! \defgroup logging_internal Osmocom logging internals + * @{ + * \file logging_internal.h */ + +#include <osmocom/core/utils.h> + +extern void *tall_log_ctx; +extern const struct log_info *osmo_log_info; +extern const struct value_string loglevel_strs[]; + +void assert_loginfo(const char *src); + +/*! @} */ diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h index b1cb6ec7..2449151d 100644 --- a/include/osmocom/core/msgb.h +++ b/include/osmocom/core/msgb.h @@ -169,6 +169,18 @@ static inline unsigned int msgb_l3len(const struct msgb *msgb) return msgb->tail - (uint8_t *)msgb_l3(msgb); } +/*! determine length of L4 message + * \param[in] msgb message buffer + * \returns size of L4 message in bytes + * + * This function computes the number of bytes between the tail of the + * message and the layer 4 header. + */ +static inline unsigned int msgb_l4len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_sms(msgb); +} + /*! determine the length of the header * \param[in] msgb message buffer * \returns number of bytes between start of buffer and start of msg @@ -354,6 +366,15 @@ static inline void msgb_push_u32(struct msgb *msg, uint32_t word) osmo_store32be(word, space); } +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) +{ + uint8_t *data = msgb_push(msgb, 2); + + data[0] = tag; + data[1] = msgb->len - 2; + return data; +} + /*! remove (pull) a header from the front of the message buffer * \param[in] msgb message buffer * \param[in] len number of octets to be pulled diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index f23a2436..28f89a50 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -9,6 +9,7 @@ #include <stdint.h> #include <stdbool.h> +#include <stddef.h> struct sockaddr; struct osmo_fd; @@ -56,6 +57,11 @@ int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); char *osmo_sock_get_name(void *ctx, int fd); +int osmo_sock_get_local_ip(int fd, char *host, size_t len); +int osmo_sock_get_local_ip_port(int fd, char *port, size_t len); +int osmo_sock_get_remote_ip(int fd, char *host, size_t len); +int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len); + int osmo_sock_mcast_loop_set(int fd, bool enable); int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl); diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h index e2d51349..0b54c880 100644 --- a/include/osmocom/core/utils.h +++ b/include/osmocom/core/utils.h @@ -1,10 +1,13 @@ #pragma once #include <stdbool.h> +#include <stdint.h> +#include <stdio.h> #include <osmocom/core/backtrace.h> #include <osmocom/core/talloc.h> #include <osmocom/core/panic.h> +#include <osmocom/core/defs.h> /*! \defgroup utils General-purpose utility functions * @{ @@ -30,10 +33,6 @@ /*! Copy a C-string into a sized buffer using sizeof to detect buffer's size */ #define OSMO_STRLCPY_ARRAY(array, src) osmo_strlcpy(array, src, sizeof(array)) -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> - /*! A mapping between human-readable string and numeric value */ struct value_string { unsigned int value; /*!< numeric value */ @@ -59,8 +58,18 @@ char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__(( #define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__)); -void osmo_str2lower(char *out, const char *in); -void osmo_str2upper(char *out, const char *in); +void osmo_str2lower(char *out, const char *in) + OSMO_DEPRECATED("Use osmo_str_tolower() or osmo_str_tolower_buf() instead," + " to properly check target memory bounds"); +void osmo_str2upper(char *out, const char *in) + OSMO_DEPRECATED("Use osmo_str_toupper() or osmo_str_toupper_buf() instead," + " to properly check target memory bounds"); + +size_t osmo_str_tolower_buf(char *dest, size_t dest_len, const char *src); +const char *osmo_str_tolower(const char *src); + +size_t osmo_str_toupper_buf(char *dest, size_t dest_len, const char *src); +const char *osmo_str_toupper(const char *src); #define OSMO_SNPRINTF_RET(ret, rem, offset, len) \ do { \ diff --git a/include/osmocom/gsm/gsm0808.h b/include/osmocom/gsm/gsm0808.h index 5ae0af83..9b19d691 100644 --- a/include/osmocom/gsm/gsm0808.h +++ b/include/osmocom/gsm/gsm0808.h @@ -45,12 +45,14 @@ struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osm const struct gsm0808_speech_codec_list *scl); struct msgb *gsm0808_create_reset(void); struct msgb *gsm0808_create_reset_ack(void); -struct msgb *gsm0808_create_clear_command(uint8_t reason); +struct msgb *gsm0808_create_clear_command(uint8_t cause); struct msgb *gsm0808_create_clear_complete(void); struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei, const uint8_t *cipher_response_mode); struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); -struct msgb *gsm0808_create_cipher_reject(uint8_t cause); +struct msgb *gsm0808_create_cipher_reject(enum gsm0808_cause cause); +struct msgb *gsm0808_create_cipher_reject_ext(enum gsm0808_cause_class class, uint8_t ext); +struct msgb *gsm0808_create_classmark_request(); struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len); struct msgb *gsm0808_create_sapi_reject(uint8_t link_id); @@ -172,9 +174,13 @@ void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id); const struct tlv_definition *gsm0808_att_tlvdef(void); +/*! Parse BSSAP TLV structure using \ref tlv_parse */ +#define osmo_bssap_tlv_parse(dec, buf, len) tlv_parse(dec, gsm0808_att_tlvdef(), buf, len, 0, 0) + const char *gsm0808_bssmap_name(uint8_t msg_type); const char *gsm0808_bssap_name(uint8_t msg_type); -const char *gsm0808_cause_name(uint8_t cause); +const char *gsm0808_cause_name(enum gsm0808_cause cause); +const char *gsm0808_cause_class_name(enum gsm0808_cause_class class); extern const struct value_string gsm0808_lcls_config_names[]; extern const struct value_string gsm0808_lcls_control_names[]; diff --git a/include/osmocom/gsm/gsm0808_utils.h b/include/osmocom/gsm/gsm0808_utils.h index 242bce94..c5bf2803 100644 --- a/include/osmocom/gsm/gsm0808_utils.h +++ b/include/osmocom/gsm/gsm0808_utils.h @@ -29,6 +29,7 @@ struct sockaddr_storage; #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/gsm23003.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/tlv.h> /*! (225-1)/2 is the maximum number of elements in a cell identifier list. */ #define GSM0808_CELL_ID_LIST2_MAXLEN 127 @@ -57,6 +58,14 @@ struct gsm0808_cell_id_list2 { unsigned int id_list_len; }; +/*! LCLS-related parameters from 3GPP TS 48.008 */ +struct osmo_lcls { + enum gsm0808_lcls_config config; /* §3.2.2.116 Configuration */ + enum gsm0808_lcls_control control; /* §3.2.2.117 Connection Status Control */ + struct gsm0808_gcr *gcr; /* §3.2.2.115 Global Call Reference */ + bool corr_needed; /* §3.2.2.118 Correlation-Not-Needed */ +}; + extern const struct value_string gsm0808_cell_id_discr_names[]; static inline const char *gsm0808_cell_id_discr_name(enum CELL_IDENT id_discr) { return get_value_string(gsm0808_cell_id_discr_names, id_discr); } @@ -104,8 +113,25 @@ int gsm0808_dec_cell_id(struct gsm0808_cell_id *ci, const uint8_t *elem, uint8_t int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch); int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc, uint8_t perm_spch); +uint16_t gsm0808_sc_cfg_from_gsm48_mr_cfg(const struct gsm48_multi_rate_conf *cfg, bool fr); +void gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg, uint16_t s15_s0); + +/*! \returns 3GPP TS 08.08 §3.2.2.5 Class of a given Cause */ +static inline enum gsm0808_cause_class gsm0808_cause_class(enum gsm0808_cause cause) +{ + return (cause << 1) >> 4; +} + +/*! \returns true if 3GPP TS 08.08 §3.2.2.5 Class has extended bit set */ +static inline bool gsm0808_cause_ext(enum gsm0808_cause cause) +{ + /* check that cause looks like 1XXX0000 where XXX represent class */ + return (cause & 0x80) && !(cause & 0x0F); +} + +int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp); -/*! Return 3GPP TS 48.008 3.2.2.49 Current Channel Type 1 from enum gsm_chan_t. */ +/*! \returns 3GPP TS 48.008 3.2.2.49 Current Channel Type 1 from enum gsm_chan_t. */ static inline uint8_t gsm0808_current_channel_type_1(enum gsm_chan_t type) { switch (type) { diff --git a/include/osmocom/gsm/gsm23003.h b/include/osmocom/gsm/gsm23003.h index fd4f3694..2f380aec 100644 --- a/include/osmocom/gsm/gsm23003.h +++ b/include/osmocom/gsm/gsm23003.h @@ -101,6 +101,7 @@ const char *osmo_plmn_name2(const struct osmo_plmn_id *plmn); const char *osmo_lai_name(const struct osmo_location_area_id *lai); const char *osmo_cgi_name(const struct osmo_cell_global_id *cgi); const char *osmo_cgi_name2(const struct osmo_cell_global_id *cgi); +const char *osmo_gummei_name(const struct osmo_gummei *gummei); void osmo_plmn_to_bcd(uint8_t *bcd_dst, const struct osmo_plmn_id *plmn); void osmo_plmn_from_bcd(const uint8_t *bcd_src, struct osmo_plmn_id *plmn); @@ -120,3 +121,9 @@ static inline int osmo_mcc_from_str(const char *mcc_str, uint16_t *mcc) int osmo_mnc_cmp(uint16_t a_mnc, bool a_mnc_3_digits, uint16_t b_mnc, bool b_mnc_3_digits); int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b); + +int osmo_gen_home_network_domain(char *out, const struct osmo_plmn_id *plmn); +int osmo_parse_home_network_domain(struct osmo_plmn_id *out, const char *in); +int osmo_gen_mme_domain(char *out, const struct osmo_gummei *gummei); +int osmo_gen_mme_group_domain(char *out, uint16_t mmegi, const struct osmo_plmn_id *plmn); +int osmo_parse_mme_domain(struct osmo_gummei *out, const char *in); diff --git a/include/osmocom/gsm/gsm_utils.h b/include/osmocom/gsm/gsm_utils.h index 6ff44598..fe5903db 100644 --- a/include/osmocom/gsm/gsm_utils.h +++ b/include/osmocom/gsm/gsm_utils.h @@ -161,7 +161,8 @@ static inline int rach_max_trans_raw2val(int raw) { #define ARFCN_UPLINK 0x4000 #define ARFCN_FLAG_MASK 0xf000 /* Reserve the upper 5 bits for flags */ -enum gsm_band gsm_arfcn2band(uint16_t arfcn); +int gsm_arfcn2band_rc(uint16_t arfcn, enum gsm_band *band); +enum gsm_band gsm_arfcn2band(uint16_t arfcn) OSMO_DEPRECATED("Use gsm_arfcn2band_rc() instead"); /* Convert an ARFCN to the frequency in MHz * 10 */ uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); @@ -228,6 +229,8 @@ enum gsm_chan_t { }; extern const struct value_string gsm_chan_t_names[]; +static inline const char *gsm_chan_t_name(enum gsm48_chan_mode val) +{ return get_value_string(gsm_chan_t_names, val); } /* Deprectated functions */ /* Limit encoding and decoding to use no more than this amount of buffer bytes */ diff --git a/include/osmocom/gsm/gsup.h b/include/osmocom/gsm/gsup.h index 0ef5a759..cd6fd31f 100644 --- a/include/osmocom/gsm/gsup.h +++ b/include/osmocom/gsm/gsup.h @@ -207,7 +207,7 @@ struct osmo_gsup_message { /*! Session state \ref osmo_gsup_session_state */ enum osmo_gsup_session_state session_state; /*! Unique session identifier and origination flag. - * Encoded only when \ref session_state != 0x00 */ + * Encoded only when \ref session_state != 0x00 */ uint32_t session_id; /*! ASN.1 encoded MAP payload for Supplementary Services */ diff --git a/include/osmocom/gsm/protocol/gsm_04_08.h b/include/osmocom/gsm/protocol/gsm_04_08.h index 46350147..e218295c 100644 --- a/include/osmocom/gsm/protocol/gsm_04_08.h +++ b/include/osmocom/gsm/protocol/gsm_04_08.h @@ -1495,6 +1495,10 @@ enum gsm48_cc_cause { GSM48_CC_CAUSE_INTERWORKING = 127, }; +extern const struct value_string gsm48_cc_cause_names[]; +static inline const char *gsm48_cc_cause_name(enum gsm48_cc_cause val) +{ return get_value_string(gsm48_cc_cause_names, val); } + /* Annex G, GSM specific cause values for mobility management */ enum gsm48_reject_value { GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2, diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h index 69007b5c..cb9fe011 100644 --- a/include/osmocom/gsm/protocol/gsm_08_08.h +++ b/include/osmocom/gsm/protocol/gsm_08_08.h @@ -384,6 +384,18 @@ enum gsm0808_cause { GSM0808_CAUSE_DTM_HO_TIMER_EXPIRY = 0x62, }; +/* 3GPP TS 08.08 §3.2.2.5 Cause Class */ +enum gsm0808_cause_class { + GSM0808_CAUSE_CLASS_NORM0 = 0, + GSM0808_CAUSE_CLASS_NORM1 = 1, + GSM0808_CAUSE_CLASS_RES_UNAVAIL = 2, + GSM0808_CAUSE_CLASS_SRV_OPT_NA = 3, + GSM0808_CAUSE_CLASS_SRV_OPT_NIMPL = 4, + GSM0808_CAUSE_CLASS_INVAL = 5, + GSM0808_CAUSE_CLASS_PERR = 6, + GSM0808_CAUSE_CLASS_INTW = 7, +}; + /* GSM 08.08 3.2.2.11 Channel Type */ enum gsm0808_chan_indicator { GSM0808_CHAN_SPEECH = 1, @@ -499,10 +511,24 @@ enum gsm0808_speech_codec_defaults { GSM0808_SC_CFG_DEFAULT_HR_AMR = 0x073f, GSM0808_SC_CFG_DEFAULT_OHR_AMR = 0x57ff, GSM0808_SC_CFG_DEFAULT_FR_AMR_WB = 0x01, - GSM0808_SC_CFG_DEFAULT_OFR_AMR_WB = 0x3f, + GSM0808_SC_CFG_DEFAULT_OFR_AMR_WB = 0x15, GSM0808_SC_CFG_DEFAULT_OHR_AMR_WB = 0x01, }; +/*! Default speech codec configurations broken down by reate. + * See also: 3GPP TS 28.062, Table 7.11.3.1.3-2: Preferred Configurations for + * the Adaptive Multi-Rate Codec Types. */ +enum gsm0808_speech_codec_rate_defaults { + GSM0808_SC_CFG_DEFAULT_AMR_4_75 = 0xff03, + GSM0808_SC_CFG_DEFAULT_AMR_5_15 = 0x0000, + GSM0808_SC_CFG_DEFAULT_AMR_5_90 = 0xff06, + GSM0808_SC_CFG_DEFAULT_AMR_6_70 = 0x3e08, + GSM0808_SC_CFG_DEFAULT_AMR_7_40 = 0x0c12, + GSM0808_SC_CFG_DEFAULT_AMR_7_95 = 0xc020, + GSM0808_SC_CFG_DEFAULT_AMR_10_2 = 0x3040, + GSM0808_SC_CFG_DEFAULT_AMR_12_2 = 0xc082 +}; + /* 3GPP TS 48.008 3.2.2.103 Speech Codec List */ #define SPEECH_CODEC_MAXLEN 255 struct gsm0808_speech_codec_list { @@ -550,6 +576,7 @@ enum gsm0808_lcls_config { GSM0808_LCLS_CFG_BOTH_WAY_AND_SEND_DL_BLOCK_LOCAL_DL = 0x03, GSM0808_LCLS_CFG_BOTH_WAY_AND_BICAST_UL_SEND_DL = 0x04, GSM0808_LCLS_CFG_BOTH_WAY_AND_BICAST_UL_SEND_DL_BLOCK_LOCAL_DL = 0x05, + GSM0808_LCLS_CFG_NA = 0xFF }; /* TS 48.008 3.2.2.117 */ @@ -559,6 +586,7 @@ enum gsm0808_lcls_control { GSM0808_LCLS_CSC_RELEASE_LCLS = 0x02, GSM0808_LCLS_CSC_BICAST_UL_AT_HANDOVER = 0x03, GSM0808_LCLS_CSC_BICAST_UL_AND_RECV_DL_AT_HANDOVER = 0x04, + GSM0808_LCLS_CSC_NA = 0xFF }; /* TS 48.008 3.2.2.119 */ @@ -568,4 +596,5 @@ enum gsm0808_lcls_status { GSM0808_LCLS_STS_NO_LONGER_LS = 0x02, GSM0808_LCLS_STS_REQ_LCLS_NOT_SUPP = 0x03, GSM0808_LCLS_STS_LOCALLY_SWITCHED = 0x04, + GSM0808_LCLS_STS_NA = 0xFF }; diff --git a/include/osmocom/gsm/protocol/gsm_08_58.h b/include/osmocom/gsm/protocol/gsm_08_58.h index e5ff4646..363f33d7 100644 --- a/include/osmocom/gsm/protocol/gsm_08_58.h +++ b/include/osmocom/gsm/protocol/gsm_08_58.h @@ -25,10 +25,50 @@ #include <stdint.h> +#include <osmocom/core/endian.h> + /*! \addtogroup rsl * @{ * \file gsm_08_58.h */ +/* Channel Number 9.3.1 */ +union abis_rsl_chan_nr { +#if OSMO_IS_BIG_ENDIAN + uint8_t cbits:5, + tn:3; +#elif OSMO_IS_LITTLE_ENDIAN + uint8_t tn:3, + cbits:5; +#endif + uint8_t chan_nr; +} __attribute__ ((packed)); +#define ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs 0x01 +#define ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(ss) (0x02 + (ss)) +#define ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(ss) (0x04 + (ss)) +#define ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(ss) (0x08 + (ss)) +#define ABIS_RSL_CHAN_NR_CBITS_BCCH 0x10 +#define ABIS_RSL_CHAN_NR_CBITS_RACH 0x11 +#define ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH 0x12 +#define ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH 0x18 /*< non-standard, for dyn TS */ + +/* Link Identifier 9.3.2 */ +union abis_rsl_link_id { +#if OSMO_IS_BIG_ENDIAN + uint8_t cbits:2, + na:1, + reserved:2, + sapi:3; +#elif OSMO_IS_LITTLE_ENDIAN + uint8_t sapi:3, + reserved:2, + na:1, + cbits:2; +#endif + uint8_t link_id; +} __attribute__ ((packed)); +#define ABIS_RSL_LINK_ID_CBITS_FACCH_SDCCH 0x00 +#define ABIS_RSL_LINK_ID_CBITS_SACCH 0x01 + /*! RSL common header */ struct abis_rsl_common_hdr { uint8_t msg_discr; /*!< message discriminator (ABIS_RSL_MDISC_*) */ @@ -40,9 +80,15 @@ struct abis_rsl_common_hdr { struct abis_rsl_rll_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \ref RSL_IE_CHAN_NR (tag) */ - uint8_t chan_nr; /*!< RSL channel number (value) */ + union { + uint8_t chan_nr; /* API backward compat */ + union abis_rsl_chan_nr chan_nr_fields; /*!< RSL channel number (value) */ + }; uint8_t ie_link_id; /*!< \ref RSL_IE_LINK_IDENT (tag) */ - uint8_t link_id; /*!< RSL link identifier (value) */ + union { + uint8_t link_id; /* API backward compat */ + union abis_rsl_link_id link_id_fields; /*!< RSL link identifier (value) */ + }; uint8_t data[0]; /*!< message payload data */ } __attribute__ ((packed)); @@ -50,7 +96,10 @@ struct abis_rsl_rll_hdr { struct abis_rsl_dchan_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \ref RSL_IE_CHAN_NR (tag) */ - uint8_t chan_nr; /*!< RSL channel number (value) */ + union { + uint8_t chan_nr; /* API backward compat */ + union abis_rsl_chan_nr chan_nr_fields; /*!< RSL channel number (value) */ + }; uint8_t data[0]; /*!< message payload data */ } __attribute__ ((packed)); @@ -58,7 +107,10 @@ struct abis_rsl_dchan_hdr { struct abis_rsl_cchan_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \ref RSL_IE_CHAN_NR (tag) */ - uint8_t chan_nr; /*!< RSL channel number (value) */ + union { + uint8_t chan_nr; /* API backward compat */ + union abis_rsl_chan_nr chan_nr_fields; /*!< RSL channel number (value) */ + }; uint8_t data[0]; /*!< message payload data */ } __attribute__ ((packed)); diff --git a/include/osmocom/gsm/protocol/gsm_23_003.h b/include/osmocom/gsm/protocol/gsm_23_003.h index 0e669399..ee697ff4 100644 --- a/include/osmocom/gsm/protocol/gsm_23_003.h +++ b/include/osmocom/gsm/protocol/gsm_23_003.h @@ -24,3 +24,9 @@ GSM23003_IMEI_SNR_NUM_DIGITS + 1) #define GSM23003_IMEISV_NUM_DIGITS (GSM23003_IMEI_TAC_NUM_DIGITS + \ GSM23003_IMEI_SNR_NUM_DIGITS + 2) + +/* Chapter 19.2 "epc.mnc000.mcc000.3gppnetwork.org" */ +#define GSM23003_HOME_NETWORK_DOMAIN_LEN 33 + +/* Chapter 19.4.2.4: "mmec00.mmegi0000.mme.epc.mnc000.mcc000.3gppnetwork.org" */ +#define GSM23003_MME_DOMAIN_LEN 55 diff --git a/include/osmocom/gsm/protocol/gsm_29_118.h b/include/osmocom/gsm/protocol/gsm_29_118.h new file mode 100644 index 00000000..bf210ad0 --- /dev/null +++ b/include/osmocom/gsm/protocol/gsm_29_118.h @@ -0,0 +1,173 @@ +#pragma once + +#include <osmocom/core/utils.h> + +/* TS 29.118 Section 9.2 */ +enum sgsap_msg_type { + /* unassigned */ + SGSAP_MSGT_PAGING_REQ = 0x01, + SGSAP_MSGT_PAGING_REJ = 0x02, + /* unassigned */ + SGSAP_MSGT_SERVICE_REQ = 0x06, + SGSAP_MSGT_DL_UD = 0x07, + SGSAP_MSGT_UL_UD = 0x08, + SGSAP_MSGT_LOC_UPD_REQ = 0x09, + SGSAP_MSGT_LOC_UPD_ACK = 0x0a, + SGSAP_MSGT_LOC_UPD_REJ = 0x0b, + SGSAP_MSGT_TMSI_REALL_CMPL = 0x0c, + SGSAP_MSGT_ALERT_REQ = 0x0d, + SGSAP_MSGT_ALERT_ACK = 0x0e, + SGSAP_MSGT_ALERT_REJ = 0x0f, + SGSAP_MSGT_UE_ACT_IND = 0x10, + SGSAP_MSGT_EPS_DET_IND = 0x11, + SGSAP_MSGT_EPS_DET_ACK = 0x12, + SGSAP_MSGT_IMSI_DET_IND = 0x13, + SGSAP_MSGT_IMSI_DET_ACK = 0x14, + SGSAP_MSGT_RESET_IND = 0x15, + SGSAP_MSGT_RESET_ACK = 0x16, + SGSAP_MSGT_SERVICE_ABORT_REQ = 0x17, + SGSAP_MSGT_MO_CSFB_IND = 0x18, + /* unassigned */ + SGSAP_MSGT_MM_INFO_REQ = 0x1a, + SGSAP_MSGT_RELEASE_REQ = 0x1b, + /* unassigned */ + SGSAP_MSGT_STATUS = 0x1d, + /* unassigned */ + SGSAP_MSGT_UE_UNREACHABLE = 0x1f, +}; +const struct value_string sgsap_msg_type_names[]; +static inline const char *sgsap_msg_type_name(enum sgsap_msg_type msgt) { + return get_value_string(sgsap_msg_type_names, msgt); +} + +/* TS 29.118 Section 9.3 */ +enum sgsap_iei { + SGSAP_IE_IMSI = 0x01, + SGSAP_IE_VLR_NAME = 0x02, + SGSAP_IE_TMSI = 0x03, + SGSAP_IE_LAI = 0x04, + SGSAP_IE_CHAN_NEEDED = 0x05, + SGSAP_IE_EMLPP_PRIORITY = 0x06, + SGSAP_IE_TMSI_STATUS = 0x07, + SGSAP_IE_SGS_CAUSE = 0x08, + SGSAP_IE_MME_NAME = 0x09, + SGSAP_IE_EPS_LU_TYPE = 0x0a, + SGSAP_IE_GLOBAL_CN_ID = 0x0b, + SGSAP_IE_MOBILE_ID = 0x0e, + SGSAP_IE_REJECT_CAUSE = 0x0f, + SGSAP_IE_IMSI_DET_EPS_TYPE = 0x10, + SGSAP_IE_IMSI_DET_NONEPS_TYPE = 0x11, + SGSAP_IE_IMEISV = 0x15, + SGSAP_IE_NAS_MSG_CONTAINER = 0x16, + SGSAP_IE_MM_INFO = 0x17, + SGSAP_IE_ERR_MSG = 0x1b, + SGSAP_IE_CLI = 0x1c, + SGSAP_IE_LCS_CLIENT_ID = 0x1d, + SGSAP_IE_LCS_INDICATOR = 0x1e, + SGSAP_IE_SS_CODE = 0x1f, + SGSAP_IE_SERVICE_INDICATOR = 0x20, + SGSAP_IE_UE_TIMEZONE = 0x21, + SGSAP_IE_MS_CLASSMARK2 = 0x22, + SGSAP_IE_TAI = 0x23, + SGSAP_IE_EUTRAN_CGI = 0x24, + SGSAP_IE_UE_EMM_MODE = 0x25, + SGSAP_IE_ADDL_PAGING_INDICATORS = 0x26, + SGSAP_IE_TMSI_BASED_NRI_CONT = 0x27, +}; +const struct value_string sgsap_iei_names[]; +static inline const char *sgsap_iei_name(enum sgsap_iei iei) { + return get_value_string(sgsap_iei_names, iei); +} + +/* TS 29.118 Section 9.4.2 */ +enum sgsap_eps_lu_type { + SGSAP_EPS_LUT_IMSI_ATTACH = 0x01, + SGSAP_EPS_LUT_NORMAL = 0x02, +}; +const struct value_string sgsap_eps_lu_type_names[]; +static inline const char *sgsap_eps_lu_type_name(enum sgsap_eps_lu_type lut) { + return get_value_string(sgsap_eps_lu_type_names, lut); +} + +/* TS 29.118 Section 9.4.7 */ +enum sgsap_imsi_det_eps_type { + SGSAP_ID_EPS_T_NETWORK_INITIATED = 0x01, + SGSAP_ID_EPS_T_UE_INITIATED = 0x02, + SGSAP_ID_EPS_T_EPS_NOT_ALLOWED = 0x03, +}; +const struct value_string sgsap_ismi_det_eps_type_names[]; +static inline const char *sgsap_imsi_det_eps_type_name(enum sgsap_imsi_det_eps_type idt) { + return get_value_string(sgsap_ismi_det_eps_type_names, idt); +} + +/* TS 29.118 Section 9.4.8 */ +enum sgsap_imsi_det_noneps_type { + SGSAP_ID_NONEPS_T_EXPLICIT_UE_NONEPS = 0x01, + SGSAP_ID_NONEPS_T_COMBINED_UE_EPS_NONEPS = 0x02, + SGSAP_ID_NONEPS_T_IMPLICIT_UE_EPS_NONEPS = 0x03, +}; +const struct value_string sgsap_ismi_det_noneps_type_names[]; +static inline const char *sgsap_imsi_det_noneps_type_name(enum sgsap_imsi_det_noneps_type idt) { + return get_value_string(sgsap_ismi_det_noneps_type_names, idt); +} + +/* TS 29.118 Section 9.4.17 */ +enum sgsap_service_ind { + SGSAP_SERV_IND_CS_CALL = 0x01, + SGSAP_SERV_IND_SMS = 0x02, +}; +const struct value_string sgsap_service_ind_names[]; +static inline const char *sgsap_service_ind_name(enum sgsap_service_ind si) { + return get_value_string(sgsap_service_ind_names, si); +} + +/* TS 29.118 Section 9.4.18 */ +enum sgsap_sgs_cause { + SGSAP_SGS_CAUSE_IMSI_DET_EPS = 0x01, + SGSAP_SGS_CAUSE_IMSI_DET_EPS_NONEPS = 0x02, + SGSAP_SGS_CAUSE_IMSI_UNKNOWN = 0x03, + SGSAP_SGS_CAUSE_IMSI_DET_NON_EPS = 0x04, + SGSAP_SGS_CAUSE_IMSI_IMPL_DET_NON_EPS = 0x05, + SGSAP_SGS_CAUSE_UE_UNREACHABLE = 0x06, + SGSAP_SGS_CAUSE_MSG_INCOMP_STATE = 0x07, + SGSAP_SGS_CAUSE_MISSING_MAND_IE = 0x08, + SGSAP_SGS_CAUSE_INVALID_MAND_IE = 0x09, + SGSAP_SGS_CAUSE_COND_IE_ERROR = 0x0a, + SGSAP_SGS_CAUSE_SEMANT_INCORR_MSG = 0x0b, + SGSAP_SGS_CAUSE_MSG_UNKNOWN = 0x0c, + SGSAP_SGS_CAUSE_MT_CSFB_REJ_USER = 0x0d, + SGSAP_SGS_CAUSE_UE_TEMP_UNREACHABLE = 0x0e, +}; +const struct value_string sgsap_sgs_cause_names[]; +static inline const char *sgsap_sgs_cause_name(enum sgsap_sgs_cause cause) { + return get_value_string(sgsap_sgs_cause_names, cause); +} + +/* TS 29.118 Section 9.4.21c */ +enum sgsap_ue_emm_mode { + SGSAP_UE_EMM_MODE_IDLE = 0x00, + SGSAP_UE_EMM_MODE_CONNECTED = 0x01, +}; +const struct value_string sgsap_ue_emm_mode_names[]; +static inline const char *sgsap_ue_emm_mode_name(enum sgsap_ue_emm_mode mode) { + return get_value_string(sgsap_ue_emm_mode_names, mode); +} + +/* TS 29.118 Section 10.1 Table 10.1.2 */ +#define SGS_TS5_DEFAULT 10 /* Guards the Paging Procedure at the VLR */ +#define SGS_TS6_2_DEFAULT 40 /* Guards the TMSI reallocation procedure */ +#define SGS_TS7_DEFAULT 4 /* Guards the non-EPS alert procedure */ +#define SGS_TS11_DEFAULT 4 /* Guards the VLR reset procedure */ +#define SGS_TS14_DEFAULT 10 /* Guards the UE fallback to UTRAN/GERAN */ +#define SGS_TS15_DEFAULT 10 /* Guards the MO UE fallback to UTRAN/GERAN */ + +/* TS 29.118 Section 10.2 Table 10.2.1 */ +#define SGS_NS7_DEFAULT 2 +#define SGS_NS11_DEFAULT 2 +/* TS 29.118 Section 10.2 Table 10.2.2 */ +#define SGS_NS8_DEFAULT 2 +#define SGS_NS9_DEFAULT 2 +#define SGS_NS10_DEFAULT 2 +#define SGS_NS12_DEFAULT 2 + +const struct tlv_definition sgsap_ie_tlvdef; diff --git a/include/osmocom/gsm/protocol/gsm_44_318.h b/include/osmocom/gsm/protocol/gsm_44_318.h index 76e8396c..6ac02cfd 100644 --- a/include/osmocom/gsm/protocol/gsm_44_318.h +++ b/include/osmocom/gsm/protocol/gsm_44_318.h @@ -170,15 +170,6 @@ struct gan_cch_desc_ie { dtm:1, att:1, mscr:1; -#if 0 - uint8_t mscr:1, - att:1, - dtm:1, - gprs:1, - nmo:2, - ecmc:1, - spare:1; -#endif uint8_t t3212; uint8_t rac; uint8_t sgsnr:1, @@ -187,13 +178,5 @@ struct gan_cch_desc_ie { pfcfm:1, tgecs:2, spare2:2; -#if 0 - uint8_t spare2:2, - tgecs:2, - pfcfm:1, - re:1, - ecmp:1, - sgsnr:1; -#endif uint8_t access_class[2]; } __attribute__((packed)); diff --git a/include/osmocom/gsm/protocol/ipaccess.h b/include/osmocom/gsm/protocol/ipaccess.h index 0f5d54f2..80413d10 100644 --- a/include/osmocom/gsm/protocol/ipaccess.h +++ b/include/osmocom/gsm/protocol/ipaccess.h @@ -38,6 +38,7 @@ enum ipaccess_proto_ext { IPAC_PROTO_EXT_ORC = 0x04, /* OML Router Control */ IPAC_PROTO_EXT_GSUP = 0x05, /* GSUP GPRS extension */ IPAC_PROTO_EXT_OAP = 0x06, /* Osmocom Authn Protocol */ + IPAC_PROTO_EXT_RSPRO = 0x07, /* Remote SIM protocol */ }; enum ipaccess_msgtype { diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h index 84fd511e..1ab964ad 100644 --- a/include/osmocom/gsm/tlv.h +++ b/include/osmocom/gsm/tlv.h @@ -341,6 +341,12 @@ static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, return buf; } +/*! push 1-byte tagged value */ +static inline uint8_t *msgb_tlv1_push(struct msgb *msg, uint8_t tag, uint8_t val) +{ + return msgb_tlv_push(msg, tag, 1, &val); +} + /*! push (prepend) a TV field to a \ref msgb * \returns pointer to first byte of newly-pushed information */ static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val) diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h index 13d6e89d..d63dbdef 100644 --- a/include/osmocom/vty/command.h +++ b/include/osmocom/vty/command.h @@ -132,9 +132,10 @@ struct cmd_node { char name[64]; }; +/*! Attributes (flags) for \ref cmd_element */ enum { - CMD_ATTR_DEPRECATED = 1, - CMD_ATTR_HIDDEN, + CMD_ATTR_DEPRECATED = (1 << 0), + CMD_ATTR_HIDDEN = (1 << 1), }; /*! Structure of a command element */ diff --git a/src/coding/gsm0503_coding.c b/src/coding/gsm0503_coding.c index c72aabc0..7385d233 100644 --- a/src/coding/gsm0503_coding.c +++ b/src/coding/gsm0503_coding.c @@ -938,6 +938,11 @@ int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t struct egprs_cps cps; union gprs_rlc_ul_hdr_egprs *hdr; + if (n_errors) + *n_errors = 0; + if (n_bits_total) + *n_bits_total = 0; + if ((nbits != GSM0503_GPRS_BURSTS_NBITS) && (nbits != GSM0503_EGPRS_BURSTS_NBITS)) { /* Invalid EGPRS bit length */ @@ -979,6 +984,9 @@ int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t if (rc < 0) return -EFAULT; } else { + /* Bit counters for the second block */ + int n_errors2, n_bits_total2; + /* MCS-7,8,9 block 1 */ rc = egprs_decode_data(l2_data, c1, cps.mcs, cps.p[0], 0, n_errors, n_bits_total); @@ -987,7 +995,11 @@ int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts, uint16_t /* MCS-7,8,9 block 2 */ rc = egprs_decode_data(l2_data, c2, cps.mcs, cps.p[1], - 1, n_errors, n_bits_total); + 1, &n_errors2, &n_bits_total2); + if (n_errors) + *n_errors += n_errors2; + if (n_bits_total) + *n_bits_total += n_bits_total2; if (rc < 0) return -EFAULT; } diff --git a/src/ctrl/control_if.c b/src/ctrl/control_if.c index 5962f7eb..ed18a2fc 100644 --- a/src/ctrl/control_if.c +++ b/src/ctrl/control_if.c @@ -389,20 +389,44 @@ int ctrl_handle_msg(struct ctrl_handle *ctrl, struct ctrl_connection *ccon, stru struct ipaccess_head_ext *iph_ext; int result; - if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { + if (msg->len < sizeof(*iph)) { LOGP(DLCTRL, LOGL_ERROR, "The message is too short.\n"); return -EINVAL; } - iph = (struct ipaccess_head *) msg->data; + if (iph->proto == IPAC_PROTO_IPACCESS) { + uint8_t msg_type = *(msg->l2h); + switch (msg_type) { + case IPAC_MSGT_PING: + if (ipa_ccm_send_pong(ccon->write_queue.bfd.fd) < 0) + LOGP(DLINP, LOGL_ERROR, "Cannot send PONG message. Reason: %s\n", strerror(errno)); + break; + case IPAC_MSGT_PONG: + break; + case IPAC_MSGT_ID_ACK: + if (ipa_ccm_send_id_ack(ccon->write_queue.bfd.fd) < 0) + LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK message. Reason: %s\n", strerror(errno)); + break; + default: + LOGP(DLCTRL, LOGL_DEBUG, "Received unhandled IPACCESS protocol message of type 0x%x: %s\n", + msg_type, msgb_hexdump(msg)); + break; + } + return 0; + } if (iph->proto != IPAC_PROTO_OSMO) { - LOGP(DLCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); + LOGP(DLCTRL, LOGL_ERROR, "Protocol mismatch. Received protocol 0x%x message: %s\n", + iph->proto, msgb_hexdump(msg)); + return -EINVAL; + } + if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { + LOGP(DLCTRL, LOGL_ERROR, "The message is too short.\n"); return -EINVAL; } - iph_ext = (struct ipaccess_head_ext *) iph->data; if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { - LOGP(DLCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); + LOGP(DLCTRL, LOGL_ERROR, "Extended protocol mismatch. Received protocol 0x%x message: %s\n", + iph_ext->proto, msgb_hexdump(msg)); return -EINVAL; } @@ -414,7 +414,7 @@ const char *osmo_fsm_inst_name(struct osmo_fsm_inst *fi) return fi->fsm->name; } -/*! get human-readable name of FSM instance +/*! get human-readable name of FSM state * \param[in] fsm FSM descriptor * \param[in] state FSM state number * \returns string rendering of the FSM state diff --git a/src/gb/gprs_ns.c b/src/gb/gprs_ns.c index 9b7cc056..1281c9f4 100644 --- a/src/gb/gprs_ns.c +++ b/src/gb/gprs_ns.c @@ -1413,8 +1413,16 @@ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg, /* Section 7.2: unblocking procedure */ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", (*nsvc)->nsei); ns_mark_unblocked(*nsvc); - ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0); + /* This UNBLOCK_ACK message will cause our peer to move us into NS_UNBLOCKED state. */ rc = gprs_ns_tx_simple(*nsvc, NS_PDUT_UNBLOCK_ACK); + if (rc < 0) + break; + /* + * UNBLOCK_ACK has been transmitted. + * Signal handlers may send additional messages following UNBLOCK_ACK under + * the assumption that NS is now in UNBLOCKED state at our peer's end. + */ + ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0); break; case NS_PDUT_UNBLOCK_ACK: LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", (*nsvc)->nsei); @@ -1628,8 +1636,11 @@ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi) LOGP(DNS, LOGL_NOTICE, "Listening for nsip packets on %s:%u\n", inet_ntoa(in), nsi->nsip.local_port); } - if (ret < 0) + if (ret < 0) { + nsi->nsip.fd.cb = NULL; + nsi->nsip.fd.data = NULL; return ret; + } ret = setsockopt(nsi->nsip.fd.fd, IPPROTO_IP, IP_TOS, &nsi->nsip.dscp, sizeof(nsi->nsip.dscp)); diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am index 29299a64..e28ea335 100644 --- a/src/gsm/Makefile.am +++ b/src/gsm/Makefile.am @@ -30,7 +30,8 @@ libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \ milenage/aes-internal.c milenage/aes-internal-enc.c \ milenage/milenage.c gan.c ipa.c gsm0341.c apn.c \ gsup.c gprs_gea.c gsm0503_conv.c oap.c gsm0808_utils.c \ - gsm23003.c mncc.c bts_features.c oap_client.c + gsm23003.c mncc.c bts_features.c oap_client.c \ + gsm29118.c libgsmint_la_LDFLAGS = -no-undefined libgsmint_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/gsm/gsm0480.c b/src/gsm/gsm0480.c index 7756ecba..021db62f 100644 --- a/src/gsm/gsm0480.c +++ b/src/gsm/gsm0480.c @@ -68,35 +68,6 @@ const struct value_string gsm0480_op_code_names[] = { { 0, NULL } }; -static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) -{ - uint8_t *data = msgb_push(msgb, 2); - - data[0] = tag; - data[1] = msgb->len - 2; - return data; -} - -static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, - uint8_t value) -{ - uint8_t *data = msgb_push(msgb, 3); - - data[0] = tag; - data[1] = 1; - data[2] = value; - return data; -} - -static inline unsigned char *msgb_push_NULL(struct msgb *msgb) -{ - uint8_t *data = msgb_push(msgb, 2); - - data[0] = ASN1_NULL_TYPE_TAG; - data[1] = 0; - return data; -} - /* wrap an invoke around it... the other way around * * 1.) Invoke Component tag @@ -107,10 +78,10 @@ static inline unsigned char *msgb_push_NULL(struct msgb *msgb) int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id) { /* 3. operation */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op); + msgb_tlv1_push(msg, GSM0480_OPERATION_CODE, op); /* 2. invoke id tag */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id); + msgb_tlv1_push(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id); /* 1. component tag */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE); @@ -825,20 +796,20 @@ struct msgb *gsm0480_gen_ussd_resp_7bit(uint8_t invoke_id, const char *text) msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); + msgb_tlv1_push(msg, ASN1_OCTET_STRING_TAG, 0x0F); /* Then wrap these as a Sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + msgb_tlv1_push(msg, GSM0480_OPERATION_CODE, GSM0480_OP_CODE_PROCESS_USS_REQ); /* Wrap the operation code and IA5 string as a sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + msgb_tlv1_push(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); /* Wrap this up as a Return Result component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); @@ -889,10 +860,10 @@ struct msgb *gsm0480_gen_return_error(uint8_t invoke_id, uint8_t error_code) return NULL; /* First insert the problem code */ - msgb_push_TLV1(msg, GSM_0480_ERROR_CODE_TAG, error_code); + msgb_tlv1_push(msg, GSM_0480_ERROR_CODE_TAG, error_code); /* Before it, insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + msgb_tlv1_push(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); /* Wrap this up as a Reject component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_ERROR); @@ -919,13 +890,13 @@ struct msgb *gsm0480_gen_reject(int invoke_id, uint8_t problem_tag, uint8_t prob return NULL; /* First insert the problem code */ - msgb_push_TLV1(msg, problem_tag, problem_code); + msgb_tlv1_push(msg, problem_tag, problem_code); /* If the Invoke ID is not available, Universal NULL (table 3.9) with length=0 shall be used */ if (invoke_id < 0 || invoke_id > 255) - msgb_push_NULL(msg); + msgb_tv_push(msg, ASN1_NULL_TYPE_TAG, 0); else - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + msgb_tlv1_push(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); /* Wrap this up as a Reject component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c index e3b10d0c..fe7bc2ca 100644 --- a/src/gsm/gsm0808.c +++ b/src/gsm/gsm0808.c @@ -182,9 +182,9 @@ struct msgb *gsm0808_create_clear_complete(void) } /*! Create BSSMAP Clear Command message - * \param[in] reason TS 08.08 cause value + * \param[in] cause TS 08.08 cause value * \returns callee-allocated msgb with BSSMAP Clear Command message */ -struct msgb *gsm0808_create_clear_command(uint8_t reason) +struct msgb *gsm0808_create_clear_command(uint8_t cause) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: clear command"); @@ -193,7 +193,7 @@ struct msgb *gsm0808_create_clear_command(uint8_t reason) msg->l3h = msgb_tv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 4); msgb_v_put(msg, BSS_MAP_MSG_CLEAR_CMD); - msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &reason); + msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause); return msg; } @@ -265,17 +265,42 @@ struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id) } /*! Create BSSMAP Cipher Mode Reject message - * \param[in] reason TS 08.08 cause value + * \param[in] cause 3GPP TS 08.08 §3.2.2.5 cause value * \returns callee-allocated msgb with BSSMAP Cipher Mode Reject message */ -struct msgb *gsm0808_create_cipher_reject(uint8_t cause) +struct msgb *gsm0808_create_cipher_reject(enum gsm0808_cause cause) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, - "bssmap: clear complete"); + "bssmap: cipher mode reject"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_REJECT); - msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause); + + msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, (const uint8_t *)&cause); + + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + + return msg; +} + +/*! Create BSSMAP Cipher Mode Reject message + * \param[in] class 3GPP TS 08.08 §3.2.2.5 cause's class + * \param[in] ext 3GPP TS 08.08 §3.2.2.5 cause value (national application extension) + * \returns callee-allocated msgb with BSSMAP Cipher Mode Reject message */ +struct msgb *gsm0808_create_cipher_reject_ext(enum gsm0808_cause_class class, uint8_t ext) +{ + uint8_t c[2]; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: cipher mode reject"); + if (!msg) + return NULL; + + c[0] = 0x80 | (class << 4); /* set the high bit to indicate extended cause */ + c[1] = ext; + + msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_REJECT); + + msgb_tlv_put(msg, GSM0808_IE_CAUSE, 2, c); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); @@ -341,6 +366,19 @@ struct msgb *gsm0808_create_lcls_notification(enum gsm0808_lcls_status status, b return msg; } +/*! Create BSSMAP Classmark Request message + * \returns callee-allocated msgb with BSSMAP Classmark Request message */ +struct msgb *gsm0808_create_classmark_request() +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "classmark-request"); + if (!msg) + return NULL; + + msgb_v_put(msg, BSS_MAP_MSG_CLASSMARK_RQST); + msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); + return msg; +} /*! Create BSSMAP Classmark Update message * \param[in] cm2 Classmark 2 @@ -386,12 +424,12 @@ struct msgb *gsm0808_create_sapi_reject(uint8_t link_id) return msg; } -/*! Create BSSMAP Assignment Request message +/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1 * \param[in] ct Channel Type * \param[in] cic Circuit Identity Code (Classic A only) * \param[in] ss Socket Address of MSC-side RTP socket (AoIP only) * \param[in] scl Speech Codec List (AoIP only) - * \param[in] ci Call Identifier (Optional, LCLS) + * \param[in] ci Call Identifier (Optional), §3.2.2.105 * \returns callee-allocated msgb with BSSMAP Assignment Request message */ struct msgb *gsm0808_create_ass(const struct gsm0808_channel_type *ct, const uint16_t *cic, @@ -1257,8 +1295,26 @@ static const struct value_string gsm0808_cause_names[] = { { 0, NULL } }; +static const struct value_string gsm0808_cause_class_names[] = { + { GSM0808_CAUSE_CLASS_NORM0, "Normal event" }, + { GSM0808_CAUSE_CLASS_NORM1, "Normal event" }, + { GSM0808_CAUSE_CLASS_RES_UNAVAIL, "Resource unavailable" }, + { GSM0808_CAUSE_CLASS_SRV_OPT_NA, "Service or option not available" }, + { GSM0808_CAUSE_CLASS_SRV_OPT_NIMPL, "Service or option not implemented" }, + { GSM0808_CAUSE_CLASS_INVAL, "Invalid message" }, + { GSM0808_CAUSE_CLASS_PERR, "Protocol error" }, + { GSM0808_CAUSE_CLASS_INTW, "Interworking" }, + { 0, NULL } +}; + +/*! Return string name of BSSMAP Cause Class name */ +const char *gsm0808_cause_class_name(enum gsm0808_cause_class class) +{ + return get_value_string(gsm0808_cause_class_names, class); +} + /*! Return string name of BSSMAP Cause name */ -const char *gsm0808_cause_name(uint8_t cause) +const char *gsm0808_cause_name(enum gsm0808_cause cause) { return get_value_string(gsm0808_cause_names, cause); } @@ -1275,6 +1331,7 @@ const struct value_string gsm0808_lcls_config_names[] = { "Connect both-way, bi-cast UL to CN, send access DL from CN" }, { GSM0808_LCLS_CFG_BOTH_WAY_AND_BICAST_UL_SEND_DL_BLOCK_LOCAL_DL, "Connect both-way, bi-cast UL to CN, send access DL from CN, block local DL" }, + { GSM0808_LCLS_CFG_NA, "Not available" }, { 0, NULL } }; @@ -1284,6 +1341,7 @@ const struct value_string gsm0808_lcls_control_names[] = { { GSM0808_LCLS_CSC_RELEASE_LCLS, "Release LCLS" }, { GSM0808_LCLS_CSC_BICAST_UL_AT_HANDOVER, "Bi-cast UL at Handover" }, { GSM0808_LCLS_CSC_BICAST_UL_AND_RECV_DL_AT_HANDOVER, "Bi-cast UL and receive DL at Handover" }, + { GSM0808_LCLS_CSC_NA, "Not available" }, { 0, NULL } }; @@ -1293,6 +1351,7 @@ const struct value_string gsm0808_lcls_status_names[] = { { GSM0808_LCLS_STS_NO_LONGER_LS, "Call is no longer locally switched" }, { GSM0808_LCLS_STS_REQ_LCLS_NOT_SUPP, "Requested LCLS configuration is not supported" }, { GSM0808_LCLS_STS_LOCALLY_SWITCHED, "Call is locally switched with requested LCLS config" }, + { GSM0808_LCLS_STS_NA, "Not available" }, { 0, NULL } }; diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c index 73f02341..c58d8284 100644 --- a/src/gsm/gsm0808_utils.c +++ b/src/gsm/gsm0808_utils.c @@ -349,9 +349,6 @@ uint8_t gsm0808_enc_speech_codec_list(struct msgb *msg, OSMO_ASSERT(msg); OSMO_ASSERT(scl); - /* Empty list */ - OSMO_ASSERT(scl->len >= 1); - msgb_put_u8(msg, GSM0808_IE_SPEECH_CODEC_LIST); tlv_len = msgb_put(msg, 1); old_tail = msg->tail; @@ -384,8 +381,6 @@ int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl, OSMO_ASSERT(scl); if (!elem) return -EINVAL; - if (len == 0) - return -EINVAL; memset(scl, 0, sizeof(*scl)); @@ -404,11 +399,6 @@ int gsm0808_dec_speech_codec_list(struct gsm0808_speech_codec_list *scl, scl->len = decoded; - /* Empty list */ - if (decoded < 1) { - return -EINVAL; - } - return (int)(elem - old_elem); } @@ -1161,6 +1151,106 @@ int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc, return 0; } +/*! Determine a set of AMR speech codec configuration bits (S0-S15) from a + * given GSM 04.08 AMR configuration struct. + * \param[in] cfg AMR configuration in GSM 04.08 format. + * \param[in] hint if the resulting configuration shall be used with a FR or HR TCH. + * \returns configuration bits (S0-S15) */ +uint16_t gsm0808_sc_cfg_from_gsm48_mr_cfg(const struct gsm48_multi_rate_conf *cfg, + bool fr) +{ + uint16_t s15_s0 = 0; + + /* Check each rate bit in the AMR multirate configuration and pick the + * matching default configuration as specified in 3GPP TS 28.062, + * Table 7.11.3.1.3-2. */ + if (cfg->m4_75) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_4_75; + if (cfg->m5_15) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_15; + if (cfg->m5_90) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_5_90; + if (cfg->m6_70) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_6_70; + if (cfg->m7_40) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_40; + if (cfg->m7_95) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_7_95; + if (cfg->m10_2) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_10_2; + if (cfg->m12_2) + s15_s0 |= GSM0808_SC_CFG_DEFAULT_AMR_12_2; + + /* Note: 3GPP TS 48.008, chapter 3GPP TS 48.008 states that for AMR + * some of the configuration bits must be coded as zeros. The applied + * bitmask matches the default codec settings. See also the definition + * of enum gsm0808_speech_codec_defaults in gsm_08_08.h and + * 3GPP TS 28.062, Table 7.11.3.1.3-2. */ + if (fr) + s15_s0 &= GSM0808_SC_CFG_DEFAULT_FR_AMR; + else + s15_s0 &= GSM0808_SC_CFG_DEFAULT_HR_AMR; + + return s15_s0; +} + +/*! Determine a GSM 04.08 AMR configuration struct from a set of speech codec + * configuration bits (S0-S15) + * \param[out] cfg AMR configuration in GSM 04.08 format. + * \param[in] s15_s0 configuration bits (S0-S15). */ +void gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg, + uint16_t s15_s0) +{ + memset(cfg, 0, sizeof(*cfg)); + + /* Strip option bits */ + s15_s0 &= 0x00ff; + + /* Rate 5,15k must always be present */ + cfg->m5_15 = 1; + + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_4_75 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_4_75 & 0xff)) + cfg->m4_75 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_5_90 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_5_90 & 0xff)) + cfg->m5_90 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_6_70 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_6_70 & 0xff)) + cfg->m6_70 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_7_40 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_7_40 & 0xff)) + cfg->m7_40 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_7_95 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_7_95 & 0xff)) + cfg->m7_95 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_10_2 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_10_2 & 0xff)) + cfg->m10_2 = 1; + if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_12_2 & 0xff) == + (GSM0808_SC_CFG_DEFAULT_AMR_12_2 & 0xff)) + cfg->m12_2 = 1; + + cfg->ver = 1; + cfg->icmi = 1; +} + +int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp) +{ + const uint8_t *buf = TLVP_VAL_MINLEN(tp, GSM0808_IE_CAUSE, 1); + + if (!buf) + return -EBADMSG; + + if (TLVP_LEN(tp, GSM0808_IE_CAUSE) > 1) { + if (!gsm0808_cause_ext(buf[0])) + return -EINVAL; + return buf[1]; + } + + return buf[0]; +} + /*! Print a human readable name of the cell identifier to the char buffer. * This is useful both for struct gsm0808_cell_id and struct gsm0808_cell_id_list2. * See also gsm0808_cell_id_name() and gsm0808_cell_id_list_name(). diff --git a/src/gsm/gsm23003.c b/src/gsm/gsm23003.c index 2c3b21e6..4fdad48a 100644 --- a/src/gsm/gsm23003.c +++ b/src/gsm/gsm23003.c @@ -169,6 +169,14 @@ static void to_bcd(uint8_t *bcd, uint16_t val) bcd[0] = val % 10; } +const char *osmo_gummei_name(const struct osmo_gummei *gummei) +{ + static char buf[32]; + snprintf(buf, sizeof(buf), "%s-%04x-%02x", osmo_plmn_name(&gummei->plmn), + gummei->mme.group_id, gummei->mme.code); + return buf; +} + /* Convert MCC + MNC to BCD representation * \param[out] bcd_dst caller-allocated memory for output * \param[in] mcc Mobile Country Code @@ -297,3 +305,81 @@ int osmo_plmn_cmp(const struct osmo_plmn_id *a, const struct osmo_plmn_id *b) return 1; return osmo_mnc_cmp(a->mnc, a->mnc_3_digits, b->mnc, b->mnc_3_digits); } + +/*! Generate TS 23.003 Section 19.2 Home Network Realm/Domain (text form) + * \param out[out] caller-provided output buffer, at least 33 bytes long + * \param plmn[in] Osmocom representation of PLMN ID (MCC + MNC) + * \returns number of characters printed (excluding NUL); negative on error */ +int osmo_gen_home_network_domain(char *out, const struct osmo_plmn_id *plmn) +{ + if (plmn->mcc > 999) + return -EINVAL; + if (plmn->mnc > 999) + return -EINVAL; + return sprintf(out, "epc.mnc%03u.mcc%03u.3gppnetwork.org", plmn->mnc, plmn->mcc); +} + +/*! Parse a TS 23.003 Section 19.2 Home Network Realm/Domain (text form) into a \ref osmo_plmn_id + * \param out[out] caller-allocated output structure + * \param in[in] character string representation to be parsed + * \returns 0 on success; negative on error */ +int osmo_parse_home_network_domain(struct osmo_plmn_id *out, const char *in) +{ + int rc; + + memset(out, 0, sizeof(*out)); + rc = sscanf(in, "epc.mnc%03hu.mcc%03hu.3gppnetwork.org", &out->mnc, &out->mcc); + if (rc < 0) + return rc; + if (rc != 2) + return -EINVAL; + return 0; +} + +/*! Generate TS 23.003 Section 19.4.2.4 MME Domain (text form) + * \param out[out] caller-provided output buffer, at least 56 bytes long + * \param gummei[in] Structure representing the Globally Unique MME Identifier + * \returns number of characters printed (excluding NUL); negative on error */ +int osmo_gen_mme_domain(char *out, const struct osmo_gummei *gummei) +{ + char domain[GSM23003_HOME_NETWORK_DOMAIN_LEN+1]; + int rc; + rc = osmo_gen_home_network_domain(domain, &gummei->plmn); + if (rc < 0) + return rc; + return sprintf(out, "mmec%02x.mmegi%04x.mme.%s", gummei->mme.code, gummei->mme.group_id, domain); +} + +/*! Parse a TS 23.003 Section 19.4.2.4 MME Domain (text form) into a \ref osmo_gummei + * \param out[out] caller-allocated output GUMMEI structure + * \param in[in] character string representation to be parsed + * \returns 0 on success; negative on error */ +int osmo_parse_mme_domain(struct osmo_gummei *out, const char *in) +{ + int rc; + + memset(out, 0, sizeof(*out)); + rc = sscanf(in, "mmec%02hhx.mmegi%04hx.mme.epc.mnc%03hu.mcc%03hu.3gppnetwork.org", + &out->mme.code, &out->mme.group_id, + &out->plmn.mnc, &out->plmn.mcc); + if (rc < 0) + return rc; + if (rc != 4) + return -EINVAL; + return 0; +} + +/*! Generate TS 23.003 Section 19.4.2.4 MME Group Domain (text form) + * \param out[out] caller-provided output buffer, at least 56 bytes long + * \param mmegi[in] MME Group Identifier + * \param plmn[in] Osmocom representation of PLMN ID (MCC + MNC) + * \returns number of characters printed (excluding NUL); negative on error */ +int osmo_gen_mme_group_domain(char *out, uint16_t mmegi, const struct osmo_plmn_id *plmn) +{ + char domain[GSM23003_HOME_NETWORK_DOMAIN_LEN+1]; + int rc; + rc = osmo_gen_home_network_domain(domain, plmn); + if (rc < 0) + return rc; + return sprintf(out, "mmegi%04x.mme.%s", mmegi, domain); +} diff --git a/src/gsm/gsm29118.c b/src/gsm/gsm29118.c new file mode 100644 index 00000000..3898be64 --- /dev/null +++ b/src/gsm/gsm29118.c @@ -0,0 +1,158 @@ +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> + +#include <osmocom/gsm/protocol/gsm_29_118.h> + +const struct value_string sgsap_msg_type_names[] = { + { SGSAP_MSGT_PAGING_REQ, "PAGING-REQUEST" }, + { SGSAP_MSGT_PAGING_REJ, "PAGING-REJECT" }, + { SGSAP_MSGT_SERVICE_REQ, "SERVICE-REQUEST" }, + { SGSAP_MSGT_DL_UD, "DOWNLINK-UNITDATA" }, + { SGSAP_MSGT_UL_UD, "UPLINK-UNITDATA" }, + { SGSAP_MSGT_LOC_UPD_REQ, "LOCATION-UPDATE-REQUEST" }, + { SGSAP_MSGT_LOC_UPD_ACK, "LOCATION-UPDATE-ACCEPT" }, + { SGSAP_MSGT_LOC_UPD_REJ, "LOCATION-UPDATE-REJECT" }, + { SGSAP_MSGT_TMSI_REALL_CMPL, "TMSI-REALLOCATION-COMPLETE" }, + { SGSAP_MSGT_ALERT_REQ, "ALERT-REQQUEST" }, + { SGSAP_MSGT_ALERT_ACK, "ALERT-ACK" }, + { SGSAP_MSGT_ALERT_REJ, "ALERT-REJECT" }, + { SGSAP_MSGT_UE_ACT_IND, "UE-ACTIVITY-INDICATION" }, + { SGSAP_MSGT_EPS_DET_IND, "EPS-DETACH-INDICATION" }, + { SGSAP_MSGT_EPS_DET_ACK, "EPS-DETACH-ACK" }, + { SGSAP_MSGT_IMSI_DET_IND, "IMSI-DETACH-INDICATION" }, + { SGSAP_MSGT_IMSI_DET_ACK, "IMSI-DETACH-ACK" }, + { SGSAP_MSGT_RESET_IND, "RESET-INDICATION" }, + { SGSAP_MSGT_RESET_ACK, "RESET-ACK" }, + { SGSAP_MSGT_SERVICE_ABORT_REQ, "SERVICE-ABORT-REQUEST" }, + { SGSAP_MSGT_MO_CSFB_IND, "MO-CSFB-INDICATION" }, + { SGSAP_MSGT_MM_INFO_REQ, "MM-INFO-REQUEST" }, + { SGSAP_MSGT_RELEASE_REQ, "RELEASE-REQUEST" }, + { SGSAP_MSGT_STATUS, "STATUS" }, + { SGSAP_MSGT_UE_UNREACHABLE, "UE-UNREACHABLE" }, + { 0, NULL } +}; + +const struct value_string sgsap_iei_names[] = { + { SGSAP_IE_IMSI, "IMSI" }, + { SGSAP_IE_VLR_NAME, "VLR-NAME" }, + { SGSAP_IE_TMSI, "TMSI" }, + { SGSAP_IE_LAI, "LAI" }, + { SGSAP_IE_CHAN_NEEDED, "CHAN-NEEDED" }, + { SGSAP_IE_EMLPP_PRIORITY, "EMLPP-PRIORITY" }, + { SGSAP_IE_TMSI_STATUS, "TMSI-STATUS" }, + { SGSAP_IE_SGS_CAUSE, "SGS-CAUSE" }, + { SGSAP_IE_MME_NAME, "MME-NAME" }, + { SGSAP_IE_EPS_LU_TYPE, "EPS-LU-TYPE" }, + { SGSAP_IE_GLOBAL_CN_ID, "GLOBAL-CN-ID" }, + { SGSAP_IE_MOBILE_ID, "MOBILE-ID" }, + { SGSAP_IE_REJECT_CAUSE, "REJECT-CAUSE" }, + { SGSAP_IE_IMSI_DET_EPS_TYPE, "IMSI-DET-EPS-TYPE" }, + { SGSAP_IE_IMSI_DET_NONEPS_TYPE, "IMSI-DET-NONEPS-TYPE" }, + { SGSAP_IE_IMEISV, "IMEISV" }, + { SGSAP_IE_NAS_MSG_CONTAINER, "NAS-MSG-CONTAINER" }, + { SGSAP_IE_MM_INFO, "MM-INFO" }, + { SGSAP_IE_ERR_MSG, "ERR-MSG" }, + { SGSAP_IE_CLI, "CLI" }, + { SGSAP_IE_LCS_CLIENT_ID, "LCS-CLIENT-ID" }, + { SGSAP_IE_LCS_INDICATOR, "LCS-INDICATOR" }, + { SGSAP_IE_SS_CODE, "SS-CODE" }, + { SGSAP_IE_SERVICE_INDICATOR, "SERVICE-INDICATOR" }, + { SGSAP_IE_UE_TIMEZONE, "UE-TIMEZONE" }, + { SGSAP_IE_MS_CLASSMARK2, "MS-CLASSMARK2" }, + { SGSAP_IE_TAI, "TAI" }, + { SGSAP_IE_EUTRAN_CGI, "EUTRAN-CGI" }, + { SGSAP_IE_UE_EMM_MODE, "UE-EMM-MODE" }, + { SGSAP_IE_ADDL_PAGING_INDICATORS, "ADDL-PAGING-INDICATORS" }, + { SGSAP_IE_TMSI_BASED_NRI_CONT, "TMSI-BASED-NRI-CONT" }, + { 0, NULL } +}; + +const struct value_string sgsap_eps_lu_type_names[] = { + { SGSAP_EPS_LUT_IMSI_ATTACH, "IMSI Attach" }, + { SGSAP_EPS_LUT_NORMAL, "Normal" }, + { 0, NULL } +}; + +const struct value_string sgsap_ismi_det_eps_type_names[] = { + { SGSAP_ID_EPS_T_NETWORK_INITIATED, "Network initiated IMSI detach from EPS" }, + { SGSAP_ID_EPS_T_UE_INITIATED, "UE initiated IMSI detach from EPS" }, + { SGSAP_ID_EPS_T_EPS_NOT_ALLOWED, "EPS not allowed" }, + { 0, NULL } +}; + +const struct value_string sgsap_ismi_det_noneps_type_names[] = { + { SGSAP_ID_NONEPS_T_EXPLICIT_UE_NONEPS, + "Explicit UE initiated IMSI detach from non-EPS" }, + { SGSAP_ID_NONEPS_T_COMBINED_UE_EPS_NONEPS, + "Combined UE initiated IMSI detach from EPS and non-EPS" }, + { SGSAP_ID_NONEPS_T_IMPLICIT_UE_EPS_NONEPS, + "Implicit network initiated IMSI detach from EPS and non-EPS" }, + { 0, NULL } +}; + +const struct value_string sgsap_service_ind_names[] = { + { SGSAP_SERV_IND_CS_CALL, "CS Call" }, + { SGSAP_SERV_IND_SMS, "SMS" }, + { 0, NULL } +}; + +const struct value_string sgsap_sgs_cause_names[] = { + { SGSAP_SGS_CAUSE_IMSI_DET_EPS, "IMSI detached for EPS" }, + { SGSAP_SGS_CAUSE_IMSI_DET_EPS_NONEPS, "IMSI detached for EPS and non-EPS" }, + { SGSAP_SGS_CAUSE_IMSI_UNKNOWN, "IMSI unknown" }, + { SGSAP_SGS_CAUSE_IMSI_DET_NON_EPS, "IMSI detached for non-EPS" }, + { SGSAP_SGS_CAUSE_IMSI_IMPL_DET_NON_EPS,"IMSI implicitly detached for non-EPS" }, + { SGSAP_SGS_CAUSE_UE_UNREACHABLE, "UE unreachable" }, + { SGSAP_SGS_CAUSE_MSG_INCOMP_STATE, "Message not compatible with protocol state" }, + { SGSAP_SGS_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" }, + { SGSAP_SGS_CAUSE_INVALID_MAND_IE, "Invalid mandatory IE" }, + { SGSAP_SGS_CAUSE_COND_IE_ERROR, "Conditional IE error" }, + { SGSAP_SGS_CAUSE_SEMANT_INCORR_MSG, "Semantically incorrect message" }, + { SGSAP_SGS_CAUSE_MSG_UNKNOWN, "Message unknown" }, + { SGSAP_SGS_CAUSE_MT_CSFB_REJ_USER, "MT CSFB call rejected by user" }, + { SGSAP_SGS_CAUSE_UE_TEMP_UNREACHABLE, "UE temporarily unreachable" }, + { 0, NULL } +}; + + +const struct value_string sgsap_ue_emm_mode_names[] = { + { SGSAP_UE_EMM_MODE_IDLE, "EMM-IDLE" }, + { SGSAP_UE_EMM_MODE_CONNECTED, "EMM-CONNECTED" }, + { 0, NULL } +}; + +const struct tlv_definition sgsap_ie_tlvdef = { + .def = { + [SGSAP_IE_IMSI] = { TLV_TYPE_TLV }, + [SGSAP_IE_VLR_NAME] = { TLV_TYPE_TLV }, + [SGSAP_IE_TMSI] = { TLV_TYPE_TLV }, + [SGSAP_IE_LAI] = { TLV_TYPE_TLV }, + [SGSAP_IE_CHAN_NEEDED] = { TLV_TYPE_TLV }, + [SGSAP_IE_EMLPP_PRIORITY] = { TLV_TYPE_TLV }, + [SGSAP_IE_TMSI_STATUS] = { TLV_TYPE_TLV }, + [SGSAP_IE_SGS_CAUSE] = { TLV_TYPE_TLV }, + [SGSAP_IE_MME_NAME] = { TLV_TYPE_TLV }, + [SGSAP_IE_EPS_LU_TYPE] = { TLV_TYPE_TLV }, + [SGSAP_IE_GLOBAL_CN_ID] = { TLV_TYPE_TLV }, + [SGSAP_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [SGSAP_IE_REJECT_CAUSE] = { TLV_TYPE_TLV }, + [SGSAP_IE_IMSI_DET_EPS_TYPE] = { TLV_TYPE_TLV }, + [SGSAP_IE_IMSI_DET_NONEPS_TYPE] = { TLV_TYPE_TLV }, + [SGSAP_IE_IMEISV] = { TLV_TYPE_TLV }, + [SGSAP_IE_NAS_MSG_CONTAINER] = { TLV_TYPE_TLV }, + [SGSAP_IE_MM_INFO] = { TLV_TYPE_TLV }, + [SGSAP_IE_ERR_MSG] = { TLV_TYPE_TLV }, + [SGSAP_IE_CLI] = { TLV_TYPE_TLV }, + [SGSAP_IE_LCS_CLIENT_ID] = { TLV_TYPE_TLV }, + [SGSAP_IE_LCS_INDICATOR] = { TLV_TYPE_TLV }, + [SGSAP_IE_SS_CODE] = { TLV_TYPE_TLV }, + [SGSAP_IE_SERVICE_INDICATOR] = { TLV_TYPE_TLV }, + [SGSAP_IE_UE_TIMEZONE] = { TLV_TYPE_TLV }, + [SGSAP_IE_MS_CLASSMARK2] = { TLV_TYPE_TLV }, + [SGSAP_IE_TAI] = { TLV_TYPE_TLV }, + [SGSAP_IE_EUTRAN_CGI] = { TLV_TYPE_TLV }, + [SGSAP_IE_UE_EMM_MODE] = { TLV_TYPE_TLV }, + [SGSAP_IE_ADDL_PAGING_INDICATORS]={ TLV_TYPE_TLV }, + [SGSAP_IE_TMSI_BASED_NRI_CONT] = { TLV_TYPE_TLV }, + }, +}; diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c index 136b9375..ae9a1cf6 100644 --- a/src/gsm/gsm48.c +++ b/src/gsm/gsm48.c @@ -604,6 +604,7 @@ int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi) /*! Generate TS 24.008 §10.5.1.4 Mobile ID * \param[out] buf Caller-provided output buffer * \param[in] id Identity to be encoded + * \param[in] mi_type Type of identity (e.g. GSM_MI_TYPE_TMSI) * \returns number of bytes used in \a buf */ uint8_t gsm48_generate_mid(uint8_t *buf, const char *id, uint8_t mi_type) { @@ -945,6 +946,60 @@ const struct value_string gsm48_cc_msgtype_names[] = { { 0, NULL } }; +/*! TS 04.08 10.5..4.11 Call Control Cause Values */ +const struct value_string gsm48_cc_cause_names[] = { + { GSM48_CC_CAUSE_UNASSIGNED_NR, "UNASSIGNED_NR" }, + { GSM48_CC_CAUSE_NO_ROUTE, "NO_ROUTE" }, + { GSM48_CC_CAUSE_CHAN_UNACCEPT, "CHAN_UNACCEPT" }, + { GSM48_CC_CAUSE_OP_DET_BARRING, "OP_DET_BARRING" }, + { GSM48_CC_CAUSE_NORM_CALL_CLEAR, "NORM_CALL_CLEAR" }, + { GSM48_CC_CAUSE_USER_BUSY, "USER_BUSY" }, + { GSM48_CC_CAUSE_USER_NOTRESPOND, "USER_NOTRESPOND" }, + { GSM48_CC_CAUSE_USER_ALERTING_NA, "USER_ALERTING_NA" }, + { GSM48_CC_CAUSE_CALL_REJECTED, "CALL_REJECTED" }, + { GSM48_CC_CAUSE_NUMBER_CHANGED, "NUMBER_CHANGED" }, + { GSM48_CC_CAUSE_PRE_EMPTION, "PRE_EMPTION" }, + { GSM48_CC_CAUSE_NONSE_USER_CLR, "NONSE_USER_CLR" }, + { GSM48_CC_CAUSE_DEST_OOO, "DEST_OOO" }, + { GSM48_CC_CAUSE_INV_NR_FORMAT, "INV_NR_FORMAT" }, + { GSM48_CC_CAUSE_FACILITY_REJ, "FACILITY_REJ" }, + { GSM48_CC_CAUSE_RESP_STATUS_INQ, "RESP_STATUS_INQ" }, + { GSM48_CC_CAUSE_NORMAL_UNSPEC, "NORMAL_UNSPEC" }, + { GSM48_CC_CAUSE_NO_CIRCUIT_CHAN, "NO_CIRCUIT_CHAN" }, + { GSM48_CC_CAUSE_NETWORK_OOO, "NETWORK_OOO" }, + { GSM48_CC_CAUSE_TEMP_FAILURE, "TEMP_FAILURE" }, + { GSM48_CC_CAUSE_SWITCH_CONG, "SWITCH_CONG" }, + { GSM48_CC_CAUSE_ACC_INF_DISCARD, "ACC_INF_DISCARD" }, + { GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL, "REQ_CHAN_UNAVAIL" }, + { GSM48_CC_CAUSE_RESOURCE_UNAVAIL, "RESOURCE_UNAVAIL" }, + { GSM48_CC_CAUSE_QOS_UNAVAIL, "QOS_UNAVAIL" }, + { GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC, "REQ_FAC_NOT_SUBSC" }, + { GSM48_CC_CAUSE_INC_BARRED_CUG, "INC_BARRED_CUG" }, + { GSM48_CC_CAUSE_BEARER_CAP_UNAUTH, "BEARER_CAP_UNAUTH" }, + { GSM48_CC_CAUSE_BEARER_CA_UNAVAIL, "BEARER_CA_UNAVAIL" }, + { GSM48_CC_CAUSE_SERV_OPT_UNAVAIL, "SERV_OPT_UNAVAIL" }, + { GSM48_CC_CAUSE_BEARERSERV_UNIMPL, "BEARERSERV_UNIMPL" }, + { GSM48_CC_CAUSE_ACM_GE_ACM_MAX, "ACM_GE_ACM_MAX" }, + { GSM48_CC_CAUSE_REQ_FAC_NOTIMPL, "REQ_FAC_NOTIMPL" }, + { GSM48_CC_CAUSE_RESTR_BCAP_AVAIL, "RESTR_BCAP_AVAIL" }, + { GSM48_CC_CAUSE_SERV_OPT_UNIMPL, "SERV_OPT_UNIMPL" }, + { GSM48_CC_CAUSE_INVAL_TRANS_ID, "INVAL_TRANS_ID" }, + { GSM48_CC_CAUSE_USER_NOT_IN_CUG, "USER_NOT_IN_CUG" }, + { GSM48_CC_CAUSE_INCOMPAT_DEST, "INCOMPAT_DEST" }, + { GSM48_CC_CAUSE_INVAL_TRANS_NET, "INVAL_TRANS_NET" }, + { GSM48_CC_CAUSE_SEMANTIC_INCORR, "SEMANTIC_INCORR" }, + { GSM48_CC_CAUSE_INVAL_MAND_INF, "INVAL_MAND_INF" }, + { GSM48_CC_CAUSE_MSGTYPE_NOTEXIST, "MSGTYPE_NOTEXIST" }, + { GSM48_CC_CAUSE_MSGTYPE_INCOMPAT, "MSGTYPE_INCOMPAT" }, + { GSM48_CC_CAUSE_IE_NOTEXIST, "IE_NOTEXIST" }, + { GSM48_CC_CAUSE_COND_IE_ERR, "COND_IE_ERR" }, + { GSM48_CC_CAUSE_MSG_INCOMP_STATE, "MSG_INCOMP_STATE" }, + { GSM48_CC_CAUSE_RECOVERY_TIMER, "RECOVERY_TIMER" }, + { GSM48_CC_CAUSE_PROTO_ERR, "PROTO_ERR" }, + { GSM48_CC_CAUSE_INTERWORKING, "INTERWORKING" }, + { 0 , NULL } +}; + /*! TS 04.80, section 3.4 Messages for supplementary services control */ const struct value_string gsm48_nc_ss_msgtype_names[] = { OSMO_VALUE_STRING(GSM0480_MTYPE_RELEASE_COMPLETE), diff --git a/src/gsm/gsm48_ie.c b/src/gsm/gsm48_ie.c index 1baf2879..ffe3ebae 100644 --- a/src/gsm/gsm48_ie.c +++ b/src/gsm/gsm48_ie.c @@ -807,7 +807,7 @@ static int32_t smod(int32_t n, int32_t m) } /*! Decode TS 04.08 Cell Channel Description IE (10.5.2.1b) and other frequency lists - * \param[out] f Caller-provided output memory + * \param[out] f Caller-provided output memory, an array of 1024 elements * \param[in] cd Cell Channel Description IE * \param[in] len Length of \a cd in bytes * \returns 0 on success; negative on error */ diff --git a/src/gsm/gsm_utils.c b/src/gsm/gsm_utils.c index 7e6c7947..8b4b5586 100644 --- a/src/gsm/gsm_utils.c +++ b/src/gsm/gsm_utils.c @@ -706,36 +706,61 @@ enum gsm_band gsm_band_parse(const char* mhz) } } -/*! Resolve GSM band from ARFCN +/*! Resolve GSM band from ARFCN. * In Osmocom, we use the highest bit of the \a arfcn to indicate PCS * \param[in] arfcn Osmocom ARFCN, highest bit determines PCS mode - * \returns GSM Band */ -enum gsm_band gsm_arfcn2band(uint16_t arfcn) + * \param[out] band GSM Band containing \arfcn if arfcn is valid, undetermined otherwise + * \returns 0 if arfcn is valid and \a band was set, negative on error */ +int gsm_arfcn2band_rc(uint16_t arfcn, enum gsm_band *band) { int is_pcs = arfcn & ARFCN_PCS; arfcn &= ~ARFCN_FLAG_MASK; - if (is_pcs) - return GSM_BAND_1900; - else if (arfcn <= 124) - return GSM_BAND_900; - else if (arfcn >= 955 && arfcn <= 1023) - return GSM_BAND_900; - else if (arfcn >= 128 && arfcn <= 251) - return GSM_BAND_850; - else if (arfcn >= 512 && arfcn <= 885) - return GSM_BAND_1800; - else if (arfcn >= 259 && arfcn <= 293) - return GSM_BAND_450; - else if (arfcn >= 306 && arfcn <= 340) - return GSM_BAND_480; - else if (arfcn >= 350 && arfcn <= 425) - return GSM_BAND_810; - else if (arfcn >= 438 && arfcn <= 511) - return GSM_BAND_750; - else - return GSM_BAND_1800; + if (is_pcs) { + *band = GSM_BAND_1900; + return 0; + } else if (arfcn <= 124) { + *band = GSM_BAND_900; + return 0; + } else if (arfcn >= 955 && arfcn <= 1023) { + *band = GSM_BAND_900; + return 0; + } else if (arfcn >= 128 && arfcn <= 251) { + *band = GSM_BAND_850; + return 0; + } else if (arfcn >= 512 && arfcn <= 885) { + *band = GSM_BAND_1800; + return 0; + } else if (arfcn >= 259 && arfcn <= 293) { + *band = GSM_BAND_450; + return 0; + } else if (arfcn >= 306 && arfcn <= 340) { + *band = GSM_BAND_480; + return 0; + } else if (arfcn >= 350 && arfcn <= 425) { + *band = GSM_BAND_810; + return 0; + } else if (arfcn >= 438 && arfcn <= 511) { + *band = GSM_BAND_750; + return 0; + } + return -1; +} + +/*! Resolve GSM band from ARFCN, aborts process on invalid ARFCN. + * In Osmocom, we use the highest bit of the \a arfcn to indicate PCS. + * DEPRECATED: Use gsm_arfcn2band_rc instead. + * \param[in] arfcn Osmocom ARFCN, highest bit determines PCS mode + * \returns GSM Band if ARFCN is valid (part of any valid band), aborts otherwise */ +enum gsm_band gsm_arfcn2band(uint16_t arfcn) +{ + enum gsm_band band; + if (gsm_arfcn2band_rc(arfcn, &band) == 0) + return band; + + osmo_panic("%s:%d Invalid arfcn %" PRIu16 " passed to gsm_arfcn2band\n", + __FILE__, __LINE__, arfcn); } struct gsm_freq_range { diff --git a/src/gsm/gsup.c b/src/gsm/gsup.c index 9c2f8175..18b35806 100644 --- a/src/gsm/gsup.c +++ b/src/gsm/gsup.c @@ -535,7 +535,7 @@ int osmo_gsup_encode(struct msgb *msg, const struct osmo_gsup_message *gsup_msg) /* generic part */ if(!gsup_msg->message_type) - return -ENOMEM; + return -EINVAL; msgb_v_put(msg, gsup_msg->message_type); diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index 1da398c1..3fe9dfcf 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -148,6 +148,7 @@ gsm0808_att_tlvdef; gsm0808_bssap_name; gsm0808_bssmap_name; gsm0808_cause_name; +gsm0808_cause_class_name; gsm0808_create_ass; gsm0808_create_assignment_completed; gsm0808_create_ass_compl; @@ -156,6 +157,9 @@ gsm0808_create_ass_fail; gsm0808_create_cipher; gsm0808_create_cipher_complete; gsm0808_create_cipher_reject; +gsm0808_create_cipher_reject_ext; +gsm0808_get_cipher_reject_cause; +gsm0808_create_classmark_request; gsm0808_create_classmark_update; gsm0808_create_clear_command; gsm0808_create_clear_complete; @@ -204,6 +208,8 @@ gsm0808_cell_id_discr_names; gsm0808_cell_id_u_name; gsm0808_chan_type_to_speech_codec; gsm0808_speech_codec_from_chan_type; +gsm0808_sc_cfg_from_gsm48_mr_cfg; +gsm48_mr_cfg_from_gsm0808_sc_cfg; gsm0808_speech_codec_type_names; gsm0808_permitted_speech_names; gsm0808_chosen_enc_alg_names; @@ -312,15 +318,22 @@ osmo_lai_name; osmo_rai_name; osmo_cgi_name; osmo_cgi_name2; +osmo_gummei_name; osmo_mnc_from_str; osmo_mnc_cmp; osmo_plmn_cmp; +osmo_gen_home_network_domain; +osmo_parse_home_network_domain; +osmo_gen_mme_domain; +osmo_parse_mme_domain; +osmo_gen_mme_group_domain; gsm48_chan_mode_names; gsm_chan_t_names; gsm48_pdisc_names; gsm48_rr_msgtype_names; gsm48_mm_msgtype_names; gsm48_cc_msgtype_names; +gsm48_cc_cause_names; gsm48_pdisc_msgtype_name; gsm48_reject_value_names; @@ -336,6 +349,7 @@ gsm_7bit_decode_n_hdr; gsm_7bit_encode_n; gsm_7bit_encode_n_ussd; +gsm_arfcn2band_rc; gsm_arfcn2band; gsm_arfcn2freq10; gsm_freq102arfcn; @@ -501,5 +515,15 @@ osmo_oap_client_handle; osmo_oap_client_init; osmo_oap_client_register; +sgsap_msg_type_names; +sgsap_iei_names; +sgsap_eps_lu_type_names; +sgsap_ismi_det_eps_type_names; +sgsap_ismi_det_noneps_type_names; +sgsap_service_ind_names; +sgsap_sgs_cause_names; +sgsap_ue_emm_mode_names; +sgsap_ie_tlvdef; + local: *; }; diff --git a/src/logging.c b/src/logging.c index de0f2b0f..e7cc4729 100644 --- a/src/logging.c +++ b/src/logging.c @@ -35,7 +35,6 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <ctype.h> #ifdef HAVE_STRINGS_H #include <strings.h> @@ -61,13 +60,10 @@ osmo_static_assert(_LOG_FLT_COUNT <= 8*sizeof(((struct log_target*)NULL)->filter struct log_info *osmo_log_info; static struct log_context log_context; -static void *tall_log_ctx = NULL; +void *tall_log_ctx = NULL; LLIST_HEAD(osmo_log_target_list); -#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/ - -static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = { - { 0, "EVERYTHING" }, +const struct value_string loglevel_strs[] = { { LOGL_DEBUG, "DEBUG" }, { LOGL_INFO, "INFO" }, { LOGL_NOTICE, "NOTICE" }, @@ -175,19 +171,7 @@ static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = { }, }; -/*! descriptive string for each log level */ -/* You have to keep this in sync with the structure loglevel_strs. */ -static const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = { - "Don't use. It doesn't log anything", - "Log debug messages and higher levels", - "Log informational messages and higher levels", - "Log noticeable messages and higher levels", - "Log error messages and higher levels", - "Log only fatal messages", - NULL, -}; - -static void assert_loginfo(const char *src) +void assert_loginfo(const char *src) { if (!osmo_log_info) { fprintf(stderr, "ERROR: osmo_log_info == NULL! " @@ -963,149 +947,6 @@ int log_targets_reopen(void) return rc; } -/*! Generates the logging command string for VTY - * \param[in] unused_info Deprecated parameter, no longer used! - * \returns vty command string for use by VTY command node - */ -const char *log_vty_command_string() -{ - struct log_info *info = osmo_log_info; - int len = 0, offset = 0, ret, i, rem; - int size = strlen("logging level (all|) ()") + 1; - char *str; - - assert_loginfo(__func__); - - for (i = 0; i < info->num_cat; i++) { - if (info->cat[i].name == NULL) - continue; - size += strlen(info->cat[i].name) + 1; - } - - for (i = 0; i < LOGLEVEL_DEFS; i++) - size += strlen(loglevel_strs[i].str) + 1; - - rem = size; - str = talloc_zero_size(tall_log_ctx, size); - if (!str) - return NULL; - - ret = snprintf(str + offset, rem, "logging level (all|"); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - for (i = 0; i < info->num_cat; i++) { - if (info->cat[i].name) { - int j, name_len = strlen(info->cat[i].name)+1; - char name[name_len]; - - for (j = 0; j < name_len; j++) - name[j] = tolower((unsigned char)info->cat[i].name[j]); - - name[name_len-1] = '\0'; - ret = snprintf(str + offset, rem, "%s|", name+1); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - } - offset--; /* to remove the trailing | */ - rem++; - - ret = snprintf(str + offset, rem, ") ("); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - for (i = 0; i < LOGLEVEL_DEFS; i++) { - int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1; - char loglevel_str[loglevel_str_len]; - - for (j = 0; j < loglevel_str_len; j++) - loglevel_str[j] = tolower((unsigned char)loglevel_strs[i].str[j]); - - loglevel_str[loglevel_str_len-1] = '\0'; - ret = snprintf(str + offset, rem, "%s|", loglevel_str); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - offset--; /* to remove the trailing | */ - rem++; - - ret = snprintf(str + offset, rem, ")"); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); -err: - str[size-1] = '\0'; - return str; -} - -/*! Generates the logging command description for VTY - * \param[in] unused_info Deprecated parameter, no longer used! - * \returns logging command description for use by VTY command node - */ -const char *log_vty_command_description() -{ - struct log_info *info = osmo_log_info; - char *str; - int i, ret, len = 0, offset = 0, rem; - unsigned int size = - strlen(LOGGING_STR - "Set the log level for a specified category\n") + 1; - - assert_loginfo(__func__); - - for (i = 0; i < info->num_cat; i++) { - if (info->cat[i].name == NULL) - continue; - size += strlen(info->cat[i].description) + 1; - } - - for (i = 0; i < LOGLEVEL_DEFS; i++) - size += strlen(loglevel_descriptions[i]) + 1; - - size += strlen("Global setting for all subsystems") + 1; - rem = size; - str = talloc_zero_size(tall_log_ctx, size); - if (!str) - return NULL; - - ret = snprintf(str + offset, rem, LOGGING_STR - "Set the log level for a specified category\n"); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - ret = snprintf(str + offset, rem, - "Global setting for all subsystems\n"); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - - for (i = 0; i < info->num_cat; i++) { - if (info->cat[i].name == NULL) - continue; - ret = snprintf(str + offset, rem, "%s\n", - info->cat[i].description); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } - for (i = 0; i < LOGLEVEL_DEFS; i++) { - ret = snprintf(str + offset, rem, "%s\n", - loglevel_descriptions[i]); - if (ret < 0) - goto err; - OSMO_SNPRINTF_RET(ret, rem, offset, len); - } -err: - str[size-1] = '\0'; - return str; -} - /*! Initialize the Osmocom logging core * \param[in] inf Information regarding logging categories * \param[in] ctx \ref talloc context for logging allocations diff --git a/src/select.c b/src/select.c index 0b115c61..b594ca55 100644 --- a/src/select.c +++ b/src/select.c @@ -324,11 +324,18 @@ int osmo_timerfd_setup(struct osmo_fd *ofd, int (*cb)(struct osmo_fd *, unsigned ofd->when = BSC_FD_READ; if (ofd->fd < 0) { + int rc; + ofd->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); if (ofd->fd < 0) return ofd->fd; - osmo_fd_register(ofd); + rc = osmo_fd_register(ofd); + if (rc < 0) { + close(ofd->fd); + ofd->fd = -1; + return rc; + } } return 0; } diff --git a/src/socket.c b/src/socket.c index 6f56efb5..0e17a28d 100644 --- a/src/socket.c +++ b/src/socket.c @@ -57,7 +57,7 @@ static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t const char *host, uint16_t port, bool passive) { struct addrinfo hints, *result; - char portbuf[16]; + char portbuf[6]; int rc; snprintf(portbuf, sizeof(portbuf), "%u", port); @@ -605,29 +605,27 @@ int osmo_sock_unix_init(uint16_t type, uint8_t proto, struct sockaddr_un local; int sfd, rc, on = 1; unsigned int namelen; - const size_t socket_path_len = strlen(socket_path); if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) return -EINVAL; local.sun_family = AF_UNIX; - if (socket_path_len == sizeof(local.sun_path)) { - /* Handle corner-case where sun_path is not NUL-terminated. See the unix(7) man page. */ - memcpy(local.sun_path, socket_path, sizeof(local.sun_path)); - } else if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) { + /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */ + if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) { LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n", sizeof(local.sun_path), socket_path); return -ENOSPC; } #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = socket_path_len; + local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else - namelen = socket_path_len + offsetof(struct sockaddr_un, sun_path); + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); #endif sfd = socket(AF_UNIX, type, proto); @@ -684,44 +682,105 @@ int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags)); } -/*! Get address/port information on soocket in dyn-alloc string - * \param[in] ctx talloc context from which to allocate string buffer +/*! Get the IP and/or port number on socket. This is for internal usage. + * Convenience wrappers: osmo_sock_get_local_ip(), + * osmo_sock_get_local_ip_port(), osmo_sock_get_remote_ip(), + * osmo_sock_get_remote_ip_port() and osmo_sock_get_name() * \param[in] fd file descriptor of socket - * \returns string identifying the connection of this socket + * \param[out] ip IP address (will be filled in when not NULL) + * \param[in] ip_len length of the ip buffer + * \param[out] port number (will be filled in when not NULL) + * \param[in] port_len length of the port buffer + * \param[in] local (true) or remote (false) name will get looked at + * \returns 0 on success; negative otherwise */ -char *osmo_sock_get_name(void *ctx, int fd) +static int osmo_sock_get_name2(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local) { - struct sockaddr sa_l, sa_r; - socklen_t sa_len_l = sizeof(sa_l); - socklen_t sa_len_r = sizeof(sa_r); - char hostbuf_l[64], hostbuf_r[64]; - char portbuf_l[16], portbuf_r[16]; + struct sockaddr sa; + socklen_t len = sizeof(sa); + char ipbuf[INET6_ADDRSTRLEN], portbuf[6]; int rc; - rc = getsockname(fd, &sa_l, &sa_len_l); + rc = local ? getsockname(fd, &sa, &len) : getpeername(fd, &sa, &len); if (rc < 0) - return NULL; + return rc; - rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l), - portbuf_l, sizeof(portbuf_l), + rc = getnameinfo(&sa, len, ipbuf, sizeof(ipbuf), + portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc < 0) - return NULL; + return rc; - rc = getpeername(fd, &sa_r, &sa_len_r); - if (rc < 0) - goto local_only; + if (ip) + strncpy(ip, ipbuf, ip_len); + if (port) + strncpy(port, portbuf, port_len); + return 0; +} - rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r), - portbuf_r, sizeof(portbuf_r), - NI_NUMERICHOST | NI_NUMERICSERV); - if (rc < 0) - goto local_only; +/*! Get local IP address on socket + * \param[in] fd file descriptor of socket + * \param[out] ip IP address (will be filled in) + * \param[in] len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_local_ip(int fd, char *ip, size_t len) +{ + return osmo_sock_get_name2(fd, ip, len, NULL, 0, true); +} + +/*! Get local port on socket + * \param[in] fd file descriptor of socket + * \param[out] port number (will be filled in) + * \param[in] len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_local_ip_port(int fd, char *port, size_t len) +{ + return osmo_sock_get_name2(fd, NULL, 0, port, len, true); +} + +/*! Get remote IP address on socket + * \param[in] fd file descriptor of socket + * \param[out] ip IP address (will be filled in) + * \param[in] len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_remote_ip(int fd, char *ip, size_t len) +{ + return osmo_sock_get_name2(fd, ip, len, NULL, 0, false); +} + +/*! Get remote port on socket + * \param[in] fd file descriptor of socket + * \param[out] port number (will be filled in) + * \param[in] len length of the output buffer + * \returns 0 on success; negative otherwise + */ +int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len) +{ + return osmo_sock_get_name2(fd, NULL, 0, port, len, false); +} + +/*! Get address/port information on socket in dyn-alloc string + * \param[in] ctx talloc context from which to allocate string buffer + * \param[in] fd file descriptor of socket + * \returns string identifying the connection of this socket + */ +char *osmo_sock_get_name(void *ctx, int fd) +{ + char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN]; + char portbuf_l[6], portbuf_r[6]; + + /* get local */ + if (osmo_sock_get_name2(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true)) + return NULL; - return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r, - hostbuf_l, portbuf_l); + /* get remote */ + if (!osmo_sock_get_name2(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false)) + return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l); -local_only: + /* local only: different format */ return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l); } diff --git a/src/stats_statsd.c b/src/stats_statsd.c index 5ae25702..c3f739e2 100644 --- a/src/stats_statsd.c +++ b/src/stats_statsd.c @@ -184,20 +184,17 @@ static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int64_t value) { - const char *unit = desc->unit; - - if (unit == OSMO_STAT_ITEM_NO_UNIT) { - unit = "g"; - if (value < 0) - osmo_stats_reporter_statsd_send(srep, + if (value < 0) { + return osmo_stats_reporter_statsd_send(srep, statg->desc->group_name_prefix, statg->idx, - desc->name, 0, unit); + desc->name, 0, "g"); + } else { + return osmo_stats_reporter_statsd_send(srep, + statg->desc->group_name_prefix, + statg->idx, + desc->name, value, "g"); } - return osmo_stats_reporter_statsd_send(srep, - statg->desc->group_name_prefix, - statg->idx, - desc->name, value, unit); } #endif /* !EMBEDDED */ diff --git a/src/utils.c b/src/utils.c index ea0bbde0..e6adcf86 100644 --- a/src/utils.c +++ b/src/utils.c @@ -557,8 +557,8 @@ const char *osmo_escape_str(const char *str, int in_len) /*! Like osmo_escape_str(), but returns double-quotes around a string, or "NULL" for a NULL string. * This allows passing any char* value and get its C representation as string. * \param[in] str A string that may contain any characters. - * \param[in] len Pass -1 to print until nul char, or >= 0 to force a length. - * \returns buf containing an escaped representation, possibly truncated, or str itself. + * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns buf containing a quoted and escaped representation, possibly truncated. */ const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bufsize) { @@ -587,6 +587,12 @@ const char *osmo_quote_str_buf(const char *str, int in_len, char *buf, size_t bu return buf; } +/*! Like osmo_quote_str_buf() but returns the result in a static buffer. + * The static buffer is shared with get_value_string() and osmo_escape_str(). + * \param[in] str A string that may contain any characters. + * \param[in] in_len Pass -1 to print until nul char, or >= 0 to force a length. + * \returns static buffer containing a quoted and escaped representation, possibly truncated. + */ const char *osmo_quote_str(const char *str, int in_len) { return osmo_quote_str_buf(str, in_len, namebuf, sizeof(namebuf)); @@ -632,4 +638,90 @@ uint32_t osmo_isqrt32(uint32_t x) return g0; } +/*! Convert a string to lowercase, while checking buffer size boundaries. + * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. + * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters + * length as well as nul terminated. + * Note: similar osmo_str2lower(), but safe to use for src strings of arbitrary length. + * \param[out] dest Target buffer to write lowercase string. + * \param[in] dest_len Maximum buffer size of dest (e.g. sizeof(dest)). + * \param[in] src String to convert to lowercase. + * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. + */ +size_t osmo_str_tolower_buf(char *dest, size_t dest_len, const char *src) +{ + size_t rc; + if (dest == src) { + if (dest_len < 1) + return 0; + dest[dest_len - 1] = '\0'; + rc = strlen(dest); + } else { + if (dest_len < 1) + return strlen(src); + rc = osmo_strlcpy(dest, src, dest_len); + } + for (; *dest; dest++) + *dest = tolower(*dest); + return rc; +} + +/*! Convert a string to lowercase, using a static buffer. + * The resulting string may be truncated if the internally used static buffer is shorter than src. + * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a + * terminating nul. + * See also osmo_str_tolower_buf(). + * \param[in] src String to convert to lowercase. + * \returns Resulting lowercase string in a static buffer, always nul terminated. + */ +const char *osmo_str_tolower(const char *src) +{ + static char buf[128]; + osmo_str_tolower_buf(buf, sizeof(buf), src); + return buf; +} + +/*! Convert a string to uppercase, while checking buffer size boundaries. + * The result written to \a dest is guaranteed to be nul terminated if \a dest_len > 0. + * If dest == src, the string is converted in-place, if necessary truncated at dest_len - 1 characters + * length as well as nul terminated. + * Note: similar osmo_str2upper(), but safe to use for src strings of arbitrary length. + * \param[out] dest Target buffer to write uppercase string. + * \param[in] dest_len Maximum buffer size of dest (e.g. sizeof(dest)). + * \param[in] src String to convert to uppercase. + * \returns Length of \a src, like osmo_strlcpy(), but if \a dest == \a src at most \a dest_len - 1. + */ +size_t osmo_str_toupper_buf(char *dest, size_t dest_len, const char *src) +{ + size_t rc; + if (dest == src) { + if (dest_len < 1) + return 0; + dest[dest_len - 1] = '\0'; + rc = strlen(dest); + } else { + if (dest_len < 1) + return strlen(src); + rc = osmo_strlcpy(dest, src, dest_len); + } + for (; *dest; dest++) + *dest = toupper(*dest); + return rc; +} + +/*! Convert a string to uppercase, using a static buffer. + * The resulting string may be truncated if the internally used static buffer is shorter than src. + * The internal buffer is at least 128 bytes long, i.e. guaranteed to hold at least 127 characters and a + * terminating nul. + * See also osmo_str_toupper_buf(). + * \param[in] src String to convert to uppercase. + * \returns Resulting uppercase string in a static buffer, always nul terminated. + */ +const char *osmo_str_toupper(const char *src) +{ + static char buf[128]; + osmo_str_toupper_buf(buf, sizeof(buf), src); + return buf; +} + /*! @} */ diff --git a/src/vty/command.c b/src/vty/command.c index 43f974ce..a540cd5b 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -679,7 +679,7 @@ static int vty_dump_nodes(struct vty *vty) elem = vector_slot(cnode->cmd_vector, j); if (!vty_command_is_common(elem)) continue; - if (!elem->attr & CMD_ATTR_DEPRECATED) + if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) vty_dump_element(elem, vty); } } @@ -717,7 +717,7 @@ static int vty_dump_nodes(struct vty *vty) elem = vector_slot(cnode->cmd_vector, j); if (vty_command_is_common(elem)) continue; - if (!elem->attr & CMD_ATTR_DEPRECATED) + if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) vty_dump_element(elem, vty); } @@ -2728,8 +2728,7 @@ gDEFUN(config_list, config_list_cmd, "list", "Print command list\n") for (i = 0; i < vector_active(cnode->cmd_vector); i++) if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL - && !(cmd->attr == CMD_ATTR_DEPRECATED - || cmd->attr == CMD_ATTR_HIDDEN)) + && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); return CMD_SUCCESS; } diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 8c8a3326..f3e1419c 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -28,6 +28,7 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h> +#include <osmocom/core/logging_internal.h> #include <osmocom/core/utils.h> #include <osmocom/core/strrb.h> #include <osmocom/core/loggingrb.h> @@ -40,6 +41,25 @@ #include <osmocom/vty/logging.h> #define LOG_STR "Configure logging sub-system\n" +#define LEVEL_STR "Set the log level for a specified category\n" + +#define CATEGORY_ALL_STR "Deprecated alias for 'force-all'\n" +#define FORCE_ALL_STR \ + "Globally force all logging categories to a specific level. This is released by the" \ + " 'no logging level force-all' command. Note: any 'logging level <category> <level>'" \ + " commands will have no visible effect after this, until the forced level is released.\n" +#define NO_FORCE_ALL_STR \ + "Release any globally forced log level set with 'logging level force-all <level>'\n" + +#define LOG_LEVEL_ARGS "(debug|info|notice|error|fatal)" +#define LOG_LEVEL_STRS \ + "Log debug messages and higher levels\n" \ + "Log informational messages and higher levels\n" \ + "Log noticeable messages and higher levels\n" \ + "Log error messages and higher levels\n" \ + "Log only fatal messages\n" + +#define EVERYTHING_STR "Deprecated alias for 'no logging level force-all'\n" /*! \file logging_vty.c * Configuration of logging from VTY @@ -58,8 +78,6 @@ * */ -extern const struct log_info *osmo_log_info; - static void _vty_output(struct log_target *tgt, unsigned int level, const char *line) { @@ -268,6 +286,46 @@ DEFUN(logging_prnt_file, return CMD_SUCCESS; } +static void add_category_strings(char **cmd_str_p, char **doc_str_p, + const struct log_info *categories) +{ + int i; + for (i = 0; i < categories->num_cat; i++) { + if (categories->cat[i].name == NULL) + continue; + /* skip the leading 'D' in each category name, hence '+ 1' */ + osmo_talloc_asprintf(tall_log_ctx, *cmd_str_p, "%s%s", + i ? "|" : "", + osmo_str_tolower(categories->cat[i].name + 1)); + osmo_talloc_asprintf(tall_log_ctx, *doc_str_p, "%s\n", + categories->cat[i].description); + } +} + +static void gen_logging_level_cmd_strs(struct cmd_element *cmd, + const char *level_args, const char *level_strs) +{ + char *cmd_str = NULL; + char *doc_str = NULL; + + assert_loginfo(__func__); + + OSMO_ASSERT(cmd->string == NULL); + OSMO_ASSERT(cmd->doc == NULL); + + osmo_talloc_asprintf(tall_log_ctx, cmd_str, "logging level ("); + osmo_talloc_asprintf(tall_log_ctx, doc_str, + LOGGING_STR + LEVEL_STR); + add_category_strings(&cmd_str, &doc_str, osmo_log_info); + osmo_talloc_asprintf(tall_log_ctx, cmd_str, ") %s", level_args); + osmo_talloc_asprintf(tall_log_ctx, doc_str, "%s", level_strs); + + cmd->string = cmd_str; + cmd->doc = doc_str; +} + +/* logging level (<categories>) (debug|...|fatal) */ DEFUN(logging_level, logging_level_cmd, NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */ @@ -285,17 +343,6 @@ DEFUN(logging_level, return CMD_WARNING; } - if (strcmp(argv[1], "everything") == 0) { /* FIXME: remove this check once 'everything' is phased out */ - vty_out(vty, "%% Ignoring deprecated logging level %s%s", argv[1], VTY_NEWLINE); - return CMD_SUCCESS; - } - - /* Check for special case where we want to set global log level */ - if (!strcmp(argv[0], "all")) { - log_set_log_level(tgt, level); - return CMD_SUCCESS; - } - if (category < 0) { vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; @@ -307,6 +354,74 @@ DEFUN(logging_level, return CMD_SUCCESS; } +DEFUN(logging_level_set_all, logging_level_set_all_cmd, + "logging level set-all " LOG_LEVEL_ARGS, + LOGGING_STR LEVEL_STR + "Once-off set all categories to the given log level. There is no single command" + " to take back these changes -- each category is set to the given level, period.\n" + LOG_LEVEL_STRS) +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + int level = log_parse_level(argv[0]); + int i; + + if (!tgt) + return CMD_WARNING; + + for (i = 0; i < osmo_log_info->num_cat; i++) { + struct log_category *cat = &tgt->categories[i]; + /* skip empty entries in the array */ + if (!osmo_log_info->cat[i].name) + continue; + + cat->enabled = 1; + cat->loglevel = level; + } + return CMD_SUCCESS; +} + +/* logging level (<categories>) everything */ +DEFUN_DEPRECATED(deprecated_logging_level_everything, deprecated_logging_level_everything_cmd, + NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */ + NULL) /* same thing for helpstr. */ +{ + vty_out(vty, "%% Ignoring deprecated logging level 'everything' keyword%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(logging_level_force_all, logging_level_force_all_cmd, + "logging level force-all " LOG_LEVEL_ARGS, + LOGGING_STR LEVEL_STR FORCE_ALL_STR LOG_LEVEL_STRS) +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + int level = log_parse_level(argv[0]); + if (!tgt) + return CMD_WARNING; + log_set_log_level(tgt, level); + return CMD_SUCCESS; +} + +DEFUN(no_logging_level_force_all, no_logging_level_force_all_cmd, + "no logging level force-all", + NO_STR LOGGING_STR LEVEL_STR NO_FORCE_ALL_STR) +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + if (!tgt) + return CMD_WARNING; + log_set_log_level(tgt, 0); + return CMD_SUCCESS; +} + +/* 'logging level all (debug|...|fatal)' */ +ALIAS_DEPRECATED(logging_level_force_all, deprecated_logging_level_all_cmd, + "logging level all " LOG_LEVEL_ARGS, + LOGGING_STR LEVEL_STR CATEGORY_ALL_STR LOG_LEVEL_STRS); + +/* 'logging level all everything' */ +ALIAS_DEPRECATED(no_logging_level_force_all, deprecated_logging_level_all_everything_cmd, + "logging level all everything", + LOGGING_STR LEVEL_STR CATEGORY_ALL_STR EVERYTHING_STR); + DEFUN(logging_set_category_mask, logging_set_category_mask_cmd, "logging set-log-mask MASK", @@ -754,7 +869,6 @@ DEFUN(cfg_no_log_alarms, cfg_no_log_alarms_cmd, static int config_write_log_single(struct vty *vty, struct log_target *tgt) { int i; - char level_lower[32]; switch (tgt->type) { case LOG_TGT_TYPE_VTY: @@ -784,43 +898,59 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) break; } - vty_out(vty, " logging filter all %u%s", + vty_out(vty, " logging filter all %u%s", tgt->filter_map & (1 << LOG_FLT_ALL) ? 1 : 0, VTY_NEWLINE); /* save filters outside of libosmocore, i.e. in app code */ if (osmo_log_info->save_fn) osmo_log_info->save_fn(vty, osmo_log_info, tgt); - vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0, + vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0, VTY_NEWLINE); - vty_out(vty, " logging print category %d%s", + vty_out(vty, " logging print category %d%s", tgt->print_category ? 1 : 0, VTY_NEWLINE); if (tgt->print_ext_timestamp) - vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE); + vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE); else - vty_out(vty, " logging timestamp %u%s", + vty_out(vty, " logging timestamp %u%s", tgt->print_timestamp ? 1 : 0, VTY_NEWLINE); if (tgt->print_level) - vty_out(vty, " logging print level 1%s", VTY_NEWLINE); - vty_out(vty, " logging print file %s%s", + vty_out(vty, " logging print level 1%s", VTY_NEWLINE); + vty_out(vty, " logging print file %s%s", get_value_string(logging_print_file_args, tgt->print_filename2), VTY_NEWLINE); - /* stupid old osmo logging API uses uppercase strings... */ - osmo_str2lower(level_lower, log_level_str(tgt->loglevel)); - vty_out(vty, " logging level all %s%s", level_lower, VTY_NEWLINE); + if (tgt->loglevel) { + const char *level_str = get_value_string_or_null(loglevel_strs, tgt->loglevel); + level_str = osmo_str_tolower(level_str); + if (!level_str) + vty_out(vty, "%% Invalid log level %u for 'force-all'%s", + tgt->loglevel, VTY_NEWLINE); + else + vty_out(vty, " logging level force-all %s%s", level_str, VTY_NEWLINE); + } for (i = 0; i < osmo_log_info->num_cat; i++) { const struct log_category *cat = &tgt->categories[i]; - char cat_lower[32]; + const char *cat_name; + const char *level_str; /* skip empty entries in the array */ if (!osmo_log_info->cat[i].name) continue; - /* stupid old osmo logging API uses uppercase strings... */ - osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1); - osmo_str2lower(level_lower, log_level_str(cat->loglevel)); - vty_out(vty, " logging level %s %s%s", cat_lower, level_lower, VTY_NEWLINE); + /* Note: cat_name references the static buffer returned by osmo_str_tolower(), will + * become invalid after next osmo_str_tolower() invocation. */ + cat_name = osmo_str_tolower(osmo_log_info->cat[i].name+1); + + level_str = get_value_string_or_null(loglevel_strs, cat->loglevel); + if (!level_str) { + vty_out(vty, "%% Invalid log level %u for %s%s", cat->loglevel, cat_name, + VTY_NEWLINE); + continue; + } + + vty_out(vty, " logging level %s", cat_name); + vty_out(vty, " %s%s", osmo_str_tolower(level_str), VTY_NEWLINE); } return 1; @@ -846,11 +976,11 @@ void logging_vty_add_deprecated_subsys(void *ctx, const char *name) { struct cmd_element *cmd = talloc_zero(ctx, struct cmd_element); OSMO_ASSERT(cmd); - cmd->string = talloc_asprintf(cmd, "logging level %s (everything|debug|info|notice|error|fatal)", + cmd->string = talloc_asprintf(cmd, "logging level %s (debug|info|notice|error|fatal)", name); printf("%s\n", cmd->string); cmd->func = log_deprecated_func; - cmd->doc = "Set the log level for a specified category\n" + cmd->doc = LEVEL_STR "Deprecated Category\n"; cmd->attr = CMD_ATTR_DEPRECATED; @@ -874,10 +1004,21 @@ void logging_vty_add_cmds() install_element_ve(&logging_set_category_mask_cmd); install_element_ve(&logging_set_category_mask_old_cmd); - /* Logging level strings are generated dynamically. */ - logging_level_cmd.string = log_vty_command_string(); - logging_level_cmd.doc = log_vty_command_description(); + /* logging level (<categories>) (debug|...|fatal) */ + gen_logging_level_cmd_strs(&logging_level_cmd, + LOG_LEVEL_ARGS, + LOG_LEVEL_STRS); + /* logging level (<categories>) everything */ + gen_logging_level_cmd_strs(&deprecated_logging_level_everything_cmd, + "everything", EVERYTHING_STR); + install_element_ve(&logging_level_cmd); + install_element_ve(&logging_level_set_all_cmd); + install_element_ve(&logging_level_force_all_cmd); + install_element_ve(&no_logging_level_force_all_cmd); + install_element_ve(&deprecated_logging_level_everything_cmd); + install_element_ve(&deprecated_logging_level_all_cmd); + install_element_ve(&deprecated_logging_level_all_everything_cmd); install_element_ve(&show_logging_vty_cmd); install_element_ve(&show_alarms_cmd); @@ -891,6 +1032,12 @@ void logging_vty_add_cmds() install_element(CFG_LOG_NODE, &logging_prnt_level_cmd); install_element(CFG_LOG_NODE, &logging_prnt_file_cmd); install_element(CFG_LOG_NODE, &logging_level_cmd); + install_element(CFG_LOG_NODE, &logging_level_set_all_cmd); + install_element(CFG_LOG_NODE, &logging_level_force_all_cmd); + install_element(CFG_LOG_NODE, &no_logging_level_force_all_cmd); + install_element(CFG_LOG_NODE, &deprecated_logging_level_everything_cmd); + install_element(CFG_LOG_NODE, &deprecated_logging_level_all_cmd); + install_element(CFG_LOG_NODE, &deprecated_logging_level_all_everything_cmd); install_element(CONFIG_NODE, &cfg_log_stderr_cmd); install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c index 5ded7a44..c9110879 100644 --- a/src/vty/stats_vty.c +++ b/src/vty/stats_vty.c @@ -533,6 +533,7 @@ DEFUN(show_stats_asciidoc_table, static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_) { struct vty *vty = sctx_; + vty_out(vty, "%s %u:%s", ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE); vty_out_rate_ctr_group_fmt(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg); return 0; } diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c index 0ccf8dc6..fcb4c8db 100644 --- a/src/vty/telnet_interface.c +++ b/src/vty/telnet_interface.c @@ -101,7 +101,7 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) return -1; } - LOGP(DLGLOBAL, LOGL_NOTICE, "telnet at %s %d\n", ip, port); + LOGP(DLGLOBAL, LOGL_NOTICE, "Available via telnet %s %d\n", ip, port); return 0; } @@ -177,7 +177,7 @@ static int telnet_new_connection(struct osmo_fd *fd, unsigned int what) connection->vty = vty_create(new_connection, connection); if (!connection->vty) { LOGP(0, LOGL_ERROR, "couldn't create VTY\n"); - close(new_connection); + /* vty_create() is already closing the fd if it returns NULL */ talloc_free(connection); return -1; } diff --git a/src/vty/utils.c b/src/vty/utils.c index 0d663c6e..0358d9bd 100644 --- a/src/vty/utils.c +++ b/src/vty/utils.c @@ -214,9 +214,6 @@ void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt, struct rate_ctr_group *ctrg) { struct vty_out_context vctx = {vty, fmt}; - - vty_out(vty, "%s:%s", ctrg->desc->group_description, VTY_NEWLINE); - rate_ctr_for_each_counter(ctrg, rate_ctr_handler_fmt, &vctx); } diff --git a/src/vty/vty.c b/src/vty/vty.c index ad535371..70f6811a 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -221,8 +221,10 @@ void vty_close(struct vty *vty) vector_unset(vtyvec, vty->fd); /* Close socket. */ - if (vty->fd > 0) + if (vty->fd > 0) { close(vty->fd); + vty->fd = -1; + } if (vty->buf) { talloc_free(vty->buf); diff --git a/tests/Makefile.am b/tests/Makefile.am index 072bb4a2..18d4bb4a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,7 +24,9 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ abis/abis_test endian/endian_test sercomm/sercomm_test \ prbs/prbs_test gsm23003/gsm23003_test \ codec/codec_ecu_fr_test timer/clk_override_test \ - oap/oap_client_test + oap/oap_client_test \ + logging/logging_vty_test \ + $(NULL) if ENABLE_MSGFILE check_PROGRAMS += msgfile/msgfile_test @@ -76,7 +78,10 @@ abis_abis_test_SOURCES = abis/abis_test.c abis_abis_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la ctrl_ctrl_test_SOURCES = ctrl/ctrl_test.c -ctrl_ctrl_test_LDADD = $(LDADD) $(top_builddir)/src/ctrl/libosmoctrl.la +ctrl_ctrl_test_LDADD = $(LDADD) \ + $(top_builddir)/src/ctrl/libosmoctrl.la \ + $(top_builddir)/src/gsm/libosmogsm.la \ + $(top_builddir)/src/vty/libosmovty.la gea_gea_test_SOURCES = gea/gea_test.c gea_gea_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la @@ -130,20 +135,27 @@ ussd_ussd_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c gb_bssgp_fc_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la \ + $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c gb_gprs_bssgp_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \ + $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c gb_gprs_ns_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \ + $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la logging_logging_test_SOURCES = logging/logging_test.c +logging_logging_vty_test_SOURCES = logging/logging_vty_test.c +logging_logging_vty_test_LDADD = $(LDADD) $(top_builddir)/src/vty/libosmovty.la + fr_fr_test_SOURCES = fr/fr_test.c fr_fr_test_LDADD = $(LDADD) $(top_builddir)/src/gb/libosmogb.la $(LIBRARY_DLSYM) \ + $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la codec_codec_test_SOURCES = codec/codec_test.c @@ -177,7 +189,11 @@ oap_oap_client_test_SOURCES = oap/oap_client_test.c oap_oap_client_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la fsm_fsm_test_SOURCES = fsm/fsm_test.c -fsm_fsm_test_LDADD = $(LDADD) $(top_builddir)/src/ctrl/libosmoctrl.la +fsm_fsm_test_LDADD = \ + $(LDADD) \ + $(top_builddir)/src/ctrl/libosmoctrl.la \ + $(top_builddir)/src/gsm/libosmogsm.la \ + $(top_builddir)/src/vty/libosmovty.la write_queue_wqueue_test_SOURCES = write_queue/wqueue_test.c @@ -269,6 +285,7 @@ TESTSUITE = $(srcdir)/testsuite check-local: atconfig $(TESTSUITE) cat /proc/cpuinfo $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) + $(MAKE) $(AM_MAKEFLAGS) ext-tests installcheck-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ @@ -287,3 +304,25 @@ $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 conv/gsm0503_test_vectors.c: $(top_srcdir)/utils/conv_gen.py $(top_srcdir)/utils/conv_codes_gsm.py $(AM_V_GEN)python $(top_srcdir)/utils/conv_gen.py gen_vectors gsm \ --target-path $(builddir)/conv + +if ENABLE_EXT_TESTS +ext-tests: +# don't run vty and ctrl tests concurrently so that the ports don't conflict + $(MAKE) vty-test + $(MAKE) ctrl-test +else +ext-tests: + echo "Not running python-based external tests (determined at configure-time)" +endif + +# To update the VTY script from current application behavior, +# pass -u to osmo_verify_transcript_vty.py by doing: +# make vty-test U=-u +vty-test: + osmo_verify_transcript_vty.py -v \ + -p 42042 \ + -r "$(top_builddir)/tests/logging/logging_vty_test" \ + $(U) $(srcdir)/logging/*.vty + +ctrl-test: + echo "No CTRL tests exist currently" diff --git a/tests/gb/gprs_ns_test.ok b/tests/gb/gprs_ns_test.ok index 669e1824..3cb1dfcf 100644 --- a/tests/gb/gprs_ns_test.ok +++ b/tests/gb/gprs_ns_test.ok @@ -133,10 +133,10 @@ result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 -==> got signal NS_UNBLOCK, NS-VC 0x1122/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 +==> got signal NS_UNBLOCK, NS-VC 0x1122/1.2.3.4:1111 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 @@ -277,10 +277,10 @@ result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 -==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 @@ -318,10 +318,10 @@ result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:2222 06 -==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS, msg length 1 07 +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:2222 @@ -421,10 +421,10 @@ result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 -==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 +==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 @@ -597,10 +597,10 @@ result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:2222 06 -==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:2222 MESSAGE to BSS, msg length 1 07 +==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:2222 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:2222 diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c index ae138be8..197ec06d 100644 --- a/tests/gsm0808/gsm0808_test.c +++ b/tests/gsm0808/gsm0808_test.c @@ -102,7 +102,16 @@ static void test_create_layer3_aoip() 0xef, 0xcd, GSM0808_SCT_FR2 | 0xa0, 0x9f, GSM0808_SCT_CSD | 0x90, 0xc0 }; - + struct osmo_cell_global_id cgi = { + .lai = { + .plmn = { + .mcc = 0x2244, + .mnc = 0x1122, + }, + .lac = 0x3366, + }, + .cell_identity = 0x4488, + }; struct msgb *msg, *in_msg; struct gsm0808_speech_codec_list sc_list; printf("Testing creating Layer3 (AoIP)\n"); @@ -113,9 +122,8 @@ static void test_create_layer3_aoip() in_msg->l3h = in_msg->data; msgb_v_put(in_msg, 0x23); - msg = - gsm0808_create_layer3_aoip(in_msg, 0x1122, 0x2244, 0x3366, 0x4488, - &sc_list); + msg = gsm0808_create_layer3_2(in_msg, &cgi, &sc_list); + VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); @@ -242,14 +250,55 @@ static void test_create_cipher_complete() msgb_free(l3); } +static inline void parse_cipher_reject(struct msgb *msg, uint8_t exp) +{ + struct tlv_parsed tp; + int rc; + + /* skip header and message type so we can parse Cause IE directly */ + msg->l2h = msgb_data(msg) + sizeof(struct bssmap_header) + 1; + + rc = osmo_bssap_tlv_parse(&tp, msg->l2h, msgb_l2len(msg)); + if (rc < 0) + printf("FIXME: failed (%d) to parse created message %s\n", rc, msgb_hexdump(msg)); + + rc = gsm0808_get_cipher_reject_cause(&tp); + if (rc < 0) + printf("FIXME: failed (%s) to extract Cause from created message %s\n", + strerror(-rc), msgb_hexdump(msg)); + + if (exp != (enum gsm0808_cause)rc) + printf("FIXME: wrong Cause %d != %u (" OSMO_BIN_SPEC ") extracted from created message %s\n", + rc, exp, OSMO_BIT_PRINT(exp), msgb_hexdump(msg)); +} + static void test_create_cipher_reject() { static const uint8_t res[] = { 0x00, 0x04, 0x59, 0x04, 0x01, 0x23 }; + enum gsm0808_cause cause = GSM0808_CAUSE_CCCH_OVERLOAD; struct msgb *msg; printf("Testing creating Cipher Reject\n"); - msg = gsm0808_create_cipher_reject(0x23); + msg = gsm0808_create_cipher_reject(cause); + VERIFY(msg, res, ARRAY_SIZE(res)); + + parse_cipher_reject(msg, cause); + + msgb_free(msg); +} + +static void test_create_cipher_reject_ext() +{ + static const uint8_t res[] = { 0x00, 0x05, 0x59, 0x04, 0x02, 0xd0, 0xFA }; + uint8_t cause = 0xFA; + struct msgb *msg; + + printf("Testing creating Cipher Reject (extended)\n"); + msg = gsm0808_create_cipher_reject_ext(GSM0808_CAUSE_CLASS_INVAL, cause); VERIFY(msg, res, ARRAY_SIZE(res)); + + parse_cipher_reject(msg, cause); + msgb_free(msg); } @@ -695,6 +744,28 @@ static void test_gsm0808_enc_dec_speech_codec_list() msgb_free(msg); } +static void test_gsm0808_enc_dec_empty_speech_codec_list() +{ + struct gsm0808_speech_codec_list enc_scl = { + .len = 0, + }; + struct gsm0808_speech_codec_list dec_scl = {}; + struct msgb *msg; + uint8_t rc_enc; + int rc_dec; + + msg = msgb_alloc(1024, "output buffer"); + rc_enc = gsm0808_enc_speech_codec_list(msg, &enc_scl); + OSMO_ASSERT(rc_enc == 2); + + rc_dec = gsm0808_dec_speech_codec_list(&dec_scl, msg->data + 2, msg->len - 2); + OSMO_ASSERT(rc_dec == 0); + + OSMO_ASSERT(memcmp(&enc_scl, &dec_scl, sizeof(enc_scl)) == 0); + + msgb_free(msg); +} + static void test_gsm0808_enc_dec_channel_type() { struct gsm0808_channel_type enc_ct = { @@ -1444,6 +1515,258 @@ static void test_gsm0808_enc_dec_cell_id_global() msgb_free(msg); } +static void test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(struct gsm48_multi_rate_conf *cfg) +{ + uint16_t s15_s0; + + printf("Input:\n"); + printf(" m4_75= %u smod= %u\n", cfg->m4_75, cfg->smod); + printf(" m5_15= %u spare= %u\n", cfg->m5_15, cfg->spare); + printf(" m5_90= %u icmi= %u\n", cfg->m5_90, cfg->icmi); + printf(" m6_70= %u nscb= %u\n", cfg->m6_70, cfg->nscb); + printf(" m7_40= %u ver= %u\n", cfg->m7_40, cfg->ver); + printf(" m7_95= %u\n", cfg->m7_95); + printf(" m10_2= %u\n", cfg->m10_2); + printf(" m12_2= %u\n", cfg->m12_2); + + s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, true); + printf("Result (fr):\n"); + printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0, + OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0)); + + s15_s0 = gsm0808_sc_cfg_from_gsm48_mr_cfg(cfg, false); + printf("Result (hr):\n"); + printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0, + OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0)); + + printf("\n"); +} + +static void test_gsm0808_sc_cfg_from_gsm48_mr_cfg(void) +{ + struct gsm48_multi_rate_conf cfg; + + printf("Testing gsm0808_sc_cfg_from_gsm48_mr_cfg():\n"); + + memset(&cfg, 0, sizeof(cfg)); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 1; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 1; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 1; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 1; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 1; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 1; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 1; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 1; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 1; + cfg.m5_15 = 1; + cfg.m5_90 = 1; + cfg.m6_70 = 1; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 1; + cfg.m7_95 = 1; + cfg.m10_2 = 1; + cfg.m12_2 = 1; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 0; + cfg.m5_90 = 1; + cfg.m6_70 = 1; + cfg.m7_40 = 0; + cfg.m7_95 = 0; + cfg.m10_2 = 1; + cfg.m12_2 = 1; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 1; + cfg.m5_15 = 1; + cfg.m5_90 = 0; + cfg.m6_70 = 0; + cfg.m7_40 = 1; + cfg.m7_95 = 1; + cfg.m10_2 = 0; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 0; + cfg.m5_15 = 1; + cfg.m5_90 = 0; + cfg.m6_70 = 1; + cfg.m7_40 = 0; + cfg.m7_95 = 1; + cfg.m10_2 = 0; + cfg.m12_2 = 1; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 1; + cfg.m5_15 = 0; + cfg.m5_90 = 1; + cfg.m6_70 = 0; + cfg.m7_40 = 1; + cfg.m7_95 = 0; + cfg.m10_2 = 1; + cfg.m12_2 = 0; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); + + cfg.m4_75 = 1; + cfg.m5_15 = 1; + cfg.m5_90 = 1; + cfg.m6_70 = 1; + cfg.m7_40 = 1; + cfg.m7_95 = 1; + cfg.m10_2 = 1; + cfg.m12_2 = 1; + test_gsm0808_sc_cfg_from_gsm48_mr_cfg_single(&cfg); +} + +static void test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single(uint16_t s15_s0) +{ + struct gsm48_multi_rate_conf cfg; + + printf("Input:\n"); + printf(" S15-S0 = %04x = 0b" OSMO_BIN_SPEC OSMO_BIN_SPEC "\n", s15_s0, + OSMO_BIN_PRINT(s15_s0 >> 8), OSMO_BIN_PRINT(s15_s0)); + + gsm48_mr_cfg_from_gsm0808_sc_cfg(&cfg, s15_s0); + + printf("Output:\n"); + printf(" m4_75= %u smod= %u\n", cfg.m4_75, cfg.smod); + printf(" m5_15= %u spare= %u\n", cfg.m5_15, cfg.spare); + printf(" m5_90= %u icmi= %u\n", cfg.m5_90, cfg.icmi); + printf(" m6_70= %u nscb= %u\n", cfg.m6_70, cfg.nscb); + printf(" m7_40= %u ver= %u\n", cfg.m7_40, cfg.ver); + printf(" m7_95= %u\n", cfg.m7_95); + printf(" m10_2= %u\n", cfg.m10_2); + printf(" m12_2= %u\n", cfg.m12_2); + + printf("\n"); +} + +void test_gsm48_mr_cfg_from_gsm0808_sc_cfg() +{ + printf("Testing gsm48_mr_cfg_from_gsm0808_sc_cfg():\n"); + + /* Only one codec per setting */ + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_4_75); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_5_15); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_5_90); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_6_70); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_7_40); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_7_95); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_10_2); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_12_2); + + /* Combinations */ + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_4_75 | GSM0808_SC_CFG_DEFAULT_AMR_6_70 | + GSM0808_SC_CFG_DEFAULT_AMR_10_2); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_10_2 | GSM0808_SC_CFG_DEFAULT_AMR_12_2 | + GSM0808_SC_CFG_DEFAULT_AMR_7_40); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg_single + (GSM0808_SC_CFG_DEFAULT_AMR_7_95 | GSM0808_SC_CFG_DEFAULT_AMR_12_2); +} + int main(int argc, char **argv) { printf("Testing generation of GSM0808 messages\n"); @@ -1456,6 +1779,7 @@ int main(int argc, char **argv) test_create_cipher(); test_create_cipher_complete(); test_create_cipher_reject(); + test_create_cipher_reject_ext(); test_create_cm_u(); test_create_sapi_reject(); test_create_ass(); @@ -1473,6 +1797,7 @@ int main(int argc, char **argv) test_gsm0808_enc_dec_speech_codec_ext_with_cfg(); test_gsm0808_enc_dec_speech_codec_with_cfg(); test_gsm0808_enc_dec_speech_codec_list(); + test_gsm0808_enc_dec_empty_speech_codec_list(); test_gsm0808_enc_dec_channel_type(); test_gsm0808_enc_dec_encrypt_info(); @@ -1495,6 +1820,9 @@ int main(int argc, char **argv) test_gsm0808_enc_dec_cell_id_lac_and_ci(); test_gsm0808_enc_dec_cell_id_global(); + test_gsm0808_sc_cfg_from_gsm48_mr_cfg(); + test_gsm48_mr_cfg_from_gsm0808_sc_cfg(); + printf("Done\n"); return EXIT_SUCCESS; } diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok index 6cd7982b..a48cf1d5 100644 --- a/tests/gsm0808/gsm0808_test.ok +++ b/tests/gsm0808/gsm0808_test.ok @@ -8,6 +8,7 @@ Testing creating Clear Complete Testing creating Chipher Mode Command Testing creating Cipher Complete Testing creating Cipher Reject +Testing creating Cipher Reject (extended) Testing creating CM U Testing creating SAPI Reject Testing creating Assignment Request @@ -74,4 +75,362 @@ test_gsm0808_enc_dec_cell_id_lai_and_lac: encoded: 05 06 04 21 63 54 23 42 (rc = test_gsm0808_enc_dec_cell_id_ci: encoded: 05 03 02 04 23 (rc = 5) test_gsm0808_enc_dec_cell_id_lac_and_ci: encoded: 05 05 01 04 23 02 35 (rc = 7) test_gsm0808_enc_dec_cell_id_global: encoded: 05 08 00 21 63 54 23 42 04 23 (rc = 10) +Testing gsm0808_sc_cfg_from_gsm48_mr_cfg(): +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 0000 = 0b0000000000000000 +Result (hr): + S15-S0 = 0000 = 0b0000000000000000 + +Input: + m4_75= 1 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 5703 = 0b0101011100000011 +Result (hr): + S15-S0 = 0703 = 0b0000011100000011 + +Input: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 0000 = 0b0000000000000000 +Result (hr): + S15-S0 = 0000 = 0b0000000000000000 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 1 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 5706 = 0b0101011100000110 +Result (hr): + S15-S0 = 0706 = 0b0000011100000110 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 1608 = 0b0001011000001000 +Result (hr): + S15-S0 = 0608 = 0b0000011000001000 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 0412 = 0b0000010000010010 +Result (hr): + S15-S0 = 0412 = 0b0000010000010010 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 1 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 4020 = 0b0100000000100000 +Result (hr): + S15-S0 = 0020 = 0b0000000000100000 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 1 + m12_2= 0 +Result (fr): + S15-S0 = 1040 = 0b0001000001000000 +Result (hr): + S15-S0 = 0000 = 0b0000000000000000 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 1 +Result (fr): + S15-S0 = 4082 = 0b0100000010000010 +Result (hr): + S15-S0 = 0002 = 0b0000000000000010 + +Input: + m4_75= 1 smod= 0 + m5_15= 1 spare= 0 + m5_90= 1 icmi= 0 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 570f = 0b0101011100001111 +Result (hr): + S15-S0 = 070f = 0b0000011100001111 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 0 + m7_95= 1 + m10_2= 1 + m12_2= 1 +Result (fr): + S15-S0 = 54f2 = 0b0101010011110010 +Result (hr): + S15-S0 = 0432 = 0b0000010000110010 + +Input: + m4_75= 0 smod= 0 + m5_15= 0 spare= 0 + m5_90= 1 icmi= 0 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 0 + m10_2= 1 + m12_2= 1 +Result (fr): + S15-S0 = 57ce = 0b0101011111001110 +Result (hr): + S15-S0 = 070e = 0b0000011100001110 + +Input: + m4_75= 1 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 0 + m7_95= 1 + m10_2= 0 + m12_2= 0 +Result (fr): + S15-S0 = 5733 = 0b0101011100110011 +Result (hr): + S15-S0 = 0733 = 0b0000011100110011 + +Input: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 0 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 0 + m7_95= 1 + m10_2= 0 + m12_2= 1 +Result (fr): + S15-S0 = 56aa = 0b0101011010101010 +Result (hr): + S15-S0 = 062a = 0b0000011000101010 + +Input: + m4_75= 1 smod= 0 + m5_15= 0 spare= 0 + m5_90= 1 icmi= 0 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 0 + m7_95= 0 + m10_2= 1 + m12_2= 0 +Result (fr): + S15-S0 = 5757 = 0b0101011101010111 +Result (hr): + S15-S0 = 0717 = 0b0000011100010111 + +Input: + m4_75= 1 smod= 0 + m5_15= 1 spare= 0 + m5_90= 1 icmi= 0 + m6_70= 1 nscb= 0 + m7_40= 1 ver= 0 + m7_95= 1 + m10_2= 1 + m12_2= 1 +Result (fr): + S15-S0 = 57ff = 0b0101011111111111 +Result (hr): + S15-S0 = 073f = 0b0000011100111111 + +Testing gsm48_mr_cfg_from_gsm0808_sc_cfg(): +Input: + S15-S0 = ff03 = 0b1111111100000011 +Output: + m4_75= 1 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = 0000 = 0b0000000000000000 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = ff06 = 0b1111111100000110 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 1 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = 3e08 = 0b0011111000001000 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = 0c12 = 0b0000110000010010 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = c020 = 0b1100000000100000 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 1 + m10_2= 0 + m12_2= 0 + +Input: + S15-S0 = 3040 = 0b0011000001000000 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 1 + m12_2= 0 + +Input: + S15-S0 = c082 = 0b1100000010000010 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 0 + m12_2= 1 + +Input: + S15-S0 = ff4b = 0b1111111101001011 +Output: + m4_75= 1 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 1 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 0 + m10_2= 1 + m12_2= 0 + +Input: + S15-S0 = fcd2 = 0b1111110011010010 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 1 ver= 1 + m7_95= 0 + m10_2= 1 + m12_2= 1 + +Input: + S15-S0 = c0a2 = 0b1100000010100010 +Output: + m4_75= 0 smod= 0 + m5_15= 1 spare= 0 + m5_90= 0 icmi= 1 + m6_70= 0 nscb= 0 + m7_40= 0 ver= 1 + m7_95= 1 + m10_2= 0 + m12_2= 1 + Done diff --git a/tests/gsm23003/gsm23003_test.c b/tests/gsm23003/gsm23003_test.c index 947aa18d..79965cfb 100644 --- a/tests/gsm23003/gsm23003_test.c +++ b/tests/gsm23003/gsm23003_test.c @@ -24,8 +24,10 @@ #include <stdio.h> #include <errno.h> #include <strings.h> +#include <string.h> #include <osmocom/gsm/gsm23003.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> #include <osmocom/core/utils.h> #define BOOL_STR(b) ((b)? "true" : "false") @@ -170,6 +172,76 @@ static bool test_mnc_from_str() return pass; } +static bool test_gummei_name() +{ + static const struct osmo_gummei gummei = { + .plmn = { .mcc = 901, .mnc = 70 }, + .mme = { .group_id = 0xA123, .code = 0xB1 } + }; + const char *out; + bool pass = true; + + out = osmo_gummei_name(&gummei); + printf("%s\n", out); + if (strcmp(out, "901-70-a123-b1")) + pass = false; + + return pass; +} + +static bool test_domain_gen() +{ + static const struct osmo_gummei gummei = { + .plmn = { .mcc = 901, .mnc = 70 }, + .mme = { .group_id = 0xA123, .code = 0xB1 } + }; + char out[GSM23003_MME_DOMAIN_LEN]; + bool pass = true; + int rc; + + rc = osmo_gen_home_network_domain(out, &gummei.plmn); + if (rc < 0) + pass = false; + printf("%s -> %s\n", osmo_plmn_name(&gummei.plmn), out); + if (strcmp(out, "epc.mnc070.mcc901.3gppnetwork.org")) + pass = false; + + rc = osmo_gen_mme_domain(out, &gummei); + printf("%s -> %s\n", osmo_gummei_name(&gummei), out); + if (strcmp(out, "mmecb1.mmegia123.mme.epc.mnc070.mcc901.3gppnetwork.org")) + pass = false; + + return pass; +} + + +static bool test_domain_parse() +{ + static const char *mme_dom_valid = "mmec01.mmegiA001.mme.epc.mnc070.mcc901.3gppnetwork.org"; + static const char *home_dom_valid = "epc.mnc070.mcc901.3gppnetwork.org"; + struct osmo_gummei gummei; + struct osmo_plmn_id plmn; + bool pass = true; + int rc; + + rc = osmo_parse_home_network_domain(&plmn, home_dom_valid); + if (rc < 0) + pass = false; + printf("%s -> %s\n", home_dom_valid, osmo_plmn_name(&plmn)); + if (plmn.mcc != 901 || plmn.mnc != 70) + pass = false; + + rc = osmo_parse_mme_domain(&gummei, mme_dom_valid); + if (rc < 0) + pass = false; + printf("%s -> %s\n", mme_dom_valid, osmo_gummei_name(&gummei)); + if (gummei.plmn.mcc != 901 || gummei.plmn.mnc != 70 || + gummei.mme.group_id != 0xA001 || gummei.mme.code != 1) + pass = false; + + return pass; +} + int main(int argc, char **argv) { bool pass = true; @@ -177,6 +249,9 @@ int main(int argc, char **argv) pass = pass && test_valid_imsi(); pass = pass && test_valid_msisdn(); pass = pass && test_mnc_from_str(); + pass = pass && test_gummei_name(); + pass = pass && test_domain_gen(); + pass = pass && test_domain_parse(); OSMO_ASSERT(pass); diff --git a/tests/gsm23003/gsm23003_test.ok b/tests/gsm23003/gsm23003_test.ok index b435f450..c64f515c 100644 --- a/tests/gsm23003/gsm23003_test.ok +++ b/tests/gsm23003/gsm23003_test.ok @@ -59,3 +59,8 @@ 13: " 023" rc=-22 mnc=0 mnc_3_digits=0 pass 14: "023 " rc=-22 mnc=0 mnc_3_digits=0 pass 15: "023 " rc=-22 mnc=0 mnc_3_digits=0 pass +901-70-a123-b1 +901-70 -> epc.mnc070.mcc901.3gppnetwork.org +901-70-a123-b1 -> mmecb1.mmegia123.mme.epc.mnc070.mcc901.3gppnetwork.org +epc.mnc070.mcc901.3gppnetwork.org -> 901-70 +mmec01.mmegiA001.mme.epc.mnc070.mcc901.3gppnetwork.org -> 901-70-a001-01 diff --git a/tests/logging/logging_vty_test.c b/tests/logging/logging_vty_test.c new file mode 100644 index 00000000..806a460c --- /dev/null +++ b/tests/logging/logging_vty_test.c @@ -0,0 +1,296 @@ +/* test program with a vty interface to test logging behavior */ +/* + * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels@hofmeyr.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#define _GNU_SOURCE +#include <getopt.h> + +#include <signal.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/application.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/misc.h> +#include <osmocom/vty/telnet_interface.h> + +#include <stdlib.h> + +#include "config.h" + +void *root_ctx = NULL; + +enum { + DAA, + DBB, + DCCC, + DDDDD, + DEEE, +}; + +DEFUN(log_sweep, log_sweep_cmd, + "log-sweep [CATEGORY]", + "Log once for all categories on all levels\n") +{ + int only_category = argc ? log_parse_category(argv[0]) : -1; + + if (argc && only_category < 0) { + vty_out(vty, "%% Error: unknown category: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + +#define LOG_LEVEL(CAT, LEVEL) \ + if (only_category < 0 || only_category == CAT) \ + LOGP(CAT, LEVEL, "Log message for " #CAT " on level " #LEVEL "\n") + +#define LOG_ALL_LEVELS(CAT) \ + LOG_LEVEL(CAT, LOGL_DEBUG); \ + LOG_LEVEL(CAT, LOGL_INFO); \ + LOG_LEVEL(CAT, LOGL_NOTICE); \ + LOG_LEVEL(CAT, LOGL_ERROR); \ + LOG_LEVEL(CAT, LOGL_FATAL) + + LOG_ALL_LEVELS(DAA); + LOG_ALL_LEVELS(DBB); + LOG_ALL_LEVELS(DCCC); + LOG_ALL_LEVELS(DDDDD); + LOG_ALL_LEVELS(DEEE); + + vty_out(vty, "%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void vty_commands_init() +{ + install_element_ve(&log_sweep_cmd); +} + +static const struct log_info_cat default_categories[] = { + [DAA] = { + .name = "DAA", + .description = "Antropomorphic Armadillos (AA)", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DBB] = { + .name = "DBB", + .description = "Bidirectional Breadspread (BB)", + .color = "\033[1;32m", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DCCC] = { + .name = "DCCC", + .description = "Chaos Communication Congress (CCC)", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DDDDD] = { + .name = "DDDDD", + .description = "Dehydrated Dribbling Duck Dunkers (DDDD)", + .color = "\033[1;34m", + .enabled = 1, .loglevel = LOGL_ERROR, + }, + [DEEE] = { + .name = "DEEE", + .description = "Exhaustive Entropy Extraction (EEE)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_FATAL, + }, +}; + +const struct log_info log_info = { + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +static void print_help() +{ + printf( "options:\n" + " -h --help this text\n" + " -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n" + " -D --daemonize For the process into a background daemon\n" + " -c --config-file Specify the filename of the config file\n" + " -s --disable-color Don't use colors in stderr log output\n" + " -T --timestamp Prefix every log line with a timestamp\n" + " -V --version Print version information and exit\n" + " -e --log-level Set a global log-level\n" + ); +} + +static struct { + const char *config_file; + int daemonize; +} cmdline_config = {}; + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_idx = 0, c; + static const 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' }, + {} + }; + + c = getopt_long(argc, argv, "hc:d:Dc:sTVe:", + long_options, &option_idx); + if (c == -1) + break; + + switch (c) { + case 'h': + 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': + cmdline_config.daemonize = 1; + break; + case 'c': + cmdline_config.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: + /* catch unknown options *as well as* missing arguments. */ + fprintf(stderr, "Error in command line options. Exiting.\n"); + exit(-1); + } + } +} + +static int quit = 0; + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + case SIGTERM: + quit++; + break; + case SIGABRT: + osmo_generate_backtrace(); + /* 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(root_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +static struct vty_app_info vty_info = { + .name = "logging_vty_test", + .version = PACKAGE_VERSION, +}; + +int main(int argc, char **argv) +{ + int rc; + + root_ctx = talloc_named_const(NULL, 0, "logging_vty_test"); + + vty_info.tall_ctx = root_ctx; + vty_init(&vty_info); + + osmo_init_logging2(root_ctx, &log_info); + + vty_commands_init(); + + handle_options(argc, argv); + + logging_vty_add_cmds(&log_info); + osmo_talloc_vty_add_cmds(); + + log_set_print_category(osmo_stderr_target, 1); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_level(osmo_stderr_target, 1); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); + + if (cmdline_config.config_file) { + rc = vty_read_config_file(cmdline_config.config_file, NULL); + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_FATAL, "Failed to parse the config file: '%s'\n", + cmdline_config.config_file); + return 1; + } + } + + rc = telnet_init_dynif(root_ctx, NULL, vty_get_bind_addr(), 42042); + if (rc < 0) + return 2; + + 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 (cmdline_config.daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + return 6; + } + } + + while (!quit) { + log_reset_context(); + osmo_select_main(0); + } + + log_fini(); + + talloc_free(root_ctx); + talloc_free(tall_vty_ctx); + + return 0; +} diff --git a/tests/logging/logging_vty_test.vty b/tests/logging/logging_vty_test.vty new file mode 100644 index 00000000..b1909271 --- /dev/null +++ b/tests/logging/logging_vty_test.vty @@ -0,0 +1,470 @@ +logging_vty_test> enable + +logging_vty_test# show running-config +... +log stderr +... !logging level all + logging level aa debug + logging level bb info + logging level ccc notice + logging level dddd error + logging level eee fatal +... + +logging_vty_test# configure terminal +logging_vty_test(config)# log stderr + +logging_vty_test(config-log)# logging level force-all notice +logging_vty_test(config-log)# show running-config +... !logging level all + logging level force-all notice +... !logging level all + +logging_vty_test(config-log)# no logging level force-all +logging_vty_test(config-log)# show running-config +... !logging level force-all + +logging_vty_test(config-log)# exit +logging_vty_test(config)# no log stderr +logging_vty_test(config)# exit + +logging_vty_test# logging level force-all notice +Logging was not enabled. + +logging_vty_test# logging enable +logging_vty_test# logging filter all 1 +logging_vty_test# logging print category-hex 0 +logging_vty_test# logging print category 1 +logging_vty_test# logging print level 1 +logging_vty_test# logging color 0 +logging_vty_test# logging print file 0 + + +logging_vty_test# list +... !logging + logging enable + logging disable + logging filter all (0|1) + logging color (0|1) + logging timestamp (0|1) + logging print extended-timestamp (0|1) + logging print category (0|1) + logging print category-hex (0|1) + logging print level (0|1) + logging print file (0|1|basename) [last] + logging set-log-mask MASK + logging level (aa|bb|ccc|dddd|eee|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf) (debug|info|notice|error|fatal) + logging level set-all (debug|info|notice|error|fatal) + logging level force-all (debug|info|notice|error|fatal) + no logging level force-all +... !^ logging + +logging_vty_test# logging ? + enable Enables logging to this vty + disable Disables logging to this vty + filter Filter log messages + color Configure color-printing for log messages + timestamp Configure log message timestamping + print Log output settings + set-log-mask Set the logmask of this logging target + level Set the log level for a specified category + +logging_vty_test# logging level ? +... ! all + aa Antropomorphic Armadillos (AA) + bb Bidirectional Breadspread (BB) + ccc Chaos Communication Congress (CCC) + dddd Dehydrated Dribbling Duck Dunkers (DDDD) + eee Exhaustive Entropy Extraction (EEE) + lglobal Library-internal global log family +... ! all + set-all Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period. + force-all Globally force all logging categories to a specific level. This is released by the 'no logging level force-all' command. Note: any 'logging level <category> <level>' commands will have no visible effect after this, until the forced level is released. + +logging_vty_test# logging level aa ? + debug Log debug messages and higher levels + info Log informational messages and higher levels + notice Log noticeable messages and higher levels + error Log error messages and higher levels + fatal Log only fatal messages + +logging_vty_test# logging level all ? +% There is no matched command. + +logging_vty_test# logging level force-all ? + debug Log debug messages and higher levels + info Log informational messages and higher levels + notice Log noticeable messages and higher levels + error Log error messages and higher levels + fatal Log only fatal messages + +logging_vty_test# no logging level ? + force-all Release any globally forced log level set with 'logging level force-all <level>' + +logging_vty_test# logging level set-all ? + debug Log debug messages and higher levels + info Log informational messages and higher levels + notice Log noticeable messages and higher levels + error Log error messages and higher levels + fatal Log only fatal messages + + +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# ! The deprecated 'logging level all' still does what it did +logging_vty_test# logging level all fatal +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level all error +logging_vty_test# log-sweep +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level all notice +logging_vty_test# log-sweep +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level all debug +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB DEBUG Log message for DBB on level LOGL_DEBUG +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC DEBUG Log message for DCCC on level LOGL_DEBUG +DCCC INFO Log message for DCCC on level LOGL_INFO +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD DEBUG Log message for DDDDD on level LOGL_DEBUG +DDDDD INFO Log message for DDDDD on level LOGL_INFO +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE DEBUG Log message for DEEE on level LOGL_DEBUG +DEEE INFO Log message for DEEE on level LOGL_INFO +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# ! 'logging level all' overrides everything, be it stronger or weaker +logging_vty_test# logging level all notice +logging_vty_test# logging level eee debug +logging_vty_test# log-sweep eee +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level all notice +logging_vty_test# logging level eee fatal +logging_vty_test# log-sweep eee +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# ! Deprecated 'logging level all everything' lifts the globally forced level +logging_vty_test# logging level all everything +logging_vty_test# log-sweep eee +DEEE FATAL Log message for DEEE on level LOGL_FATAL + + +logging_vty_test# ! Now do the same dance with the new 'logging level force-all' commands +logging_vty_test# logging level force-all fatal +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level force-all error +logging_vty_test# log-sweep +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level force-all notice +logging_vty_test# log-sweep +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level force-all debug +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB DEBUG Log message for DBB on level LOGL_DEBUG +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC DEBUG Log message for DCCC on level LOGL_DEBUG +DCCC INFO Log message for DCCC on level LOGL_INFO +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD DEBUG Log message for DDDDD on level LOGL_DEBUG +DDDDD INFO Log message for DDDDD on level LOGL_INFO +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE DEBUG Log message for DEEE on level LOGL_DEBUG +DEEE INFO Log message for DEEE on level LOGL_INFO +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# ! 'force-all' overrides everything, be it stronger or weaker +logging_vty_test# logging level force-all notice +logging_vty_test# logging level eee debug +logging_vty_test# log-sweep eee +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level force-all notice +logging_vty_test# logging level eee fatal +logging_vty_test# log-sweep eee +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# ! lift the globally forced level +logging_vty_test# no logging level force-all +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + + +logging_vty_test# ! test 'set-all' +logging_vty_test# logging level set-all fatal +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level set-all error +logging_vty_test# log-sweep +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level set-all notice +logging_vty_test# log-sweep +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level set-all info +logging_vty_test# log-sweep +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC INFO Log message for DCCC on level LOGL_INFO +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD INFO Log message for DDDDD on level LOGL_INFO +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE INFO Log message for DEEE on level LOGL_INFO +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level set-all debug +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB DEBUG Log message for DBB on level LOGL_DEBUG +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC DEBUG Log message for DCCC on level LOGL_DEBUG +DCCC INFO Log message for DCCC on level LOGL_INFO +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD DEBUG Log message for DDDDD on level LOGL_DEBUG +DDDDD INFO Log message for DDDDD on level LOGL_INFO +DDDDD NOTICE Log message for DDDDD on level LOGL_NOTICE +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE DEBUG Log message for DEEE on level LOGL_DEBUG +DEEE INFO Log message for DEEE on level LOGL_INFO +DEEE NOTICE Log message for DEEE on level LOGL_NOTICE +DEEE ERROR Log message for DEEE on level LOGL_ERROR +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level set-all fatal +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level dddd error +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level ccc notice +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level bb info +logging_vty_test# log-sweep +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL + +logging_vty_test# logging level aa debug +logging_vty_test# log-sweep +DAA DEBUG Log message for DAA on level LOGL_DEBUG +DAA INFO Log message for DAA on level LOGL_INFO +DAA NOTICE Log message for DAA on level LOGL_NOTICE +DAA ERROR Log message for DAA on level LOGL_ERROR +DAA FATAL Log message for DAA on level LOGL_FATAL +DBB INFO Log message for DBB on level LOGL_INFO +DBB NOTICE Log message for DBB on level LOGL_NOTICE +DBB ERROR Log message for DBB on level LOGL_ERROR +DBB FATAL Log message for DBB on level LOGL_FATAL +DCCC NOTICE Log message for DCCC on level LOGL_NOTICE +DCCC ERROR Log message for DCCC on level LOGL_ERROR +DCCC FATAL Log message for DCCC on level LOGL_FATAL +DDDDD ERROR Log message for DDDDD on level LOGL_ERROR +DDDDD FATAL Log message for DDDDD on level LOGL_FATAL +DEEE FATAL Log message for DEEE on level LOGL_FATAL diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c index 2f1e87da..2bb1f9ca 100644 --- a/tests/utils/utils_test.c +++ b/tests/utils/utils_test.c @@ -616,6 +616,189 @@ static void osmo_sockaddr_to_str_and_uint_test(void) } } +struct osmo_str_tolowupper_test_data { + const char *in; + bool use_static_buf; + size_t buflen; + const char *expect_lower; + const char *expect_upper; + size_t expect_rc; + size_t expect_rc_inplace; +}; + +struct osmo_str_tolowupper_test_data osmo_str_tolowupper_tests[] = { + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .use_static_buf = true, + .expect_lower = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz!@#$%^&*()", + .expect_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + }, + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .buflen = 99, + .expect_lower = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz!@#$%^&*()", + .expect_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .expect_rc = 62, + .expect_rc_inplace = 62, + }, + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .buflen = 0, + .expect_lower = "Unset", + .expect_upper = "Unset", + .expect_rc = 62, + .expect_rc_inplace = 0, + }, + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .buflen = 1, + .expect_lower = "", + .expect_upper = "", + .expect_rc = 62, + .expect_rc_inplace = 0, + }, + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .buflen = 2, + .expect_lower = "a", + .expect_upper = "A", + .expect_rc = 62, + .expect_rc_inplace = 1, + }, + { + .in = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", + .buflen = 28, + .expect_lower = "abcdefghijklmnopqrstuvwxyza", + .expect_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZA", + .expect_rc = 62, + .expect_rc_inplace = 27, + }, +}; + + +static void osmo_str_tolowupper_test() +{ + int i; + char buf[128]; + bool ok = true; + printf("\n%s\n", __func__); + + for (i = 0; i < ARRAY_SIZE(osmo_str_tolowupper_tests); i++) { + struct osmo_str_tolowupper_test_data *d = &osmo_str_tolowupper_tests[i]; + size_t rc = 0; + const char *res; + + /* tolower */ + if (d->use_static_buf) { + res = osmo_str_tolower(d->in); + printf("osmo_str_tolower(%s)\n", osmo_quote_str(d->in, -1)); + printf(" = %s\n", osmo_quote_str(res, -1)); + } else { + OSMO_ASSERT(sizeof(buf) >= d->buflen); + osmo_strlcpy(buf, "Unset", sizeof(buf)); + rc = osmo_str_tolower_buf(buf, d->buflen, d->in); + res = buf; + printf("osmo_str_tolower_buf(%zu, %s)\n", d->buflen, osmo_quote_str(d->in, -1)); + printf(" = %zu, %s\n", rc, osmo_quote_str(res, -1)); + } + + if (strcmp(res, d->expect_lower)) { + printf("ERROR: osmo_str_tolowupper_test[%d] tolower\n" + " got %s\n", i, osmo_quote_str(res, -1)); + printf(" expected %s\n", osmo_quote_str(d->expect_lower, -1)); + ok = false; + } + + if (!d->use_static_buf && d->expect_rc != rc) { + printf("ERROR: osmo_str_tolowupper_test[%d] tolower\n" + " got rc=%zu, expected rc=%zu\n", i, rc, d->expect_rc); + ok = false; + } + + /* tolower, in-place */ + if (!d->use_static_buf) { + osmo_strlcpy(buf, + d->buflen ? d->in : "Unset", + sizeof(buf)); + rc = osmo_str_tolower_buf(buf, d->buflen, buf); + res = buf; + printf("osmo_str_tolower_buf(%zu, %s, in-place)\n", + d->buflen, osmo_quote_str(d->in, -1)); + printf(" = %zu, %s\n", rc, osmo_quote_str(res, -1)); + + if (strcmp(res, d->expect_lower)) { + printf("ERROR: osmo_str_tolowupper_test[%d] tolower in-place\n" + " got %s\n", i, osmo_quote_str(res, -1)); + printf(" expected %s\n", osmo_quote_str(d->expect_lower, -1)); + ok = false; + } + + if (d->expect_rc_inplace != rc) { + printf("ERROR: osmo_str_tolowupper_test[%d] tolower in-place\n" + " got rc=%zu, expected rc=%zu\n", + i, rc, d->expect_rc_inplace); + ok = false; + } + } + + /* toupper */ + if (d->use_static_buf) { + res = osmo_str_toupper(d->in); + printf("osmo_str_toupper(%s)\n", osmo_quote_str(d->in, -1)); + printf(" = %s\n", osmo_quote_str(res, -1)); + } else { + OSMO_ASSERT(sizeof(buf) >= d->buflen); + osmo_strlcpy(buf, "Unset", sizeof(buf)); + rc = osmo_str_toupper_buf(buf, d->buflen, d->in); + res = buf; + printf("osmo_str_toupper_buf(%zu, %s)\n", d->buflen, osmo_quote_str(d->in, -1)); + printf(" = %zu, %s\n", rc, osmo_quote_str(res, -1)); + } + + if (strcmp(res, d->expect_upper)) { + printf("ERROR: osmo_str_tolowupper_test[%d] toupper\n" + " got %s\n", i, osmo_quote_str(res, -1)); + printf(" expected %s\n", osmo_quote_str(d->expect_upper, -1)); + ok = false; + } + + if (!d->use_static_buf && d->expect_rc != rc) { + printf("ERROR: osmo_str_tolowupper_test[%d] toupper\n" + " got rc=%zu, expected rc=%zu\n", i, rc, d->expect_rc); + ok = false; + } + + /* toupper, in-place */ + if (!d->use_static_buf) { + osmo_strlcpy(buf, + d->buflen ? d->in : "Unset", + sizeof(buf)); + rc = osmo_str_toupper_buf(buf, d->buflen, buf); + res = buf; + printf("osmo_str_toupper_buf(%zu, %s, in-place)\n", + d->buflen, osmo_quote_str(d->in, -1)); + printf(" = %zu, %s\n", rc, osmo_quote_str(res, -1)); + + if (strcmp(res, d->expect_upper)) { + printf("ERROR: osmo_str_tolowupper_test[%d] toupper in-place\n" + " got %s\n", i, osmo_quote_str(res, -1)); + printf(" expected %s\n", osmo_quote_str(d->expect_upper, -1)); + ok = false; + } + + if (d->expect_rc_inplace != rc) { + printf("ERROR: osmo_str_tolowupper_test[%d] toupper in-place\n" + " got rc=%zu, expected rc=%zu\n", + i, rc, d->expect_rc_inplace); + ok = false; + } + } + } + + OSMO_ASSERT(ok); +} + + int main(int argc, char **argv) { static const struct log_info log_info = {}; @@ -631,5 +814,6 @@ int main(int argc, char **argv) str_quote_test(); isqrt_test(); osmo_sockaddr_to_str_and_uint_test(); + osmo_str_tolowupper_test(); return 0; } diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok index abc7317a..3ea8ec6a 100644 --- a/tests/utils/utils_test.ok +++ b/tests/utils/utils_test.ok @@ -153,3 +153,49 @@ osmo_sockaddr_to_str_and_uint_test [5] 234.23.42.123:1234 (omit addr) addr_len=0 --> :1234 rc=0 [6] 234.23.42.123:1234 addr_len=0 --> :1234 rc=13 [7] 234.23.42.123:1234 (omit addr) (omit port) addr_len=0 --> :0 rc=0 + +osmo_str_tolowupper_test +osmo_str_tolower("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz!@#$%^&*()" +osmo_str_toupper("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()" +osmo_str_tolower_buf(99, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz!@#$%^&*()" +osmo_str_tolower_buf(99, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 62, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz!@#$%^&*()" +osmo_str_toupper_buf(99, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()" +osmo_str_toupper_buf(99, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 62, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()" +osmo_str_tolower_buf(0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "Unset" +osmo_str_tolower_buf(0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 0, "Unset" +osmo_str_toupper_buf(0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "Unset" +osmo_str_toupper_buf(0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 0, "Unset" +osmo_str_tolower_buf(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "" +osmo_str_tolower_buf(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 0, "" +osmo_str_toupper_buf(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "" +osmo_str_toupper_buf(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 0, "" +osmo_str_tolower_buf(2, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "a" +osmo_str_tolower_buf(2, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 1, "a" +osmo_str_toupper_buf(2, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "A" +osmo_str_toupper_buf(2, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 1, "A" +osmo_str_tolower_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "abcdefghijklmnopqrstuvwxyza" +osmo_str_tolower_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 27, "abcdefghijklmnopqrstuvwxyza" +osmo_str_toupper_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()") + = 62, "ABCDEFGHIJKLMNOPQRSTUVWXYZA" +osmo_str_toupper_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place) + = 27, "ABCDEFGHIJKLMNOPQRSTUVWXYZA" diff --git a/utils/Makefile.am b/utils/Makefile.am index d4999bde..fb791906 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -5,12 +5,16 @@ LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la EXTRA_DIST = conv_gen.py conv_codes_gsm.py -bin_PROGRAMS = osmo-arfcn osmo-auc-gen +bin_PROGRAMS = osmo-arfcn osmo-auc-gen osmo-config-merge osmo_arfcn_SOURCES = osmo-arfcn.c osmo_auc_gen_SOURCES = osmo-auc-gen.c +osmo_config_merge_SOURCES = osmo-config-merge.c +osmo_config_merge_LDADD = $(LDADD) $(TALLOC_LIBS) +osmo_config_merge_CFLAGS = $(TALLOC_CFLAGS) + if ENABLE_PCSC noinst_PROGRAMS = osmo-sim-test osmo_sim_test_SOURCES = osmo-sim-test.c diff --git a/utils/osmo-arfcn.c b/utils/osmo-arfcn.c index 61108f8d..aee132c7 100644 --- a/utils/osmo-arfcn.c +++ b/utils/osmo-arfcn.c @@ -62,6 +62,7 @@ static int arfcn2freq(int arfcn) static int freq2arfcn(int freq10, int uplink) { uint16_t arfcn; + enum gsm_band band; if (uplink != 0 && uplink != 1) { fprintf(stderr, "Need to specify uplink or downlink\n"); @@ -75,8 +76,13 @@ static int freq2arfcn(int freq10, int uplink) return -EINVAL; } + if (gsm_arfcn2band_rc(arfcn, &band) < 0) { + fprintf(stderr, "ARFCN contains no valid band\n"); + return -EINVAL; + } + printf("%s: ARFCN %4d\n", - gsm_band_name(gsm_arfcn2band(arfcn)), + gsm_band_name(band), arfcn & ~ARFCN_FLAG_MASK); return 0; } diff --git a/utils/osmo-config-merge.c b/utils/osmo-config-merge.c new file mode 100644 index 00000000..afaf86b5 --- /dev/null +++ b/utils/osmo-config-merge.c @@ -0,0 +1,283 @@ +/*! \file osmo-config-merge.c + * Utility program for merging config files with patches */ +/* + * (C) 2018 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* + This utility allows you to merge an incremental config "patch" + into an osmocom-style config file. + + The patch file follows the same syntax as the original config file. + + It works by appending the leaf nodes of the patch file to the respective + nodes of the input config file. + + This process allows configuration file changes/updates to be performed + in a more stable/reliable way than by means of [unified] diff files, + as they break every time the context lines break. + + osmo-config-merge doesn't suffer from this problem, as it understands + the tree-like nature of VTY config files. + + NITE: This only works with configuration files that have proper + indenting, i.e. every level in the hierarchy must be indented excatly + one character, not multiple. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +struct node { + struct node *parent; /* back-pointer */ + struct llist_head list; /* part of parent->children */ + struct llist_head children; /* our own children */ + char *line; +}; + +/* allocate a new node */ +static struct node *node_alloc(void *ctx) +{ + struct node *node = talloc_zero(ctx, struct node); + OSMO_ASSERT(node); + INIT_LLIST_HEAD(&node->children); + return node; +} + +/* allocate a new node as child of given parent */ +static struct node *node_alloc_child(struct node *parent) +{ + struct node *node = node_alloc(parent); + node->parent = parent; + llist_add_tail(&node->list, &parent->children); + return node; +} + +/* find a given child specified by name/line string within given parent */ +static struct node *node_find_child(struct node *parent, const char *line) +{ + struct node *n; + + llist_for_each_entry(n, &parent->children, list) { + if (!strcmp(line, n->line)) + return n; + } + return NULL; +} + +/* count the number of spaces / indent level */ +static int count_indent(const char *line) +{ + int i; + + for (i = 0; i < strlen(line); i++) { + if (line[i] != ' ') + return i; + } + return i; +} + +/* strip any triling CR / LF */ +static void chomp(char *line) +{ + while (1) { + int len = strlen(line); + if (len == 0) + return; + char *lastch = &line[len-1]; + switch (*lastch) { + case '\n': + case '\r': + *lastch = '\0'; + default: + return; + } + } +} + +/* read a config file and parse it into a tree of nodes */ +static struct node *file_read(void *ctx, const char *fname) +{ + struct node *root, *last; + FILE *infile; + char line[1024]; + int cur_indent = -1; + unsigned int line_num = 0; + + infile = fopen(fname, "r"); + if (!infile) { + fprintf(stderr, "Could not open file '%s': %s\n", + fname, strerror(errno)); + return NULL; + } + + root = node_alloc(ctx); + last = root; + while (fgets(line, sizeof(line), infile)) { + line_num++; + chomp(line); + int indent = count_indent(line); + struct node *n; + if (indent > cur_indent) { + if (indent > cur_indent+1) { + fprintf(stderr, "File '%s' isn't well-formed in line %u, aborting!\n", + fname, line_num); + fclose(infile); + return NULL; + } + /* new child to last node */ + n = node_alloc_child(last); + } else if (indent < cur_indent) { + int i; + for (i = 0; i < cur_indent - indent; i++) { + /* go to parent, add another sibling */ + if (last->parent) + last = last->parent; + } + n = node_alloc_child(last->parent); + } else { + /* add a new sibling (child of parent) */ + n = node_alloc_child(last->parent); + } + n->line = talloc_strdup(n, line); + + last = n; + cur_indent = indent; + } + + fclose(infile); + return root; +} + +static void append_patch(struct node *cfg, struct node *patch) +{ + struct node *n; + + llist_for_each_entry(n, &patch->children, list) { + if (llist_empty(&n->children)) { + struct node *t; + /* we are an end-node, i.e. something that needs to be + * patched into the original tree. We do this by simply + * appending it to the list of siblings */ + t = node_alloc_child(cfg); + t->line = talloc_strdup(t, n->line); + } else { + struct node *c; + /* we need to iterate / recurse further */ + + /* try to find the matching original node */ + c = node_find_child(cfg, n->line); + if (!c) { + /* create it, if it's missing */ + c = node_alloc_child(cfg); + c->line = talloc_strdup(c, n->line); + } + append_patch(c, n); + } + } +} + + +static int level = -1; + +static void dump_node(struct node *root, FILE *out, bool print_node_depth) +{ + struct node *n; + level++; + + if (root->line) { + if (print_node_depth) { + int i; + for (i = 0; i < level; i++) + fputc('*', out); + } + + fprintf(out, "%s\n", root->line); + } + + llist_for_each_entry(n, &root->children, list) { + dump_node(n, out, print_node_depth); + } + level--; +} + +static void exit_usage(int rc) +{ + fprintf(stderr, "Usage: osmo-config-merge <config-file> <config-patch> [--debug]\n"); + exit(rc); +} + + +int main(int argc, char **argv) +{ + const char *base_fname, *patch_fname; + struct node *base_tree, *patch_tree; + bool debug_enabled = false; + void *ctx; + + if (argc < 3) + exit_usage(1); + + base_fname = argv[1]; + patch_fname = argv[2]; + + if (argc > 3) { + if (!strcmp(argv[3], "--debug")) + debug_enabled = true; + else + exit_usage(1); + } + + ctx = talloc_named_const(NULL, 0, "root"); + + base_tree = file_read(ctx, base_fname); + patch_tree = file_read(ctx, patch_fname); + + if (!base_tree || ! patch_tree) { + talloc_free(ctx); + return 2; + } + + if (debug_enabled) { + fprintf(stderr, "====== dumping tree (base)\n"); + dump_node(base_tree, stderr, true); + fprintf(stderr, "====== dumping tree (patch)\n"); + dump_node(patch_tree, stderr, true); + } + + append_patch(base_tree, patch_tree); + + if (debug_enabled) + fprintf(stderr, "====== dumping tree (patched)\n"); + dump_node(base_tree, stdout, false); + fflush(stdout); + + /* make AddressSanitizer / LeakSanitizer happy by recursively freeing the trees */ + talloc_free(patch_tree); + talloc_free(base_tree); + talloc_free(ctx); + + return 0; +} |