diff options
Diffstat (limited to 'src/ipaccess')
-rw-r--r-- | src/ipaccess/Makefile.am | 21 | ||||
-rw-r--r-- | src/ipaccess/Makefile.in | 520 | ||||
-rw-r--r-- | src/ipaccess/ipaccess-config.c | 865 | ||||
-rw-r--r-- | src/ipaccess/ipaccess-find.c | 227 | ||||
-rw-r--r-- | src/ipaccess/ipaccess-firmware.c | 135 | ||||
-rw-r--r-- | src/ipaccess/ipaccess-proxy.c | 1373 | ||||
-rw-r--r-- | src/ipaccess/network_listen.c | 251 |
7 files changed, 3392 insertions, 0 deletions
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am new file mode 100644 index 000000000..144cca1ee --- /dev/null +++ b/src/ipaccess/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +bin_PROGRAMS = ipaccess-find ipaccess-config ipaccess-proxy + +ipaccess_find_SOURCES = ipaccess-find.c + +ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c + +# FIXME: resolve the bogus dependencies patched around here: +ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -ldl -ldbi $(LIBCRYPT) + +ipaccess_proxy_SOURCES = ipaccess-proxy.c +ipaccess_proxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a diff --git a/src/ipaccess/Makefile.in b/src/ipaccess/Makefile.in new file mode 100644 index 000000000..864fa316d --- /dev/null +++ b/src/ipaccess/Makefile.in @@ -0,0 +1,520 @@ +# 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 = ipaccess-find$(EXEEXT) ipaccess-config$(EXEEXT) \ + ipaccess-proxy$(EXEEXT) +subdir = src/ipaccess +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_ipaccess_config_OBJECTS = ipaccess-config.$(OBJEXT) \ + ipaccess-firmware.$(OBJEXT) network_listen.$(OBJEXT) +ipaccess_config_OBJECTS = $(am_ipaccess_config_OBJECTS) +ipaccess_config_DEPENDENCIES = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a +am_ipaccess_find_OBJECTS = ipaccess-find.$(OBJEXT) +ipaccess_find_OBJECTS = $(am_ipaccess_find_OBJECTS) +ipaccess_find_LDADD = $(LDADD) +am_ipaccess_proxy_OBJECTS = ipaccess-proxy.$(OBJEXT) +ipaccess_proxy_OBJECTS = $(am_ipaccess_proxy_OBJECTS) +ipaccess_proxy_DEPENDENCIES = \ + $(top_builddir)/src/libcommon/libcommon.a +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 = $(ipaccess_config_SOURCES) $(ipaccess_find_SOURCES) \ + $(ipaccess_proxy_SOURCES) +DIST_SOURCES = $(ipaccess_config_SOURCES) $(ipaccess_find_SOURCES) \ + $(ipaccess_proxy_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) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) +ipaccess_find_SOURCES = ipaccess-find.c +ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c + +# FIXME: resolve the bogus dependencies patched around here: +ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -ldl -ldbi $(LIBCRYPT) + +ipaccess_proxy_SOURCES = ipaccess-proxy.c +ipaccess_proxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a +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/ipaccess/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/ipaccess/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) +ipaccess-config$(EXEEXT): $(ipaccess_config_OBJECTS) $(ipaccess_config_DEPENDENCIES) + @rm -f ipaccess-config$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ipaccess_config_OBJECTS) $(ipaccess_config_LDADD) $(LIBS) +ipaccess-find$(EXEEXT): $(ipaccess_find_OBJECTS) $(ipaccess_find_DEPENDENCIES) + @rm -f ipaccess-find$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ipaccess_find_OBJECTS) $(ipaccess_find_LDADD) $(LIBS) +ipaccess-proxy$(EXEEXT): $(ipaccess_proxy_OBJECTS) $(ipaccess_proxy_DEPENDENCIES) + @rm -f ipaccess-proxy$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(ipaccess_proxy_OBJECTS) $(ipaccess_proxy_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipaccess-config.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipaccess-find.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipaccess-firmware.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipaccess-proxy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network_listen.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/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c new file mode 100644 index 000000000..d02faea3a --- /dev/null +++ b/src/ipaccess/ipaccess-config.c @@ -0,0 +1,865 @@ +/* ip.access nanoBTS configuration tool */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (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 <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <sys/fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <openbsc/ipaccess.h> +#include <openbsc/gsm_data.h> +#include <openbsc/e1_input.h> +#include <openbsc/abis_nm.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> +#include <openbsc/network_listen.h> +#include <osmocore/talloc.h> + +static struct gsm_network *gsmnet; + +static int net_listen_testnr; +static int restart; +static char *prim_oml_ip; +static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; +static char *unit_id; +static u_int16_t nv_flags; +static u_int16_t nv_mask; +static char *software = NULL; +static int sw_load_state = 0; +static int oml_state = 0; +static int dump_files = 0; +static char *firmware_analysis = NULL; +static int found_trx = 0; + +struct sw_load { + u_int8_t file_id[255]; + u_int8_t file_id_len; + + u_int8_t file_version[255]; + u_int8_t file_version_len; +}; + +static void *tall_ctx_config = NULL; +static struct sw_load *sw_load1 = NULL; +static struct sw_load *sw_load2 = NULL; + +/* +static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; +static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; +*/ + +/* + * Callback function for NACK on the OML NM + * + * Currently we send the config requests but don't check the + * result. The nanoBTS will send us a NACK when we did something the + * BTS didn't like. + */ +static int ipacc_msg_nack(u_int8_t mt) +{ + fprintf(stderr, "Failure to set attribute. This seems fatal\n"); + exit(-1); + return 0; +} + +static void check_restart_or_exit(struct gsm_bts_trx *trx) +{ + if (restart) { + abis_nm_ipaccess_restart(trx); + } else { + exit(0); + } +} + +static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts_trx *trx) +{ + if (sw_load_state == 1) { + fprintf(stderr, "The new software is activaed.\n"); + check_restart_or_exit(trx); + } else if (oml_state == 1) { + fprintf(stderr, "Set the NV Attributes.\n"); + check_restart_or_exit(trx); + } + + return 0; +} + +static const uint8_t phys_conf_min[] = { 0x02 }; + +static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) +{ + uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); + int num_arfcn; + unsigned int arfcnlist_size; + + /* Create whitelist from rxlevels */ + physconf_buf[0] = phys_conf_min[0]; + physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; + num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); + arfcnlist_size = num_arfcn * 2; + *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); + DEBUGP(DNM, "physconf_buf (%s)\n", hexdump(physconf_buf, arfcnlist_size+4)); + return arfcnlist_size+4; +} + +static int nwl_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_bts_trx *trx; + uint8_t physconf_buf[2*NUM_ARFCNS+16]; + uint16_t physconf_len; + + switch (signal) { + case S_IPAC_NWL_COMPLETE: + trx = signal_data; + DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); + switch (trx->ipaccess.test_nr) { + case NM_IPACC_TESTNO_CHAN_USAGE: + /* Dump RxLev results */ + //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); + /* Create whitelist from results */ + physconf_len = build_physconf(physconf_buf, + &trx->ipaccess.rxlev_stat); + /* Start next test abbout BCCH channel usage */ + ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, + physconf_buf, physconf_len); + break; + case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: + /* Dump BCCH RxLev results */ + //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); + /* Create whitelist from results */ + physconf_len = build_physconf(physconf_buf, + &trx->ipaccess.rxlev_stat); + /* Start next test about BCCH info */ + ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, + physconf_buf, physconf_len); + break; + case NM_IPACC_TESTNO_BCCH_INFO: +#if 0 + /* re-start full process with CHAN_USAGE */ + DEBUGP(DNM, "starting next test cycle\n"); + ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, + sizeof(phys_conf_min)); +#else + exit(0); +#endif + break; + } + break; + } + return 0; +} + +static int nm_state_event(int evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst); + +static int nm_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct ipacc_ack_signal_data *ipacc_data; + struct nm_statechg_signal_data *nsd; + + switch (signal) { + case S_NM_IPACC_NACK: + ipacc_data = signal_data; + return ipacc_msg_nack(ipacc_data->msg_type); + case S_NM_IPACC_ACK: + ipacc_data = signal_data; + return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); + case S_NM_IPACC_RESTART_ACK: + printf("The BTS has acked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_IPACC_RESTART_NACK: + printf("The BTS has nacked the restart. Exiting.\n"); + exit(0); + break; + case S_NM_STATECHG_OPER: + case S_NM_STATECHG_ADM: + nsd = signal_data; + nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, + nsd->new_state, nsd->obj_inst); + break; + default: + break; + } + + return 0; +} + +/* callback function passed to the ABIS OML code */ +static int percent; +static int percent_old; +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, + void *data, void *param) +{ + struct msgb *msg; + struct gsm_bts_trx *trx; + + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + trx = (struct gsm_bts_trx *) data; + + switch (event) { + case NM_MT_LOAD_INIT_ACK: + fprintf(stdout, "Software Load Initiate ACK\n"); + break; + case NM_MT_LOAD_INIT_NACK: + fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); + exit(5); + break; + case NM_MT_LOAD_END_ACK: + fprintf(stderr, "LOAD END ACK..."); + /* now make it the default */ + sw_load_state = 1; + + msg = msgb_alloc(1024, "sw: nvattr"); + msg->l2h = msgb_put(msg, 3); + msg->l3h = &msg->l2h[3]; + + /* activate software */ + if (sw_load1) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len, + sw_load1->file_version); + } + + if (sw_load2) { + msgb_v_put(msg, NM_ATT_SW_DESCR); + msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id); + msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len, + sw_load2->file_version); + } + + /* fill in the data */ + msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; + msg->l2h[1] = msgb_l3len(msg) >> 8; + msg->l2h[2] = msgb_l3len(msg) & 0xff; + printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); + abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); + msgb_free(msg); + break; + case NM_MT_LOAD_END_NACK: + fprintf(stderr, "ERROR: Software Load End NACK\n"); + exit(3); + break; + case NM_MT_ACTIVATE_SW_NACK: + fprintf(stderr, "ERROR: Activate Software NACK\n"); + exit(4); + break; + case NM_MT_ACTIVATE_SW_ACK: + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(trx->bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + case NM_MT_LOAD_ABORT: + fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); + exit(6); + break; + } + return 0; +} + +static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); + + msgb_put_u32(nmsg, ip); + msgb_put_u32(nmsg, mask); +} + +static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); + msgb_put_u32(nmsg, addr); + msgb_put_u32(nmsg, mask); + msgb_put_u32(nmsg, gw); +} + +static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) +{ + msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, + (const uint8_t *)unit_id); +} + +static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) +{ + int len; + + /* 0x88 + IP + port */ + len = 1 + sizeof(ip) + sizeof(port); + + msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); + msgb_put_u16(nmsg, len); + + msgb_put_u8(nmsg, 0x88); + + /* IP address */ + msgb_put_u32(nmsg, ip); + + /* port number */ + msgb_put_u16(nmsg, port); +} + +static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) +{ + msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); + msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); + msgb_put_u8(nmsg, nv_flags & 0xff); + msgb_put_u8(nmsg, nv_mask & 0xff); + msgb_put_u8(nmsg, nv_flags >> 8); + msgb_put_u8(nmsg, nv_mask >> 8); +} + +/* human-readable names for the ip.access nanoBTS NVRAM Flags */ +static const struct value_string ipa_nvflag_strs[] = { + { 0x0001, "static-ip" }, + { 0x0002, "static-gw" }, + { 0x0004, "no-dhcp-vsi" }, + { 0x0008, "dhcp-enabled" }, + { 0x0040, "led-disabled" }, + { 0x0100, "secondary-oml-enabled" }, + { 0x0200, "diag-enabled" }, + { 0x0400, "cli-enabled" }, + { 0x0800, "http-enabled" }, + { 0x1000, "post-enabled" }, + { 0x2000, "snmp-enabled" }, + { 0, NULL } +}; + +/* set the flags in flags/mask according to a string-identified flag and 'enable' */ +static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) +{ + int rc; + rc = get_string_value(ipa_nvflag_strs, name); + if (rc < 0) + return rc; + + *mask |= rc; + if (en) + *flags |= rc; + else + *flags &= ~rc; + + return 0; +} + +static void bootstrap_om(struct gsm_bts_trx *trx) +{ + struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); + int need_to_set_attr = 0; + int len; + + printf("OML link established using TRX %d\n", trx->nr); + + if (unit_id) { + len = strlen(unit_id); + if (len > nmsg->data_len-10) + goto out_err; + printf("setting Unit ID to '%s'\n", unit_id); + nv_put_unit_id(nmsg, unit_id); + need_to_set_attr = 1; + } + if (prim_oml_ip) { + struct in_addr ia; + + if (!inet_aton(prim_oml_ip, &ia)) { + fprintf(stderr, "invalid IP address: %s\n", + prim_oml_ip); + goto out_err; + } + + printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); + nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); + need_to_set_attr = 1; + } + if (nv_mask) { + printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", + nv_flags, nv_mask); + nv_put_flags(nmsg, nv_flags, nv_mask); + need_to_set_attr = 1; + } + if (bts_ip_addr && bts_ip_mask) { + struct in_addr ia_addr, ia_mask; + + if (!inet_aton(bts_ip_addr, &ia_addr)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_addr); + goto out_err; + } + + if (!inet_aton(bts_ip_mask, &ia_mask)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_mask); + goto out_err; + } + + printf("setting static IP Address/Mask\n"); + nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); + need_to_set_attr = 1; + } + if (bts_ip_gw) { + struct in_addr ia_gw; + + if (!inet_aton(bts_ip_gw, &ia_gw)) { + fprintf(stderr, "invalid IP address: %s\n", + bts_ip_gw); + goto out_err; + } + + printf("setting static IP Gateway\n"); + /* we only set the default gateway with zero addr/mask */ + nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); + need_to_set_attr = 1; + } + + if (need_to_set_attr) { + abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); + oml_state = 1; + } + + if (restart && !prim_oml_ip && !software) { + printf("restarting BTS\n"); + abis_nm_ipaccess_restart(trx); + } + +out_err: + msgb_free(nmsg); +} + +static int nm_state_event(int evt, u_int8_t obj_class, void *obj, + struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, + struct abis_om_obj_inst *obj_inst) +{ + if (obj_class == NM_OC_BASEB_TRANSC) { + if (!found_trx && obj_inst->trx_nr != 0xff) { + struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); + bootstrap_om(trx); + found_trx = 1; + } + } else if (evt == S_NM_STATECHG_OPER && + obj_class == NM_OC_RADIO_CARRIER && + new_state->availability == 3) { + struct gsm_bts_trx *trx = obj; + + if (net_listen_testnr) + ipac_nwl_test_start(trx, net_listen_testnr, + phys_conf_min, sizeof(phys_conf_min)); + else if (software) { + int rc; + printf("Attempting software upload with '%s'\n", software); + rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); + if (rc < 0) { + fprintf(stderr, "Failed to start software load\n"); + exit(-3); + } + } + } + return 0; +} + +static struct sw_load *create_swload(struct sdp_header *header) +{ + struct sw_load *load; + + load = talloc_zero(tall_ctx_config, struct sw_load); + + strncpy((char *)load->file_id, header->firmware_info.sw_part, 20); + load->file_id_len = strlen(header->firmware_info.sw_part) + 1; + + strncpy((char *)load->file_version, header->firmware_info.version, 20); + load->file_version_len = strlen(header->firmware_info.version) + 1; + + return load; +} + +static int find_sw_load_params(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return -1; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return -1; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + if (close(fd) != 0) { + perror("Close failed.\n"); + return -1; + } + + /* try to find what we are looking for */ + llist_for_each_entry(header, entry, entry) { + if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { + sw_load1 = create_swload(header); + } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { + sw_load2 = create_swload(header); + } + } + + if (!sw_load1 || !sw_load2) { + fprintf(stderr, "Did not find data.\n"); + talloc_free(tall_firm_ctx); + return -1; + } + + talloc_free(tall_firm_ctx); + return 0; +} + +static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) +{ + int out_fd; + int copied; + char filename[4096]; + off_t target; + + if (!dump_files) + return; + + if (sub_entry->header_entry.something1 == 0) + return; + + snprintf(filename, sizeof(filename), "part.%d", part++); + out_fd = open(filename, O_WRONLY | O_CREAT, 0660); + if (out_fd < 0) { + perror("Can not dump firmware"); + return; + } + + target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; + if (lseek(fd, target, SEEK_SET) != target) { + perror("seek failed"); + close(out_fd); + return; + } + + for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { + char c; + if (read(fd, &c, sizeof(c)) != sizeof(c)) { + perror("copy failed"); + break; + } + + if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { + perror("write failed"); + break; + } + } + + close(out_fd); +} + +static void analyze_firmware(const char *filename) +{ + struct stat stat; + struct sdp_header *header; + struct sdp_header_item *sub_entry; + struct llist_head *entry; + int fd; + void *tall_firm_ctx = 0; + int part = 0; + + entry = talloc_zero(tall_firm_ctx, struct llist_head); + INIT_LLIST_HEAD(entry); + + printf("Opening possible firmware '%s'\n", filename); + fd = open(filename, O_RDONLY); + if (!fd) { + perror("nada"); + return; + } + + /* verify the file */ + if (fstat(fd, &stat) == -1) { + perror("Can not stat the file"); + return; + } + + ipaccess_analyze_file(fd, stat.st_size, 0, entry); + + llist_for_each_entry(header, entry, entry) { + printf("Printing header information:\n"); + printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); + printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); + printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); + printf("sw_part: %.20s\n", header->firmware_info.sw_part); + printf("text1: %.64s\n", header->firmware_info.text1); + printf("time: %.12s\n", header->firmware_info.time); + printf("date: %.14s\n", header->firmware_info.date); + printf("text2: %.10s\n", header->firmware_info.text2); + printf("version: %.20s\n", header->firmware_info.version); + printf("subitems...\n"); + + llist_for_each_entry(sub_entry, &header->header_list, entry) { + printf("\tsomething1: %u\n", sub_entry->header_entry.something1); + printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); + printf("\ttime: %.12s\n", sub_entry->header_entry.time); + printf("\tdate: %.14s\n", sub_entry->header_entry.date); + printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); + printf("\tversion: %.20s\n", sub_entry->header_entry.version); + printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); + printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); + printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); + printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); + printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); + printf("\n\n"); + + dump_entry(sub_entry, part++, fd); + } + printf("\n\n"); + } + + if (close(fd) != 0) { + perror("Close failed.\n"); + return; + } + + talloc_free(tall_firm_ctx); +} + +static void print_usage(void) +{ + printf("Usage: ipaccess-config\n"); +} + +static void print_help(void) +{ +#if 0 + printf("Commmands for reading from the BTS:\n"); + printf(" -D --dump\t\t\tDump the BTS configuration\n"); + printf("\n"); +#endif + printf("Commmands for writing to the BTS:\n"); + printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); + printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); + printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); + printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); + printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); + printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); + printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); + printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); + printf(" -l --listen TESTNR\t\tPerform specified test number\n"); + printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); + printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); + printf("\n"); + printf("Miscellaneous commands:\n"); + printf(" -h --help\t\t\tthis text\n"); + printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); + printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); +} + +extern void bts_model_nanobts_init(); + +int main(int argc, char **argv) +{ + struct gsm_bts *bts; + struct sockaddr_in sin; + int rc, option_index = 0, stream_id = 0xff; + struct log_target *stderr_target; + + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + log_set_log_level(stderr_target, 0); + log_parse_category_mask(stderr_target, "DNM,0"); + bts_model_nanobts_init(); + + printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + while (1) { + int c; + unsigned long ul; + char *slash; + static struct option long_options[] = { + { "unit-id", 1, 0, 'u' }, + { "oml-ip", 1, 0, 'o' }, + { "ip-address", 1, 0, 'i' }, + { "ip-gateway", 1, 0, 'g' }, + { "restart", 0, 0, 'r' }, + { "nvram-flags", 1, 0, 'n' }, + { "nvattr-set", 1, 0, 'S' }, + { "nvattr-unset", 1, 0, 'U' }, + { "help", 0, 0, 'h' }, + { "listen", 1, 0, 'l' }, + { "stream-id", 1, 0, 's' }, + { "software", 1, 0, 'd' }, + { "firmware", 1, 0, 'f' }, + { "write-firmware", 0, 0, 'w' }, + { "disable-color", 0, 0, 'c'}, + { 0, 0, 0, 0 }, + }; + + c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:hs:d:f:wc", long_options, + &option_index); + + if (c == -1) + break; + + switch (c) { + case 'u': + unit_id = optarg; + break; + case 'o': + prim_oml_ip = optarg; + break; + case 'i': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + bts_ip_addr = optarg; + *slash = 0; + bts_ip_mask = slash+1; + break; + case 'g': + bts_ip_gw = optarg; + break; + case 'r': + restart = 1; + break; + case 'n': + slash = strchr(optarg, '/'); + if (!slash) + exit(2); + ul = strtoul(optarg, NULL, 16); + nv_flags = ul & 0xffff; + ul = strtoul(slash+1, NULL, 16); + nv_mask = ul & 0xffff; + break; + case 'S': + if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) + exit(2); + break; + case 'U': + if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) + exit(2); + break; + case 'l': + net_listen_testnr = atoi(optarg); + break; + case 's': + stream_id = atoi(optarg); + break; + case 'd': + software = strdup(optarg); + if (find_sw_load_params(optarg) != 0) + exit(0); + break; + case 'f': + firmware_analysis = optarg; + break; + case 'w': + dump_files = 1; + break; + case 'c': + log_set_use_color(stderr_target, 0); + break; + case 'h': + print_usage(); + print_help(); + exit(0); + } + }; + + if (firmware_analysis) + analyze_firmware(firmware_analysis); + + if (optind >= argc) { + /* only warn if we have not done anything else */ + if (!firmware_analysis) + fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); + exit(2); + } + + gsmnet = gsm_network_init(1, 1, NULL); + if (!gsmnet) + exit(1); + + bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC, + HARDCODED_BSIC); + /* ip.access supports up to 4 chained TRX */ + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + gsm_bts_trx_alloc(bts); + bts->oml_tei = stream_id; + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + register_signal_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); + + ipac_nwl_init(); + + printf("Trying to connect to ip.access BTS ...\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(argv[optind], &sin.sin_addr); + rc = ia_config_connect(bts, &sin); + if (rc < 0) { + perror("Error connecting to the BTS"); + exit(1); + } + + bts->oml_link->ts->sign.delay = 10; + bts->c0->rsl_link->ts->sign.delay = 10; + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/src/ipaccess/ipaccess-find.c b/src/ipaccess/ipaccess-find.c new file mode 100644 index 000000000..bea4b77ad --- /dev/null +++ b/src/ipaccess/ipaccess-find.c @@ -0,0 +1,227 @@ +/* ip.access nanoBTS configuration tool */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <osmocore/select.h> +#include <osmocore/timer.h> +#include <openbsc/ipaccess.h> +#include <openbsc/gsm_data.h> + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial Number", + [IPAC_IDTAG_UNITNAME] = "Unit Name", + [IPAC_IDTAG_LOCATION1] = "Location 1", + [IPAC_IDTAG_LOCATION2] = "Location 2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment Version", + [IPAC_IDTAG_SWVERSION] = "Software Version", + [IPAC_IDTAG_IPADDR] = "IP Address", + [IPAC_IDTAG_MACADDR] = "MAC Address", + [IPAC_IDTAG_UNIT] = "Unit ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +static int udp_sock(const char *ifname) +{ + int fd, rc, bc = 1; + struct sockaddr_in sa; + + fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) + return fd; + + if (ifname) { + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, + strlen(ifname)); + if (rc < 0) + goto err; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + sa.sin_addr.s_addr = INADDR_ANY; + + rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); + if (rc < 0) + goto err; + + rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); + if (rc < 0) + goto err; + +#if 0 + /* we cannot bind, since the response packets don't come from + * the broadcast address */ + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + inet_aton("255.255.255.255", &sa.sin_addr); + + rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); + if (rc < 0) + goto err; +#endif + return fd; + +err: + close(fd); + return rc; +} + +const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, + IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_IPADDR, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + + +static int bcast_find(int fd) +{ + struct sockaddr_in sa; + + sa.sin_family = AF_INET; + sa.sin_port = htons(3006); + inet_aton("255.255.255.255", &sa.sin_addr); + + return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); +} + +static int parse_response(unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + printf("%s='%s' ", ipac_idtag_name(t_tag), cur); + + cur += t_len; + } + printf("\n"); + return 0; +} + +static int read_response(int fd) +{ + unsigned char buf[255]; + struct sockaddr_in sa; + int len; + socklen_t sa_len = sizeof(sa); + + len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); + if (len < 0) + return len; + + /* 2 bytes length, 1 byte protocol (0xfe) */ + if (buf[2] != 0xfe) + return 0; + + if (buf[4] != IPAC_MSGT_ID_RESP) + return 0; + + return parse_response(buf+6, len-6); +} + +static int bfd_cb(struct bsc_fd *bfd, unsigned int flags) +{ + if (flags & BSC_FD_READ) + return read_response(bfd->fd); + if (flags & BSC_FD_WRITE) { + bfd->when &= ~BSC_FD_WRITE; + return bcast_find(bfd->fd); + } + return 0; +} + +static struct timer_list timer; + +static void timer_cb(void *_data) +{ + struct bsc_fd *bfd = _data; + + bfd->when |= BSC_FD_WRITE; + + bsc_schedule_timer(&timer, 5, 0); +} + +int main(int argc, char **argv) +{ + struct bsc_fd bfd; + char *ifname; + int rc; + + printf("ipaccess-find (C) 2009 by Harald Welte\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); + + if (argc < 2) { + fprintf(stdout, "you might need to specify the outgoing\n" + " network interface, e.g. ``%s eth0''\n", argv[0]); + } + + ifname = argv[1]; + bfd.cb = bfd_cb; + bfd.when = BSC_FD_READ | BSC_FD_WRITE; + bfd.fd = udp_sock(ifname); + if (bfd.fd < 0) { + perror("Cannot create local socket for broadcast udp"); + exit(1); + } + + bsc_register_fd(&bfd); + + timer.cb = timer_cb; + timer.data = &bfd; + + bsc_schedule_timer(&timer, 5, 0); + + printf("Trying to find ip.access BTS by broadcast UDP...\n"); + + while (1) { + rc = bsc_select_main(0); + if (rc < 0) + exit(3); + } + + exit(0); +} + diff --git a/src/ipaccess/ipaccess-firmware.c b/src/ipaccess/ipaccess-firmware.c new file mode 100644 index 000000000..7fdd0f825 --- /dev/null +++ b/src/ipaccess/ipaccess-firmware.c @@ -0,0 +1,135 @@ +/* Routines for parsing an ipacces SDP firmware file */ + +/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * 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/ipaccess.h> +#include <osmocore/talloc.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define PART_LENGTH 138 + +static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); +static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length); + +/* more magic, the second "int" in the header */ +static char more_magic[] = { 0x10, 0x02 }; + +int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) +{ + struct sdp_firmware *firmware_header = 0; + struct sdp_header *header; + char buf[4096]; + int rc, i; + u_int16_t table_size; + u_int16_t table_offset; + off_t table_start; + + + rc = read(fd, buf, sizeof(*firmware_header)); + if (rc < 0) { + perror("Can not read header start."); + return -1; + } + + firmware_header = (struct sdp_firmware *) &buf[0]; + if (strncmp(firmware_header->magic, " SDP", 4) != 0) { + fprintf(stderr, "Wrong magic.\n"); + return -1; + } + + if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { + fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n", + firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, + firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff); + return -1; + } + + + if (ntohl(firmware_header->file_length) != st_size) { + fprintf(stderr, "The filesize and the header do not match.\n"); + return -1; + } + + /* add the firmware */ + header = talloc_zero(list, struct sdp_header); + header->firmware_info = *firmware_header; + INIT_LLIST_HEAD(&header->header_list); + llist_add(&header->entry, list); + + table_offset = ntohs(firmware_header->table_offset); + table_start = lseek(fd, table_offset, SEEK_CUR); + if (table_start == -1) { + fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset); + return -1; + } + + if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) { + fprintf(stderr, "The table size could not be read.\n"); + return -1; + } + + table_size = ntohs(table_size); + + if (table_size % PART_LENGTH != 0) { + fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size); + return -1; + } + + /* look into each firmware now */ + for (i = 0; i < table_size / PART_LENGTH; ++i) { + struct sdp_header_entry entry; + struct sdp_header_item *header_entry; + unsigned int offset = table_start + 2; + offset += i * 138; + + if (lseek(fd, offset, SEEK_SET) != offset) { + fprintf(stderr, "Can not seek to the offset: %u.\n", offset); + return -1; + } + + rc = read(fd, &entry, sizeof(entry)); + if (rc != sizeof(entry)) { + fprintf(stderr, "Can not read the header entry.\n"); + return -1; + } + + header_entry = talloc_zero(header, struct sdp_header_item); + header_entry->header_entry = entry; + header_entry->absolute_offset = base_offset; + llist_add(&header_entry->entry, &header->header_list); + + /* now we need to find the SDP file... */ + offset = ntohl(entry.start) + 4 + base_offset; + if (lseek(fd, offset, SEEK_SET) != offset) { + perror("can't seek to sdp"); + return -1; + } + + + ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); + } + + return 0; +} + diff --git a/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c new file mode 100644 index 000000000..2dc1b2f62 --- /dev/null +++ b/src/ipaccess/ipaccess-proxy.c @@ -0,0 +1,1373 @@ +/* OpenBSC Abis/IP proxy ip.access nanoBTS */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by On-Waves + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * + * 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 <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#define _GNU_SOURCE +#include <getopt.h> + +#include <openbsc/gsm_data.h> +#include <osmocore/select.h> +#include <osmocore/tlv.h> +#include <osmocore/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/ipaccess.h> +#include <osmocore/talloc.h> + +static struct log_target *stderr_target; + +/* one instance of an ip.access protocol proxy */ +struct ipa_proxy { + /* socket where we listen for incoming OML from BTS */ + struct bsc_fd oml_listen_fd; + /* socket where we listen for incoming RSL from BTS */ + struct bsc_fd rsl_listen_fd; + /* list of BTS's (struct ipa_bts_conn */ + struct llist_head bts_list; + /* the BSC reconnect timer */ + struct timer_list reconn_timer; + /* global GPRS NS data */ + struct in_addr gprs_addr; + struct in_addr listen_addr; +}; + +/* global pointer to the proxy structure */ +static struct ipa_proxy *ipp; + +struct ipa_proxy_conn { + struct bsc_fd fd; + struct llist_head tx_queue; + struct ipa_bts_conn *bts_conn; +}; +#define MAX_TRX 4 + +/* represents a particular BTS in our proxy */ +struct ipa_bts_conn { + /* list of BTS's (ipa_proxy->bts_list) */ + struct llist_head list; + /* back pointer to the proxy which we belong to */ + struct ipa_proxy *ipp; + /* the unit ID as determined by CCM */ + struct { + u_int16_t site_id; + u_int16_t bts_id; + } unit_id; + + /* incoming connections from BTS */ + struct ipa_proxy_conn *oml_conn; + struct ipa_proxy_conn *rsl_conn[MAX_TRX]; + + /* outgoing connections to BSC */ + struct ipa_proxy_conn *bsc_oml_conn; + struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; + + /* UDP sockets for BTS and BSC injection */ + struct bsc_fd udp_bts_fd; + struct bsc_fd udp_bsc_fd; + + /* NS data */ + struct in_addr bts_addr; + struct bsc_fd gprs_ns_fd; + int gprs_local_port; + uint16_t gprs_orig_port; + uint32_t gprs_orig_ip; + + char *id_tags[0xff]; + u_int8_t *id_resp; + unsigned int id_resp_len; +}; + +enum ipp_fd_type { + OML_FROM_BTS = 1, + RSL_FROM_BTS = 2, + OML_TO_BSC = 3, + RSL_TO_BSC = 4, + UDP_TO_BTS = 5, + UDP_TO_BSC = 6, +}; + +/* some of the code against we link from OpenBSC needs this */ +void *tall_bsc_ctx; + +static char *listen_ipaddr; +static char *bsc_ipaddr; +static char *gprs_ns_ipaddr; + +static int make_gprs_sock(struct bsc_fd *bfd, int (*cb)(struct bsc_fd*,unsigned int), void *); +static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what); + +#define PROXY_ALLOC_SIZE 1200 + +static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; +static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; +static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, + 0x01, IPAC_IDTAG_UNIT, + 0x01, IPAC_IDTAG_MACADDR, + 0x01, IPAC_IDTAG_LOCATION1, + 0x01, IPAC_IDTAG_LOCATION2, + 0x01, IPAC_IDTAG_EQUIPVERS, + 0x01, IPAC_IDTAG_SWVERSION, + 0x01, IPAC_IDTAG_UNITNAME, + 0x01, IPAC_IDTAG_SERNR, + }; + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial_Number", + [IPAC_IDTAG_UNITNAME] = "Unit_Name", + [IPAC_IDTAG_LOCATION1] = "Location_1", + [IPAC_IDTAG_LOCATION2] = "Location_2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", + [IPAC_IDTAG_SWVERSION] = "Software_Version", + [IPAC_IDTAG_IPADDR] = "IP_Address", + [IPAC_IDTAG_MACADDR] = "MAC_Address", + [IPAC_IDTAG_UNIT] = "Unit_ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + while (cur < buf + len) { + t_len = *cur++; + t_tag = *cur++; + + DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len; + dec->lv[t_tag].val = cur; + + cur += t_len; + } + return 0; +} + +static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, + u_int16_t *trx_id) +{ + unsigned long ul; + char *endptr; + const char *nptr; + + nptr = str; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (site_id) + *site_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (bts_id) + *bts_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (trx_id) + *trx_id = ul & 0xffff; + + return 0; +} + +static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, + u_int16_t site_id, + u_int16_t bts_id) +{ + struct ipa_bts_conn *ipbc; + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + if (ipbc->unit_id.site_id == site_id && + ipbc->unit_id.bts_id == bts_id) + return ipbc; + } + + return NULL; +} + +struct ipa_proxy_conn *alloc_conn(void) +{ + struct ipa_proxy_conn *ipc; + + ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); + if (!ipc) + return NULL; + + INIT_LLIST_HEAD(&ipc->tx_queue); + + return ipc; +} + +static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) +{ + unsigned int i, len; + + for (i = 0; i <= 0xff; i++) { + if (!TLVP_PRESENT(tlvp, i)) + continue; + + len = TLVP_LEN(tlvp, i); +#if 0 + if (!ipbc->id_tags[i]) + ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); + else +#endif + ipbc->id_tags[i] = talloc_realloc_size(ipbc, + ipbc->id_tags[i], len); + if (!ipbc->id_tags[i]) + return -ENOMEM; + + memset(ipbc->id_tags[i], 0, len); + //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); + } + return 0; +} + + +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); + +#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) + +static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, + struct ipa_bts_conn *ipbc, u_int8_t trx_id) +{ + if (ipbc) + logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, + ipbc->unit_id.bts_id, trx_id); + else + logp2(ss, lvl, file, line, 0, "unknown "); +} + +/* UDP socket handling */ + +static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what), + void *data) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_DGRAM, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->data = data; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n", + strerror(errno)); + return -EIO; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register UDP fd"); + return ret; + } + return 0; +} + +static int handle_udp_read(struct bsc_fd *bfd) +{ + struct ipa_bts_conn *ipbc = bfd->data; + struct ipa_proxy_conn *other_conn = NULL; + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); + struct ipaccess_head *hh; + int ret; + + /* with UDP sockets, we cannot read partial packets but have to read + * all of it in one go */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, msg->data_len, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); + msgb_free(msg); + return ret; + } + if (ret == 0) { + DEBUGP(DINP, "UDP peer disappeared, dead socket\n"); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + msgb_free(msg); + return -EIO; + } + if (ret < sizeof(*hh)) { + DEBUGP(DINP, "could not even read header!?!\n"); + msgb_free(msg); + return -EIO; + } + msgb_put(msg, ret); + msg->l2h = msg->data + sizeof(*hh); + DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len)); + + if (hh->len != msg->len - sizeof(*hh)) { + DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n", + msg->len, msg->len - 3, hh->len); + msgb_free(msg); + return -EIO; + } + + switch (bfd->priv_nr & 0xff) { + case UDP_TO_BTS: + /* injection towards BTS */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + /* fall through */ + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->oml_conn; + break; + } + break; + case UDP_TO_BSC: + /* injection towards BSC */ + switch (hh->proto) { + case IPAC_PROTO_RSL: + /* FIXME: what to do about TRX > 0 */ + other_conn = ipbc->bsc_rsl_conn[0]; + break; + default: + DEBUGP(DINP, "Unknown protocol 0x%02x, sending to " + "OML FD\n", hh->proto); + case IPAC_PROTO_IPACCESS: + case IPAC_PROTO_OML: + other_conn = ipbc->bsc_oml_conn; + break; + } + break; + default: + DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); + break; + } + + if (other_conn) { + /* enqueue the message for TX on the respective FD */ + msgb_enqueue(&other_conn->tx_queue, msg); + other_conn->fd.when |= BSC_FD_WRITE; + } else + msgb_free(msg); + + return 0; +} + +static int handle_udp_write(struct bsc_fd *bfd) +{ + /* not implemented yet */ + bfd->when &= ~BSC_FD_WRITE; + + return -EIO; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_udp_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_udp_write(bfd); + + return rc; +} + + +static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd, + u_int16_t site_id, u_int16_t bts_id, + u_int16_t trx_id, struct tlv_parsed *tlvp, + struct msgb *msg) +{ + struct ipa_bts_conn *ipbc; + u_int16_t udp_port; + int ret = 0; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ", + site_id, bts_id, trx_id); + + /* OML needs to be established before RSL */ + if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { + DEBUGPC(DINP, "Not a OML connection ?!?\n"); + return -EIO; + } + + /* allocate new BTS connection data structure */ + ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); + if (!ipbc) { + ret = -ENOMEM; + goto err_out; + } + + DEBUGPC(DINP, "Created BTS Conn data structure\n"); + ipbc->ipp = ipp; + ipbc->unit_id.site_id = site_id; + ipbc->unit_id.bts_id = bts_id; + ipbc->oml_conn = ipc; + ipc->bts_conn = ipbc; + + /* store the content of the ID TAGS for later reference */ + store_idtags(ipbc, tlvp); + ipbc->id_resp_len = msg->len; + ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); + memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); + + /* Create OML TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_OML); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) { + ret = -EIO; + goto err_bsc_conn; + } + + DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n", + site_id, bts_id, trx_id); + + /* Create UDP socket for BTS packet injection */ + udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP, + UDP_TO_BTS, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bts; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); + + /* Create UDP socket for BSC packet injection */ + udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); + ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP, + UDP_TO_BSC, udp_fd_cb, ipbc); + if (ret < 0) + goto err_udp_bsc; + DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection " + "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); + + + /* GPRS NS related code */ + if (gprs_ns_ipaddr) { + struct sockaddr_in sock; + socklen_t len = sizeof(sock); + ret = make_gprs_sock(&ipbc->gprs_ns_fd, gprs_ns_cb, ipbc); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "Creating the GPRS socket failed.\n"); + goto err_udp_bsc; + } + + ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len); + ipbc->gprs_local_port = ntohs(sock.sin_port); + LOGP(DINP, LOGL_NOTICE, + "Created GPRS NS Socket. Listening on: %s:%d\n", + inet_ntoa(sock.sin_addr), ipbc->gprs_local_port); + + ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len); + ipbc->bts_addr = sock.sin_addr; + } + + llist_add(&ipbc->list, &ipp->bts_list); + + return 0; + +err_udp_bsc: + bsc_unregister_fd(&ipbc->udp_bts_fd); +err_udp_bts: + bsc_unregister_fd(&ipbc->bsc_oml_conn->fd); + close(ipbc->bsc_oml_conn->fd.fd); + talloc_free(ipbc->bsc_oml_conn); + ipbc->bsc_oml_conn = NULL; +err_bsc_conn: + talloc_free(ipbc->id_resp); + talloc_free(ipbc); +#if 0 + bsc_unregister_fd(bfd); + close(bfd->fd); + talloc_free(bfd); +#endif +err_out: + return ret; +} + +static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, + struct bsc_fd *bfd) +{ + struct tlv_parsed tlvp; + u_int8_t msg_type = *(msg->l2h); + u_int16_t site_id, bts_id, trx_id; + struct ipa_bts_conn *ipbc; + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = write(bfd->fd, pong, sizeof(pong)); + if (ret < 0) + return ret; + if (ret < sizeof(pong)) { + DEBUGP(DINP, "short write\n"); + return -EIO; + } + break; + case IPAC_MSGT_PONG: + DEBUGP(DMI, "PONG!\n"); + break; + case IPAC_MSGT_ID_RESP: + DEBUGP(DMI, "ID_RESP "); + /* parse tags, search for Unit ID */ + ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, + msgb_l2len(msg)-2); + DEBUGP(DMI, "\n"); + + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { + LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); + return -EIO; + } + + /* lookup BTS, create sign_link, ... */ + site_id = bts_id = trx_id = 0; + parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), + &site_id, &bts_id, &trx_id); + ipbc = find_bts_by_unitid(ipp, site_id, bts_id); + if (!ipbc) { + /* We have not found an ipbc (per-bts proxy instance) + * for this BTS yet. The first connection of a new BTS must + * be a OML connection. We allocate the associated data structures, + * and try to connect to the remote end */ + + return ipbc_alloc_connect(ipc, bfd, site_id, bts_id, + trx_id, &tlvp, msg); + /* if this fails, the caller will clean up bfd */ + } else { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + DEBUGP(DINP, "Identified BTS %u/%u/%u\n", + site_id, bts_id, trx_id); + + if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { + LOGP(DINP, LOGL_ERROR, "Second OML connection from " + "same BTS ?!?\n"); + return 0; + } + + if (trx_id > MAX_TRX) { + LOGP(DINP, LOGL_ERROR, "We don't support more " + "than %u TRX\n", MAX_TRX); + return -EINVAL; + } + + ipc->bts_conn = ipbc; + /* store TRX number in higher 8 bit of the bfd private number */ + bfd->priv_nr |= trx_id << 8; + ipbc->rsl_conn[trx_id] = ipc; + + /* Create RSL TCP connection towards BSC */ + sin.sin_port = htons(IPA_TCP_PORT_RSL); + ipbc->bsc_rsl_conn[trx_id] = + connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc); + if (!ipbc->bsc_oml_conn) + return -EIO; + DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n", + site_id, bts_id, trx_id); + } + break; + case IPAC_MSGT_ID_GET: + DEBUGP(DMI, "ID_GET\n"); + if ((bfd->priv_nr & 0xff) != OML_TO_BSC && + (bfd->priv_nr & 0xff) != RSL_TO_BSC) { + DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n"); + return -EIO; + } + ipbc = ipc->bts_conn; + if (!ipbc) { + DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); + return -EIO; + } + ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DMI, "ID_ACK? -> ACK!\n"); + ret = write(bfd->fd, id_ack, sizeof(id_ack)); + break; + default: + LOGP(DMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); + return 1; + break; + } + return 0; +} + +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); + struct ipaccess_head *hh; + int len, ret = 0; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + /* first read our 3-byte header */ + hh = (struct ipaccess_head *) msg->data; + ret = recv(bfd->fd, msg->data, 3, 0); + if (ret < 0) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msgb_put(msg, ret); + + /* then read te length as specified in header */ + msg->l2h = msg->data + sizeof(*hh); + len = ntohs(hh->len); + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DINP, LOGL_ERROR, "short read!\n"); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, + unsigned int priv_nr) +{ + struct ipa_proxy_conn *bsc_conn; + unsigned int trx_id = priv_nr >> 8; + + switch (priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + bsc_conn = ipbc->bsc_oml_conn; + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + bsc_conn = ipbc->oml_conn; + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + bsc_conn = ipbc->rsl_conn[trx_id]; + break; + default: + bsc_conn = NULL; + break; + } + return bsc_conn; +} + +static void reconn_tmr_cb(void *data) +{ + struct ipa_proxy *ipp = data; + struct ipa_bts_conn *ipbc; + struct sockaddr_in sin; + int i; + + DEBUGP(DINP, "Running reconnect timer\n"); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + inet_aton(bsc_ipaddr, &sin.sin_addr); + + llist_for_each_entry(ipbc, &ipp->bts_list, list) { + /* if OML to BSC is dead, try to restore it */ + if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { + sin.sin_port = htons(IPA_TCP_PORT_OML); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n"); + ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); + if (!ipbc->bsc_oml_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0); + LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n"); + } + /* if we (still) don't have a OML connection, skip RSL */ + if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) + continue; + + for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { + unsigned int priv_nr; + /* don't establish RSL links which we don't have */ + if (!ipbc->rsl_conn[i]) + continue; + if (ipbc->bsc_rsl_conn[i]) + continue; + priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; + priv_nr &= ~0xff; + priv_nr |= RSL_TO_BSC; + sin.sin_port = htons(IPA_TCP_PORT_RSL); + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); + ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); + if (!ipbc->bsc_rsl_conn) + goto reschedule; + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n"); + } + } + return; + +reschedule: + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); +} + +static void handle_dead_socket(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ + struct ipa_proxy_conn *bsc_conn; /* remote conn */ + struct ipa_bts_conn *ipbc = ipc->bts_conn; + unsigned int trx_id = bfd->priv_nr >> 8; + struct msgb *msg, *msg2; + + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* FIXME: clear tx_queue, remove all references, etc. */ + llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) + msgb_free(msg); + + switch (bfd->priv_nr & 0xff) { + case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ + ipbc->oml_conn = NULL; + bsc_conn = ipbc->bsc_oml_conn; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_oml_conn = NULL; + /* FIXME: do we need to delete the entire ipbc ? */ + break; + case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ + ipbc->rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->bsc_rsl_conn[trx_id]; + /* close the connection to the BSC */ + bsc_unregister_fd(&bsc_conn->fd); + close(bsc_conn->fd.fd); + llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) + msgb_free(msg); + talloc_free(bsc_conn); + ipbc->bsc_rsl_conn[trx_id] = NULL; + break; + case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ + ipbc->bsc_oml_conn = NULL; + bsc_conn = ipbc->oml_conn; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ + ipbc->bsc_rsl_conn[trx_id] = NULL; + bsc_conn = ipbc->rsl_conn[trx_id]; + /* start reconnect timer */ + bsc_schedule_timer(&ipp->reconn_timer, 5, 0); + break; + default: + bsc_conn = NULL; + break; + } + + talloc_free(ipc); +} + +static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) +{ + uint8_t *nsvci; + + if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) + return; + + if (msgb_l2len(msg) != 39) + return; + + /* + * Check if this is a IPA Set Attribute or IPA Set Attribute ACK + * and if the FOM Class is GPRS NSVC0 and then we will patch it. + * + * The patch assumes the message looks like the one from the trace + * but we only match messages with a specific size anyway... So + * this hack should work just fine. + */ + + if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && + msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && + msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { + nsvci = &msg->l2h[23]; + ipbc->gprs_orig_port = *(u_int16_t *)(nsvci+8); + ipbc->gprs_orig_ip = *(u_int32_t *)(nsvci+10); + *(u_int16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); + *(u_int32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; + } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && + msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && + msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { + nsvci = &msg->l2h[23]; + *(u_int16_t *)(nsvci+8) = ipbc->gprs_orig_port; + *(u_int32_t *)(nsvci+10) = ipbc->gprs_orig_ip; + } +} + +static int handle_tcp_read(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct ipa_proxy_conn *bsc_conn; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0; + char *btsbsc; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + msg = ipaccess_read_msg(bfd, &ret); + if (!msg) { + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_NOTICE, "%s disappeared, " + "dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + return ret; + } + + msgb_put(msg, ret); + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len)); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(ipc, msg, bfd); + if (ret < 0) { + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + msgb_free(msg); + return ret; + } else if (ret == 0) { + /* we do not forward parts of the CCM protocol + * through the proxy but rather terminate it ourselves. */ + msgb_free(msg); + return ret; + } + } + + if (!ipbc) { + LOGP(DINP, LOGL_ERROR, + "received %s packet but no ipc->bts_conn?!?\n", btsbsc); + msgb_free(msg); + return -EIO; + } + + bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); + if (bsc_conn) { + if (gprs_ns_ipaddr) + patch_gprs_msg(ipbc, bfd->priv_nr, msg); + /* enqueue packet towards BSC */ + msgb_enqueue(&bsc_conn->tx_queue, msg); + /* mark respective filedescriptor as 'we want to write' */ + bsc_conn->fd.when |= BSC_FD_WRITE; + } else { + logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); + LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, " + "since remote connection is dead\n", btsbsc); + msgb_free(msg); + } + + return ret; +} + +/* a TCP socket is ready to be written to */ +static int handle_tcp_write(struct bsc_fd *bfd) +{ + struct ipa_proxy_conn *ipc = bfd->data; + struct ipa_bts_conn *ipbc = ipc->bts_conn; + struct llist_head *lh; + struct msgb *msg; + char *btsbsc; + int ret; + + if ((bfd->priv_nr & 0xff) <= 2) + btsbsc = "BTS"; + else + btsbsc = "BSC"; + + + /* get the next msg for this timeslot */ + if (llist_empty(&ipc->tx_queue)) { + bfd->when &= ~BSC_FD_WRITE; + return 0; + } + lh = ipc->tx_queue.next; + llist_del(lh); + msg = llist_entry(lh, struct msgb, list); + + logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); + DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr, + hexdump(msg->data, msg->len)); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + if (ret == 0) { + logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); + LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); + handle_dead_socket(bfd); + } + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) { + rc = handle_tcp_read(bfd); + if (rc < 0) + return rc; + } + if (what & BSC_FD_WRITE) + rc = handle_tcp_write(bfd); + + return rc; +} + +/* callback of the listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + + if (!(what & BSC_FD_READ)) + return 0; + + ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (ret < 0) { + perror("accept"); + return ret; + } + DEBUGP(DINP, "accept()ed new %s link from %s\n", + (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", + inet_ntoa(sa.sin_addr)); + + ipc = alloc_conn(); + if (!ipc) { + close(ret); + return -ENOMEM; + } + + bfd = &ipc->fd; + bfd->fd = ret; + bfd->data = ipc; + bfd->priv_nr = listen_bfd->priv_nr; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(ipc); + return ret; + } + + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = write(bfd->fd, id_req, sizeof(id_req)); + + return 0; +} + +static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port) +{ + int ret; + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr = ip; + + ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "Failed to forward GPRS message.\n"); + } +} + +static int gprs_ns_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct ipa_bts_conn *bts; + char buf[4096]; + int ret; + struct sockaddr_in sock; + socklen_t len = sizeof(sock); + + /* 1. get the data... */ + ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno)); + return -1; + } + + bts = bfd->data; + + /* 2. figure out where to send it to */ + if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) { + LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from network.\n"); + send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000); + } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) { + LOGP(DINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n"); + send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000); + } else { + LOGP(DINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr)); + } + + return 0; +} + +static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr, + int (*cb)(struct bsc_fd *fd, unsigned int what)) +{ + struct sockaddr_in addr; + int ret, on = 1; + + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->priv_nr = priv_nr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (!listen_ipaddr) + addr.sin_addr.s_addr = INADDR_ANY; + else + inet_aton(listen_ipaddr, &addr.sin_addr); + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, + "Could not bind listen socket for IP %s with error: %s.\n", + listen_ipaddr, strerror(errno)); + return -EIO; + } + + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + return ret; + } + return 0; +} + +static int make_gprs_sock(struct bsc_fd *bfd, int (*cb)(struct bsc_fd*,unsigned int), void *data) +{ + struct sockaddr_in addr; + int ret; + + bfd->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bfd->cb = cb; + bfd->data = data; + bfd->when = BSC_FD_READ; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + inet_aton(listen_ipaddr, &addr.sin_addr); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, + "Could not bind n socket for IP %s with error: %s.\n", + listen_ipaddr, strerror(errno)); + return -EIO; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + perror("register_listen_fd"); + return ret; + } + return 0; +} + +/* Actively connect to a BSC. */ +static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) +{ + struct ipa_proxy_conn *ipc; + struct bsc_fd *bfd; + int ret, on = 1; + + ipc = alloc_conn(); + if (!ipc) + return NULL; + + ipc->bts_conn = data; + + bfd = &ipc->fd; + bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ | BSC_FD_WRITE; + bfd->data = ipc; + bfd->priv_nr = priv_nr; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n", + inet_ntoa(sa->sin_addr)); + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + /* pre-fill tx_queue with identity request */ + ret = bsc_register_fd(bfd); + if (ret < 0) { + close(bfd->fd); + talloc_free(ipc); + return NULL; + } + + return ipc; +} + +static int ipaccess_proxy_setup(void) +{ + int ret; + + ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); + if (!ipp) + return -ENOMEM; + INIT_LLIST_HEAD(&ipp->bts_list); + ipp->reconn_timer.cb = reconn_tmr_cb; + ipp->reconn_timer.data = ipp; + + /* Listen for OML connections */ + ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML, + OML_FROM_BTS, listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL, + RSL_FROM_BTS, listen_fd_cb); + + if (ret < 0) + return ret; + + /* Connect the GPRS NS Socket */ + if (gprs_ns_ipaddr) { + inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); + inet_aton(listen_ipaddr, &ipp->listen_addr); + } + + return ret; +} + +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + 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_full(tall_bsc_ctx, stderr); + break; + default: + break; + } +} + +static void print_help() +{ + printf(" ipaccess-proxy is a proxy BTS.\n"); + printf(" -h --help. This help text.\n"); + printf(" -l --listen IP. The ip to listen to.\n"); + printf(" -b --bsc IP. The BSC IP address.\n"); + printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); + printf("\n"); + printf(" -s --disable-color. Disable the color inside the logging message.\n"); + printf(" -e --log-level number. Set the global loglevel.\n"); + printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); + printf(" -V --version. Print the version of OpenBSC.\n"); +} + +static void print_usage() +{ + printf("Usage: ipaccess-proxy\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'}, + {"disable-color", 0, 0, 's'}, + {"timestamp", 0, 0, 'T'}, + {"log-level", 1, 0, 'e'}, + {"listen", 1, 0, 'l'}, + {"bsc", 1, 0, 'b'}, + {"udp", 1, 0, 'u'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hsTe:l:b:g:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 'l': + listen_ipaddr = optarg; + break; + case 'b': + bsc_ipaddr = optarg; + break; + case 'g': + gprs_ns_ipaddr = optarg; + break; + case 's': + log_set_use_color(stderr_target, 0); + break; + case 'T': + log_set_print_timestamp(stderr_target, 1); + break; + case 'e': + log_set_log_level(stderr_target, atoi(optarg)); + break; + default: + /* ignore */ + break; + } + } +} + +int main(int argc, char **argv) +{ + int rc; + + listen_ipaddr = "192.168.100.11"; + bsc_ipaddr = "192.168.100.239"; + + tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); + + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + log_parse_category_mask(stderr_target, "DINP:DMI"); + + handle_options(argc, argv); + + rc = ipaccess_proxy_setup(); + if (rc < 0) + exit(1); + + signal(SIGUSR1, &signal_handler); + signal(SIGABRT, &signal_handler); + + while (1) { + bsc_select_main(0); + } +} diff --git a/src/ipaccess/network_listen.c b/src/ipaccess/network_listen.c new file mode 100644 index 000000000..aaf7c974a --- /dev/null +++ b/src/ipaccess/network_listen.c @@ -0,0 +1,251 @@ +/* ip.access nanoBTS network listen mode */ + +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> + +#include <arpa/inet.h> + +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/rxlev_stat.h> +#include <osmocore/gsm48_ie.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/abis_nm.h> +#include <openbsc/signal.h> +#include <openbsc/debug.h> + +#define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1) + +int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, + uint16_t max_num_arfcns) +{ + int i; + unsigned int num_arfcn = 0; + + for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) { + int16_t arfcn = -1; + + while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { + *buf++ = htons(arfcn); + num_arfcn++; + + } + + if (num_arfcn > max_num_arfcns) + break; + } + + return num_arfcn; +} + +enum ipac_test_state { + IPAC_TEST_S_IDLE, + IPAC_TEST_S_RQD, + IPAC_TEST_S_EXEC, + IPAC_TEST_S_PARTIAL, +}; + +int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, + const uint8_t *phys_conf, unsigned int phys_conf_len) +{ + struct msgb *msg; + + if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) { + fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state); + return -EINVAL; + } + + switch (testnr) { + case NM_IPACC_TESTNO_CHAN_USAGE: + case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: + rxlev_stat_reset(&trx->ipaccess.rxlev_stat); + break; + } + + msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML"); + + if (phys_conf && phys_conf_len) { + uint8_t *payload; + /* first put the phys conf header */ + msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len); + payload = msgb_put(msg, phys_conf_len); + memcpy(payload, phys_conf, phys_conf_len); + } + + abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff, + testnr, 1, msg); + trx->ipaccess.test_nr = testnr; + + /* FIXME: start safety timer until when test is supposed to complete */ + + return 0; +} + +static uint16_t last_arfcn; +static struct gsm_sysinfo_freq nwl_si_freq[1024]; +#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ +#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ +#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ + +struct ipacc_ferr_elem { + int16_t freq_err; + uint8_t freq_qual; + uint8_t arfcn; +} __attribute__((packed)); + +struct ipacc_cusage_elem { + uint16_t arfcn:10, + rxlev:6; +} __attribute__ ((packed)); + +static int test_rep(void *_msg) +{ + struct msgb *msg = _msg; + struct abis_om_fom_hdr *foh = msgb_l3(msg); + uint16_t test_rep_len, ferr_list_len; + struct ipacc_ferr_elem *ife; + struct ipac_bcch_info binfo; + int i, rc; + + DEBUGP(DNM, "TEST REPORT: "); + + if (foh->data[0] != NM_ATT_TEST_NO || + foh->data[2] != NM_ATT_TEST_REPORT) + return -EINVAL; + + DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); + /* data[2] == NM_ATT_TEST_REPORT */ + /* data[3..4]: test_rep_len */ + test_rep_len = ntohs(*(uint16_t *) &foh->data[3]); + /* data[5]: ip.access test result */ + DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5])); + + /* data[6]: ip.access nested IE. 3 == freq_err_list */ + switch (foh->data[6]) { + case NM_IPAC_EIE_FREQ_ERR_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); + + /* data[9...]: frequency error list elements */ + for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { + ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); + DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", + ife->arfcn, ntohs(ife->freq_err)); + } + break; + case NM_IPAC_EIE_CHAN_USE_LIST: + /* data[7..8]: length of ferr_list */ + ferr_list_len = ntohs(*(uint16_t *) &foh->data[7]); + + /* data[9...]: channel usage list elements */ + for (i = 0; i < ferr_list_len; i+= 2) { + uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i); + uint16_t cu = ntohs(*cu_ptr); + uint16_t arfcn = cu & 0x3ff; + uint8_t rxlev = cu >> 10; + DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev); + rxlev_stat_input(&msg->trx->ipaccess.rxlev_stat, + arfcn, rxlev); + } + break; + case NM_IPAC_EIE_BCCH_INFO_TYPE: + break; + case NM_IPAC_EIE_BCCH_INFO: + rc = ipac_parse_bcch_info(&binfo, foh->data+6); + if (rc < 0) { + DEBUGP(DNM, "BCCH Info parsing failed\n"); + break; + } + DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d BSIC %u\n", + binfo.arfcn, binfo.rx_lev, binfo.rx_qual, + binfo.cgi.mcc, binfo.cgi.mnc, + binfo.cgi.lac, binfo.cgi.ci, binfo.bsic); + + if (binfo.arfcn != last_arfcn) { + /* report is on a new arfcn, need to clear channel list */ + memset(nwl_si_freq, 0, sizeof(nwl_si_freq)); + last_arfcn = binfo.arfcn; + } + if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) { + DEBUGP(DNM, "BA SI2: %s\n", hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2))); + gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2), + 0x8c, FREQ_TYPE_NCELL_2); + } + if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) { + DEBUGP(DNM, "BA SI2bis: %s\n", hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis))); + gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis), + 0x8e, FREQ_TYPE_NCELL_2bis); + } + if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) { + DEBUGP(DNM, "BA SI2ter: %s\n", hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter))); + gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter), + 0x8e, FREQ_TYPE_NCELL_2ter); + } + for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) { + if (nwl_si_freq[i].mask) + DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i); + } + break; + default: + break; + } + + switch (foh->data[5]) { + case NM_IPACC_TESTRES_SUCCESS: + case NM_IPACC_TESTRES_STOPPED: + case NM_IPACC_TESTRES_TIMEOUT: + case NM_IPACC_TESTRES_NO_CHANS: + msg->trx->ipaccess.test_state = IPAC_TEST_S_IDLE; + /* Send signal to notify higher layers of test completion */ + DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n"); + dispatch_signal(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE, msg->trx); + break; + case NM_IPACC_TESTRES_PARTIAL: + msg->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL; + break; + } + + return 0; +} + +static int nwl_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + switch (signal) { + case S_NM_TEST_REP: + return test_rep(signal_data); + default: + break; + } + + return 0; +} + +void ipac_nwl_init(void) +{ + register_signal_handler(SS_NM, nwl_sig_cb, NULL); +} |