diff options
Diffstat (limited to 'src/osmo-bsc')
-rw-r--r-- | src/osmo-bsc/Makefile.am | 18 | ||||
-rw-r--r-- | src/osmo-bsc/Makefile.in | 513 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_api.c | 174 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_audio.c | 70 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_bssap.c | 548 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_filter.c | 170 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_grace.c | 107 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_main.c | 261 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_msc.c | 368 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_rf.c | 364 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_sccp.c | 288 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_vty.c | 311 |
12 files changed, 3192 insertions, 0 deletions
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am new file mode 100644 index 000000000..95b9ef4f1 --- /dev/null +++ b/src/osmo-bsc/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = osmo-bsc + + +osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_rf.c osmo_bsc_vty.c osmo_bsc_api.c \ + osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \ + osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c +# once again since TRAU uses CC symbol :( +osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOSCCP_LIBS) diff --git a/src/osmo-bsc/Makefile.in b/src/osmo-bsc/Makefile.in new file mode 100644 index 000000000..d83952c98 --- /dev/null +++ b/src/osmo-bsc/Makefile.in @@ -0,0 +1,513 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = osmo-bsc$(EXEEXT) +subdir = src/osmo-bsc +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/bscconfig.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_osmo_bsc_OBJECTS = osmo_bsc_main.$(OBJEXT) osmo_bsc_rf.$(OBJEXT) \ + osmo_bsc_vty.$(OBJEXT) osmo_bsc_api.$(OBJEXT) \ + osmo_bsc_grace.$(OBJEXT) osmo_bsc_msc.$(OBJEXT) \ + osmo_bsc_sccp.$(OBJEXT) osmo_bsc_filter.$(OBJEXT) \ + osmo_bsc_bssap.$(OBJEXT) osmo_bsc_audio.$(OBJEXT) +osmo_bsc_OBJECTS = $(am_osmo_bsc_OBJECTS) +am__DEPENDENCIES_1 = +osmo_bsc_DEPENDENCIES = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(osmo_bsc_SOURCES) +DIST_SOURCES = $(osmo_bsc_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COVERAGE_CFLAGS = @COVERAGE_CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GPRS_LIBGTP = @GPRS_LIBGTP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBOSMOCORE_CFLAGS = @LIBOSMOCORE_CFLAGS@ +LIBOSMOCORE_LIBS = @LIBOSMOCORE_LIBS@ +LIBOSMOSCCP_CFLAGS = @LIBOSMOSCCP_CFLAGS@ +LIBOSMOSCCP_LIBS = @LIBOSMOSCCP_LIBS@ +LIBOSMOVTY_CFLAGS = @LIBOSMOVTY_CFLAGS@ +LIBOSMOVTY_LIBS = @LIBOSMOVTY_LIBS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SYMBOL_VISIBILITY = @SYMBOL_VISIBILITY@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) +osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_rf.c osmo_bsc_vty.c osmo_bsc_api.c \ + osmo_bsc_grace.c osmo_bsc_msc.c osmo_bsc_sccp.c \ + osmo_bsc_filter.c osmo_bsc_bssap.c osmo_bsc_audio.c + +# once again since TRAU uses CC symbol :( +osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOSCCP_LIBS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/osmo-bsc/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/osmo-bsc/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +osmo-bsc$(EXEEXT): $(osmo_bsc_OBJECTS) $(osmo_bsc_DEPENDENCIES) + @rm -f osmo-bsc$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(osmo_bsc_OBJECTS) $(osmo_bsc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_api.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_audio.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_bssap.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_filter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_grace.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_msc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_rf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_sccp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osmo_bsc_vty.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c new file mode 100644 index 000000000..b8cbcf2f3 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_api.c @@ -0,0 +1,174 @@ +/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/debug.h> + +#include <osmocore/protocol/gsm_08_08.h> +#include <osmocore/gsm0808.h> + +#define return_when_not_connected(conn) \ + if (!conn->sccp_con) {\ + LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ + return; \ + } + +#define return_when_not_connected_val(conn, ret) \ + if (!conn->sccp_con) {\ + LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ + return ret; \ + } + +#define queue_msg_or_return(resp) \ + if (!resp) { \ + LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ + return; \ + } \ + bsc_queue_for_msc(conn->sccp_con, resp); + +static uint16_t get_network_code_for_msc(struct gsm_network *net) +{ + if (net->msc_data->core_ncc != -1) + return net->msc_data->core_ncc; + return net->network_code; +} + +static uint16_t get_country_code_for_msc(struct gsm_network *net) +{ + if (net->msc_data->core_mcc != -1) + return net->msc_data->core_mcc; + return net->country_code; +} + +static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) +{ + struct msgb *resp; + return_when_not_connected(conn); + + resp = gsm0808_create_sapi_reject(dlci); + queue_msg_or_return(resp); +} + +static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, + struct msgb *msg, uint8_t chosen_encr) +{ + struct msgb *resp; + return_when_not_connected(conn); + + LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n"); + resp = gsm0808_create_cipher_complete(msg, chosen_encr); + queue_msg_or_return(resp); +} + +/* + * Instruct to reserve data for a new connectiom, create the complete + * layer three message, send it to open the connection. + */ +static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, + uint16_t chosen_channel) +{ + struct msgb *resp; + uint16_t network_code = get_network_code_for_msc(conn->bts->network); + uint16_t country_code = get_country_code_for_msc(conn->bts->network); + + /* allocate resource for a new connection */ + if (bsc_create_new_connection(conn) != 0) + return BSC_API_CONN_POL_REJECT; + + bsc_scan_bts_msg(conn, msg); + resp = gsm0808_create_layer3(msg, network_code, country_code, + conn->bts->location_area_code, + conn->bts->cell_identity); + if (!resp) { + LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); + bsc_delete_connection(conn->sccp_con); + return BSC_API_CONN_POL_REJECT; + } + + if (bsc_open_connection(conn->sccp_con, resp) != 0) { + bsc_delete_connection(conn->sccp_con); + msgb_free(resp); + return BSC_API_CONN_POL_REJECT; + } + + return BSC_API_CONN_POL_ACCEPT; +} + +static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) +{ + struct msgb *resp; + return_when_not_connected(conn); + + bsc_scan_bts_msg(conn, msg); + resp = gsm0808_create_dtap(msg, link_id); + queue_msg_or_return(resp); +} + +static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_model) +{ + struct msgb *resp; + return_when_not_connected(conn); + + resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel, + encr_alg_id, speech_model); + queue_msg_or_return(resp); +} + +static void bsc_assign_fail(struct gsm_subscriber_connection *conn, + uint8_t cause, uint8_t *rr_cause) +{ + struct msgb *resp; + return_when_not_connected(conn); + + resp = gsm0808_create_assignment_failure(cause, rr_cause); + queue_msg_or_return(resp); +} + +static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) +{ + struct msgb *resp; + return_when_not_connected_val(conn, 1); + + resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); + return 0; + } + + bsc_queue_for_msc(conn->sccp_con, resp); + return 0; +} + +static struct bsc_api bsc_handler = { + .sapi_n_reject = bsc_sapi_n_reject, + .cipher_mode_compl = bsc_cipher_mode_compl, + .compl_l3 = bsc_compl_l3, + .dtap = bsc_dtap, + .assign_compl = bsc_assign_compl, + .assign_fail = bsc_assign_fail, + .clear_request = bsc_clear_request, +}; + +struct bsc_api *osmo_bsc_api() +{ + return &bsc_handler; +} diff --git a/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c new file mode 100644 index 000000000..515cfa7fb --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_audio.c @@ -0,0 +1,70 @@ +/* + * ipaccess audio handling + * + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_msc_data.h> +#include <openbsc/osmo_bsc.h> +#include <openbsc/abis_rsl.h> +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/signal.h> + +#include <arpa/inet.h> + +static int handle_abisip_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_subscriber_connection *con; + struct gsm_lchan *lchan = signal_data; + int rc; + + if (subsys != SS_ABISIP) + return 0; + + con = lchan->conn; + if (!con || !con->sccp_con) + return 0; + + switch (signal) { + case S_ABISIP_CRCX_ACK: + /* we can ask it to connect now */ + LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n", + con->sccp_con->rtp_port, lchan->abis_ip.conn_id); + + rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY), + con->sccp_con->rtp_port, + lchan->abis_ip.rtp_payload2); + if (rc < 0) { + LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc); + return rc; + } + break; + } + + return 0; +} + +int osmo_bsc_audio_init(struct gsm_network *net) +{ + net->hardcoded_rtp_payload = 98; + register_signal_handler(SS_ABISIP, handle_abisip_signal, net); + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c new file mode 100644 index 000000000..f8711314d --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -0,0 +1,548 @@ +/* GSM 08.08 BSSMAP handling */ +/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_bsc_grace.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/mgcp.h> +#include <openbsc/paging.h> + +#include <osmocore/gsm0808.h> +#include <osmocore/protocol/gsm_08_08.h> + +#include <arpa/inet.h> + +static uint16_t read_data16(const uint8_t *data) +{ + uint16_t res; + + memcpy(&res, data, sizeof(res)); + return res; +} + +/* + * helpers for the assignment command + */ +enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) +{ + if (audio->hr) { + switch (audio->ver) { + case 1: + return GSM0808_PERM_HR1; + break; + case 2: + return GSM0808_PERM_HR2; + break; + case 3: + return GSM0808_PERM_HR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_FR1; + } + } else { + switch (audio->ver) { + case 1: + return GSM0808_PERM_FR1; + break; + case 2: + return GSM0808_PERM_FR2; + break; + case 3: + return GSM0808_PERM_FR3; + break; + default: + LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); + return GSM0808_PERM_HR1; + } + } +} + +enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) +{ + switch (speech) { + case GSM0808_PERM_HR1: + case GSM0808_PERM_FR1: + return GSM48_CMODE_SPEECH_V1; + break; + case GSM0808_PERM_HR2: + case GSM0808_PERM_FR2: + return GSM48_CMODE_SPEECH_EFR; + break; + case GSM0808_PERM_HR3: + case GSM0808_PERM_FR3: + return GSM48_CMODE_SPEECH_AMR; + break; + } + + LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); + return GSM48_CMODE_SPEECH_AMR; +} + +static int bssmap_handle_reset_ack(struct gsm_network *net, + struct msgb *msg, unsigned int length) +{ + LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); + return 0; +} + +/* GSM 08.08 ยง 3.2.1.19 */ +static int bssmap_handle_paging(struct gsm_network *net, + struct msgb *msg, unsigned int payload_length) +{ + struct gsm_subscriber *subscr; + struct tlv_parsed tp; + char mi_string[GSM48_MI_SIZE]; + uint32_t tmsi = GSM_RESERVED_TMSI; + unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; + uint8_t data_length; + const uint8_t *data; + uint8_t chan_needed = RSL_CHANNEED_ANY; + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { + LOGP(DMSC, LOGL_ERROR, "Mandantory IMSI not present.\n"); + return -1; + } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { + LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); + return -1; + } + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { + LOGP(DMSC, LOGL_ERROR, "Mandantory CELL IDENTIFIER LIST not present.\n"); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI)) { + gsm48_mi_to_string(mi_string, sizeof(mi_string), + TLVP_VAL(&tp, GSM0808_IE_TMSI), TLVP_LEN(&tp, GSM0808_IE_TMSI)); + tmsi = strtoul(mi_string, NULL, 10); + } + + + /* + * parse the IMSI + */ + gsm48_mi_to_string(mi_string, sizeof(mi_string), + TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI)); + + /* + * parse the cell identifier list + */ + data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); + data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); + + /* + * Support paging to all network or one BTS at one LAC + */ + if (data_length == 3 && data[0] == CELL_IDENT_LAC) { + lac = ntohs(read_data16(&data[1])); + } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { + LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", hexdump(data, data_length)); + return -1; + } + + if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) + chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; + + if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { + LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); + } + + subscr = subscr_get_or_create(net, mi_string); + if (!subscr) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string); + return -1; + } + + subscr->lac = lac; + subscr->tmsi = tmsi; + + LOGP(DMSC, LOGL_DEBUG, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); + paging_request(net, subscr, chan_needed, NULL, NULL); + return 0; +} + +/* + * GSM 08.08 ยง 3.1.9.1 and 3.2.1.21... + * release our gsm_subscriber_connection and send message + */ +static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int payload_length) +{ + struct msgb *resp; + + /* TODO: handle the cause of this package */ + + if (conn->conn) { + LOGP(DMSC, LOGL_DEBUG, "Releasing all transactions on %p\n", conn); + gsm0808_clear(conn->conn); + subscr_con_free(conn->conn); + conn->conn = NULL; + } + + /* send the clear complete message */ + resp = gsm0808_create_clear_complete(); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return 0; +} + +/* + * GSM 08.08 ยง 3.4.7 cipher mode handling. We will have to pick + * the cipher to be used for this. In case we are already using + * a cipher we will have to send cipher mode reject to the MSC, + * otherwise we will have to pick something that we and the MS + * is supporting. Currently we are doing it in a rather static + * way by picking one ecnryption or no encrytpion. + */ +static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int payload_length) +{ + uint16_t len; + struct gsm_network *network = NULL; + const uint8_t *data; + struct tlv_parsed tp; + struct msgb *resp; + int reject_cause = -1; + int include_imeisv = 1; + + if (!conn->conn) { + LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); + goto reject; + } + + if (conn->ciphering_handled) { + LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); + goto reject; + } + + conn->ciphering_handled = 1; + + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); + if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { + LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); + goto reject; + } + + /* + * check if our global setting is allowed + * - Currently we check for A5/0 and A5/1 + * - Copy the key if that is necessary + * - Otherwise reject + */ + len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + if (len < 1) { + LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); + goto reject; + } + + network = conn->conn->bts->network; + data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); + + if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) + include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; + + if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { + gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv); + } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { + gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv); + } else { + LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n"); + goto reject; + } + +reject: + resp = gsm0808_create_cipher_reject(reject_cause); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return -1; +} + +/* + * Handle the assignment request message. + * + * See ยง3.2.1.1 for the message type + */ +static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + struct msgb *resp; + struct gsm_network *network; + struct tlv_parsed tp; + uint8_t *data; + uint16_t cic; + uint8_t timeslot; + uint8_t multiplex; + enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; + int i, supported, port, full_rate = -1; + + if (!conn->conn) { + LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); + return -1; + } + + network = conn->conn->bts->network; + tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { + LOGP(DMSC, LOGL_ERROR, "Mandantory channel type not present.\n"); + goto reject; + } + + if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { + LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n"); + goto reject; + } + + cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE))); + timeslot = cic & 0x1f; + multiplex = (cic & ~0x1f) >> 5; + + /* + * Currently we only support a limited subset of all + * possible channel types. The limitation ends by not using + * multi-slot, limiting the channel coding, speech... + */ + if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { + LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", + TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); + goto reject; + } + + /* + * Try to figure out if we support the proposed speech codecs. For + * now we will always pick the full rate codecs. + */ + + data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); + if ((data[0] & 0xf) != 0x1) { + LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); + goto reject; + } + + if (data[1] != GSM0808_SPEECH_FULL_PREF && data[1] != GSM0808_SPEECH_HALF_PREF) { + LOGP(DMSC, LOGL_ERROR, "ChannelType full not allowed: %d\n", data[1]); + goto reject; + } + + /* + * go through the list of preferred codecs of our gsm network + * and try to find it among the permitted codecs. If we found + * it we will send chan_mode to the right mode and break the + * inner loop. The outer loop will exit due chan_mode having + * the correct value. + */ + full_rate = 0; + for (supported = 0; + chan_mode == GSM48_CMODE_SIGN && supported < network->msc_data->audio_length; + ++supported) { + + int perm_val = audio_support_to_gsm88(network->msc_data->audio_support[supported]); + for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { + if ((data[i] & 0x7f) == perm_val) { + chan_mode = gsm88_to_chan_mode(perm_val); + full_rate = (data[i] & 0x4) == 0; + break; + } else if ((data[i] & 0x80) == 0x00) { + break; + } + } + } + + if (chan_mode == GSM48_CMODE_SIGN) { + LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); + goto reject; + } + + /* map it to a MGCP Endpoint and a RTP port */ + port = mgcp_timeslot_to_endpoint(multiplex, timeslot); + conn->rtp_port = rtp_calculate_port(port, + network->msc_data->rtp_base); + + return gsm0808_assign_req(conn->conn, chan_mode, full_rate); + +reject: + resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); + if (!resp) { + LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); + return -1; + } + + bsc_queue_for_msc(conn, resp); + return -1; +} + +static int bssmap_rcvmsg_udt(struct gsm_network *net, + struct msgb *msg, unsigned int length) +{ + int ret = 0; + + if (length < 1) { + LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); + return -1; + } + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_RESET_ACKNOWLEDGE: + ret = bssmap_handle_reset_ack(net, msg, length); + break; + case BSS_MAP_MSG_PAGING: + if (bsc_grace_allow_new_connection(net)) + ret = bssmap_handle_paging(net, msg, length); + break; + } + + return ret; +} + +static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + int ret = 0; + + if (length < 1) { + LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); + return -1; + } + + switch (msg->l4h[0]) { + case BSS_MAP_MSG_CLEAR_CMD: + ret = bssmap_handle_clear_command(conn, msg, length); + break; + case BSS_MAP_MSG_CIPHER_MODE_CMD: + ret = bssmap_handle_cipher_mode(conn, msg, length); + break; + case BSS_MAP_MSG_ASSIGMENT_RQST: + ret = bssmap_handle_assignm_req(conn, msg, length); + break; + default: + LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l4h[0]); + break; + } + + return ret; +} + +static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int length) +{ + struct dtap_header *header; + struct msgb *gsm48; + uint8_t *data; + + if (!conn->conn) { + LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); + return -1; + } + + header = (struct dtap_header *) msg->l3h; + if (sizeof(*header) >= length) { + LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %u got: %u\n", sizeof(*header), length); + LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length)); + return -1; + } + + if (header->length > length - sizeof(*header)) { + LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); + LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length)); + return -1; + } + + LOGP(DMSC, LOGL_DEBUG, "DTAP message: SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); + + /* forward the data */ + gsm48 = gsm48_msgb_alloc(); + if (!gsm48) { + LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); + return -1; + } + + gsm48->l3h = gsm48->data; + data = msgb_put(gsm48, length - sizeof(*header)); + memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); + + /* pass it to the filter for extra actions */ + bsc_scan_msc_msg(conn->conn, gsm48); + return gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1); +} + +int bsc_handle_udt(struct gsm_network *network, + struct bsc_msc_connection *conn, + struct msgb *msgb, unsigned int length) +{ + struct bssmap_header *bs; + + LOGP(DMSC, LOGL_DEBUG, "Incoming SCCP message ftom MSC: %s\n", + hexdump(msgb->l3h, length)); + + if (length < sizeof(*bs)) { + LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); + return -1; + } + + bs = (struct bssmap_header *) msgb->l3h; + if (bs->length < length - sizeof(*bs)) + return -1; + + switch (bs->type) { + case BSSAP_MSG_BSS_MANAGEMENT: + msgb->l4h = &msgb->l3h[sizeof(*bs)]; + bssmap_rcvmsg_udt(network, msgb, length - sizeof(*bs)); + break; + default: + LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %d\n", bs->type); + } + + return 0; +} + +int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, + struct msgb *msg, unsigned int len) +{ + if (len < sizeof(struct bssmap_header)) { + LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); + } + + switch (msg->l3h[0]) { + case BSSAP_MSG_BSS_MANAGEMENT: + msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; + bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); + break; + case BSSAP_MSG_DTAP: + dtap_rcvmsg(conn, msg, len); + break; + default: + LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l3h[0]); + } + + return -1; +} diff --git a/src/osmo-bsc/osmo_bsc_filter.c b/src/osmo-bsc/osmo_bsc_filter.c new file mode 100644 index 000000000..d2735a63a --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_filter.c @@ -0,0 +1,170 @@ +/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/gsm_04_80.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> +#include <openbsc/paging.h> + +#include <stdlib.h> + +static void handle_lu_request(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct gsm48_loc_upd_req *lu; + struct gsm48_loc_area_id lai; + struct gsm_network *net; + + if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { + LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); + return; + } + + net = conn->bts->network; + + gh = msgb_l3(msg); + lu = (struct gsm48_loc_upd_req *) gh->data; + + gsm48_generate_lai(&lai, net->country_code, net->network_code, + conn->bts->location_area_code); + + if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { + LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); + conn->sccp_con->new_subscriber = 1; + } +} + +/* we will need to stop the paging request */ +static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + uint8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + struct gsm48_hdr *gh; + struct gsm48_pag_resp *resp; + struct gsm_subscriber *subscr; + + if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { + LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); + return -1; + } + + gh = msgb_l3(msg); + resp = (struct gsm48_pag_resp *) &gh->data[0]; + + gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), + mi_string, &mi_type); + DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_active_by_tmsi(conn->bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_active_by_imsi(conn->bts->network, mi_string); + break; + default: + subscr = NULL; + break; + } + + if (!subscr) { + LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); + return -1; + } + + paging_request_stop(conn->bts, subscr, conn, msg); + subscr_put(subscr); + return 0; +} + +/** + * This is used to scan a message for extra functionality of the BSC. This + * includes scanning for location updating requests/acceptd and then send + * a welcome USSD message to the subscriber. + */ +int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t mtype = gh->msg_type & 0xbf; + + if (pdisc == GSM48_PDISC_MM) { + if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) + handle_lu_request(conn, msg); + } else if (pdisc == GSM48_PDISC_RR) { + if (mtype == GSM48_MT_RR_PAG_RESP) + handle_page_resp(conn, msg); + } + + return 0; +} + +static void send_welcome_ussd(struct gsm_subscriber_connection *conn) +{ + struct gsm_network *net; + net = conn->bts->network; + + if (!net->msc_data->ussd_welcome_txt) + return; + + gsm0480_send_ussdNotify(conn, 1, net->msc_data->ussd_welcome_txt); + gsm0480_send_releaseComplete(conn); +} + +/** + * Messages coming back from the MSC. + */ +int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm_network *net; + struct gsm48_loc_area_id *lai; + struct gsm48_hdr *gh; + uint8_t mtype; + + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); + return -1; + } + + gh = (struct gsm48_hdr *) msgb_l3(msg); + mtype = gh->msg_type & 0xbf; + net = conn->bts->network; + + if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { + if (net->msc_data->core_ncc != -1 || + net->msc_data->core_mcc != -1) { + if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { + lai = (struct gsm48_loc_area_id *) &gh->data[0]; + gsm48_generate_lai(lai, net->country_code, + net->network_code, + conn->bts->location_area_code); + } + } + + if (conn->sccp_con->new_subscriber) + send_welcome_ussd(conn); + } + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_grace.c b/src/osmo-bsc/osmo_bsc_grace.c new file mode 100644 index 000000000..f699cf39c --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_grace.c @@ -0,0 +1,107 @@ +/* + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_bsc_grace.h> +#include <openbsc/osmo_bsc_rf.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/gsm_04_80.h> +#include <openbsc/signal.h> + +int bsc_grace_allow_new_connection(struct gsm_network *network) +{ + if (!network->msc_data->rf_ctl) + return 1; + return network->msc_data->rf_ctl->policy == S_RF_ON; +} + +static int handle_sub(struct gsm_lchan *lchan, const char *text) +{ + struct gsm_subscriber_connection *conn; + + /* only send it to TCH */ + if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) + return -1; + + /* only send on the primary channel */ + conn = lchan->conn; + if (!conn) + return -1; + + if (conn->lchan != lchan) + return -1; + + /* only when active */ + if (lchan->state != LCHAN_S_ACTIVE) + return -1; + + gsm0480_send_ussdNotify(conn, 0, text); + gsm0480_send_releaseComplete(conn); + + return 0; +} + +/* + * The place to handle the grace mode. Right now we will send + * USSD messages to the subscriber, in the future we might start + * a timer to have different modes for the grace period. + */ +static int handle_grace(struct gsm_network *network) +{ + int ts_nr, lchan_nr; + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + + if (!network->msc_data->mid_call_txt) + return 0; + + llist_for_each_entry(bts, &network->bts_list, list) { + llist_for_each_entry(trx, &bts->trx_list, list) { + for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { + handle_sub(&ts->lchan[lchan_nr], + network->msc_data->mid_call_txt); + } + } + } + } + return 0; +} + +static int handle_rf_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct rf_signal_data *sig; + + if (subsys != SS_RF) + return -1; + + sig = signal_data; + + if (signal == S_RF_GRACE) + handle_grace(sig->net); + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_grace(void) +{ + register_signal_handler(SS_RF, handle_rf_signal, NULL); +} diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c new file mode 100644 index 000000000..b5f64ab37 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_main.c @@ -0,0 +1,261 @@ +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_bsc_rf.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/signal.h> +#include <openbsc/vty.h> + +#include <osmocore/talloc.h> +#include <osmocore/process.h> + +#include <osmocom/sccp/sccp.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + + +#include "../../bscconfig.h" + +static struct log_target *stderr_target; +struct gsm_network *bsc_gsmnet = 0; +static const char *config_file = "openbsc.cfg"; +static const char *rf_ctl = NULL; +extern const char *openbsc_copyright; +static int daemonize = 0; + +extern int bsc_bootstrap_network(int (*layer4)(struct gsm_network *, struct msgb *), const char *cfg_file); + +static void print_usage() +{ + printf("Usage: bsc_msc_ip\n"); +} + +static void print_help() +{ + printf(" Some useful help...\n"); + printf(" -h --help this text\n"); + printf(" -D --daemonize Fork the process into a background daemon\n"); + printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); + printf(" -s --disable-color\n"); + printf(" -T --timestamp. Print a timestamp in the debug output.\n"); + printf(" -c --config-file filename The config file to use.\n"); + printf(" -l --local=IP. The local address of the MGCP.\n"); + printf(" -e --log-level number. Set a global loglevel.\n"); + printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n"); + printf(" -t --testmode. A special mode to provoke failures at the MSC.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"daemonize", 0, 0, 'D'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"timestamp", 0, 0, 'T'}, + {"local", 1, 0, 'l'}, + {"log-level", 1, 0, 'e'}, + {"rf-ctl", 1, 0, 'r'}, + {"testmode", 0, 0, 't'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:DsTc:e:r:t", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + log_set_use_color(stderr_target, 0); + break; + case 'd': + log_parse_category_mask(stderr_target, optarg); + break; + case 'D': + daemonize = 1; + break; + case 'c': + config_file = strdup(optarg); + break; + case 'T': + log_set_print_timestamp(stderr_target, 1); + break; + case 'P': + ipacc_rtp_direct = 0; + break; + case 'e': + log_set_log_level(stderr_target, atoi(optarg)); + break; + case 'r': + rf_ctl = optarg; + break; + default: + /* ignore */ + break; + } + } +} + +extern int bts_model_unknown_init(void); +extern int bts_model_bs11_init(void); +extern int bts_model_nanobts_init(void); + +extern enum node_type bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info vty_info = { + .name = "OsmoBSC", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +extern int bsc_shutdown_net(struct gsm_network *net); +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + bsc_shutdown_net(bsc_gsmnet); + dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + sleep(3); + exit(0); + break; + case SIGABRT: + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_bsc_ctx, stderr); + break; + case SIGUSR2: + if (!bsc_gsmnet->msc_data) + return; + if (!bsc_gsmnet->msc_data->msc_con) + return; + if (!bsc_gsmnet->msc_data->msc_con->is_connected) + return; + bsc_msc_lost(bsc_gsmnet->msc_data->msc_con); + break; + default: + break; + } +} + +int main(int argc, char **argv) +{ + int rc; + + log_init(&log_info); + tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + + bts_model_unknown_init(); + bts_model_bs11_init(); + bts_model_nanobts_init(); + + /* enable filters */ + log_set_all_filter(stderr_target, 1); + + /* This needs to precede handle_options() */ + vty_info.copyright = openbsc_copyright; + vty_init(&vty_info); + bsc_vty_init(); + + /* parse options */ + handle_options(argc, argv); + + /* seed the PRNG */ + srand(time(NULL)); + + /* initialize SCCP */ + sccp_set_log_area(DSCCP); + + + rc = bsc_bootstrap_network(NULL, config_file); + if (rc < 0) { + fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); + exit(1); + } + bsc_api_init(bsc_gsmnet, osmo_bsc_api()); + + if (rf_ctl) { + struct osmo_msc_data *data = bsc_gsmnet->msc_data; + data->rf_ctl = osmo_bsc_rf_create(rf_ctl, bsc_gsmnet); + if (!data->rf_ctl) { + fprintf(stderr, "Failed to create the RF service.\n"); + exit(1); + } + } + + if (osmo_bsc_msc_init(bsc_gsmnet) != 0) { + LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n"); + exit(1); + } + + if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) { + LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n"); + exit(1); + } + + if (osmo_bsc_audio_init(bsc_gsmnet) != 0) { + LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n"); + exit(1); + } + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + signal(SIGPIPE, SIG_IGN); + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + while (1) { + bsc_select_main(0); + } + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c new file mode 100644 index 000000000..2e8cf0579 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_msc.c @@ -0,0 +1,368 @@ +/* + * Handle the connection to the MSC. This include ping/timeout/reconnect + * (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/bsc_nat.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/ipaccess.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/signal.h> + +#include <osmocore/gsm0808.h> + +#include <osmocom/sccp/sccp.h> + +#include <sys/socket.h> +#include <netinet/tcp.h> +#include <unistd.h> + + +static void initialize_if_needed(struct bsc_msc_connection *conn); +static void send_id_get_response(struct osmo_msc_data *data, int fd); +static void send_ping(struct osmo_msc_data *data); + +/* + * MGCP forwarding code + */ +static int mgcp_do_read(struct bsc_fd *fd) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) fd->data; + struct msgb *mgcp; + int ret; + + mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); + if (!mgcp) { + LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); + return -1; + } + + ret = read(fd->fd, mgcp->data, 4096 - 128); + if (ret <= 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); + msgb_free(mgcp); + return -1; + } else if (ret > 4096 - 128) { + LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); + msgb_free(mgcp); + return -1; + } + + mgcp->l2h = msgb_put(mgcp, ret); + msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD); + return 0; +} + +static int mgcp_do_write(struct bsc_fd *fd, struct msgb *msg) +{ + int ret; + + LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); + + ret = write(fd->fd, msg->data, msg->len); + if (ret != msg->len) + LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno)); + + return ret; +} + +static void mgcp_forward(struct osmo_msc_data *data, struct msgb *msg) +{ + struct msgb *mgcp; + + if (msgb_l2len(msg) > 4096) { + LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n"); + return; + } + + mgcp = msgb_alloc(4096, "mgcp_to_gw"); + if (!mgcp) { + LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n"); + return; + } + + msgb_put(mgcp, msgb_l2len(msg)); + memcpy(mgcp->data, msg->l2h, mgcp->len); + if (write_queue_enqueue(&data->mgcp_agent, mgcp) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n"); + msgb_free(mgcp); + } +} + +static int mgcp_create_port(struct osmo_msc_data *data) +{ + int on; + struct sockaddr_in addr; + + data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); + if (data->mgcp_agent.bfd.fd < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); + return -1; + } + + on = 1; + setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* try to bind the socket */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n"); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + /* connect to the remote */ + addr.sin_port = htons(2427); + if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno)); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + write_queue_init(&data->mgcp_agent, 10); + data->mgcp_agent.bfd.when = BSC_FD_READ; + data->mgcp_agent.bfd.data = data; + data->mgcp_agent.read_cb = mgcp_do_read; + data->mgcp_agent.write_cb = mgcp_do_write; + + if (bsc_register_fd(&data->mgcp_agent.bfd) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); + close(data->mgcp_agent.bfd.fd); + data->mgcp_agent.bfd.fd = -1; + return -1; + } + + return 0; +} + +/* + * Send data to the network + */ +int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto) +{ + ipaccess_prepend_header(msg, proto); + if (write_queue_enqueue(&conn->write_queue, msg) != 0) { + LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto); + msgb_free(msg); + return -1; + } + + return 0; +} + +static int msc_alink_do_write(struct bsc_fd *fd, struct msgb *msg) +{ + int ret; + + LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); + LOGP(DMI, LOGL_DEBUG, "MSC TX %s\n", hexdump(msg->data, msg->len)); + + ret = write(fd->fd, msg->data, msg->len); + if (ret < msg->len) + perror("MSC: Failed to send SCCP"); + + return ret; +} + +static int ipaccess_a_fd_cb(struct bsc_fd *bfd) +{ + int error; + struct msgb *msg = ipaccess_read_msg(bfd, &error); + struct ipaccess_head *hh; + struct osmo_msc_data *data = (struct osmo_msc_data *) bfd->data; + + if (!msg) { + if (error == 0) { + LOGP(DMSC, LOGL_ERROR, "The connection to the MSC was lost.\n"); + bsc_msc_lost(data->msc_con); + return -1; + } + + LOGP(DMSC, LOGL_ERROR, "Failed to parse ip access message: %d\n", error); + return -1; + } + + LOGP(DMSC, LOGL_DEBUG, "From MSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]); + + /* handle base message handling */ + hh = (struct ipaccess_head *) msg->data; + ipaccess_rcvmsg_base(msg, bfd); + + /* initialize the networking. This includes sending a GSM08.08 message */ + if (hh->proto == IPAC_PROTO_IPACCESS) { + if (msg->l2h[0] == IPAC_MSGT_ID_ACK) + initialize_if_needed(data->msc_con); + else if (msg->l2h[0] == IPAC_MSGT_ID_GET) { + send_id_get_response(data, bfd->fd); + } else if (msg->l2h[0] == IPAC_MSGT_PONG) { + bsc_del_timer(&data->pong_timer); + } + } else if (hh->proto == IPAC_PROTO_SCCP) { + sccp_system_incoming(msg); + } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { + mgcp_forward(data, msg); + } + + msgb_free(msg); + return 0; +} + +static void send_ping(struct osmo_msc_data *data) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(4096, 128, "ping"); + if (!msg) { + LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n"); + return; + } + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_PING; + + msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); +} + +static void msc_ping_timeout_cb(void *_data) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) _data; + if (data->ping_timeout < 0) + return; + + send_ping(data); + + /* send another ping in 20 seconds */ + bsc_schedule_timer(&data->ping_timer, data->ping_timeout, 0); + + /* also start a pong timer */ + bsc_schedule_timer(&data->pong_timer, data->pong_timeout, 0); +} + +static void msc_pong_timeout_cb(void *_data) +{ + struct osmo_msc_data *data = (struct osmo_msc_data *) _data; + + LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); + bsc_msc_lost(data->msc_con); +} + +static void msc_connection_connected(struct bsc_msc_connection *con) +{ + struct msc_signal_data sig; + struct osmo_msc_data *data; + int ret, on; + on = 1; + ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + if (ret != 0) + LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); + + data = (struct osmo_msc_data *) con->write_queue.bfd.data; + msc_ping_timeout_cb(data); + + sig.data = data; + dispatch_signal(SS_MSC, S_MSC_CONNECTED, &sig); +} + +/* + * The connection to the MSC was lost and we will need to free all + * resources and then attempt to reconnect. + */ +static void msc_connection_was_lost(struct bsc_msc_connection *msc) +{ + struct msc_signal_data sig; + struct osmo_msc_data *data; + + LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); + + data = (struct osmo_msc_data *) msc->write_queue.bfd.data; + bsc_del_timer(&data->ping_timer); + bsc_del_timer(&data->pong_timer); + + sig.data = data; + dispatch_signal(SS_MSC, S_MSC_LOST, &sig); + + msc->is_authenticated = 0; + bsc_msc_schedule_connect(msc); +} + +static void initialize_if_needed(struct bsc_msc_connection *conn) +{ + struct msgb *msg; + + if (!conn->is_authenticated) { + /* send a gsm 08.08 reset message from here */ + msg = gsm0808_create_reset(); + if (!msg) { + LOGP(DMSC, LOGL_ERROR, "Failed to create the reset message.\n"); + return; + } + + sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0); + msgb_free(msg); + conn->is_authenticated = 1; + } +} + +static void send_id_get_response(struct osmo_msc_data *data, int fd) +{ + struct msgb *msg; + + msg = bsc_msc_id_get_resp(data->bsc_token); + if (!msg) + return; + msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); +} + +int osmo_bsc_msc_init(struct gsm_network *network) +{ + struct osmo_msc_data *data = network->msc_data; + + if (mgcp_create_port(data) != 0) + return -1; + + data->msc_con = bsc_msc_create(data->msc_ip, + data->msc_port, + data->msc_ip_dscp); + if (!data->msc_con) { + LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n"); + return -1; + } + + data->ping_timer.cb = msc_ping_timeout_cb; + data->ping_timer.data = data; + data->pong_timer.cb = msc_pong_timeout_cb; + data->pong_timer.data = data; + + data->msc_con->write_queue.bfd.data = data; + data->msc_con->connection_loss = msc_connection_was_lost; + data->msc_con->connected = msc_connection_connected; + data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; + data->msc_con->write_queue.write_cb = msc_alink_do_write; + bsc_msc_connect(data->msc_con); + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_rf.c b/src/osmo-bsc/osmo_bsc_rf.c new file mode 100644 index 000000000..5652c9df5 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_rf.c @@ -0,0 +1,364 @@ +/* RF Ctl handling socket */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/osmo_bsc_rf.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <openbsc/signal.h> +#include <openbsc/osmo_msc_data.h> + +#include <osmocore/talloc.h> +#include <osmocore/protocol/gsm_12_21.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <errno.h> +#include <unistd.h> + +#define RF_CMD_QUERY '?' +#define RF_CMD_OFF '0' +#define RF_CMD_ON '1' +#define RF_CMD_D_OFF 'd' +#define RF_CMD_ON_G 'g' + +static int lock_each_trx(struct gsm_network *net, int lock) +{ + struct gsm_bts *bts; + + llist_for_each_entry(bts, &net->bts_list, list) { + struct gsm_bts_trx *trx; + llist_for_each_entry(trx, &bts->trx_list, list) { + gsm_trx_lock_rf(trx, lock); + } + } + + return 0; +} + +static void send_resp(struct osmo_bsc_rf_conn *conn, char send) +{ + struct msgb *msg; + + msg = msgb_alloc(10, "RF Query"); + if (!msg) { + LOGP(DINP, LOGL_ERROR, "Failed to allocate response msg.\n"); + return; + } + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = send; + + if (write_queue_enqueue(&conn->queue, msg) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); + msgb_free(msg); + return; + } + + return; +} + + +/* + * Send a + * 'g' when we are in grace mode + * '1' when one TRX is online, + * '0' otherwise + */ +static void handle_query(struct osmo_bsc_rf_conn *conn) +{ + struct gsm_bts *bts; + char send = RF_CMD_OFF; + + if (conn->rf->policy == S_RF_GRACE) + return send_resp(conn, RF_CMD_ON_G); + + llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { + struct gsm_bts_trx *trx; + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nm_state.availability == NM_AVSTATE_OK && + trx->nm_state.operational != NM_STATE_LOCKED) { + send = RF_CMD_ON; + break; + } + } + } + + send_resp(conn, send); +} + +static void rf_check_cb(void *_data) +{ + struct gsm_bts *bts; + struct osmo_bsc_rf *rf = _data; + + llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { + struct gsm_bts_trx *trx; + + /* don't bother to check a booting or missing BTS */ + if (!bts->oml_link || !is_ipaccess_bts(bts)) + continue; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nm_state.availability != NM_AVSTATE_OK || + trx->nm_state.operational != NM_OPSTATE_ENABLED || + trx->nm_state.administrative != NM_STATE_UNLOCKED) { + LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); + ipaccess_drop_oml(bts); + break; + } + } + } +} + +static void send_signal(struct osmo_bsc_rf *rf, int val) +{ + struct rf_signal_data sig; + sig.net = rf->gsm_network; + + rf->policy = val; + dispatch_signal(SS_RF, val, &sig); +} + +static int switch_rf_off(struct osmo_bsc_rf *rf) +{ + lock_each_trx(rf->gsm_network, 1); + send_signal(rf, S_RF_OFF); + + return 0; +} + +static void grace_timeout(void *_data) +{ + struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; + + LOGP(DINP, LOGL_NOTICE, "Grace timeout. Disabling the TRX.\n"); + switch_rf_off(rf); +} + +static int enter_grace(struct osmo_bsc_rf *rf) +{ + rf->grace_timeout.cb = grace_timeout; + rf->grace_timeout.data = rf; + bsc_schedule_timer(&rf->grace_timeout, rf->gsm_network->msc_data->mid_call_timeout, 0); + LOGP(DINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", + rf->gsm_network->msc_data->mid_call_timeout); + + send_signal(rf, S_RF_GRACE); + return 0; +} + +static void rf_delay_cmd_cb(void *data) +{ + struct osmo_bsc_rf *rf = data; + + switch (rf->last_request) { + case RF_CMD_D_OFF: + rf->last_state_command = "RF Direct Off"; + bsc_del_timer(&rf->rf_check); + bsc_del_timer(&rf->grace_timeout); + switch_rf_off(rf); + break; + case RF_CMD_ON: + rf->last_state_command = "RF Direct On"; + bsc_del_timer(&rf->grace_timeout); + lock_each_trx(rf->gsm_network, 0); + send_signal(rf, S_RF_ON); + bsc_schedule_timer(&rf->rf_check, 3, 0); + break; + case RF_CMD_OFF: + rf->last_state_command = "RF Scheduled Off"; + bsc_del_timer(&rf->rf_check); + enter_grace(rf); + break; + } +} + +static int rf_read_cmd(struct bsc_fd *fd) +{ + struct osmo_bsc_rf_conn *conn = fd->data; + char buf[1]; + int rc; + + rc = read(fd->fd, buf, sizeof(buf)); + if (rc != sizeof(buf)) { + LOGP(DINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); + bsc_unregister_fd(fd); + close(fd->fd); + write_queue_clear(&conn->queue); + talloc_free(conn); + return -1; + } + + switch (buf[0]) { + case RF_CMD_QUERY: + handle_query(conn); + break; + case RF_CMD_D_OFF: + case RF_CMD_ON: + case RF_CMD_OFF: + conn->rf->last_request = buf[0]; + if (!bsc_timer_pending(&conn->rf->delay_cmd)) + bsc_schedule_timer(&conn->rf->delay_cmd, 1, 0); + break; + default: + conn->rf->last_state_command = "Unknown command"; + LOGP(DINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); + break; + } + + return 0; +} + +static int rf_write_cmd(struct bsc_fd *fd, struct msgb *msg) +{ + int rc; + + rc = write(fd->fd, msg->data, msg->len); + if (rc != msg->len) { + LOGP(DINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what) +{ + struct osmo_bsc_rf_conn *conn; + struct osmo_bsc_rf *rf = bfd->data; + struct sockaddr_un addr; + socklen_t len = sizeof(addr); + int fd; + + fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); + if (fd < 0) { + LOGP(DINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", + errno, strerror(errno)); + return -1; + } + + conn = talloc_zero(rf, struct osmo_bsc_rf_conn); + if (!conn) { + LOGP(DINP, LOGL_ERROR, "Failed to allocate mem.\n"); + close(fd); + return -1; + } + + write_queue_init(&conn->queue, 10); + conn->queue.bfd.data = conn; + conn->queue.bfd.fd = fd; + conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; + conn->queue.read_cb = rf_read_cmd; + conn->queue.write_cb = rf_write_cmd; + conn->rf = rf; + + if (bsc_register_fd(&conn->queue.bfd) != 0) { + close(fd); + talloc_free(conn); + return -1; + } + + return 0; +} + +struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) +{ + unsigned int namelen; + struct sockaddr_un local; + struct bsc_fd *bfd; + struct osmo_bsc_rf *rf; + int rc; + + rf = talloc_zero(NULL, struct osmo_bsc_rf); + if (!rf) { + LOGP(DINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); + return NULL; + } + + bfd = &rf->listen; + bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "Can not create socket. %d/%s\n", + errno, strerror(errno)); + return NULL; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", + local.sun_path, errno, strerror(errno)); + close(bfd->fd); + talloc_free(rf); + return NULL; + } + + if (listen(bfd->fd, 0) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); + close(bfd->fd); + talloc_free(rf); + return NULL; + } + + bfd->when = BSC_FD_READ; + bfd->cb = rf_ctl_accept; + bfd->data = rf; + + if (bsc_register_fd(bfd) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to register bfd.\n"); + close(bfd->fd); + talloc_free(rf); + return NULL; + } + + rf->gsm_network = net; + rf->policy = S_RF_ON; + rf->last_state_command = ""; + + /* check the rf state */ + rf->rf_check.data = rf; + rf->rf_check.cb = rf_check_cb; + + /* delay cmd handling */ + rf->delay_cmd.data = rf; + rf->delay_cmd.cb = rf_delay_cmd_cb; + + return rf; +} + diff --git a/src/osmo-bsc/osmo_bsc_sccp.c b/src/osmo-bsc/osmo_bsc_sccp.c new file mode 100644 index 000000000..1abb47394 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_sccp.c @@ -0,0 +1,288 @@ +/* Interaction with the SCCP subsystem */ +/* + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/gsm_data.h> +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_bsc_grace.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <openbsc/signal.h> + +#include <osmocore/gsm0808.h> +#include <osmocore/talloc.h> +#include <osmocore/protocol/gsm_08_08.h> + +#include <osmocom/sccp/sccp.h> + +/* SCCP helper */ +#define SCCP_IT_TIMER 60 + +static LLIST_HEAD(active_connections); + +static void free_queued(struct osmo_bsc_sccp_con *conn) +{ + struct msgb *msg; + + while (!llist_empty(&conn->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&conn->sccp_queue); + msgb_free(msg); + } + + conn->sccp_queue_size = 0; +} + +static void send_queued(struct osmo_bsc_sccp_con *conn) +{ + struct msgb *msg; + + while (!llist_empty(&conn->sccp_queue)) { + /* this is not allowed to fail */ + msg = msgb_dequeue(&conn->sccp_queue); + sccp_connection_write(conn->sccp, msg); + msgb_free(msg); + conn->sccp_queue_size -= 1; + } +} + +static void msc_outgoing_sccp_data(struct sccp_connection *conn, + struct msgb *msg, unsigned int len) +{ + struct osmo_bsc_sccp_con *bsc_con = + (struct osmo_bsc_sccp_con *) conn->data_ctx; + + bsc_handle_dt1(bsc_con, msg, len); +} + +static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) +{ + struct osmo_bsc_sccp_con *con_data; + + if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { + con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; + if(con_data->conn) { + LOGP(DMSC, LOGL_ERROR, + "ERROR: The lchan is still associated\n."); + gsm0808_clear(con_data->conn); + subscr_con_free(con_data->conn); + con_data->conn = NULL; + } + + con_data->sccp = NULL; + free_queued(con_data); + sccp_connection_free(conn); + bsc_delete_connection(con_data); + } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { + LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); + con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; + + bsc_del_timer(&con_data->sccp_cc_timeout); + bsc_schedule_timer(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); + + send_queued(con_data); + } +} + +static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) +{ + if (data->conn) { + gsm0808_clear(data->conn); + subscr_con_free(data->conn); + data->conn = NULL; + } + + free_queued(data); + sccp_connection_force_free(data->sccp); + data->sccp = NULL; + bsc_delete_connection(data); +} + +static void sccp_it_timeout(void *_data) +{ + struct osmo_bsc_sccp_con *data = + (struct osmo_bsc_sccp_con *) _data; + + sccp_connection_send_it(data->sccp); + bsc_schedule_timer(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); +} + +static void sccp_cc_timeout(void *_data) +{ + struct osmo_bsc_sccp_con *data = + (struct osmo_bsc_sccp_con *) _data; + + if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) + return; + + LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); + bsc_sccp_force_free(data); +} + +static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, void *data) +{ + struct gsm_network *net = (struct gsm_network *) data; + msc_queue_write(net->msc_data->msc_con, msg, IPAC_PROTO_SCCP); +} + +static int msc_sccp_accept(struct sccp_connection *connection, void *data) +{ + LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); + return -1; +} + +static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) +{ + struct gsm_network *net = (struct gsm_network *) data; + return bsc_handle_udt(net, net->msc_data->msc_con, msgb, length); +} + +int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + struct sccp_connection *sccp = conn->sccp; + + if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { + LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); + msgb_free(msg); + } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED + && conn->sccp_queue_size == 0) { + sccp_connection_write(sccp, msg); + msgb_free(msg); + } else if (conn->sccp_queue_size > 10) { + LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); + msgb_free(msg); + } else { + LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); + conn->sccp_queue_size += 1; + msgb_enqueue(&conn->sccp_queue, msg); + } + + return 0; +} + +int bsc_create_new_connection(struct gsm_subscriber_connection *conn) +{ + struct gsm_network *net; + struct osmo_bsc_sccp_con *bsc_con; + struct sccp_connection *sccp; + + net = conn->bts->network; + if (!net->msc_data->msc_con->is_authenticated) { + LOGP(DMSC, LOGL_ERROR, "Not connected to a MSC. Not forwarding data.\n"); + return -1; + } + + if (!bsc_grace_allow_new_connection(net)) { + LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); + return -1; + } + + sccp = sccp_connection_socket(); + if (!sccp) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); + if (!bsc_con) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n"); + sccp_connection_free(sccp); + return -1; + } + + /* callbacks */ + sccp->state_cb = msc_outgoing_sccp_state; + sccp->data_cb = msc_outgoing_sccp_data; + sccp->data_ctx = bsc_con; + + /* prepare the timers */ + bsc_con->sccp_it_timeout.cb = sccp_it_timeout; + bsc_con->sccp_it_timeout.data = bsc_con; + bsc_con->sccp_cc_timeout.cb = sccp_cc_timeout; + bsc_con->sccp_cc_timeout.data = bsc_con; + + INIT_LLIST_HEAD(&bsc_con->sccp_queue); + + bsc_con->sccp = sccp; + bsc_con->msc_con = net->msc_data->msc_con; + bsc_con->conn = conn; + llist_add(&bsc_con->entry, &active_connections); + conn->sccp_con = bsc_con; + return 0; +} + +int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + bsc_schedule_timer(&conn->sccp_cc_timeout, 10, 0); + sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg); + msgb_free(msg); + return 0; +} + +int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp) +{ + if (!sccp) + return 0; + + if (sccp->conn) + LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n"); + + llist_del(&sccp->entry); + bsc_del_timer(&sccp->sccp_it_timeout); + bsc_del_timer(&sccp->sccp_cc_timeout); + talloc_free(sccp); + return 0; +} + +static void bsc_close_connections(struct bsc_msc_connection *msc_con) +{ + struct osmo_bsc_sccp_con *con, *tmp; + + llist_for_each_entry_safe(con, tmp, &active_connections, entry) + bsc_sccp_force_free(con); +} + +static int handle_msc_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmo_msc_data *data; + + if (subsys != SS_MSC) + return 0; + + data = (struct osmo_msc_data *) signal_data; + if (signal == S_MSC_LOST) + bsc_close_connections(data->msc_con); + + return 0; +} + +int osmo_bsc_sccp_init(struct gsm_network *gsmnet) +{ + sccp_set_log_area(DSCCP); + sccp_system_init(msc_sccp_write_ipa, gsmnet); + sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL); + sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet); + + register_signal_handler(SS_MSC, handle_msc_signal, gsmnet); + + return 0; +} diff --git a/src/osmo-bsc/osmo_bsc_vty.c b/src/osmo-bsc/osmo_bsc_vty.c new file mode 100644 index 000000000..166774275 --- /dev/null +++ b/src/osmo-bsc/osmo_bsc_vty.c @@ -0,0 +1,311 @@ +/* Osmo BSC VTY Configuration */ +/* (C) 2009-2010 by Holger Hans Peter Freyther + * (C) 2009-2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/gsm_data.h> +#include <openbsc/osmo_msc_data.h> +#include <openbsc/vty.h> + +#include <osmocore/talloc.h> + + +#define IPA_STR "IP.ACCESS specific\n" + +extern struct gsm_network *bsc_gsmnet; + +static struct osmo_msc_data *osmo_msc_data(struct vty *vty) +{ + return bsc_gsmnet->msc_data; +} + +static struct cmd_node msc_node = { + MSC_NODE, + "%s(msc)#", + 1, +}; + +DEFUN(cfg_net_msc, cfg_net_msc_cmd, + "msc", "Configure MSC details") +{ + vty->index = bsc_gsmnet; + vty->node = MSC_NODE; + + return CMD_SUCCESS; +} + +static int config_write_msc(struct vty *vty) +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + vty_out(vty, " msc%s", VTY_NEWLINE); + if (data->bsc_token) + vty_out(vty, " token %s%s", data->bsc_token, VTY_NEWLINE); + if (data->core_ncc != -1) + vty_out(vty, " core-mobile-network-code %d%s", + data->core_ncc, VTY_NEWLINE); + if (data->core_mcc != -1) + vty_out(vty, " core-mobile-country-code %d%s", + data->core_mcc, VTY_NEWLINE); + vty_out(vty, " ip.access rtp-base %d%s", data->rtp_base, VTY_NEWLINE); + vty_out(vty, " ip %s%s", data->msc_ip, VTY_NEWLINE); + vty_out(vty, " port %d%s", data->msc_port, VTY_NEWLINE); + vty_out(vty, " ip-dscp %d%s", data->msc_ip_dscp, VTY_NEWLINE); + vty_out(vty, " timeout-ping %d%s", data->ping_timeout, VTY_NEWLINE); + vty_out(vty, " timeout-pong %d%s", data->pong_timeout, VTY_NEWLINE); + if (data->mid_call_txt) + vty_out(vty, "mid-call-text %s%s", data->mid_call_txt, VTY_NEWLINE); + vty_out(vty, " mid-call-timeout %d%s", data->mid_call_timeout, VTY_NEWLINE); + if (data->ussd_welcome_txt) + vty_out(vty, " bsc-welcome-text %s%s", data->ussd_welcome_txt, VTY_NEWLINE); + + if (data->audio_length != 0) { + int i; + + vty_out(vty, " codec_list "); + for (i = 0; i < data->audio_length; ++i) { + if (i != 0) + vty_out(vty, ", "); + + if (data->audio_support[i]->hr) + vty_out(vty, "hr%.1u", data->audio_support[i]->ver); + else + vty_out(vty, "fr%.1u", data->audio_support[i]->ver); + } + vty_out(vty, "%s", VTY_NEWLINE); + + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_token, + cfg_net_bsc_token_cmd, + "token TOKEN", + "A token for the BSC to be sent to the MSC") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + bsc_replace_string(data, &data->bsc_token, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_ncc, + cfg_net_bsc_ncc_cmd, + "core-mobile-network-code <1-999>", + "Use this network code for the backbone\n" "NCC value\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->core_ncc = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_mcc, + cfg_net_bsc_mcc_cmd, + "core-mobile-country-code <1-999>", + "Use this country code for the backbone\n" "MCC value\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->core_mcc = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_rtp_base, + cfg_net_bsc_rtp_base_cmd, + "ip.access rtp-base <1-65000>", + IPA_STR + "Set the rtp-base port for the RTP stream\n" + "Port number\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->rtp_base = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_bsc_codec_list, + cfg_net_bsc_codec_list_cmd, + "codec-list .LIST", + "Set the allowed audio codecs\n" + "List of audio codecs\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + int saw_fr, saw_hr; + int i; + + saw_fr = saw_hr = 0; + + /* free the old list... if it exists */ + if (data->audio_support) { + talloc_free(data->audio_support); + data->audio_support = NULL; + data->audio_length = 0; + } + + /* create a new array */ + data->audio_support = + talloc_zero_array(data, struct gsm_audio_support *, argc); + data->audio_length = argc; + + for (i = 0; i < argc; ++i) { + /* check for hrX or frX */ + if (strlen(argv[i]) != 3 + || argv[i][1] != 'r' + || (argv[i][0] != 'h' && argv[i][0] != 'f') + || argv[i][2] < 0x30 + || argv[i][2] > 0x39) + goto error; + + data->audio_support[i] = talloc_zero(data->audio_support, + struct gsm_audio_support); + data->audio_support[i]->ver = atoi(argv[i] + 2); + + if (strncmp("hr", argv[i], 2) == 0) { + data->audio_support[i]->hr = 1; + saw_hr = 1; + } else if (strncmp("fr", argv[i], 2) == 0) { + data->audio_support[i]->hr = 0; + saw_fr = 1; + } + + if (saw_hr && saw_fr) { + vty_out(vty, "Can not have full-rate and half-rate codec.%s", + VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + } + + return CMD_SUCCESS; + +error: + vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", + argv[i], VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; +} + +DEFUN(cfg_net_msc_ip, + cfg_net_msc_ip_cmd, + "ip A.B.C.D", "Set the MSC/MUX IP address.") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + + bsc_replace_string(data, &data->msc_ip, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_port, + cfg_net_msc_port_cmd, + "port <1-65000>", + "Set the MSC/MUX port.") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->msc_port = atoi(argv[0]); + return CMD_SUCCESS; +} + + +DEFUN(cfg_net_msc_prio, + cfg_net_msc_prio_cmd, + "ip-dscp <0-255>", + "Set the IP_TOS socket attribite") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->msc_ip_dscp = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_ping_time, + cfg_net_msc_ping_time_cmd, + "timeout-ping NR", + "Set the PING interval, negative for not sending PING") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->ping_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_pong_time, + cfg_net_msc_pong_time_cmd, + "timeout-pong NR", + "Set the time to wait for a PONG.") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->pong_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_mid_call_text, + cfg_net_msc_mid_call_text_cmd, + "mid-call-text .TEXT", + "Set the USSD notifcation to be send.\n" "Text to be sent\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + char *txt = argv_concat(argv, argc, 0); + if (!txt) + return CMD_WARNING; + + bsc_replace_string(data, &data->mid_call_txt, txt); + talloc_free(txt); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_mid_call_timeout, + cfg_net_msc_mid_call_timeout_cmd, + "mid-call-timeout NR", + "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + data->mid_call_timeout = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_msc_welcome_ussd, + cfg_net_msc_welcome_ussd_cmd, + "bsc-welcome-text .TEXT", + "Set the USSD notification to be sent.\n" "Text to be sent\n") +{ + struct osmo_msc_data *data = osmo_msc_data(vty); + char *str = argv_concat(argv, argc, 0); + if (!str) + return CMD_WARNING; + + bsc_replace_string(data, &data->ussd_welcome_txt, str); + talloc_free(str); + return CMD_SUCCESS; +} + +int bsc_vty_init_extra(void) +{ + install_element(GSMNET_NODE, &cfg_net_msc_cmd); + install_node(&msc_node, config_write_msc); + install_default(MSC_NODE); + install_element(MSC_NODE, &cfg_net_bsc_token_cmd); + install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd); + install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd); + install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd); + install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd); + install_element(MSC_NODE, &cfg_net_msc_ip_cmd); + install_element(MSC_NODE, &cfg_net_msc_port_cmd); + install_element(MSC_NODE, &cfg_net_msc_prio_cmd); + install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd); + install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); + install_element(MSC_NODE, &cfg_net_msc_mid_call_text_cmd); + install_element(MSC_NODE, &cfg_net_msc_mid_call_timeout_cmd); + install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd); + + return 0; +} |