diff options
Diffstat (limited to 'src/shared')
154 files changed, 25502 insertions, 0 deletions
diff --git a/src/shared/.gitignore b/src/shared/.gitignore new file mode 100644 index 00000000..5058adb1 --- /dev/null +++ b/src/shared/.gitignore @@ -0,0 +1,5 @@ +libosmocore/build-host/tests/msgfile/msgfile_test +libosmocore/build-host/tests/smscb/smscb_test +libosmocore/build-host/tests/sms/sms_test +libosmocore/build-host/tests/timer/timer_test +libosmocore/build-host/tests/ussd/ussd_test diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore new file mode 100644 index 00000000..d39ae4a8 --- /dev/null +++ b/src/shared/libosmocore/.gitignore @@ -0,0 +1,32 @@ +Makefile +Makefile.in +.deps +.libs +*.o +*.lo +*.la +*.pc +aclocal.m4 +m4/*.m4 +autom4te.cache +config.h* +config.sub +config.log +config.status +config.guess +configure +depcomp +missing +ltmain.sh +install-sh +stamp-h1 +libtool + +.tarball-version +.version + +tests/sms/sms_test +tests/timer/timer_test +tests/msgfile/msgfile_test +tests/ussd/ussd_test +tests/smscb/smscb_test diff --git a/src/shared/libosmocore/COPYING b/src/shared/libosmocore/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/shared/libosmocore/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/shared/libosmocore/Makefile.am b/src/shared/libosmocore/Makefile.am new file mode 100644 index 00000000..e391de89 --- /dev/null +++ b/src/shared/libosmocore/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 +ACLOCAL_AMFLAGS = -I m4 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +SUBDIRS = include src tests + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc + +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/src/shared/libosmocore/configure.ac b/src/shared/libosmocore/configure.ac new file mode 100644 index 00000000..b923a218 --- /dev/null +++ b/src/shared/libosmocore/configure.ac @@ -0,0 +1,123 @@ +AC_INIT([libosmocore], + m4_esyscmd([./git-version-gen .tarball-version]), + [openbsc-devel@lists.openbsc.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +LT_INIT +AC_PROG_LIBTOOL + +AC_CONFIG_MACRO_DIR([m4]) + +dnl checks for header files +AC_HEADER_STDC +AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h) + +# The following test is taken from WebKit's webkit.m4 +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden " +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], + [ AC_MSG_RESULT([yes]) + SYMBOL_VISIBILITY="-fvisibility=hidden"], + AC_MSG_RESULT([no])) +CFLAGS="$saved_CFLAGS" +AC_SUBST(SYMBOL_VISIBILITY) + +dnl Generate the output +AM_CONFIG_HEADER(config.h) + +AC_ARG_ENABLE(talloc, + [AS_HELP_STRING( + [--disable-talloc], + [Disable building talloc memory allocator] + )], + [enable_talloc=0], [enable_talloc=1]) +AM_CONDITIONAL(ENABLE_TALLOC, test "x$enable_talloc" = "x1") + +AC_ARG_ENABLE(plugin, + [AS_HELP_STRING( + [--disable-plugin], + [Disable support for dlopen plugins], + )], + [enable_plugin=0], [enable_plugin=1]) +AM_CONDITIONAL(ENABLE_PLUGIN, test "x$enable_plugin" = "x1") + +AC_ARG_ENABLE(tests, + [AS_HELP_STRING( + [--disable-tests], + [Disable building test programs] + )], + [enable_tests=0], [enable_tests=1]) +AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1") + +AC_ARG_ENABLE(vty, + [AS_HELP_STRING( + [--disable-vty], + [Disable building VTY telnet interface] + )], + [enable_vty=0], [enable_vty=1]) +AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1") + +AC_ARG_ENABLE(panic_infloop, + [AS_HELP_STRING( + [--enable-panic-infloop], + [Trigger infinite loop on panic rather than fprintf/abort] + )], + [panic_infloop=1], [panic_infloop=0]) +if test "x$panic_infloop" = "x1" +then + AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort]) +fi + +AC_ARG_ENABLE(bsc_fd_check, + [AS_HELP_STRING( + [--enable-bsc-fd-check], + [Instrument bsc_register_fd to check that the fd is registered] + )], + [fd_check=1], [fd_check=0]) +if test "x$fd_check" = "x1" +then + AC_DEFINE([BSC_FD_CHECK],[1],[Instrument the bsc_register_fd]) +fi + +AC_ARG_ENABLE(msgfile, + [AS_HELP_STRING( + [--disable-msgfile], + [Disable support for the msgfile], + )], + [enable_msgfile=0], [enable_msgfile=1]) +AM_CONDITIONAL(ENABLE_MSGFILE, test "x$enable_msgfile" = "x1") + + +AC_OUTPUT( + libosmocore.pc + libosmocodec.pc + libosmovty.pc + libosmogsm.pc + include/osmocom/Makefile + include/osmocom/vty/Makefile + include/osmocom/codec/Makefile + include/osmocom/crypt/Makefile + include/osmocom/gsm/Makefile + include/osmocom/gsm/protocol/Makefile + include/osmocom/core/Makefile + include/Makefile + src/Makefile + src/vty/Makefile + src/codec/Makefile + src/gsm/Makefile + tests/Makefile + tests/timer/Makefile + tests/sms/Makefile + tests/msgfile/Makefile + tests/ussd/Makefile + tests/smscb/Makefile + Makefile) diff --git a/src/shared/libosmocore/debian/changelog b/src/shared/libosmocore/debian/changelog new file mode 100644 index 00000000..20a04938 --- /dev/null +++ b/src/shared/libosmocore/debian/changelog @@ -0,0 +1,17 @@ +libosmocore (0.3.0) natty; urgency=low + + * New upstream version of libosmocore + + -- Harald Welte <laforge@gnumonks.org> Tue, 10 May 2011 17:28:24 +0200 + +libosmocore (0.1.27) natty; urgency=low + + * New upstream version of libosmocore. + + -- Holger Hans Peter Freyther <holger@freyther.de> Thu, 13 Jan 2011 18:07:36 +0800 + +libosmocore (0.1.17-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP> + + -- Harald Welte <laforge@gnumonks.org> Tue, 24 Aug 2010 10:55:04 +0200 diff --git a/src/shared/libosmocore/debian/compat b/src/shared/libosmocore/debian/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/src/shared/libosmocore/debian/compat @@ -0,0 +1 @@ +7 diff --git a/src/shared/libosmocore/debian/control b/src/shared/libosmocore/debian/control new file mode 100644 index 00000000..3f92875c --- /dev/null +++ b/src/shared/libosmocore/debian/control @@ -0,0 +1,27 @@ +Source: libosmocore +Section: libs +Priority: optional +Maintainer: Harald Welte <laforge@gnumonks.org> +Build-Depends: debhelper (>= 7.0.50~), autotools-dev, autoconf, automake, libtool, dh-autoreconf +Standards-Version: 3.8.4 +Homepage: http://bb.osmocom.org/trac/wiki/libosmocore +Vcs-Git: git://git.osmocom.org/libosmocore.git +Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmocore.git;a=summary + +Package: libosmocore +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Open Source MObile COMmunications CORE library + +Package: libosmocore-dev +Section: libdevel +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libosmocore +Description: Development headers for Open Source MObile COMmunications CORE library + +#Package: libosmocore-dbg +#Section: libdevel +#Architecture: any +#Depends: ${shlibs:Depends}, ${misc:Depends} +#Description: Debug symbols for Open Source MObile COMmunications CORE library diff --git a/src/shared/libosmocore/debian/copyright b/src/shared/libosmocore/debian/copyright new file mode 100644 index 00000000..c450be58 --- /dev/null +++ b/src/shared/libosmocore/debian/copyright @@ -0,0 +1,54 @@ +This work was packaged for Debian by: + + Harald Welte <laforge@gnumonks.org> on Tue, 24 Aug 2010 10:55:04 +0200 + +It was downloaded from: + + git://git.osmocom.org/libosmocore.git + +Upstream Author(s): + + Harald Welte <laforge@gnumonks.org> + Holger Hans Peter Freyther <zecke@selfish.org> + Sylvain Munaut <tnt@246tNt.com> + Daniel Willmann <daniel@totalueberwachung.de> + Golde <nico@ngolde.de> + For src/talloc.c and include/osmocore/talloc.h: + Andrew Tridgell + Stefan Metzmacher + For src/vty/* and include/osmocom/vty/* + Kunihiro Ishiguro + +Copyright: + + Copyright (C) 2008-2010 Harald Welte <laforge@gnumonks.org> + Copyright (C) 2008-2010 Holger Hans Peter Freyther <zecke@selfish.org> + Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com> + Copyright (C) 2009-2010 On-Waves + Copyright (C) 2008 Daniel Willmann <daniel@totalueberwachung.de> + Copyright (C) 2010 Nico Golde <nico@ngolde.de> + For src/talloc.c and include/osmocore/talloc.h: + Copyright (C) 2004 Andrew Tridgell + Copyright (C) 2006 Stefan Metzmacher + For src/vty/* and include/osmocom/vty/* + Copyright (C) 1998 Kunihiro Ishiguro + +License: + + GNU General Public License, Version 2 or later + +The Debian packaging is: + + Copyright (C) 2010 Harald Welte <laforge@gnumonks.org> + +# Please chose a license for your packaging work. If the program you package +# uses a mainstream license, using the same license is the safest choice. +# Please avoid to pick license terms that are more restrictive than the +# packaged work, as it may make Debian's contributions unacceptable upstream. +# If you just want it to be GPL version 3, leave the following lines in. + +and is licensed under the GPL version 3, +see "/usr/share/common-licenses/GPL-3". + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff --git a/src/shared/libosmocore/debian/docs b/src/shared/libosmocore/debian/docs new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/shared/libosmocore/debian/docs diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log new file mode 100644 index 00000000..2742cb47 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore-dbg.debhelper.log @@ -0,0 +1,6 @@ +dh_auto_configure +dh_auto_build +dh_auto_test +dh_prep +dh_installdirs +dh_auto_install diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.dirs b/src/shared/libosmocore/debian/libosmocore-dbg.dirs new file mode 100644 index 00000000..af59b0a9 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore-dbg.dirs @@ -0,0 +1 @@ +usr/lib/debug/lib diff --git a/src/shared/libosmocore/debian/libosmocore-dbg.install b/src/shared/libosmocore/debian/libosmocore-dbg.install new file mode 100644 index 00000000..7ce02127 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore-dbg.install @@ -0,0 +1 @@ +usr/lib/debug/lib/* diff --git a/src/shared/libosmocore/debian/libosmocore-dev.dirs b/src/shared/libosmocore/debian/libosmocore-dev.dirs new file mode 100644 index 00000000..94090a39 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore-dev.dirs @@ -0,0 +1,8 @@ +usr/lib +usr/include +usr/include/osmocom +usr/include/osmocom/codec +usr/include/osmocom/core +usr/include/osmocom/crypt +usr/include/osmocom/gsm +usr/include/osmocom/vty diff --git a/src/shared/libosmocore/debian/libosmocore-dev.install b/src/shared/libosmocore/debian/libosmocore-dev.install new file mode 100644 index 00000000..eec0e15e --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore-dev.install @@ -0,0 +1,5 @@ +usr/include/* +usr/lib/lib*.a +usr/lib/lib*.so +usr/lib/lib*.la +usr/lib/pkgconfig/* diff --git a/src/shared/libosmocore/debian/libosmocore.dirs b/src/shared/libosmocore/debian/libosmocore.dirs new file mode 100644 index 00000000..94090a39 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore.dirs @@ -0,0 +1,8 @@ +usr/lib +usr/include +usr/include/osmocom +usr/include/osmocom/codec +usr/include/osmocom/core +usr/include/osmocom/crypt +usr/include/osmocom/gsm +usr/include/osmocom/vty diff --git a/src/shared/libosmocore/debian/libosmocore.install b/src/shared/libosmocore/debian/libosmocore.install new file mode 100644 index 00000000..d0dbfd18 --- /dev/null +++ b/src/shared/libosmocore/debian/libosmocore.install @@ -0,0 +1 @@ +usr/lib/lib*.so.* diff --git a/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 new file mode 100644 index 00000000..c0a54bd7 --- /dev/null +++ b/src/shared/libosmocore/debian/patches/debian-changes-0.1.17-1 @@ -0,0 +1,46 @@ +Description: Upstream changes introduced in version 0.1.17-1 + This patch has been created by dpkg-source during the package build. + Here's the last changelog entry, hopefully it gives details on why + those changes were made: + . + libosmocore (0.1.17-1) unstable; urgency=low + . + * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP> + . + The person named in the Author field signed this changelog entry. +Author: Harald Welte <laforge@gnumonks.org> + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: <vendor|upstream|other>, <url of original patch> +Bug: <url in upstream bugtracker> +Bug-Debian: http://bugs.debian.org/<bugnumber> +Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber> +Forwarded: <no|not-needed|url proving that it has been forwarded> +Reviewed-By: <name and email of someone who approved the patch> +Last-Update: <YYYY-MM-DD> + +--- /dev/null ++++ libosmocore-0.1.17/.version +@@ -0,0 +1 @@ ++0.1.17 +--- /dev/null ++++ libosmocore-0.1.17/copyright +@@ -0,0 +1,14 @@ ++Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 ++Name: libosmocore ++Maintainer: Harald Welte <laforge@gnumonks.org> ++Source: git://git.osmocom.org/libosmocore.git ++ ++Copyright: 2008-2010 Harald Welte <laforge@gnumonks.org> ++License: GPL-2+ ++ ++Files: src/talloc.c include/osmocore/talloc.h ++Copyright: 2004 Andrew Tridgell ++License: LGPL-3+ ++ ++Files: include/osmocore/linuxlist.h ++License: GPL-2 diff --git a/src/shared/libosmocore/debian/patches/series b/src/shared/libosmocore/debian/patches/series new file mode 100644 index 00000000..0ca407b1 --- /dev/null +++ b/src/shared/libosmocore/debian/patches/series @@ -0,0 +1 @@ +debian-changes-0.1.17-1 diff --git a/src/shared/libosmocore/debian/rules b/src/shared/libosmocore/debian/rules new file mode 100755 index 00000000..15f78f2c --- /dev/null +++ b/src/shared/libosmocore/debian/rules @@ -0,0 +1,19 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +CFLAGS = -Wall -g + +%: + dh --with autoreconf $@ + +#override_dh_strip: +# dh_strip --dbg-package=libosmocore-dbg + diff --git a/src/shared/libosmocore/debian/source/format b/src/shared/libosmocore/debian/source/format new file mode 100644 index 00000000..af745b31 --- /dev/null +++ b/src/shared/libosmocore/debian/source/format @@ -0,0 +1 @@ +3.0 (git) diff --git a/src/shared/libosmocore/git-version-gen b/src/shared/libosmocore/git-version-gen new file mode 100755 index 00000000..42cf3d2b --- /dev/null +++ b/src/shared/libosmocore/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + [0-9]*) ;; + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/shared/libosmocore/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am new file mode 100644 index 00000000..3578a80e --- /dev/null +++ b/src/shared/libosmocore/include/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = osmocom diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am new file mode 100644 index 00000000..21f4f2d0 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/Makefile.am @@ -0,0 +1,5 @@ +if ENABLE_VTY +SUBDIRS = vty codec crypt gsm core +else +SUBDIRS = codec crypt gsm core +endif diff --git a/src/shared/libosmocore/include/osmocom/codec/Makefile.am b/src/shared/libosmocore/include/osmocom/codec/Makefile.am new file mode 100644 index 00000000..c2136023 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/codec/Makefile.am @@ -0,0 +1,3 @@ +osmocodec_HEADERS = codec.h + +osmocodecdir = $(includedir)/osmocom/codec diff --git a/src/shared/libosmocore/include/osmocom/codec/codec.h b/src/shared/libosmocore/include/osmocom/codec/codec.h new file mode 100644 index 00000000..6f9ffea5 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/codec/codec.h @@ -0,0 +1,20 @@ +#ifndef _OSMOCOM_CODEC_H +#define _OSMOCOM_CODEC_H + +#include <stdint.h> + +extern uint16_t gsm610_bitorder[]; /* FR */ +extern uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */ +extern uint16_t gsm620_voiced_bitorder[]; /* HR voiced */ +extern uint16_t gsm660_bitorder[]; /* EFR */ + +extern uint16_t gsm690_12_2_bitorder[]; /* AMR 12.2 kbits */ +extern uint16_t gsm690_10_2_bitorder[]; /* AMR 10.2 kbits */ +extern uint16_t gsm690_7_95_bitorder[]; /* AMR 7.95 kbits */ +extern uint16_t gsm690_7_4_bitorder[]; /* AMR 7.4 kbits */ +extern uint16_t gsm690_6_7_bitorder[]; /* AMR 6.7 kbits */ +extern uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */ +extern uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */ +extern uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */ + +#endif /* _OSMOCOM_CODEC_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/Makefile.am b/src/shared/libosmocore/include/osmocom/core/Makefile.am new file mode 100644 index 00000000..3c30362c --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/Makefile.am @@ -0,0 +1,12 @@ +osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h bits.h \ + bitvec.h statistics.h utils.h socket.h \ + gsmtap.h write_queue.h \ + logging.h rate_ctr.h gsmtap_util.h \ + plugin.h crc16.h panic.h process.h msgfile.h \ + backtrace.h conv.h application.h + +if ENABLE_TALLOC +osmocore_HEADERS += talloc.h +endif + +osmocoredir = $(includedir)/osmocom/core diff --git a/src/shared/libosmocore/include/osmocom/core/application.h b/src/shared/libosmocore/include/osmocom/core/application.h new file mode 100644 index 00000000..c1642ec4 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/application.h @@ -0,0 +1,16 @@ +#ifndef OSMO_APPLICATION_H +#define OSMO_APPLICATION_H + +/** + * Routines for helping with the application setup. + */ + +struct log_info; +struct log_target; + +extern struct log_target *osmo_stderr_target; + +void osmo_init_ignore_signals(void); +int osmo_init_logging(const struct log_info *); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/backtrace.h b/src/shared/libosmocore/include/osmocom/core/backtrace.h new file mode 100644 index 00000000..5a8a8161 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/backtrace.h @@ -0,0 +1,6 @@ +#ifndef _OSMO_BACKTRACE_H_ +#define _OSMO_BACKTRACE_H_ + +void osmo_generate_backtrace(); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/bits.h b/src/shared/libosmocore/include/osmocom/core/bits.h new file mode 100644 index 00000000..eb22d07c --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/bits.h @@ -0,0 +1,45 @@ +#ifndef _OSMO_BITS_H +#define _OSMO_BITS_H + +#include <stdint.h> + +typedef int8_t sbit_t; /* soft bit (-127...127) */ +typedef uint8_t ubit_t; /* unpacked bit (0 or 1) */ +typedef uint8_t pbit_t; /* packed bis (8 bits in a byte) */ + +/* + NOTE on the endianess of pbit_t: + Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit. + Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8)) +*/ + +/* determine how many bytes we would need for 'num_bits' packed bits */ +static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits) +{ + unsigned int pbit_bytesize = num_bits / 8; + + if (num_bits % 8) + pbit_bytesize++; + + return pbit_bytesize; +} + +/* convert unpacked bits to packed bits, return length in bytes */ +int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits); + +/* convert packed bits to unpacked bits, return length in bytes */ +int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits); + +/* convert unpacked bits to packed bits (extended options but slower), + * return length in bytes (max written ofs of output buffer + 1) */ +int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, + const ubit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode); + +/* convert packed bits to unpacked bits (extended options but slower), + * return length in bytes (max written ofs of output buffer + 1) */ +int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, + const pbit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/bitvec.h b/src/shared/libosmocore/include/osmocom/core/bitvec.h new file mode 100644 index 00000000..bbe1641b --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/bitvec.h @@ -0,0 +1,77 @@ +#ifndef _BITVEC_H +#define _BITVEC_H + +/* bit vector utility routines */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are + * defined relative to the 0x2b padding pattern */ +enum bit_value { + ZERO = 0, + ONE = 1, + L = 2, + H = 3, +}; + +struct bitvec { + unsigned int cur_bit; /* curser to the next unused bit */ + unsigned int data_len; /* length of data array in bytes */ + uint8_t *data; /* pointer to data array */ +}; + +/* check if the bit is 0 or 1 for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr); + +/* check if the bit is L or H for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr); + +/* get the Nth set bit inside the bit vector */ +unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n); + +/* Set a bit at given position */ +int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum, + enum bit_value bit); + +/* Set the next bit in the vector */ +int bitvec_set_bit(struct bitvec *bv, enum bit_value bit); + +/* get the next bit (low/high) inside a bitvec */ +int bitvec_get_bit_high(struct bitvec *bv); + +/* Set multiple bits at the current position */ +int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count); + +/* Add an unsigned integer (of length count bits) to current position */ +int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count); + +/* get multiple bits (based on numeric value) from current pos */ +int bitvec_get_uint(struct bitvec *bv, int num_bits); + +/* find the first bit set in bit vector */ +int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val); + +/* Pad the bit vector up to a certain bit position */ +int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); + +#endif /* _BITVEC_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/conv.h b/src/shared/libosmocore/include/osmocom/core/conv.h new file mode 100644 index 00000000..af676eed --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/conv.h @@ -0,0 +1,101 @@ +/* + * conv.h + * + * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __OSMO_CONV_H__ +#define __OSMO_CONV_H__ + +#include <stdint.h> + +#include <osmocom/core/bits.h> + +struct osmo_conv_code { + int N; + int K; + int len; + + const uint8_t (*next_output)[2]; + const uint8_t (*next_state)[2]; + + const uint8_t *next_term_output; + const uint8_t *next_term_state; + + const int *puncture; +}; + + +/* Encoding */ + + /* Low level API */ +struct osmo_conv_encoder { + const struct osmo_conv_code *code; + int i_idx; /* Next input bit index */ + int p_idx; /* Current puncture index */ + uint8_t state; /* Current state */ +}; + +void osmo_conv_encode_init(struct osmo_conv_encoder *encoder, + const struct osmo_conv_code *code); +int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, + const ubit_t *input, ubit_t *output, int n); +int osmo_conv_encode_finish(struct osmo_conv_encoder *encoder, ubit_t *output); + + /* All-in-one */ +int osmo_conv_encode(const struct osmo_conv_code *code, + const ubit_t *input, ubit_t *output); + + +/* Decoding */ + + /* Low level API */ +struct osmo_conv_decoder { + const struct osmo_conv_code *code; + + int n_states; + + int len; /* Max o_idx (excl. termination) */ + + int o_idx; /* output index */ + int p_idx; /* puncture index */ + + unsigned int *ae; /* accumulater error */ + unsigned int *ae_next; /* next accumulated error (tmp in scan) */ + uint8_t *state_history; /* state history [len][n_states] */ +}; + +void osmo_conv_decode_init(struct osmo_conv_decoder *decoder, + const struct osmo_conv_code *code, int len); +void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder); +void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder); + +int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, + const sbit_t *input, int n); +int osmo_conv_decode_finish(struct osmo_conv_decoder *decoder, + const sbit_t *input); +int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, + ubit_t *output, int has_finish); + + /* All-in-one */ +int osmo_conv_decode(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output); + + +#endif /* __OSMO_CONV_H__ */ diff --git a/src/shared/libosmocore/include/osmocom/core/crc16.h b/src/shared/libosmocore/include/osmocom/core/crc16.h new file mode 100644 index 00000000..0e524176 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/crc16.h @@ -0,0 +1,34 @@ +/* + * This was copied from the linux kernel and adjusted for our types. + */ +/* + * crc16.h - CRC-16 routine + * + * Implements the standard CRC-16: + * Width 16 + * Poly 0x8005 (x^16 + x^15 + x^2 + 1) + * Init 0 + * + * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#ifndef __CRC16_H +#define __CRC16_H + +#include <stdint.h> + +#include <sys/types.h> + +extern uint16_t const osmo_crc16_table[256]; + +extern uint16_t osmo_crc16(uint16_t crc, const uint8_t *buffer, size_t len); + +static inline uint16_t osmo_crc16_byte(uint16_t crc, const uint8_t data) +{ + return (crc >> 8) ^ osmo_crc16_table[(crc ^ data) & 0xff]; +} + +#endif /* __CRC16_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/gsmtap.h b/src/shared/libosmocore/include/osmocom/core/gsmtap.h new file mode 100644 index 00000000..236b25ac --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/gsmtap.h @@ -0,0 +1,89 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload */ + +/* GSMTAP is a generic header format for GSM protocol captures, + * it uses the IANA-assigned UDP port number 4729 and carries + * payload in various formats of GSM interfaces such as Um MAC + * blocks or Um bursts. + * + * Example programs generating GSMTAP data are airprobe + * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) + */ + +#include <stdint.h> + +#define GSMTAP_VERSION 0x02 + +#define GSMTAP_TYPE_UM 0x01 +#define GSMTAP_TYPE_ABIS 0x02 +#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ +#define GSMTAP_TYPE_SIM 0x04 +#define GSMTAP_TYPE_TETRA_I1 0x05 /* tetra air interface */ +#define GSMTAP_TYPE_TETRA_I1_BURST 0x06 /* tetra air interface */ + +/* sub-types for TYPE_UM_BURST */ +#define GSMTAP_BURST_UNKNOWN 0x00 +#define GSMTAP_BURST_FCCH 0x01 +#define GSMTAP_BURST_PARTIAL_SCH 0x02 +#define GSMTAP_BURST_SCH 0x03 +#define GSMTAP_BURST_CTS_SCH 0x04 +#define GSMTAP_BURST_COMPACT_SCH 0x05 +#define GSMTAP_BURST_NORMAL 0x06 +#define GSMTAP_BURST_DUMMY 0x07 +#define GSMTAP_BURST_ACCESS 0x08 +#define GSMTAP_BURST_NONE 0x09 + +/* sub-types for TYPE_UM */ +#define GSMTAP_CHANNEL_UNKNOWN 0x00 +#define GSMTAP_CHANNEL_BCCH 0x01 +#define GSMTAP_CHANNEL_CCCH 0x02 +#define GSMTAP_CHANNEL_RACH 0x03 +#define GSMTAP_CHANNEL_AGCH 0x04 +#define GSMTAP_CHANNEL_PCH 0x05 +#define GSMTAP_CHANNEL_SDCCH 0x06 +#define GSMTAP_CHANNEL_SDCCH4 0x07 +#define GSMTAP_CHANNEL_SDCCH8 0x08 +#define GSMTAP_CHANNEL_TCH_F 0x09 +#define GSMTAP_CHANNEL_TCH_H 0x0a +#define GSMTAP_CHANNEL_ACCH 0x80 + +/* sub-types for TYPE_TETRA_AIR */ +#define GSMTAP_TETRA_BSCH 0x01 +#define GSMTAP_TETRA_AACH 0x02 +#define GSMTAP_TETRA_SCH_HU 0x03 +#define GSMTAP_TETRA_SCH_HD 0x04 +#define GSMTAP_TETRA_SCH_F 0x05 +#define GSMTAP_TETRA_BNCH 0x06 +#define GSMTAP_TETRA_STCH 0x07 +#define GSMTAP_TETRA_TCH_F 0x08 + +/* flags for the ARFCN */ +#define GSMTAP_ARFCN_F_PCS 0x8000 +#define GSMTAP_ARFCN_F_UPLINK 0x4000 +#define GSMTAP_ARFCN_MASK 0x3fff + +/* IANA-assigned well-known UDP port for GSMTAP messages */ +#define GSMTAP_UDP_PORT 4729 + +struct gsmtap_hdr { + uint8_t version; /* version, set to 0x01 currently */ + uint8_t hdr_len; /* length in number of 32bit words */ + uint8_t type; /* see GSMTAP_TYPE_* */ + uint8_t timeslot; /* timeslot (0..7 on Um) */ + + uint16_t arfcn; /* ARFCN (frequency) */ + int8_t signal_dbm; /* signal level in dBm */ + int8_t snr_db; /* signal/noise ratio in dB */ + + uint32_t frame_number; /* GSM Frame Number (FN) */ + + uint8_t sub_type; /* Type of burst/channel, see above */ + uint8_t antenna_nr; /* Antenna Number */ + uint8_t sub_slot; /* sub-slot within timeslot */ + uint8_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + +#endif /* _GSMTAP_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h new file mode 100644 index 00000000..f553c17a --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/gsmtap_util.h @@ -0,0 +1,53 @@ +#ifndef _GSMTAP_UTIL_H +#define _GSMTAP_UTIL_H + +#include <stdint.h> +#include <osmocom/core/write_queue.h> +#include <osmocom/core/select.h> + +/* convert RSL channel number to GSMTAP channel type */ +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id); + +/* generate msgb from data + metadata */ +struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, + uint8_t ss, uint32_t fn, int8_t signal_dbm, + uint8_t snr, const uint8_t *data, unsigned int len); + +/* one gsmtap instance */ +struct gsmtap_inst { + int ofd_wq_mode; + struct osmo_wqueue wq; + struct osmo_fd sink_ofd; +}; + +static inline int gsmtap_inst_fd(struct gsmtap_inst *gti) +{ + return gti->wq.bfd.fd; +} + +/* Open a GSMTAP source (sending) socket, conncet it to host/port and + * return resulting fd */ +int gsmtap_source_init_fd(const char *host, uint16_t port); + +/* Add a local sink to an existing GSMTAP source and return fd */ +int gsmtap_source_add_sink_fd(int gsmtap_fd); + +/* Open GSMTAP source (sending) socket, connect it to host/port, + * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue + * registration */ +struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, + int ofd_wq_mode); + +/* Add a local sink to an existing GSMTAP source instance */ +int gsmtap_source_add_sink(struct gsmtap_inst *gti); + +/* Send a msgb through a GSMTAP source */ +int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg); + +/* generate a message and send it via GSMTAP */ +int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, + uint8_t chan_type, uint8_t ss, uint32_t fn, + int8_t signal_dbm, uint8_t snr, const uint8_t *data, + unsigned int len); + +#endif /* _GSMTAP_UTIL_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/linuxlist.h b/src/shared/libosmocore/include/osmocom/core/linuxlist.h new file mode 100644 index 00000000..ff2c4915 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/linuxlist.h @@ -0,0 +1,360 @@ +#ifndef _LINUX_LLIST_H +#define _LINUX_LLIST_H + +#include <stddef.h> + +#ifndef inline +#define inline __inline__ +#endif + +static inline void prefetch(__attribute__((unused)) const void *x) {;} + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \ + (type *)( (char *)__mptr - offsetof(type, member) );}) + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized llist entries. + */ +#define LLIST_POISON1 ((void *) 0x00100100) +#define LLIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked llist implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole llists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct llist_head { + struct llist_head *next, *prev; +}; + +#define LLIST_HEAD_INIT(name) { &(name), &(name) } + +#define LLIST_HEAD(name) \ + struct llist_head name = LLIST_HEAD_INIT(name) + +#define INIT_LLIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_add(struct llist_head *_new, + struct llist_head *prev, + struct llist_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * llist_add - add a new entry + * @new: new entry to be added + * @head: llist head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void llist_add(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head, head->next); +} + +/** + * llist_add_tail - add a new entry + * @new: new entry to be added + * @head: llist head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head) +{ + __llist_add(_new, head->prev, head); +} + +/* + * Delete a llist entry by making the prev/next entries + * point to each other. + * + * This is only for internal llist manipulation where we know + * the prev/next entries already! + */ +static inline void __llist_del(struct llist_head * prev, struct llist_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * llist_del - deletes entry from llist. + * @entry: the element to delete from the llist. + * Note: llist_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void llist_del(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + entry->next = (struct llist_head *)LLIST_POISON1; + entry->prev = (struct llist_head *)LLIST_POISON2; +} + +/** + * llist_del_init - deletes entry from llist and reinitialize it. + * @entry: the element to delete from the llist. + */ +static inline void llist_del_init(struct llist_head *entry) +{ + __llist_del(entry->prev, entry->next); + INIT_LLIST_HEAD(entry); +} + +/** + * llist_move - delete from one llist and add as another's head + * @llist: the entry to move + * @head: the head that will precede our entry + */ +static inline void llist_move(struct llist_head *llist, struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add(llist, head); +} + +/** + * llist_move_tail - delete from one llist and add as another's tail + * @llist: the entry to move + * @head: the head that will follow our entry + */ +static inline void llist_move_tail(struct llist_head *llist, + struct llist_head *head) +{ + __llist_del(llist->prev, llist->next); + llist_add_tail(llist, head); +} + +/** + * llist_empty - tests whether a llist is empty + * @head: the llist to test. + */ +static inline int llist_empty(const struct llist_head *head) +{ + return head->next == head; +} + +static inline void __llist_splice(struct llist_head *llist, + struct llist_head *head) +{ + struct llist_head *first = llist->next; + struct llist_head *last = llist->prev; + struct llist_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * llist_splice - join two llists + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + */ +static inline void llist_splice(struct llist_head *llist, struct llist_head *head) +{ + if (!llist_empty(llist)) + __llist_splice(llist, head); +} + +/** + * llist_splice_init - join two llists and reinitialise the emptied llist. + * @llist: the new llist to add. + * @head: the place to add it in the first llist. + * + * The llist at @llist is reinitialised + */ +static inline void llist_splice_init(struct llist_head *llist, + struct llist_head *head) +{ + if (!llist_empty(llist)) { + __llist_splice(llist, head); + INIT_LLIST_HEAD(llist); + } +} + +/** + * llist_entry - get the struct for this entry + * @ptr: the &struct llist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the llist_struct within the struct. + */ +#define llist_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * __llist_for_each - iterate over a llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + * + * This variant differs from llist_for_each() in that it's the + * simplest possible llist iteration code, no prefetching is done. + * Use this for code that knows the llist to be very short (empty + * or 1 entry) most of the time. + */ +#define __llist_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * llist_for_each_prev - iterate over a llist backwards + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_prev(pos, head) \ + for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ + pos = pos->prev, prefetch(pos->prev)) + +/** + * llist_for_each_safe - iterate over a llist safe against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * llist_for_each_entry - iterate over llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_reverse - iterate backwards over llist of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_reverse(pos, head, member) \ + for (pos = llist_entry((head)->prev, typeof(*pos), member), \ + prefetch(pos->member.prev); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.prev, typeof(*pos), member), \ + prefetch(pos->member.prev)) + +/** + * llist_for_each_entry_continue - iterate over llist of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_continue(pos, head, member) \ + for (pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + prefetch(pos->member.next)) + +/** + * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_safe(pos, n, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + n = llist_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = llist_entry(n->member.next, typeof(*n), member)) + +/** + * llist_for_each_rcu - iterate over an rcu-protected llist + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_rcu(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) + +#define __llist_for_each_rcu(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next, ({ smp_read_barrier_depends(); 0;})) + +/** + * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe + * against removal of llist entry + * @pos: the &struct llist_head to use as a loop counter. + * @n: another &struct llist_head to use as temporary storage + * @head: the head for your llist. + */ +#define llist_for_each_safe_rcu(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) + +/** + * llist_for_each_entry_rcu - iterate over rcu llist of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your llist. + * @member: the name of the llist_struct within the struct. + */ +#define llist_for_each_entry_rcu(pos, head, member) \ + for (pos = llist_entry((head)->next, typeof(*pos), member), \ + prefetch(pos->member.next); \ + &pos->member != (head); \ + pos = llist_entry(pos->member.next, typeof(*pos), member), \ + ({ smp_read_barrier_depends(); 0;}), \ + prefetch(pos->member.next)) + + +/** + * llist_for_each_continue_rcu - iterate over an rcu-protected llist + * continuing after existing point. + * @pos: the &struct llist_head to use as a loop counter. + * @head: the head for your llist. + */ +#define llist_for_each_continue_rcu(pos, head) \ + for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ + (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) + + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/logging.h b/src/shared/libosmocore/include/osmocom/core/logging.h new file mode 100644 index 00000000..db029402 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/logging.h @@ -0,0 +1,154 @@ +#ifndef _OSMOCORE_LOGGING_H +#define _OSMOCORE_LOGGING_H + +#include <stdio.h> +#include <stdint.h> +#include <osmocom/core/linuxlist.h> + +#define LOG_MAX_CATEGORY 32 +#define LOG_MAX_CTX 8 +#define LOG_MAX_FILTERS 8 + +#define DEBUG + +#ifdef DEBUG +#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args) +#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args) +#else +#define DEBUGP(xss, fmt, args...) +#define DEBUGPC(ss, fmt, args...) +#endif + + +void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); + +/* new logging interface */ +#define LOGP(ss, level, fmt, args...) \ + logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) +#define LOGPC(ss, level, fmt, args...) \ + logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) + +/* different levels */ +#define LOGL_DEBUG 1 /* debugging information */ +#define LOGL_INFO 3 +#define LOGL_NOTICE 5 /* abnormal/unexpected condition */ +#define LOGL_ERROR 7 /* error condition, requires user action */ +#define LOGL_FATAL 8 /* fatal, program aborted */ + +#define LOG_FILTER_ALL 0x0001 + +struct log_category { + uint8_t loglevel; + uint8_t enabled; +}; + +struct log_info_cat { + const char *name; + const char *color; + const char *description; + uint8_t loglevel; + uint8_t enabled; +}; + +/* log context information, passed to filter */ +struct log_context { + void *ctx[LOG_MAX_CTX+1]; +}; + +struct log_target; + +typedef int log_filter(const struct log_context *ctx, + struct log_target *target); + +struct log_info { + /* filter callback function */ + log_filter *filter_fn; + + /* per-category information */ + const struct log_info_cat *cat; + unsigned int num_cat; +}; + +enum log_target_type { + LOG_TGT_TYPE_VTY, + LOG_TGT_TYPE_SYSLOG, + LOG_TGT_TYPE_FILE, + LOG_TGT_TYPE_STDERR, +}; + +struct log_target { + struct llist_head entry; + + int filter_map; + void *filter_data[LOG_MAX_FILTERS+1]; + + struct log_category categories[LOG_MAX_CATEGORY+1]; + uint8_t loglevel; + int use_color:1; + int print_timestamp:1; + + enum log_target_type type; + + union { + struct { + FILE *out; + const char *fname; + } tgt_file; + + struct { + int priority; + int facility; + } tgt_syslog; + + struct { + void *vty; + } tgt_vty; + }; + + void (*output) (struct log_target *target, unsigned int level, + const char *string); +}; + +/* use the above macros */ +void logp2(unsigned int subsys, unsigned int level, char *file, + int line, int cont, const char *format, ...) + __attribute__ ((format (printf, 6, 7))); +void log_init(const struct log_info *cat); + +/* context management */ +void log_reset_context(void); +int log_set_context(uint8_t ctx, void *value); + +/* filter on the targets */ +void log_set_all_filter(struct log_target *target, int); + +void log_set_use_color(struct log_target *target, int); +void log_set_print_timestamp(struct log_target *target, int); +void log_set_log_level(struct log_target *target, int log_level); +void log_parse_category_mask(struct log_target *target, const char* mask); +int log_parse_level(const char *lvl); +const char *log_level_str(unsigned int lvl); +int log_parse_category(const char *category); +void log_set_category_filter(struct log_target *target, int category, + int enable, int level); + +/* management of the targets */ +struct log_target *log_target_create(void); +void log_target_destroy(struct log_target *target); +struct log_target *log_target_create_stderr(void); +struct log_target *log_target_create_file(const char *fname); +struct log_target *log_target_create_syslog(const char *ident, int option, + int facility); +int log_target_file_reopen(struct log_target *tgt); + +void log_add_target(struct log_target *target); +void log_del_target(struct log_target *target); + +/* Generate command string for VTY use */ +const char *log_vty_command_string(const struct log_info *info); +const char *log_vty_command_description(const struct log_info *info); + +struct log_target *log_target_find(int type, const char *fname); +extern struct llist_head osmo_log_target_list; + +#endif /* _OSMOCORE_LOGGING_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/msgb.h b/src/shared/libosmocore/include/osmocom/core/msgb.h new file mode 100644 index 00000000..8665c2bf --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/msgb.h @@ -0,0 +1,197 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/utils.h> + +#define MSGB_DEBUG + +struct msgb { + struct llist_head list; + + /* Part of which TRX logical channel we were received / transmitted */ + /* FIXME: move them into the control buffer */ + struct gsm_bts_trx *trx; + struct gsm_lchan *lchan; + + /* the Layer1 header (if any) */ + unsigned char *l1h; + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + /* the layer 4 header */ + unsigned char *l4h; + + /* the 'control buffer', large enough to contain 5 pointers */ + unsigned long cb[5]; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; + unsigned char *tail; + unsigned char *data; + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +extern void msgb_free(struct msgb *m); +extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg); +extern struct msgb *msgb_dequeue(struct llist_head *queue); +extern void msgb_reset(struct msgb *m); + +#ifdef MSGB_DEBUG +#include <osmocom/core/panic.h> +#define MSGB_ABORT(msg, fmt, args ...) do { \ + osmo_panic("msgb(%p): " fmt, msg, ## args); \ + } while(0) +#else +#define MSGB_ABORT(msg, fmt, args ...) +#endif + +#define msgb_l1(m) ((void *)(m->l1h)) +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_sms(m) ((void *)(m->l4h)) + +static inline unsigned int msgb_l1len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l1(msgb); +} + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} + +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->head + msgb->data_len) - msgb->tail; +} + +static inline int msgb_headroom(const struct msgb *msgb) +{ + return (msgb->data - msgb->head); +} + +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + if (msgb_tailroom(msgb) < (int) len) + MSGB_ABORT(msgb, "Not enough tailroom msgb_push (%u < %u)\n", + msgb_tailroom(msgb), len); + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline void msgb_put_u8(struct msgb *msgb, uint8_t word) +{ + uint8_t *space = msgb_put(msgb, 1); + space[0] = word & 0xFF; +} +static inline void msgb_put_u16(struct msgb *msgb, uint16_t word) +{ + uint8_t *space = msgb_put(msgb, 2); + space[0] = word >> 8 & 0xFF; + space[1] = word & 0xFF; +} +static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) +{ + uint8_t *space = msgb_put(msgb, 4); + space[0] = word >> 24 & 0xFF; + space[1] = word >> 16 & 0xFF; + space[2] = word >> 8 & 0xFF; + space[3] = word & 0xFF; +} +static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->data; + msgb->data += len; + msgb->len -= len; + return tmp; +} +static inline uint8_t msgb_get_u8(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 1); + return space[0]; +} +static inline uint16_t msgb_get_u16(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 2); + return space[0] << 8 | space[1]; +} +static inline uint32_t msgb_get_u32(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 4); + return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3]; +} +static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) +{ + if (msgb_headroom(msgb) < (int) len) + MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n", + msgb_headroom(msgb), len); + msgb->data -= len; + msgb->len += len; + return msgb->data; +} +static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) +{ + msgb->len -= len; + return msgb->data += len; +} + +/* increase the headroom of an empty msgb, reducing the tailroom */ +static inline void msgb_reserve(struct msgb *msg, int len) +{ + msg->data += len; + msg->tail += len; +} + +static inline struct msgb *msgb_alloc_headroom(int size, int headroom, + const char *name) +{ + osmo_static_assert(size > headroom, headroom_bigger); + + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +/* non inline functions to ease binding */ +uint8_t *msgb_data(const struct msgb *msg); +uint16_t msgb_length(const struct msgb *msg); + + +#endif /* _MSGB_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/msgfile.h b/src/shared/libosmocore/include/osmocom/core/msgfile.h new file mode 100644 index 00000000..c5e67a45 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/msgfile.h @@ -0,0 +1,49 @@ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef MSG_FILE_H +#define MSG_FILE_H + +#include <osmocom/core/linuxlist.h> + +/** + * One message in the list. + */ +struct osmo_config_entry { + struct llist_head list; + + /* number for everyone to use */ + int nr; + + /* data from the file */ + char *mcc; + char *mnc; + char *option; + char *text; +}; + +struct osmo_config_list { + struct llist_head entry; +}; + +struct osmo_config_list* osmo_config_list_parse(void *ctx, const char *filename); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/panic.h b/src/shared/libosmocore/include/osmocom/core/panic.h new file mode 100644 index 00000000..c28a8440 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/panic.h @@ -0,0 +1,11 @@ +#ifndef OSMOCORE_PANIC_H +#define OSMOCORE_PANIC_H + +#include <stdarg.h> + +typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args); + +extern void osmo_panic(const char *fmt, ...); +extern void osmo_set_panic_handler(osmo_panic_handler_t h); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/plugin.h b/src/shared/libosmocore/include/osmocom/core/plugin.h new file mode 100644 index 00000000..6c0eccc6 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/plugin.h @@ -0,0 +1,6 @@ +#ifndef _OSMO_PLUGIN_H +#define _OSMO_PLUGIN_H + +int osmo_plugin_load_all(const char *directory); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/process.h b/src/shared/libosmocore/include/osmocom/core/process.h new file mode 100644 index 00000000..2d663828 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/process.h @@ -0,0 +1,6 @@ +#ifndef _OSMO_PROCESS_H +#define _OSMO_PROCESS_H + +int osmo_daemonize(void); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/rate_ctr.h b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h new file mode 100644 index 00000000..ebb8646c --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/rate_ctr.h @@ -0,0 +1,80 @@ +#ifndef _RATE_CTR_H +#define _RATE_CTR_H + +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> + +#define RATE_CTR_INTV_NUM 4 + +enum rate_ctr_intv { + RATE_CTR_INTV_SEC, + RATE_CTR_INTV_MIN, + RATE_CTR_INTV_HOUR, + RATE_CTR_INTV_DAY, +}; + +/* for each of the intervals, we keep the following values */ +struct rate_ctr_per_intv { + uint64_t last; + uint64_t rate; +}; + +/* for each actual value, we keep the following data */ +struct rate_ctr { + uint64_t current; + struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM]; +}; + +struct rate_ctr_desc { + const char *name; + const char *description; +}; + +/* Describe a counter group class */ +struct rate_ctr_group_desc { + /* The prefix to the name of all counters in this group */ + const char *group_name_prefix; + /* The human-readable description of the group */ + const char *group_description; + /* The number of counters in this group */ + const unsigned int num_ctr; + /* Pointer to array of counter names */ + const struct rate_ctr_desc *ctr_desc; +}; + +/* One instance of a counter group class */ +struct rate_ctr_group { + /* Linked list of all counter groups in the system */ + struct llist_head list; + /* Pointer to the counter group class */ + const struct rate_ctr_group_desc *desc; + /* The index of this ctr_group within its class */ + unsigned int idx; + /* Actual counter structures below */ + struct rate_ctr ctr[0]; +}; + +/* Allocate a new group of counters according to description */ +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx); + +/* Free the memory for the specified group of counters */ +void rate_ctr_group_free(struct rate_ctr_group *grp); + +/* Add a number to the counter */ +void rate_ctr_add(struct rate_ctr *ctr, int inc); + +/* Increment the counter by 1 */ +static inline void rate_ctr_inc(struct rate_ctr *ctr) +{ + rate_ctr_add(ctr, 1); +} + +/* Initialize the counter module */ +int rate_ctr_init(void *tall_ctx); + +struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx); +const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name); +#endif /* RATE_CTR_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/select.h b/src/shared/libosmocore/include/osmocom/core/select.h new file mode 100644 index 00000000..476c564e --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/select.h @@ -0,0 +1,22 @@ +#ifndef _BSC_SELECT_H +#define _BSC_SELECT_H + +#include <osmocom/core/linuxlist.h> + +#define BSC_FD_READ 0x0001 +#define BSC_FD_WRITE 0x0002 +#define BSC_FD_EXCEPT 0x0004 + +struct osmo_fd { + struct llist_head list; + int fd; + unsigned int when; + int (*cb)(struct osmo_fd *fd, unsigned int what); + void *data; + unsigned int priv_nr; +}; + +int osmo_fd_register(struct osmo_fd *fd); +void osmo_fd_unregister(struct osmo_fd *fd); +int osmo_select_main(int polling); +#endif /* _BSC_SELECT_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/signal.h b/src/shared/libosmocore/include/osmocom/core/signal.h new file mode 100644 index 00000000..535fd185 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/signal.h @@ -0,0 +1,14 @@ +#ifndef OSMO_SIGNAL_H +#define OSMO_SIGNAL_H + +typedef int osmo_signal_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data); + + +/* Management */ +int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); +void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); + +/* Dispatch */ +void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data); + +#endif /* OSMO_SIGNAL_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/socket.h b/src/shared/libosmocore/include/osmocom/core/socket.h new file mode 100644 index 00000000..b2601c76 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/socket.h @@ -0,0 +1,20 @@ +#ifndef _OSMOCORE_SOCKET_H +#define _OSMOCORE_SOCKET_H + +#include <stdint.h> + +struct sockaddr; + +int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, int connect0_bind1); + +int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, + const char *host, uint16_t port, int connect0_bind1); + +int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, + uint8_t proto, int connect0_bind1); + +/* determine if the given address is a local address */ +int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen); + +#endif /* _OSMOCORE_SOCKET_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/statistics.h b/src/shared/libosmocore/include/osmocom/core/statistics.h new file mode 100644 index 00000000..18493277 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/statistics.h @@ -0,0 +1,33 @@ +#ifndef _STATISTICS_H +#define _STATISTICS_H + +struct osmo_counter { + struct llist_head list; + const char *name; + const char *description; + unsigned long value; +}; + +static inline void osmo_counter_inc(struct osmo_counter *ctr) +{ + ctr->value++; +} + +static inline unsigned long osmo_counter_get(struct osmo_counter *ctr) +{ + return ctr->value; +} + +static inline void osmo_counter_reset(struct osmo_counter *ctr) +{ + ctr->value = 0; +} + +struct osmo_counter *osmo_counter_alloc(const char *name); +void osmo_counter_free(struct osmo_counter *ctr); + +int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data); + +struct osmo_counter *osmo_counter_get_by_name(const char *name); + +#endif /* _STATISTICS_H */ diff --git a/src/shared/libosmocore/include/osmocom/core/talloc.h b/src/shared/libosmocore/include/osmocom/core/talloc.h new file mode 100644 index 00000000..f7f7643b --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/talloc.h @@ -0,0 +1,192 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#define HAVE_VA_COPY + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) +#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void *talloc_pool(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +char *talloc_strdup(const void *t, const char *p); +char *talloc_strdup_append(char *s, const char *a); +char *talloc_strdup_append_buffer(char *s, const char *a); + +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_strndup_append(char *s, const char *a, size_t n); +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/timer.h b/src/shared/libosmocore/include/osmocom/core/timer.h new file mode 100644 index 00000000..db2ecbf7 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/timer.h @@ -0,0 +1,72 @@ +/* + * (C) 2008, 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef TIMER_H +#define TIMER_H + +#include <sys/time.h> + +#include <osmocom/core/linuxlist.h> + +/** + * Timer management: + * - Create a struct osmo_timer_list + * - Fill out timeout and use add_timer or + * use schedule_timer to schedule a timer in + * x seconds and microseconds from now... + * - Use del_timer to remove the timer + * + * Internally: + * - We hook into select.c to give a timeval of the + * nearest timer. On already passed timers we give + * it a 0 to immediately fire after the select + * - update_timers will call the callbacks and remove + * the timers. + * + */ +struct osmo_timer_list { + struct llist_head entry; + struct timeval timeout; + unsigned int active : 1; + unsigned int handled : 1; + unsigned int in_list : 1; + + void (*cb)(void*); + void *data; +}; + +/** + * timer management + */ +void osmo_timer_add(struct osmo_timer_list *timer); +void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds); +void osmo_timer_del(struct osmo_timer_list *timer); +int osmo_timer_pending(struct osmo_timer_list *timer); + + +/** + * internal timer list management + */ +struct timeval *osmo_timers_nearest(); +void osmo_timers_prepare(); +int osmo_timers_update(); +int osmo_timers_check(void); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/utils.h b/src/shared/libosmocore/include/osmocom/core/utils.h new file mode 100644 index 00000000..0f1ea3bb --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/utils.h @@ -0,0 +1,39 @@ +#ifndef OSMOCORE_UTIL_H +#define OSMOCORE_UTIL_H + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#include <stdint.h> + +struct value_string { + unsigned int value; + const char *str; +}; + +const char *get_value_string(const struct value_string *vs, uint32_t val); +int get_string_value(const struct value_string *vs, const char *str); + +char osmo_bcd2char(uint8_t bcd); +/* only works for numbers in ascci */ +uint8_t osmo_char2bcd(char c); + +int osmo_hexparse(const char *str, uint8_t *b, int max_len); +char *osmo_hexdump(const unsigned char *buf, int len); +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len); +char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); + +#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; + +void osmo_str2lower(char *out, const char *in); +void osmo_str2upper(char *out, const char *in); + +#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \ +do { \ + len += ret; \ + if (ret > rem) \ + ret = rem; \ + offset += ret; \ + rem -= ret; \ +} while (0) + +#endif diff --git a/src/shared/libosmocore/include/osmocom/core/write_queue.h b/src/shared/libosmocore/include/osmocom/core/write_queue.h new file mode 100644 index 00000000..41748d72 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/core/write_queue.h @@ -0,0 +1,46 @@ +/* Generic write queue implementation */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef OSMO_WQUEUE_H +#define OSMO_WQUEUE_H + +#include <osmocom/core/select.h> +#include <osmocom/core/msgb.h> + +struct osmo_wqueue { + struct osmo_fd bfd; + unsigned int max_length; + unsigned int current_length; + + struct llist_head msg_queue; + + int (*read_cb)(struct osmo_fd *fd); + int (*write_cb)(struct osmo_fd *fd, struct msgb *msg); + int (*except_cb)(struct osmo_fd *fd); +}; + +void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length); +void osmo_wqueue_clear(struct osmo_wqueue *queue); +int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data); +int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/crypt/Makefile.am b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am new file mode 100644 index 00000000..7ce69fdd --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am @@ -0,0 +1,3 @@ +osmocrypt_HEADERS = gprs_cipher.h + +osmocryptdir = $(includedir)/osmocom/crypt diff --git a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h new file mode 100644 index 00000000..30510711 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h @@ -0,0 +1,54 @@ +#ifndef _GPRS_CIPHER_H +#define _GPRS_CIPHER_H + +#include <osmocom/core/linuxlist.h> + +#define GSM0464_CIPH_MAX_BLOCK 1523 + +enum gprs_ciph_algo { + GPRS_ALGO_GEA0, + GPRS_ALGO_GEA1, + GPRS_ALGO_GEA2, + GPRS_ALGO_GEA3, + _GPRS_ALGO_NUM +}; + +enum gprs_cipher_direction { + GPRS_CIPH_MS2SGSN, + GPRS_CIPH_SGSN2MS, +}; + +/* An implementation of a GPRS cipher */ +struct gprs_cipher_impl { + struct llist_head list; + enum gprs_ciph_algo algo; + const char *name; + unsigned int priority; + + /* As specified in 04.64 Annex A. Uses Kc, IV and direction + * to generate the 1523 bytes cipher stream that need to be + * XORed wit the plaintext for encrypt / ciphertext for decrypt */ + int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv, + enum gprs_cipher_direction direction); +}; + +/* register a cipher with the core (from a plugin) */ +int gprs_cipher_register(struct gprs_cipher_impl *ciph); + +/* load all available GPRS cipher plugins */ +int gprs_cipher_load(const char *path); + +/* function to be called by core code */ +int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, + uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir); + +/* Do we have an implementation for this cipher? */ +int gprs_cipher_supported(enum gprs_ciph_algo algo); + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc); + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc); + +#endif /* _GPRS_CIPHER_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/Makefile.am b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am new file mode 100644 index 00000000..547933ec --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/Makefile.am @@ -0,0 +1,7 @@ +osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \ + gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \ + sysinfo.h + +SUBDIRS = protocol + +osmogsmdir = $(includedir)/osmocom/gsm diff --git a/src/shared/libosmocore/include/osmocom/gsm/a5.h b/src/shared/libosmocore/include/osmocom/gsm/a5.h new file mode 100644 index 00000000..2c630e5f --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/a5.h @@ -0,0 +1,49 @@ +/* + * a5.h + * + * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __OSMO_A5_H__ +#define __OSMO_A5_H__ + +#include <stdint.h> + +#include <osmocom/core/bits.h> + +static inline uint32_t +osmo_a5_fn_count(uint32_t fn) +{ + int t1 = fn / (26 * 51); + int t2 = fn % 26; + int t3 = fn % 51; + return (t1 << 11) | (t3 << 5) | t2; +} + + /* Notes: + * - key must be 8 bytes long (or NULL for A5/0) + * - the dl and ul pointer must be either NULL or 114 bits long + * - fn is the _real_ GSM frame number. + * (converted internally to fn_count) + */ +void osmo_a5(int n, uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); +void osmo_a5_1(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); +void osmo_a5_2(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); + +#endif /* __OSMO_A5_H__ */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h new file mode 100644 index 00000000..dcc8d4bb --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/abis_nm.h @@ -0,0 +1,27 @@ +#ifndef _OSMO_GSM_ABIS_NM_H +#define _OSMO_GSM_ABIS_NM_H + +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/protocol/gsm_12_21.h> + +const enum abis_nm_msgtype abis_nm_reports[4]; +const enum abis_nm_msgtype abis_nm_no_ack_nack[3]; +const enum abis_nm_msgtype abis_nm_sw_load_msgs[9]; +const enum abis_nm_msgtype abis_nm_nacks[33]; + +extern const struct value_string abis_nm_obj_class_names[]; +extern const struct value_string abis_nm_adm_state_names[]; + +const char *abis_nm_nack_cause_name(uint8_t cause); +const char *abis_nm_nack_name(uint8_t nack); +const char *abis_nm_event_type_name(uint8_t cause); +const char *abis_nm_severity_name(uint8_t cause); +const struct tlv_definition abis_nm_att_tlvdef; +const char *abis_nm_opstate_name(uint8_t os); +const char *abis_nm_avail_name(uint8_t avail); +const char *abis_nm_test_name(uint8_t test); +void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh); + +int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan); +enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb); +#endif /* _OSMO_GSM_ABIS_NM_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/comp128.h b/src/shared/libosmocore/include/osmocom/gsm/comp128.h new file mode 100644 index 00000000..c37808f0 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/comp128.h @@ -0,0 +1,22 @@ +/* + * COMP128 header + * + * See comp128.c for details + */ + +#ifndef __COMP128_H__ +#define __COMP128_H__ + +#include <stdint.h> + +/* + * Performs the COMP128 algorithm (used as A3/A8) + * ki : uint8_t [16] + * srand : uint8_t [16] + * sres : uint8_t [4] + * kc : uint8_t [8] + */ +void comp128(uint8_t *ki, uint8_t *srand, uint8_t *sres, uint8_t *kc); + +#endif /* __COMP128_H__ */ + diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h new file mode 100644 index 00000000..d6626d60 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0480.h @@ -0,0 +1,26 @@ +#ifndef gsm0480_h +#define gsm0480_h + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_04_80.h> + +#define MAX_LEN_USSD_STRING 31 + +struct ussd_request { + char text[MAX_LEN_USSD_STRING + 1]; + uint8_t transaction_id; + uint8_t invoke_id; +}; + +int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, + struct ussd_request *request); + +struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text); +struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text); +struct msgb *gsm0480_create_notifySS(const char *text); + +int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id); +int gsm0480_wrap_facility(struct msgb *msg); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h new file mode 100644 index 00000000..1d853775 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h @@ -0,0 +1,46 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009,2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef OSMOCORE_GSM0808_H +#define OSMOCORE_GSM0808_H + +#include "tlv.h" + +struct msgb; + +struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, uint16_t ci); +struct msgb *gsm0808_create_reset(void); +struct msgb *gsm0808_create_clear_command(uint8_t reason); +struct msgb *gsm0808_create_clear_complete(void); +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); +struct msgb *gsm0808_create_cipher_reject(uint8_t cause); +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length); +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id); +struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode); +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause); +struct msgb *gsm0808_create_clear_rqst(uint8_t cause); + +struct msgb *gsm0808_create_dtap(struct msgb *msg, uint8_t link_id); +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id); + +const struct tlv_definition *gsm0808_att_tlvdef(); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h new file mode 100644 index 00000000..1e9403bc --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48.h @@ -0,0 +1,36 @@ +#ifndef _OSMOCORE_GSM48_H +#define _OSMOCORE_GSM48_H + +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm48_ie.h> + +/* A parsed GPRS routing area */ +struct gprs_ra_id { + uint16_t mnc; + uint16_t mcc; + uint16_t lac; + uint8_t rac; +}; + +extern const struct tlv_definition gsm48_att_tlvdef; +extern const struct tlv_definition gsm48_rr_att_tlvdef; +extern const struct tlv_definition gsm48_mm_att_tlvdef; +const char *gsm48_cc_state_name(uint8_t state); +const char *gsm48_cc_msg_name(uint8_t msgtype); +const char *rr_cause_name(uint8_t cause); + +void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, + uint16_t mnc, uint16_t lac); +int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi); +int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi); + +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, + const uint8_t *mi, const int mi_len); + +/* Parse Routeing Area Identifier */ +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h new file mode 100644 index 00000000..f4fce25c --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm48_ie.h @@ -0,0 +1,117 @@ +#ifndef _OSMOCORE_GSM48_IE_H +#define _OSMOCORE_GSM48_IE_H + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/mncc.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */ +int gsm48_decode_bcd_number(char *output, int output_len, + const uint8_t *bcd_lv, int h_len); + +/* convert a ASCII phone number to 'called/calling/connect party BCD number' */ +int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, + int h_len, const char *input); +/* decode 'bearer capability' */ +int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, + const uint8_t *lv); +/* encode 'bearer capability' */ +int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only, + const struct gsm_mncc_bearer_cap *bcap); +/* decode 'call control cap' */ +int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv); +/* encode 'call control cap' */ +int gsm48_encode_cccap(struct msgb *msg, + const struct gsm_mncc_cccap *ccap); +/* decode 'called party BCD number' */ +int gsm48_decode_called(struct gsm_mncc_number *called, + const uint8_t *lv); +/* encode 'called party BCD number' */ +int gsm48_encode_called(struct msgb *msg, + const struct gsm_mncc_number *called); +/* decode callerid of various IEs */ +int gsm48_decode_callerid(struct gsm_mncc_number *callerid, + const uint8_t *lv); +/* encode callerid of various IEs */ +int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len, + const struct gsm_mncc_number *callerid); +/* decode 'cause' */ +int gsm48_decode_cause(struct gsm_mncc_cause *cause, + const uint8_t *lv); +/* encode 'cause' */ +int gsm48_encode_cause(struct msgb *msg, int lv_only, + const struct gsm_mncc_cause *cause); +/* decode 'calling number' */ +int gsm48_decode_calling(struct gsm_mncc_number *calling, + const uint8_t *lv); +/* encode 'calling number' */ +int gsm48_encode_calling(struct msgb *msg, + const struct gsm_mncc_number *calling); +/* decode 'connected number' */ +int gsm48_decode_connected(struct gsm_mncc_number *connected, + const uint8_t *lv); +/* encode 'connected number' */ +int gsm48_encode_connected(struct msgb *msg, + const struct gsm_mncc_number *connected); +/* decode 'redirecting number' */ +int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting, + const uint8_t *lv); +/* encode 'redirecting number' */ +int gsm48_encode_redirecting(struct msgb *msg, + const struct gsm_mncc_number *redirecting); +/* decode 'facility' */ +int gsm48_decode_facility(struct gsm_mncc_facility *facility, + const uint8_t *lv); +/* encode 'facility' */ +int gsm48_encode_facility(struct msgb *msg, int lv_only, + const struct gsm_mncc_facility *facility); +/* decode 'notify' */ +int gsm48_decode_notify(int *notify, const uint8_t *v); +/* encode 'notify' */ +int gsm48_encode_notify(struct msgb *msg, int notify); +/* decode 'signal' */ +int gsm48_decode_signal(int *signal, const uint8_t *v); +/* encode 'signal' */ +int gsm48_encode_signal(struct msgb *msg, int signal); +/* decode 'keypad' */ +int gsm48_decode_keypad(int *keypad, const uint8_t *lv); +/* encode 'keypad' */ +int gsm48_encode_keypad(struct msgb *msg, int keypad); +/* decode 'progress' */ +int gsm48_decode_progress(struct gsm_mncc_progress *progress, + const uint8_t *lv); +/* encode 'progress' */ +int gsm48_encode_progress(struct msgb *msg, int lv_only, + const struct gsm_mncc_progress *p); +/* decode 'user-user' */ +int gsm48_decode_useruser(struct gsm_mncc_useruser *uu, + const uint8_t *lv); +/* encode 'useruser' */ +int gsm48_encode_useruser(struct msgb *msg, int lv_only, + const struct gsm_mncc_useruser *uu); +/* decode 'ss version' */ +int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv, + const uint8_t *lv); +/* encode 'ss version' */ +int gsm48_encode_ssversion(struct msgb *msg, + const struct gsm_mncc_ssversion *ssv); +/* decode 'more data' does not require a function, because it has no value */ +/* encode 'more data' */ +int gsm48_encode_more(struct msgb *msg); + +/* structure of one frequency */ +struct gsm_sysinfo_freq { + /* if the frequency included in the sysinfo */ + uint8_t mask; +}; + +/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ +int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, + uint8_t len, uint8_t mask, uint8_t frqt); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h new file mode 100644 index 00000000..a0ef3c4f --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h @@ -0,0 +1,143 @@ +/* GSM utility functions, e.g. coding and decoding */ +/* + * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef GSM_UTILS_H +#define GSM_UTILS_H + +#include <stdint.h> + +#define ADD_MODULO(sum, delta, modulo) do { \ + if ((sum += delta) >= modulo) \ + sum -= modulo; \ + } while (0) + +#define GSM_MAX_FN (26*51*2048) + +struct gsm_time { + uint32_t fn; /* FN count */ + uint16_t t1; /* FN div (26*51) */ + uint8_t t2; /* FN modulo 26 */ + uint8_t t3; /* FN modulo 51 */ + uint8_t tc; +}; + +enum gsm_band { + GSM_BAND_850 = 1, + GSM_BAND_900 = 2, + GSM_BAND_1800 = 4, + GSM_BAND_1900 = 8, + GSM_BAND_450 = 0x10, + GSM_BAND_480 = 0x20, + GSM_BAND_750 = 0x40, + GSM_BAND_810 = 0x80, +}; + +const char *gsm_band_name(enum gsm_band band); +enum gsm_band gsm_band_parse(const char *mhz); + +int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length); +int gsm_7bit_encode(uint8_t *result, const char *data); + +int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm); +int ms_pwr_dbm(enum gsm_band band, uint8_t lvl); + +/* According to TS 08.05 Chapter 8.1.4 */ +int rxlev2dbm(uint8_t rxlev); +uint8_t dbm2rxlev(int dbm); + +/* According to GSM 04.08 Chapter 10.5.1.6 */ +static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) { + switch (n) { + case 0: return 1; + case 1: return (cm2[0] & (1<<3)) ? 0 : 1; + case 2: return (cm2[2] & (1<<0)) ? 1 : 0; + case 3: return (cm2[2] & (1<<1)) ? 1 : 0; + default: + return 0; + } +} + +/* According to GSM 04.08 Chapter 10.5.2.29 */ +static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; } +static inline int rach_max_trans_raw2val(int raw) { + const int tbl[4] = { 1, 2, 4, 7 }; + return tbl[raw & 3]; +} + +#define ARFCN_PCS 0x8000 +#define ARFCN_UPLINK 0x4000 +#define ARFCN_FLAG_MASK 0xf000 /* Reserve the upper 5 bits for flags */ + +enum gsm_band gsm_arfcn2band(uint16_t arfcn); + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); + +/* Convert from frame number to GSM time */ +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); + +/* Convert from GSM time to frame number */ +uint32_t gsm_gsmtime2fn(struct gsm_time *time); + +/* GSM TS 03.03 Chapter 2.6 */ +enum gprs_tlli_type { + TLLI_LOCAL, + TLLI_FOREIGN, + TLLI_RANDOM, + TLLI_AUXILIARY, + TLLI_RESERVED, +}; + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli); + +uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type); + +/* Osmocom internal, not part of any gsm spec */ +enum gsm_phys_chan_config { + GSM_PCHAN_NONE, + GSM_PCHAN_CCCH, + GSM_PCHAN_CCCH_SDCCH4, + GSM_PCHAN_TCH_F, + GSM_PCHAN_TCH_H, + GSM_PCHAN_SDCCH8_SACCH8C, + GSM_PCHAN_PDCH, /* GPRS PDCH */ + GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */ + GSM_PCHAN_UNKNOWN, + _GSM_PCHAN_MAX +}; + +/* Osmocom internal, not part of any gsm spec */ +enum gsm_chan_t { + GSM_LCHAN_NONE, + GSM_LCHAN_SDCCH, + GSM_LCHAN_TCH_F, + GSM_LCHAN_TCH_H, + GSM_LCHAN_UNKNOWN, + GSM_LCHAN_CCCH, + _GSM_LCHAN_MAX +}; + + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/mncc.h b/src/shared/libosmocore/include/osmocom/gsm/mncc.h new file mode 100644 index 00000000..a094bb9b --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/mncc.h @@ -0,0 +1,71 @@ +#ifndef _OSMOCORE_MNCC_H +#define _OSMOCORE_MNCC_H + +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + +/* Expanded fields from GSM TS 04.08, Table 10.5.102 */ +struct gsm_mncc_bearer_cap { + int transfer; /* Information Transfer Capability */ + int mode; /* Transfer Mode */ + int coding; /* Coding Standard */ + int radio; /* Radio Channel Requirement */ + int speech_ctm; /* CTM text telephony indication */ + int speech_ver[8]; /* Speech version indication */ +}; + +struct gsm_mncc_number { + int type; + int plan; + int present; + int screen; + char number[33]; +}; + +struct gsm_mncc_cause { + int location; + int coding; + int rec; + int rec_val; + int value; + int diag_len; + char diag[32]; +}; + +struct gsm_mncc_useruser { + int proto; + char info[GSM_MAX_USERUSER + 1]; /* + termination char */ +}; + +struct gsm_mncc_progress { + int coding; + int location; + int descr; +}; + +struct gsm_mncc_facility { + int len; + char info[GSM_MAX_FACILITY]; +}; + +struct gsm_mncc_ssversion { + int len; + char info[GSM_MAX_SSVERSION]; +}; + +struct gsm_mncc_cccap { + int dtmf; + int pcp; +}; + +enum { + GSM_MNCC_BCAP_SPEECH = 0, + GSM_MNCC_BCAP_UNR_DIG = 1, + GSM_MNCC_BCAP_AUDIO = 2, + GSM_MNCC_BCAP_FAX_G3 = 3, + GSM_MNCC_BCAP_OTHER_ITC = 5, + GSM_MNCC_BCAP_RESERVED = 7, +}; + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am b/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am new file mode 100644 index 00000000..7f6de639 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/Makefile.am @@ -0,0 +1,6 @@ +osmogsm_proto_HEADERS = gsm_03_41.h \ + gsm_04_08.h gsm_04_11.h gsm_04_12.h gsm_04_80.h \ + gsm_08_08.h gsm_08_58.h \ + gsm_12_21.h ipaccess.h + +osmogsm_protodir = $(includedir)/osmocom/gsm/protocol diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h new file mode 100644 index 00000000..54365cbc --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_03_41.h @@ -0,0 +1,51 @@ +#ifndef PROTO_GSM_03_41_H +#define PROTO_GSM_03_41_H + +#include <stdint.h> + +/* GSM TS 03.41 definitions also TS 23.041*/ + +/* Chapter 9.3.2 */ +struct gsm341_ms_message { + struct { + uint8_t code_hi:6; + uint8_t gs:2; + uint8_t update:4; + uint8_t code_lo:4; + } serial; + uint16_t msg_id; + struct { + uint8_t language:4; + uint8_t group:4; + } dcs; + struct { + uint8_t total:4; + uint8_t current:4; + } page; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.4.1.3 */ +struct gsm341_etws_message { + struct { + uint8_t code_hi:4; + uint8_t popup:1; + uint8_t alert:1; + uint8_t gs:2; + uint8_t update:4; + uint8_t code_lo:4; + } serial; + uint16_t msg_id; + uint16_t warning_type; + uint8_t data[0]; +} __attribute__((packed)); + +#define GSM341_MSG_CODE(ms) ((ms)->serial.code_lo | ((ms)->serial.code_hi << 4)) + +/* Section 9.3.2.1 - Geographical Scope */ +#define GSM341_GS_CELL_WIDE_IMMED 0 +#define GSM341_GS_PLMN_WIDE 1 +#define GSM341_GS_LA_WIDE 2 +#define GSM341_GS_CELL_WIDE 3 + +#endif /* PROTO_GSM_03_41_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h new file mode 100644 index 00000000..39470e78 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h @@ -0,0 +1,1265 @@ +#ifndef PROTO_GSM_04_08_H +#define PROTO_GSM_04_08_H + +#include <stdint.h> + +/* GSM TS 04.08 definitions */ +struct gsm_lchan; + +/* Chapter 10.5.1.5 */ +struct gsm48_classmark1 { + uint8_t pwr_lev:3, + a5_1:1, + es_ind:1, + rev_lev:2, + spare:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.6 */ +struct gsm48_classmark2 { + uint8_t pwr_lev:3, + a5_1:1, + es_ind:1, + rev_lev:2, + spare:1; + uint8_t fc:1, + vgcs:1, + vbs:1, + sm_cap:1, + ss_scr:2, + ps_cap:1, + spare2:1; + uint8_t a5_2:1, + a5_3:1, + cmsp:1, + solsa:1, + spare3:1, + lcsva_cap:1, + spare4:1, + cm3:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.3 */ +struct gsm48_range_1024 { + uint8_t w1_hi:2, + f0:1, + form_id:5; + uint8_t w1_lo; + uint8_t w2_hi; + uint8_t w3_hi:7, + w2_lo:1; + uint8_t w4_hi:6, + w3_lo:2; + uint8_t w5_hi:6, + w4_lo:2; + uint8_t w6_hi:6, + w5_lo:2; + uint8_t w7_hi:6, + w6_lo:2; + uint8_t w8_hi:6, + w7_lo:2; + uint8_t w9:7, + w8_lo:1; + uint8_t w11_hi:1, + w10:7; + uint8_t w12_hi:2, + w11_lo:6; + uint8_t w13_hi:3, + w12_lo:5; + uint8_t w14_hi:4, + w13_lo:4; + uint8_t w15_hi:5, + w14_lo:3; + uint8_t w16:6, + w15_lo:2; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.4 */ +struct gsm48_range_512 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1_hi:7, + orig_arfcn_lo:1; + uint8_t w2_hi:6, + w1_lo:2; + uint8_t w3_hi:6, + w2_lo:2; + uint8_t w4_hi:6, + w3_lo:2; + uint8_t w5:7, + w4_lo:1; + uint8_t w7_hi:1, + w6:7; + uint8_t w8_hi:2, + w7_lo:6; + uint8_t w9_hi:4, + w8_lo:4; + uint8_t w10:6, + w9_lo:2; + uint8_t w12_hi:2, + w11:6; + uint8_t w13_hi:4, + w12_lo:4; + uint8_t w14:6, + w13_lo:2; + uint8_t w16_hi:2, + w15:6; + uint8_t w17:5, + w16_lo:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.5 */ +struct gsm48_range_256 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1_hi:7, + orig_arfcn_lo:1; + uint8_t w2:7, + w1_lo:1; + uint8_t w4_hi:1, + w3:7; + uint8_t w5_hi:3, + w4_lo:5; + uint8_t w6_hi:5, + w5_lo:3; + uint8_t w8_hi:1, + w7:6, + w6_lo:1; + uint8_t w9_hi:4, + w8_lo:4; + uint8_t w11_hi:2, + w10:5, + w9_lo:1; + uint8_t w12:5, + w11_lo:3; + uint8_t w14_hi:3, + w13:5; + uint8_t w16_hi:1, + w15:5, + w14_lo:2; + uint8_t w18_hi:1, + w17:4, + w16_lo:3; + uint8_t w20_hi:1, + w19:4, + w18_lo:3; + uint8_t spare:1, + w21:4, + w20_lo:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.6 */ +struct gsm48_range_128 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1:7, + orig_arfcn_lo:1; + uint8_t w3_hi:2, + w2:6; + uint8_t w4_hi:4, + w3_lo:4; + uint8_t w6_hi:2, + w5:5, + w4_lo:1; + uint8_t w7:5, + w6_lo:3; + uint8_t w9:4, + w8:4; + uint8_t w11:4, + w10:4; + uint8_t w13:4, + w12:4; + uint8_t w15:4, + w14:4; + uint8_t w18_hi:2, + w17:3, + w16:3; + uint8_t w21_hi:1, + w20:3, + w19:3, + w18_lo:1; + uint8_t w23:3, + w22:3, + w21_lo:2; + uint8_t w26_hi:2, + w25:3, + w24:3; + uint8_t spare:1, + w28:3, + w27:3, + w26_lo:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.7 */ +struct gsm48_var_bit { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t rrfcn1_7:7, + orig_arfcn_lo:1; + uint8_t rrfcn8_111[13]; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.5 */ +struct gsm48_chan_desc { + uint8_t chan_nr; + union { + struct { + uint8_t maio_high:4, + h:1, + tsc:3; + uint8_t hsn:6, + maio_low:2; + } h1; + struct { + uint8_t arfcn_high:2, + spare:2, + h:1, + tsc:3; + uint8_t arfcn_low; + } h0; + }; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.20 */ +struct gsm48_meas_res { + uint8_t rxlev_full:6, + dtx_used:1, + ba_used:1; + uint8_t rxlev_sub:6, + meas_valid:1, + spare:1; + uint8_t no_nc_n_hi:1, + rxqual_sub:3, + rxqual_full:3, + spare2:1; + uint8_t rxlev_nc1:6, + no_nc_n_lo:2; + uint8_t bsic_nc1_hi:3, + bcch_f_nc1:5; + uint8_t rxlev_nc2_hi:5, + bsic_nc1_lo:3; + uint8_t bsic_nc2_hi:2, + bcch_f_nc2:5, + rxlev_nc2_lo:1; + uint8_t rxlev_nc3_hi:4, + bsic_nc2_lo:4; + uint8_t bsic_nc3_hi:1, + bcch_f_nc3:5, + rxlev_nc3_lo:2; + uint8_t rxlev_nc4_hi:3, + bsic_nc3_lo:5; + uint8_t bcch_f_nc4:5, + rxlev_nc4_lo:3; + uint8_t rxlev_nc5_hi:2, + bsic_nc4:6; + uint8_t bcch_f_nc5_hi:4, + rxlev_nc5_lo:4; + uint8_t rxlev_nc6_hi:1, + bsic_nc5:6, + bcch_f_nc5_lo:1; + uint8_t bcch_f_nc6_hi:3, + rxlev_nc6_lo:5; + uint8_t bsic_nc6:6, + bcch_f_nc6_lo:2; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.21aa */ +struct gsm48_multi_rate_conf { + uint8_t smod : 2, + spare: 1, + icmi : 1, + nscb : 1, + ver : 3; + uint8_t m4_75 : 1, + m5_15 : 1, + m5_90 : 1, + m6_70 : 1, + m7_40 : 1, + m7_95 : 1, + m10_2 : 1, + m12_2 : 1; +} __attribute__((packed)); + +/* Chapter 10.5.2.28(a) */ +struct gsm48_power_cmd { + uint8_t power_level:5, + spare:2, + atc:1; +} __attribute__((packed)); + +/* Chapter 10.5.2.29 */ +struct gsm48_rach_control { + uint8_t re :1, + cell_bar :1, + tx_integer :4, + max_trans :2; + uint8_t t2; + uint8_t t3; +} __attribute__ ((packed)); + + +/* Chapter 10.5.2.30 */ +struct gsm48_req_ref { + uint8_t ra; + uint8_t t3_high:3, + t1:5; + uint8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.38 */ +struct gsm48_start_time { + uint8_t t3_high:3, + t1:5; + uint8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.39 */ +struct gsm48_sync_ind { + uint8_t si:2, + rot:1, + nci:1, + sync_ie:4; +} __attribute__((packed)); + +/* + * Chapter 9.1.5/9.1.6 + * + * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a + */ +struct gsm48_chan_mode_modify { + struct gsm48_chan_desc chan_desc; + uint8_t mode; +} __attribute__ ((packed)); + +enum gsm48_chan_mode { + GSM48_CMODE_SIGN = 0x00, + GSM48_CMODE_SPEECH_V1 = 0x01, + GSM48_CMODE_SPEECH_EFR = 0x21, + GSM48_CMODE_SPEECH_AMR = 0x41, + GSM48_CMODE_DATA_14k5 = 0x0f, + GSM48_CMODE_DATA_12k0 = 0x03, + GSM48_CMODE_DATA_6k0 = 0x0b, + GSM48_CMODE_DATA_3k6 = 0x23, +}; + +/* Chapter 9.1.2 */ +struct gsm48_ass_cmd { + /* Semantic is from 10.5.2.5a */ + struct gsm48_chan_desc chan_desc; + uint8_t power_command; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.1.13 */ +struct gsm48_frq_redef { + /* Semantic is from 10.5.2.5a */ + struct gsm48_chan_desc chan_desc; + uint8_t mob_alloc_len; + uint8_t mob_alloc[0]; +} __attribute__((packed)); + +/* Chapter 10.5.2.2 */ +struct gsm48_cell_desc { + uint8_t bcc:3, + ncc:3, + arfcn_hi:2; + uint8_t arfcn_lo; +} __attribute__((packed)); + +/* Chapter 9.1.15 */ +struct gsm48_ho_cmd { + struct gsm48_cell_desc cell_desc; + struct gsm48_chan_desc chan_desc; + uint8_t ho_ref; + uint8_t power_command; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.1.18 */ +struct gsm48_imm_ass { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_chan_desc chan_desc; + struct gsm48_req_ref req_ref; + uint8_t timing_advance; + uint8_t mob_alloc_len; + uint8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Chapter 9.1.25 */ +struct gsm48_pag_resp { + uint8_t spare:4, + key_seq:4; + uint32_t classmark2; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.3 */ +struct gsm48_loc_area_id { + uint8_t digits[3]; /* BCD! */ + uint16_t lac; +} __attribute__ ((packed)); + +/* Section 9.2.2 */ +struct gsm48_auth_req { + uint8_t key_seq:4, + spare:4; + uint8_t rand[16]; +} __attribute__ ((packed)); + +/* Section 9.2.3 */ +struct gsm48_auth_resp { + uint8_t sres[4]; +} __attribute__ ((packed)); + +/* Section 9.2.15 */ +struct gsm48_loc_upd_req { + uint8_t type:4, + key_seq:4; + struct gsm48_loc_area_id lai; + struct gsm48_classmark1 classmark1; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.1 */ +struct gsm48_hdr { + uint8_t proto_discr; + uint8_t msg_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.3x System information Type header */ +struct gsm48_system_information_type_header { + uint8_t l2_plen; + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; +} __attribute__ ((packed)); + +/* Section 10.5.2.4 Cell Selection Parameters */ +struct gsm48_cell_sel_par { + uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */ + cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */ + uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */ + neci:1, + acs:1; +} __attribute__ ((packed)); + +/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */ +struct gsm48_control_channel_descr { + uint8_t ccch_conf :3, + bs_ag_blks_res :3, + att :1, + spare1 :1; + uint8_t bs_pa_mfrms : 3, + spare2 :5; + uint8_t t3212; +} __attribute__ ((packed)); + +struct gsm48_cell_options { + uint8_t radio_link_timeout:4, + dtx:2, + pwrc:1, + spare:1; +} __attribute__ ((packed)); + +/* Section 9.2.9 CM service request */ +struct gsm48_service_request { + uint8_t cm_service_type : 4, + cipher_key_seq : 4; + /* length + 3 bytes */ + uint32_t classmark; + uint8_t mi_len; + uint8_t mi[0]; + /* optional priority level */ +} __attribute__ ((packed)); + +/* Section 9.1.31 System information Type 1 */ +struct gsm48_system_information_type_1 { + struct gsm48_system_information_type_header header; + uint8_t cell_channel_description[16]; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; /* NCH position on the CCCH */ +} __attribute__ ((packed)); + +/* Section 9.1.32 System information Type 2 */ +struct gsm48_system_information_type_2 { + struct gsm48_system_information_type_header header; + uint8_t bcch_frequency_list[16]; + uint8_t ncc_permitted; + struct gsm48_rach_control rach_control; +} __attribute__ ((packed)); + +/* Section 9.1.33 System information Type 2bis */ +struct gsm48_system_information_type_2bis { + struct gsm48_system_information_type_header header; + uint8_t bcch_frequency_list[16]; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.34 System information Type 2ter */ +struct gsm48_system_information_type_2ter { + struct gsm48_system_information_type_header header; + uint8_t ext_bcch_frequency_list[16]; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.35 System information Type 3 */ +struct gsm48_system_information_type_3 { + struct gsm48_system_information_type_header header; + uint16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_control_channel_descr control_channel_desc; + struct gsm48_cell_options cell_options; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.36 System information Type 4 */ +struct gsm48_system_information_type_4 { + struct gsm48_system_information_type_header header; + struct gsm48_loc_area_id lai; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + /* optional CBCH conditional CBCH... followed by + mandantory SI 4 Reset Octets + */ + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.37 System information Type 5 */ +struct gsm48_system_information_type_5 { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.38 System information Type 5bis */ +struct gsm48_system_information_type_5bis { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.39 System information Type 5ter */ +struct gsm48_system_information_type_5ter { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.40 System information Type 6 */ +struct gsm48_system_information_type_6 { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_cell_options cell_options; + uint8_t ncc_permitted; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.43a System Information type 13 */ +struct gsm48_system_information_type_13 { + struct gsm48_system_information_type_header header; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.2.12 IMSI Detach Indication */ +struct gsm48_imsi_detach_ind { + struct gsm48_classmark1 classmark1; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 9.1.1 */ +struct gsm48_add_ass { + /* Semantic is from 10.5.2.5 */ + struct gsm48_chan_desc chan_desc; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.3 */ +struct gsm48_ass_cpl { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 9.1.4 */ +struct gsm48_ass_fail { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 9.1.3 */ +struct gsm48_ho_cpl { + uint8_t rr_cause; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.4 */ +struct gsm48_ho_fail { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 9.1.7 */ +struct gsm48_chan_rel { + uint8_t rr_cause; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.9 */ +struct gsm48_cip_mode_cmd { + uint8_t sc:1, + alg_id:3, + cr:1, + spare:3; +} __attribute__((packed)); + +/* Section 9.1.11 */ +struct gsm48_cm_change { + uint8_t cm2_len; + struct gsm48_classmark2 cm2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.19 */ +struct gsm48_imm_ass_ext { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_chan_desc chan_desc1; + struct gsm48_req_ref req_ref1; + uint8_t timing_advance1; + struct gsm48_chan_desc chan_desc2; + struct gsm48_req_ref req_ref2; + uint8_t timing_advance2; + uint8_t mob_alloc_len; + uint8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Section 9.1.20 */ +struct gsm48_imm_ass_rej { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_req_ref req_ref1; + uint8_t wait_ind1; + struct gsm48_req_ref req_ref2; + uint8_t wait_ind2; + struct gsm48_req_ref req_ref3; + uint8_t wait_ind3; + struct gsm48_req_ref req_ref4; + uint8_t wait_ind4; + uint8_t rest[0]; +} __attribute__ ((packed)); + +/* Section 9.1.22 */ +struct gsm48_paging1 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.23 */ +struct gsm48_paging2 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint32_t tmsi1; + uint32_t tmsi2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.24 */ +struct gsm48_paging3 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint32_t tmsi1; + uint32_t tmsi2; + uint32_t tmsi3; + uint32_t tmsi4; + uint8_t cneed3:2, + cneed4:2, + spare2:4; + uint8_t rest[0]; +} __attribute__((packed)); + +/* Section 9.1.25 */ +struct gsm48_pag_rsp { + uint8_t key_seq:3, + spare:5; + uint8_t cm2_len; + struct gsm48_classmark2 cm2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.29 */ +struct gsm48_rr_status { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 10.2 + GSM 04.07 12.2.3.1.1 */ +#define GSM48_PDISC_GROUP_CC 0x00 +#define GSM48_PDISC_BCAST_CC 0x01 +#define GSM48_PDISC_PDSS1 0x02 +#define GSM48_PDISC_CC 0x03 +#define GSM48_PDISC_PDSS2 0x04 +#define GSM48_PDISC_MM 0x05 +#define GSM48_PDISC_RR 0x06 +#define GSM48_PDISC_MM_GPRS 0x08 +#define GSM48_PDISC_SMS 0x09 +#define GSM48_PDISC_SM_GPRS 0x0a +#define GSM48_PDISC_NC_SS 0x0b +#define GSM48_PDISC_LOC 0x0c +#define GSM48_PDISC_MASK 0x0f +#define GSM48_PDISC_USSD 0x11 + +/* Section 10.4 */ +#define GSM48_MT_RR_INIT_REQ 0x3c +#define GSM48_MT_RR_ADD_ASS 0x3b +#define GSM48_MT_RR_IMM_ASS 0x3f +#define GSM48_MT_RR_IMM_ASS_EXT 0x39 +#define GSM48_MT_RR_IMM_ASS_REJ 0x3a + +#define GSM48_MT_RR_CIPH_M_CMD 0x35 +#define GSM48_MT_RR_CIPH_M_COMPL 0x32 + +#define GSM48_MT_RR_CFG_CHG_CMD 0x30 +#define GSM48_MT_RR_CFG_CHG_ACK 0x31 +#define GSM48_MT_RR_CFG_CHG_REJ 0x33 + +#define GSM48_MT_RR_ASS_CMD 0x2e +#define GSM48_MT_RR_ASS_COMPL 0x29 +#define GSM48_MT_RR_ASS_FAIL 0x2f +#define GSM48_MT_RR_HANDO_CMD 0x2b +#define GSM48_MT_RR_HANDO_COMPL 0x2c +#define GSM48_MT_RR_HANDO_FAIL 0x28 +#define GSM48_MT_RR_HANDO_INFO 0x2d + +#define GSM48_MT_RR_CELL_CHG_ORDER 0x08 +#define GSM48_MT_RR_PDCH_ASS_CMD 0x23 + +#define GSM48_MT_RR_CHAN_REL 0x0d +#define GSM48_MT_RR_PART_REL 0x0a +#define GSM48_MT_RR_PART_REL_COMP 0x0f + +#define GSM48_MT_RR_PAG_REQ_1 0x21 +#define GSM48_MT_RR_PAG_REQ_2 0x22 +#define GSM48_MT_RR_PAG_REQ_3 0x24 +#define GSM48_MT_RR_PAG_RESP 0x27 +#define GSM48_MT_RR_NOTIF_NCH 0x20 +#define GSM48_MT_RR_NOTIF_FACCH 0x25 +#define GSM48_MT_RR_NOTIF_RESP 0x26 + +#define GSM48_MT_RR_SYSINFO_8 0x18 +#define GSM48_MT_RR_SYSINFO_1 0x19 +#define GSM48_MT_RR_SYSINFO_2 0x1a +#define GSM48_MT_RR_SYSINFO_3 0x1b +#define GSM48_MT_RR_SYSINFO_4 0x1c +#define GSM48_MT_RR_SYSINFO_5 0x1d +#define GSM48_MT_RR_SYSINFO_6 0x1e +#define GSM48_MT_RR_SYSINFO_7 0x1f + +#define GSM48_MT_RR_SYSINFO_2bis 0x02 +#define GSM48_MT_RR_SYSINFO_2ter 0x03 +#define GSM48_MT_RR_SYSINFO_5bis 0x05 +#define GSM48_MT_RR_SYSINFO_5ter 0x06 +#define GSM48_MT_RR_SYSINFO_9 0x04 +#define GSM48_MT_RR_SYSINFO_13 0x00 + +#define GSM48_MT_RR_SYSINFO_16 0x3d +#define GSM48_MT_RR_SYSINFO_17 0x3e + +#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10 +#define GSM48_MT_RR_STATUS 0x12 +#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17 +#define GSM48_MT_RR_FREQ_REDEF 0x14 +#define GSM48_MT_RR_MEAS_REP 0x15 +#define GSM48_MT_RR_CLSM_CHG 0x16 +#define GSM48_MT_RR_CLSM_ENQ 0x13 +#define GSM48_MT_RR_EXT_MEAS_REP 0x36 +#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37 +#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34 + +#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08 +#define GSM48_MT_RR_UPLINK_RELEASE 0x0e +#define GSM48_MT_RR_UPLINK_FREE 0x0c +#define GSM48_MT_RR_UPLINK_BUSY 0x2a +#define GSM48_MT_RR_TALKER_IND 0x11 + +#define GSM48_MT_RR_APP_INFO 0x38 + +/* Table 10.2/3GPP TS 04.08 */ +#define GSM48_MT_MM_IMSI_DETACH_IND 0x01 +#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02 +#define GSM48_MT_MM_LOC_UPD_REJECT 0x04 +#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08 + +#define GSM48_MT_MM_AUTH_REJ 0x11 +#define GSM48_MT_MM_AUTH_REQ 0x12 +#define GSM48_MT_MM_AUTH_RESP 0x14 +#define GSM48_MT_MM_ID_REQ 0x18 +#define GSM48_MT_MM_ID_RESP 0x19 +#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a +#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b + +#define GSM48_MT_MM_CM_SERV_ACC 0x21 +#define GSM48_MT_MM_CM_SERV_REJ 0x22 +#define GSM48_MT_MM_CM_SERV_ABORT 0x23 +#define GSM48_MT_MM_CM_SERV_REQ 0x24 +#define GSM48_MT_MM_CM_SERV_PROMPT 0x25 +#define GSM48_MT_MM_CM_REEST_REQ 0x28 +#define GSM48_MT_MM_ABORT 0x29 + +#define GSM48_MT_MM_NULL 0x30 +#define GSM48_MT_MM_STATUS 0x31 +#define GSM48_MT_MM_INFO 0x32 + +/* Table 10.3/3GPP TS 04.08 */ +#define GSM48_MT_CC_ALERTING 0x01 +#define GSM48_MT_CC_CALL_CONF 0x08 +#define GSM48_MT_CC_CALL_PROC 0x02 +#define GSM48_MT_CC_CONNECT 0x07 +#define GSM48_MT_CC_CONNECT_ACK 0x0f +#define GSM48_MT_CC_EMERG_SETUP 0x0e +#define GSM48_MT_CC_PROGRESS 0x03 +#define GSM48_MT_CC_ESTAB 0x04 +#define GSM48_MT_CC_ESTAB_CONF 0x06 +#define GSM48_MT_CC_RECALL 0x0b +#define GSM48_MT_CC_START_CC 0x09 +#define GSM48_MT_CC_SETUP 0x05 + +#define GSM48_MT_CC_MODIFY 0x17 +#define GSM48_MT_CC_MODIFY_COMPL 0x1f +#define GSM48_MT_CC_MODIFY_REJECT 0x13 +#define GSM48_MT_CC_USER_INFO 0x10 +#define GSM48_MT_CC_HOLD 0x18 +#define GSM48_MT_CC_HOLD_ACK 0x19 +#define GSM48_MT_CC_HOLD_REJ 0x1a +#define GSM48_MT_CC_RETR 0x1c +#define GSM48_MT_CC_RETR_ACK 0x1d +#define GSM48_MT_CC_RETR_REJ 0x1e + +#define GSM48_MT_CC_DISCONNECT 0x25 +#define GSM48_MT_CC_RELEASE 0x2d +#define GSM48_MT_CC_RELEASE_COMPL 0x2a + +#define GSM48_MT_CC_CONG_CTRL 0x39 +#define GSM48_MT_CC_NOTIFY 0x3e +#define GSM48_MT_CC_STATUS 0x3d +#define GSM48_MT_CC_STATUS_ENQ 0x34 +#define GSM48_MT_CC_START_DTMF 0x35 +#define GSM48_MT_CC_STOP_DTMF 0x31 +#define GSM48_MT_CC_STOP_DTMF_ACK 0x32 +#define GSM48_MT_CC_START_DTMF_ACK 0x36 +#define GSM48_MT_CC_START_DTMF_REJ 0x37 +#define GSM48_MT_CC_FACILITY 0x3a + +/* FIXME: Table 10.4 / 10.4a (GPRS) */ + +/* Section 10.5.3.3 CM service type */ +#define GSM48_CMSERV_MO_CALL_PACKET 1 +#define GSM48_CMSERV_EMERGENCY 2 +#define GSM48_CMSERV_SMS 4 +#define GSM48_CMSERV_SUP_SERV 8 +#define GSM48_CMSERV_VGCS 9 +#define GSM48_CMSERV_VBS 10 +#define GSM48_CMSERV_LOC_SERV 11 + +/* Section 10.5.2.26, Table 10.5.64 */ +#define GSM48_PM_MASK 0x03 +#define GSM48_PM_NORMAL 0x00 +#define GSM48_PM_EXTENDED 0x01 +#define GSM48_PM_REORG 0x02 +#define GSM48_PM_SAME 0x03 + +/* Chapter 10.5.3.5 / Table 10.5.93 */ +#define GSM48_LUPD_NORMAL 0x0 +#define GSM48_LUPD_PERIODIC 0x1 +#define GSM48_LUPD_IMSI_ATT 0x2 +#define GSM48_LUPD_RESERVED 0x3 + +/* Table 10.5.4 */ +#define GSM_MI_TYPE_MASK 0x07 +#define GSM_MI_TYPE_NONE 0x00 +#define GSM_MI_TYPE_IMSI 0x01 +#define GSM_MI_TYPE_IMEI 0x02 +#define GSM_MI_TYPE_IMEISV 0x03 +#define GSM_MI_TYPE_TMSI 0x04 +#define GSM_MI_ODD 0x08 + +#define GSM48_IE_MOBILE_ID 0x17 /* 10.5.1.4 */ +#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */ +#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */ +#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */ +#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */ +#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */ + +#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */ +#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */ +#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */ +#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */ +#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */ +#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */ +#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */ +#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */ +#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */ +#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */ +#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */ +#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */ +#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */ +#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */ +#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */ +#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */ +#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */ +#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */ +#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */ +#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */ +#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */ +#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */ +#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */ +#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */ +#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */ +#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */ +#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */ +#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */ + +/* Section 10.5.4.11 / Table 10.5.122 */ +#define GSM48_CAUSE_CS_GSM 0x60 + +/* Section 9.1.2 / Table 9.3 */ +/* RR elements */ +#define GSM48_IE_VGCS_TARGET 0x01 +//#define GSM48_IE_VGCS_T_MODE_I 0x01 +#define GSM48_IE_FRQSHORT_AFTER 0x02 +#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */ +#define GSM48_IE_FREQ_L_AFTER 0x05 +#define GSM48_IE_MSLOT_DESC 0x10 +#define GSM48_IE_CHANMODE_2 0x11 +#define GSM48_IE_FRQSHORT_BEFORE 0x12 +//#define GSM48_IE_FRQSHORT_BEFOR 0x12 +#define GSM48_IE_CHANMODE_3 0x13 +#define GSM48_IE_CHANMODE_4 0x14 +#define GSM48_IE_CHANMODE_5 0x15 +#define GSM48_IE_CHANMODE_6 0x16 +#define GSM48_IE_CHANMODE_7 0x17 +#define GSM48_IE_CHANMODE_8 0x18 +#define GSM48_IE_CHANDESC_2 0x64 +#define GSM48_IE_MA_AFTER 0x72 +#define GSM48_IE_START_TIME 0x7c +#define GSM48_IE_FREQ_L_BEFORE 0x19 +//#define GSM48_IE_FRQLIST_BEFORE 0x19 +#define GSM48_IE_CH_DESC_1_BEFORE 0x1c +//#define GSM48_IE_CHDES_1_BEFORE 0x1c +#define GSM48_IE_CH_DESC_2_BEFORE 0x1d +//#define GSM48_IE_CHDES_2_BEFORE 0x1d +#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e +//#define GSM48_IE_FRQSEQ_BEFORE 0x1e +#define GSM48_IE_CLASSMARK3 0x20 +#define GSM48_IE_MA_BEFORE 0x21 +#define GSM48_IE_RR_PACKET_UL 0x22 +#define GSM48_IE_RR_PACKET_DL 0x23 +#define GSM48_IE_CELL_CH_DESC 0x62 +#define GSM48_IE_CHANMODE_1 0x63 +#define GSM48_IE_CHDES_2_AFTER 0x64 +#define GSM48_IE_MODE_SEC_CH 0x66 +#define GSM48_IE_F_CH_SEQ_AFTER 0x69 +#define GSM48_IE_MA_AFTER 0x72 +#define GSM48_IE_BA_RANGE 0x73 +#define GSM48_IE_GROUP_CHDES 0x74 +#define GSM48_IE_BA_LIST_PREF 0x75 +#define GSM48_IE_MOB_OVSERV_DIF 0x77 +#define GSM48_IE_REALTIME_DIFF 0x7b +#define GSM48_IE_START_TIME 0x7c +#define GSM48_IE_TIMING_ADVANCE 0x7d +#define GSM48_IE_GROUP_CIP_SEQ 0x80 +#define GSM48_IE_CIP_MODE_SET 0x90 +#define GSM48_IE_GPRS_RESUMPT 0xc0 +#define GSM48_IE_SYNC_IND 0xd0 +/* System Information 4 (types are equal IEs above) */ +#define GSM48_IE_CBCH_CHAN_DESC 0x64 +#define GSM48_IE_CBCH_MOB_AL 0x72 + +/* Additional MM elements */ +#define GSM48_IE_LOCATION_AREA 0x13 +#define GSM48_IE_PRIORITY_LEV 0x80 +#define GSM48_IE_FOLLOW_ON_PROC 0xa1 +#define GSM48_IE_CTS_PERMISSION 0xa2 + +/* Section 10.5.4.23 / Table 10.5.130 */ +enum gsm48_signal_val { + GSM48_SIGNAL_DIALTONE = 0x00, + GSM48_SIGNAL_RINGBACK = 0x01, + GSM48_SIGNAL_INTERCEPT = 0x02, + GSM48_SIGNAL_NET_CONG = 0x03, + GSM48_SIGNAL_BUSY = 0x04, + GSM48_SIGNAL_CONFIRM = 0x05, + GSM48_SIGNAL_ANSWER = 0x06, + GSM48_SIGNAL_CALL_WAIT = 0x07, + GSM48_SIGNAL_OFF_HOOK = 0x08, + GSM48_SIGNAL_OFF = 0x3f, + GSM48_SIGNAL_ALERT_OFF = 0x4f, +}; + +enum gsm48_cause_loc { + GSM48_CAUSE_LOC_USER = 0x00, + GSM48_CAUSE_LOC_PRN_S_LU = 0x01, + GSM48_CAUSE_LOC_PUN_S_LU = 0x02, + GSM48_CAUSE_LOC_TRANS_NET = 0x03, + GSM48_CAUSE_LOC_PUN_S_RU = 0x04, + GSM48_CAUSE_LOC_PRN_S_RU = 0x05, + /* not defined */ + GSM48_CAUSE_LOC_INN_NET = 0x07, + GSM48_CAUSE_LOC_NET_BEYOND = 0x0a, +}; + +/* Section 10.5.2.31 RR Cause / Table 10.5.70 */ +enum gsm48_rr_cause { + GSM48_RR_CAUSE_NORMAL = 0x00, + GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01, + GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02, + GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03, + GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04, + GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05, + GSM48_RR_CAUSE_HNDOVER_IMP = 0x06, + GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07, + GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08, + GSM48_RR_CAUSE_CALL_CLEARED = 0x41, + GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f, + GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60, + GSM48_RR_CAUSE_MSG_TYPE_N = 0x61, + GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62, + GSM48_RR_CAUSE_COND_IE_ERROR = 0x64, + GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f, +}; + +/* Section 10.5.4.11 CC Cause / Table 10.5.123 */ +enum gsm48_cc_cause { + GSM48_CC_CAUSE_UNASSIGNED_NR = 1, + GSM48_CC_CAUSE_NO_ROUTE = 3, + GSM48_CC_CAUSE_CHAN_UNACCEPT = 6, + GSM48_CC_CAUSE_OP_DET_BARRING = 8, + GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16, + GSM48_CC_CAUSE_USER_BUSY = 17, + GSM48_CC_CAUSE_USER_NOTRESPOND = 18, + GSM48_CC_CAUSE_USER_ALERTING_NA = 19, + GSM48_CC_CAUSE_CALL_REJECTED = 21, + GSM48_CC_CAUSE_NUMBER_CHANGED = 22, + GSM48_CC_CAUSE_PRE_EMPTION = 25, + GSM48_CC_CAUSE_NONSE_USER_CLR = 26, + GSM48_CC_CAUSE_DEST_OOO = 27, + GSM48_CC_CAUSE_INV_NR_FORMAT = 28, + GSM48_CC_CAUSE_FACILITY_REJ = 29, + GSM48_CC_CAUSE_RESP_STATUS_INQ = 30, + GSM48_CC_CAUSE_NORMAL_UNSPEC = 31, + GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34, + GSM48_CC_CAUSE_NETWORK_OOO = 38, + GSM48_CC_CAUSE_TEMP_FAILURE = 41, + GSM48_CC_CAUSE_SWITCH_CONG = 42, + GSM48_CC_CAUSE_ACC_INF_DISCARD = 43, + GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47, + GSM48_CC_CAUSE_QOS_UNAVAIL = 49, + GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50, + GSM48_CC_CAUSE_INC_BARRED_CUG = 55, + GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58, + GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63, + GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65, + GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68, + GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69, + GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79, + GSM48_CC_CAUSE_INVAL_TRANS_ID = 81, + GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87, + GSM48_CC_CAUSE_INCOMPAT_DEST = 88, + GSM48_CC_CAUSE_INVAL_TRANS_NET = 91, + GSM48_CC_CAUSE_SEMANTIC_INCORR = 95, + GSM48_CC_CAUSE_INVAL_MAND_INF = 96, + GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98, + GSM48_CC_CAUSE_IE_NOTEXIST = 99, + GSM48_CC_CAUSE_COND_IE_ERR = 100, + GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101, + GSM48_CC_CAUSE_RECOVERY_TIMER = 102, + GSM48_CC_CAUSE_PROTO_ERR = 111, + GSM48_CC_CAUSE_INTERWORKING = 127, +}; + +/* Annex G, GSM specific cause values for mobility management */ +enum gsm48_reject_value { + GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2, + GSM48_REJECT_ILLEGAL_MS = 3, + GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4, + GSM48_REJECT_IMEI_NOT_ACCEPTED = 5, + GSM48_REJECT_ILLEGAL_ME = 6, + GSM48_REJECT_PLMN_NOT_ALLOWED = 11, + GSM48_REJECT_LOC_NOT_ALLOWED = 12, + GSM48_REJECT_ROAMING_NOT_ALLOWED = 13, + GSM48_REJECT_NETWORK_FAILURE = 17, + GSM48_REJECT_CONGESTION = 22, + GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32, + GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33, + GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34, + GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38, + GSM48_REJECT_INCORRECT_MESSAGE = 95, + GSM48_REJECT_INVALID_MANDANTORY_INF = 96, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98, + GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99, + GSM48_REJECT_CONDTIONAL_IE_ERROR = 100, + GSM48_REJECT_MSG_NOT_COMPATIBLE = 101, + GSM48_REJECT_PROTOCOL_ERROR = 111, + + /* according to G.6 Additional cause codes for GMM */ + GSM48_REJECT_GPRS_NOT_ALLOWED = 7, + GSM48_REJECT_SERVICES_NOT_ALLOWED = 8, + GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9, + GSM48_REJECT_IMPLICITLY_DETACHED = 10, + GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14, + GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16, +}; + +enum chreq_type { + CHREQ_T_EMERG_CALL, + CHREQ_T_CALL_REEST_TCH_F, + CHREQ_T_CALL_REEST_TCH_H, + CHREQ_T_CALL_REEST_TCH_H_DBL, + CHREQ_T_SDCCH, + CHREQ_T_TCH_F, + CHREQ_T_VOICE_CALL_TCH_H, + CHREQ_T_DATA_CALL_TCH_H, + CHREQ_T_LOCATION_UPD, + CHREQ_T_PAG_R_ANY_NECI0, + CHREQ_T_PAG_R_ANY_NECI1, + CHREQ_T_PAG_R_TCH_F, + CHREQ_T_PAG_R_TCH_FH, + CHREQ_T_LMU, + CHREQ_T_RESERVED_SDCCH, + CHREQ_T_RESERVED_IGNORE, +}; + +/* Chapter 11.3 */ +#define GSM48_T301 180, 0 +#define GSM48_T303 30, 0 +#define GSM48_T305 30, 0 +#define GSM48_T306 30, 0 +#define GSM48_T308 10, 0 +#define GSM48_T310 180, 0 +#define GSM48_T313 30, 0 +#define GSM48_T323 30, 0 +#define GSM48_T331 30, 0 +#define GSM48_T333 30, 0 +#define GSM48_T334 25, 0 /* min 15 */ +#define GSM48_T338 30, 0 +#define GSM48_T303_MS 30, 0 +#define GSM48_T305_MS 30, 0 +#define GSM48_T308_MS 30, 0 +#define GSM48_T310_MS 30, 0 +#define GSM48_T313_MS 30, 0 +#define GSM48_T323_MS 30, 0 +#define GSM48_T332_MS 30, 0 +#define GSM48_T335_MS 30, 0 + +/* Chapter 5.1.2.2 */ +#define GSM_CSTATE_NULL 0 +#define GSM_CSTATE_INITIATED 1 +#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */ +#define GSM_CSTATE_MO_CALL_PROC 3 +#define GSM_CSTATE_CALL_DELIVERED 4 +#define GSM_CSTATE_CALL_PRESENT 6 +#define GSM_CSTATE_CALL_RECEIVED 7 +#define GSM_CSTATE_CONNECT_REQUEST 8 +#define GSM_CSTATE_MO_TERM_CALL_CONF 9 +#define GSM_CSTATE_ACTIVE 10 +#define GSM_CSTATE_DISCONNECT_REQ 12 +#define GSM_CSTATE_DISCONNECT_IND 12 +#define GSM_CSTATE_RELEASE_REQ 19 +#define GSM_CSTATE_MO_ORIG_MODIFY 26 +#define GSM_CSTATE_MO_TERM_MODIFY 27 +#define GSM_CSTATE_CONNECT_IND 28 + +#define SBIT(a) (1 << a) +#define ALL_STATES 0xffffffff + +/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */ +#define GSM_LAC_RESERVED_DETACHED 0x0 +#define GSM_LAC_RESERVED_ALL_BTS 0xfffe + +/* GSM 04.08 Bearer Capability: Information Transfer Capability */ +enum gsm48_bcap_itcap { + GSM48_BCAP_ITCAP_SPEECH = 0, + GSM48_BCAP_ITCAP_UNR_DIG_INF = 1, + GSM48_BCAP_ITCAP_3k1_AUDIO = 2, + GSM48_BCAP_ITCAP_FAX_G3 = 3, + GSM48_BCAP_ITCAP_OTHER = 5, + GSM48_BCAP_ITCAP_RESERVED = 7, +}; + +/* GSM 04.08 Bearer Capability: Transfer Mode */ +enum gsm48_bcap_tmod { + GSM48_BCAP_TMOD_CIRCUIT = 0, + GSM48_BCAP_TMOD_PACKET = 1, +}; + +/* GSM 04.08 Bearer Capability: Coding Standard */ +enum gsm48_bcap_coding { + GSM48_BCAP_CODING_GSM_STD = 0, +}; + +/* GSM 04.08 Bearer Capability: Radio Channel Requirements */ +enum gsm48_bcap_rrq { + GSM48_BCAP_RRQ_FR_ONLY = 1, + GSM48_BCAP_RRQ_DUAL_HR = 2, + GSM48_BCAP_RRQ_DUAL_FR = 3, +}; + +#define GSM48_TMSI_LEN 5 +#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) +#define GSM48_MI_SIZE 32 + +/* Chapter 10.4.4.15 */ +struct gsm48_ra_id { + uint8_t digits[3]; /* MCC + MNC BCD digits */ + uint16_t lac; /* Location Area Code */ + uint8_t rac; /* Routing Area Code */ +} __attribute__ ((packed)); + +#define GSM48_CELL_CHAN_DESC_SIZE 16 + +#define GSM_MACBLOCK_LEN 23 +#define GSM_MACBLOCK_PADDING 0x2b + +#endif /* PROTO_GSM_04_08_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h new file mode 100644 index 00000000..c6a2b193 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_11.h @@ -0,0 +1,188 @@ +#ifndef PROTO_GSM_04_11_H +#define PROTO_GSM_04_11_H + +#include <stdint.h> + +/* GSM TS 04.11 definitions */ + +/* Chapter 5.2.3: SMC-CS states at the network side */ +enum gsm411_cp_state { + GSM411_CPS_IDLE = 0, + GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */ + GSM411_CPS_WAIT_CP_ACK = 2, + GSM411_CPS_MM_ESTABLISHED = 3, +}; + +/* Chapter 6.2.2: SMR states at the network side */ +enum gsm411_rp_state { + GSM411_RPS_IDLE = 0, + GSM411_RPS_WAIT_FOR_RP_ACK = 1, + GSM411_RPS_WAIT_TO_TX_RP_ACK = 3, +}; + +/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */ +#define GSM411_PDISC_SMS 0x09 + +/* Chapter 8.1.3 */ +#define GSM411_MT_CP_DATA 0x01 +#define GSM411_MT_CP_ACK 0x04 +#define GSM411_MT_CP_ERROR 0x10 + +enum gsm411_cp_ie { + GSM411_CP_IE_USER_DATA = 0x01, /* 8.1.4.1 */ + GSM411_CP_IE_CAUSE = 0x02, /* 8.1.4.2. */ +}; + +/* Section 8.1.4.2 / Table 8.2 */ +enum gsm411_cp_cause { + GSM411_CP_CAUSE_NET_FAIL = 17, + GSM411_CP_CAUSE_CONGESTION = 22, + GSM411_CP_CAUSE_INV_TRANS_ID = 81, + GSM411_CP_CAUSE_SEMANT_INC_MSG = 95, + GSM411_CP_CAUSE_INV_MAND_INF = 96, + GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97, + GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98, + GSM411_CP_CAUSE_IE_NOTEXIST = 99, + GSM411_CP_CAUSE_PROTOCOL_ERR = 111, +}; + +/* Chapter 8.2.2 */ +#define GSM411_MT_RP_DATA_MO 0x00 +#define GSM411_MT_RP_DATA_MT 0x01 +#define GSM411_MT_RP_ACK_MO 0x02 +#define GSM411_MT_RP_ACK_MT 0x03 +#define GSM411_MT_RP_ERROR_MO 0x04 +#define GSM411_MT_RP_ERROR_MT 0x05 +#define GSM411_MT_RP_SMMA_MO 0x06 + +enum gsm411_rp_ie { + GSM411_IE_RP_USER_DATA = 0x41, /* 8.2.5.3 */ + GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */ +}; + +/* Chapter 8.2.5.4 Table 8.4 */ +enum gsm411_rp_cause { + /* valid only for MO */ + GSM411_RP_CAUSE_MO_NUM_UNASSIGNED = 1, + GSM411_RP_CAUSE_MO_OP_DET_BARR = 8, + GSM411_RP_CAUSE_MO_CALL_BARRED = 10, + GSM411_RP_CAUSE_MO_SMS_REJECTED = 21, + GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER = 27, + GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR = 28, + GSM411_RP_CAUSE_MO_FACILITY_REJ = 29, + GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR = 30, + GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER = 38, + GSM411_RP_CAUSE_MO_TEMP_FAIL = 41, + GSM411_RP_CAUSE_MO_CONGESTION = 42, + GSM411_RP_CAUSE_MO_RES_UNAVAIL = 47, + GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR = 50, + GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL = 69, + GSM411_RP_CAUSE_MO_INTERWORKING = 127, + /* valid only for MT */ + GSM411_RP_CAUSE_MT_MEM_EXCEEDED = 22, + /* valid for both directions */ + GSM411_RP_CAUSE_INV_TRANS_REF = 81, + GSM411_RP_CAUSE_SEMANT_INC_MSG = 95, + GSM411_RP_CAUSE_INV_MAND_INF = 96, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM411_RP_CAUSE_MSG_INCOMP_STATE = 98, + GSM411_RP_CAUSE_IE_NOTEXIST = 99, + GSM411_RP_CAUSE_PROTOCOL_ERR = 111, +}; + +/* Chapter 10: Timers */ +#define GSM411_TMR_TR1M 40, 0 /* 35 < x < 45 seconds */ +#define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */ +#define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */ + +#define GSM411_TMR_TC1A 30, 0 + +/* Chapter 8.2.1 */ +struct gsm411_rp_hdr { + uint8_t len; + uint8_t msg_type; + uint8_t msg_ref; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* our own enum, not related to on-air protocol */ +enum sms_alphabet { + DCS_NONE, + DCS_7BIT_DEFAULT, + DCS_UCS2, + DCS_8BIT_DATA, +}; + +/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */ +#define GSM340_SMS_DELIVER_SC2MS 0x00 +#define GSM340_SMS_DELIVER_REP_MS2SC 0x00 +#define GSM340_SMS_STATUS_REP_SC2MS 0x02 +#define GSM340_SMS_COMMAND_MS2SC 0x02 +#define GSM340_SMS_SUBMIT_MS2SC 0x01 +#define GSM340_SMS_SUBMIT_REP_SC2MS 0x01 +#define GSM340_SMS_RESSERVED 0x03 + +/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */ +#define GSM340_TP_MMS_MORE 0 +#define GSM340_TP_MMS_NO_MORE 1 + +/* GSM 03.40 / Chapter 9.2.3.3: TP-Validity-Period-Format */ +#define GSM340_TP_VPF_NONE 0 +#define GSM340_TP_VPF_RELATIVE 2 +#define GSM340_TP_VPF_ENHANCED 1 +#define GSM340_TP_VPF_ABSOLUTE 3 + +/* GSM 03.40 / Chapter 9.2.3.4: TP-Status-Report-Indication */ +#define GSM340_TP_SRI_NONE 0 +#define GSM340_TP_SRI_PRESENT 1 + +/* GSM 03.40 / Chapter 9.2.3.5: TP-Status-Report-Request */ +#define GSM340_TP_SRR_NONE 0 +#define GSM340_TP_SRR_REQUESTED 1 + +/* GSM 03.40 / Chapter 9.2.3.9: TP-Protocol-Identifier */ +/* telematic interworking (001 or 111 in bits 7-5) */ +#define GSM340_TP_PID_IMPLICIT 0x00 +#define GSM340_TP_PID_TELEX 0x01 +#define GSM340_TP_PID_FAX_G3 0x02 +#define GSM340_TP_PID_FAX_G4 0x03 +#define GSM340_TP_PID_VOICE 0x04 +#define GSM430_TP_PID_ERMES 0x05 +#define GSM430_TP_PID_NATIONAL_PAGING 0x06 +#define GSM430_TP_PID_VIDEOTEX 0x07 +#define GSM430_TP_PID_TELETEX_UNSPEC 0x08 +#define GSM430_TP_PID_TELETEX_PSPDN 0x09 +#define GSM430_TP_PID_TELETEX_CSPDN 0x0a +#define GSM430_TP_PID_TELETEX_PSTN 0x0b +#define GSM430_TP_PID_TELETEX_ISDN 0x0c +#define GSM430_TP_PID_TELETEX_UCI 0x0d +#define GSM430_TP_PID_MSG_HANDLING 0x10 +#define GSM430_TP_PID_MSG_X400 0x11 +#define GSM430_TP_PID_EMAIL 0x12 +#define GSM430_TP_PID_GSM_MS 0x1f +/* if bit 7 = 0 and bit 6 = 1 */ +#define GSM430_TP_PID_SMS_TYPE_0 0 +#define GSM430_TP_PID_SMS_TYPE_1 1 +#define GSM430_TP_PID_SMS_TYPE_2 2 +#define GSM430_TP_PID_SMS_TYPE_3 3 +#define GSM430_TP_PID_SMS_TYPE_4 4 +#define GSM430_TP_PID_SMS_TYPE_5 5 +#define GSM430_TP_PID_SMS_TYPE_6 6 +#define GSM430_TP_PID_SMS_TYPE_7 7 +#define GSM430_TP_PID_RETURN_CALL_MSG 0x1f +#define GSM430_TP_PID_ME_DATA_DNLOAD 0x3d +#define GSM430_TP_PID_ME_DE_PERSONAL 0x3e +#define GSM430_TP_PID_ME_SIM_DNLOAD 0x3f + +/* GSM 03.38 Chapter 4: SMS Data Coding Scheme */ +#define GSM338_DCS_00_ + +#define GSM338_DCS_1110_7BIT (0 << 2) +#define GSM338_DCS_1111_7BIT (0 << 2) +#define GSM338_DCS_1111_8BIT_DATA (1 << 2) +#define GSM338_DCS_1111_CLASS0 0 +#define GSM338_DCS_1111_CLASS1_ME 1 +#define GSM338_DCS_1111_CLASS2_SIM 2 +#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */ + +#endif /* PROTO_GSM_04_11_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h new file mode 100644 index 00000000..9b1538a5 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_12.h @@ -0,0 +1,31 @@ +#ifndef PROTO_GSM_04_12_H +#define PROTO_GSM_04_12_H + +#include <stdint.h> + +/* GSM TS 04.12 definitions for Short Message Service Cell Broadcast */ + +#define GSM412_SEQ_FST_BLOCK 0x0 +#define GSM412_SEQ_SND_BLOCK 0x1 +#define GSM412_SEQ_TRD_BLOCK 0x2 +#define GSM412_SEQ_FTH_BLOCK 0x3 +#define GSM412_SEQ_FST_SCHED_BLOCK 0x8 +#define GSM412_SEQ_NULL_MSG 0xf + +struct gsm412_block_type { + uint8_t seq_nr : 4, + lb : 1, + lpd : 2, + spare : 1; +} __attribute__((packed)); + +struct gsm412_sched_msg { + uint8_t beg_slot_nr : 6, + type : 2; + uint8_t end_slot_nr : 6, + spare1 : 1, spare2: 1; + uint8_t cbsms_msg_map[6]; + uint8_t data[0]; +} __attribute__((packed)); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h new file mode 100644 index 00000000..fa5c9451 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_80.h @@ -0,0 +1,126 @@ +#ifndef PROTO_GSM_04_80_H +#define PROTO_GSM_04_80_H + +/* GSM TS 04.80 definitions (Supplementary Services Specification, Formats and Coding) */ + +/* Section 3.4 */ +#define GSM0480_MTYPE_RELEASE_COMPLETE 0x2A +#define GSM0480_MTYPE_FACILITY 0x3A +#define GSM0480_MTYPE_REGISTER 0x3B + +/* Section 3.5 */ +#define GSM0480_IE_FACILITY 0x1C +#define GSM0480_IE_SS_VERSION 0x7F + +/* Section 3.6.2 */ +#define GSM0480_CTYPE_INVOKE 0xA1 +#define GSM0480_CTYPE_RETURN_RESULT 0xA2 +#define GSM0480_CTYPE_RETURN_ERROR 0xA3 +#define GSM0480_CTYPE_REJECT 0xA4 + +/* Section 3.6.3 */ +#define GSM0480_COMPIDTAG_INVOKE_ID 0x02 +#define GSM0480_COMPIDTAG_LINKED_ID 0x80 + +/* Section 3.6.4 */ +#define GSM0480_OPERATION_CODE 0x02 + +/* Section 3.6.5 */ +#define GSM_0480_SEQUENCE_TAG 0x30 +#define GSM_0480_SET_TAG 0x31 + +/* Section 3.6.6 */ +#define GSM_0480_ERROR_CODE_TAG 0x02 + +/* Section 3.6.7 */ +/* Table 3.13 */ +#define GSM_0480_PROBLEM_CODE_TAG_GENERAL 0x80 +#define GSM_0480_PROBLEM_CODE_TAG_INVOKE 0x81 +#define GSM_0480_PROBLEM_CODE_TAG_RETURN_RESULT 0x82 +#define GSM_0480_PROBLEM_CODE_TAG_RETURN_ERROR 0x83 + +/* Table 3.14 */ +#define GSM_0480_GEN_PROB_CODE_UNRECOGNISED 0x00 +#define GSM_0480_GEN_PROB_CODE_MISTYPED 0x01 +#define GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE 0x02 + +/* Table 3.15 */ +#define GSM_0480_INVOKE_PROB_CODE_DUPLICATE_INVOKE_ID 0x00 +#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_OPERATION 0x01 +#define GSM_0480_INVOKE_PROB_CODE_MISTYPED_PARAMETER 0x02 +#define GSM_0480_INVOKE_PROB_CODE_RESOURCE_LIMITATION 0x03 +#define GSM_0480_INVOKE_PROB_CODE_INITIATING_RELEASE 0x04 +#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_LINKED_ID 0x05 +#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_RESPONSE 0x06 +#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_OPERATION 0x07 + +/* Table 3.16 */ +#define GSM_0480_RESULT_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00 +#define GSM_0480_RESULT_PROB_CODE_RETURN_RESULT_UNEXPECTED 0x01 +#define GSM_0480_RESULT_PROB_CODE_MISTYPED_PARAMETER 0x02 + +/* Table 3.17 */ +#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00 +#define GSM_0480_ERROR_PROB_CODE_RETURN_ERROR_UNEXPECTED 0x01 +#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_ERROR 0x02 +#define GSM_0480_ERROR_PROB_CODE_UNEXPECTED_ERROR 0x03 +#define GSM_0480_ERROR_PROB_CODE_MISTYPED_PARAMETER 0x04 + +/* Section 4.5 */ +#define GSM0480_OP_CODE_REGISTER_SS 0x0A +#define GSM0480_OP_CODE_ERASE_SS 0x0B +#define GSM0480_OP_CODE_ACTIVATE_SS 0x0C +#define GSM0480_OP_CODE_DEACTIVATE_SS 0x0D +#define GSM0480_OP_CODE_INTERROGATE_SS 0x0E +#define GSM0480_OP_CODE_NOTIFY_SS 0x10 +#define GSM0480_OP_CODE_REGISTER_PASSWORD 0x11 +#define GSM0480_OP_CODE_GET_PASSWORD 0x12 +#define GSM0480_OP_CODE_PROCESS_USS_DATA 0x13 +#define GSM0480_OP_CODE_FORWARD_CHECK_SS_IND 0x26 +#define GSM0480_OP_CODE_PROCESS_USS_REQ 0x3B +#define GSM0480_OP_CODE_USS_REQUEST 0x3C +#define GSM0480_OP_CODE_USS_NOTIFY 0x3D +#define GSM0480_OP_CODE_FORWARD_CUG_INFO 0x78 +#define GSM0480_OP_CODE_SPLIT_MPTY 0x79 +#define GSM0480_OP_CODE_RETRIEVE_MPTY 0x7A +#define GSM0480_OP_CODE_HOLD_MPTY 0x7B +#define GSM0480_OP_CODE_BUILD_MPTY 0x7C +#define GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE 0x7D + +#define GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER 0x01 +#define GSM0480_ERR_CODE_ILLEGAL_SUBSCRIBER 0x09 +#define GSM0480_ERR_CODE_BEARER_SERVICE_NOT_PROVISIONED 0x0A +#define GSM0480_ERR_CODE_TELESERVICE_NOT_PROVISIONED 0x0B +#define GSM0480_ERR_CODE_ILLEGAL_EQUIPMENT 0x0C +#define GSM0480_ERR_CODE_CALL_BARRED 0x0D +#define GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION 0x10 +#define GSM0480_ERR_CODE_SS_ERROR_STATUS 0x11 +#define GSM0480_ERR_CODE_SS_NOT_AVAILABLE 0x12 +#define GSM0480_ERR_CODE_SS_SUBSCRIPTION_VIOLATION 0x13 +#define GSM0480_ERR_CODE_SS_INCOMPATIBILITY 0x14 +#define GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED 0x15 +#define GSM0480_ERR_CODE_ABSENT_SUBSCRIBER 0x1B +#define GSM0480_ERR_CODE_SYSTEM_FAILURE 0x22 +#define GSM0480_ERR_CODE_DATA_MISSING 0x23 +#define GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE 0x24 +#define GSM0480_ERR_CODE_PW_REGISTRATION_FAILURE 0x25 +#define GSM0480_ERR_CODE_NEGATIVE_PW_CHECK 0x26 +#define GSM0480_ERR_CODE_NUM_PW_ATTEMPTS_VIOLATION 0x2B +#define GSM0480_ERR_CODE_UNKNOWN_ALPHABET 0x47 +#define GSM0480_ERR_CODE_USSD_BUSY 0x48 +#define GSM0480_ERR_CODE_MAX_MPTY_PARTICIPANTS 0x7E +#define GSM0480_ERR_CODE_RESOURCES_NOT_AVAILABLE 0x7F + +/* ASN.1 type-tags */ +#define ASN1_BOOLEAN_TAG 0x01 +#define ASN1_INTEGER_TAG 0x02 +#define ASN1_BIT_STRING_TAG 0x03 +#define ASN1_OCTET_STRING_TAG 0x04 +#define ASN1_NULL_TYPE_TAG 0x05 +#define ASN1_OBJECT_ID_TAG 0x06 +#define ASN1_UTF8_STRING_TAG 0x0C +#define ASN1_PRINTABLE_STRING_TAG 0x13 +#define ASN1_IA5_STRING_TAG 0x16 +#define ASN1_UNICODE_STRING_TAG 0x1E + +#endif /* PROTO_GSM_04_80_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h new file mode 100644 index 00000000..6b8f9359 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_08.h @@ -0,0 +1,303 @@ +/* From GSM08.08 */ + +#ifndef GSM_0808_H +#define GSM_0808_H + +#include <stdlib.h> + +/* + * this is from GSM 03.03 CGI but is copied in GSM 08.08 + * in § 3.2.2.27 for Cell Identifier List + */ +enum CELL_IDENT { + CELL_IDENT_WHOLE_GLOBAL = 0, + CELL_IDENT_LAC_AND_CI = 1, + CELL_IDENT_CI = 2, + CELL_IDENT_NO_CELL = 3, + CELL_IDENT_LAI_AND_LAC = 4, + CELL_IDENT_LAC = 5, + CELL_IDENT_BSS = 6, + CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8, + CELL_IDENT_UTRAN_RNC = 9, + CELL_IDENT_UTRAN_LAC_RNC = 10, +}; + + +/* GSM 08.06 § 6.3 */ +enum BSSAP_MSG_TYPE { + BSSAP_MSG_BSS_MANAGEMENT = 0x0, + BSSAP_MSG_DTAP = 0x1, +}; + +struct bssmap_header { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct dtap_header { + uint8_t type; + uint8_t link_id; + uint8_t length; +} __attribute__((packed)); + + +enum BSS_MAP_MSG_TYPE { + BSS_MAP_MSG_RESERVED_0 = 0, + + /* ASSIGNMENT MESSAGES */ + BSS_MAP_MSG_ASSIGMENT_RQST = 1, + BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2, + BSS_MAP_MSG_ASSIGMENT_FAILURE = 3, + + /* HANDOVER MESSAGES */ + BSS_MAP_MSG_HANDOVER_RQST = 16, + BSS_MAP_MSG_HANDOVER_REQUIRED = 17, + BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18, + BSS_MAP_MSG_HANDOVER_CMD = 19, + BSS_MAP_MSG_HANDOVER_COMPLETE = 20, + BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21, + BSS_MAP_MSG_HANDOVER_FAILURE = 22, + BSS_MAP_MSG_HANDOVER_PERFORMED = 23, + BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24, + BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25, + BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26, + BSS_MAP_MSG_HANDOVER_DETECT = 27, + + /* RELEASE MESSAGES */ + BSS_MAP_MSG_CLEAR_CMD = 32, + BSS_MAP_MSG_CLEAR_COMPLETE = 33, + BSS_MAP_MSG_CLEAR_RQST = 34, + BSS_MAP_MSG_RESERVED_1 = 35, + BSS_MAP_MSG_RESERVED_2 = 36, + BSS_MAP_MSG_SAPI_N_REJECT = 37, + BSS_MAP_MSG_CONFUSION = 38, + + /* OTHER CONNECTION RELATED MESSAGES */ + BSS_MAP_MSG_SUSPEND = 40, + BSS_MAP_MSG_RESUME = 41, + BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42, + BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43, + BSS_MAP_MSG_LSA_INFORMATION = 44, + BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45, + BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46, + BSS_MAP_MSG_COMMON_ID = 47, + + /* GENERAL MESSAGES */ + BSS_MAP_MSG_RESET = 48, + BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49, + BSS_MAP_MSG_OVERLOAD = 50, + BSS_MAP_MSG_RESERVED_3 = 51, + BSS_MAP_MSG_RESET_CIRCUIT = 52, + BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53, + BSS_MAP_MSG_MSC_INVOKE_TRACE = 54, + BSS_MAP_MSG_BSS_INVOKE_TRACE = 55, + BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58, + + /* TERRESTRIAL RESOURCE MESSAGES */ + BSS_MAP_MSG_BLOCK = 64, + BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65, + BSS_MAP_MSG_UNBLOCK = 66, + BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71, + BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72, + BSS_MAP_MSG_CHANGE_CIRCUIT = 78, + BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79, + + /* RADIO RESOURCE MESSAGES */ + BSS_MAP_MSG_RESOURCE_RQST = 80, + BSS_MAP_MSG_RESOURCE_INDICATION = 81, + BSS_MAP_MSG_PAGING = 82, + BSS_MAP_MSG_CIPHER_MODE_CMD = 83, + BSS_MAP_MSG_CLASSMARK_UPDATE = 84, + BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85, + BSS_MAP_MSG_QUEUING_INDICATION = 86, + BSS_MAP_MSG_COMPLETE_LAYER_3 = 87, + BSS_MAP_MSG_CLASSMARK_RQST = 88, + BSS_MAP_MSG_CIPHER_MODE_REJECT = 89, + BSS_MAP_MSG_LOAD_INDICATION = 90, + + /* VGCS/VBS */ + BSS_MAP_MSG_VGCS_VBS_SETUP = 4, + BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5, + BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29, + BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30, + BSS_MAP_MSG_UPLINK_RQST = 31, + BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39, + BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73, + BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74, + BSS_MAP_MSG_UPLINK_REJECT_CMD = 75, + BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76, + BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77, +}; + +enum GSM0808_IE_CODING { + GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1, + GSM0808_IE_RESERVED_0 = 2, + GSM0808_IE_RESOURCE_AVAILABLE = 3, + GSM0808_IE_CAUSE = 4, + GSM0808_IE_CELL_IDENTIFIER = 5, + GSM0808_IE_PRIORITY = 6, + GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7, + GSM0808_IE_IMSI = 8, + GSM0808_IE_TMSI = 9, + GSM0808_IE_ENCRYPTION_INFORMATION = 10, + GSM0808_IE_CHANNEL_TYPE = 11, + GSM0808_IE_PERIODICITY = 12, + GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13, + GSM0808_IE_NUMBER_OF_MSS = 14, + GSM0808_IE_RESERVED_1 = 15, + GSM0808_IE_RESERVED_2 = 16, + GSM0808_IE_RESERVED_3 = 17, + GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18, + GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19, + GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20, + GSM0808_IE_RR_CAUSE = 21, + GSM0808_IE_RESERVED_4 = 22, + GSM0808_IE_LAYER_3_INFORMATION = 23, + GSM0808_IE_DLCI = 24, + GSM0808_IE_DOWNLINK_DTX_FLAG = 25, + GSM0808_IE_CELL_IDENTIFIER_LIST = 26, + GSM0808_IE_RESPONSE_RQST = 27, + GSM0808_IE_RESOURCE_INDICATION_METHOD = 28, + GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29, + GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30, + GSM0808_IE_DIAGNOSTIC = 31, + GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32, + GSM0808_IE_CHOSEN_CHANNEL = 33, + GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34, + GSM0808_IE_CIPHER_RESPONSE_MODE = 35, + GSM0808_IE_CHANNEL_NEEDED = 36, + GSM0808_IE_TRACE_TYPE = 37, + GSM0808_IE_TRIGGERID = 38, + GSM0808_IE_TRACE_REFERENCE = 39, + GSM0808_IE_TRANSACTIONID = 40, + GSM0808_IE_MOBILE_IDENTITY = 41, + GSM0808_IE_OMCID = 42, + GSM0808_IE_FORWARD_INDICATOR = 43, + GSM0808_IE_CHOSEN_ENCR_ALG = 44, + GSM0808_IE_CIRCUIT_POOL = 45, + GSM0808_IE_CIRCUIT_POOL_LIST = 46, + GSM0808_IE_TIME_INDICATION = 47, + GSM0808_IE_RESOURCE_SITUATION = 48, + GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49, + GSM0808_IE_QUEUEING_INDICATOR = 50, + GSM0808_IE_SPEECH_VERSION = 64, + GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51, + GSM0808_IE_TALKER_FLAG = 53, + GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54, + GSM0808_IE_GROUP_CALL_REFERENCE = 55, + GSM0808_IE_EMLPP_PRIORITY = 56, + GSM0808_IE_CONFIG_EVO_INDI = 57, + GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58, + GSM0808_IE_LSA_IDENTIFIER = 59, + GSM0808_IE_LSA_IDENTIFIER_LIST = 60, + GSM0808_IE_LSA_INFORMATION = 61, + GSM0808_IE_LCS_QOS = 62, + GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63, + GSM0808_IE_LCS_PRIORITY = 67, + GSM0808_IE_LOCATION_TYPE = 68, + GSM0808_IE_LOCATION_ESTIMATE = 69, + GSM0808_IE_POSITIONING_DATA = 70, + GSM0808_IE_LCS_CAUSE = 71, + GSM0808_IE_LCS_CLIENT_TYPE = 72, + GSM0808_IE_APDU = 73, + GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74, + GSM0808_IE_GPS_ASSISTANCE_DATA = 75, + GSM0808_IE_DECIPHERING_KEYS = 76, + GSM0808_IE_RETURN_ERROR_RQST = 77, + GSM0808_IE_RETURN_ERROR_CAUSE = 78, + GSM0808_IE_SEGMENTATION = 79, + GSM0808_IE_SERVICE_HANDOVER = 80, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82, + GSM0808_IE_RESERVED_5 = 65, + GSM0808_IE_RESERVED_6 = 66, +}; + +enum gsm0808_cause { + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1, + GSM0808_CAUSE_UPLINK_QUALITY = 2, + GSM0808_CAUSE_UPLINK_STRENGTH = 3, + GSM0808_CAUSE_DOWNLINK_QUALITY = 4, + GSM0808_CAUSE_DOWNLINK_STRENGTH = 5, + GSM0808_CAUSE_DISTANCE = 6, + GSM0808_CAUSE_O_AND_M_INTERVENTION = 7, + GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8, + GSM0808_CAUSE_CALL_CONTROL = 9, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10, + GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11, + GSM0808_CAUSE_BETTER_CELL = 12, + GSM0808_CAUSE_DIRECTED_RETRY = 13, + GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14, + GSM0808_CAUSE_TRAFFIC = 15, + GSM0808_CAUSE_EQUIPMENT_FAILURE = 32, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33, + GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34, + GSM0808_CAUSE_CCCH_OVERLOAD = 35, + GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36, + GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37, + GSM0808_CAUSE_MS_NOT_EQUIPPED = 38, + GSM0808_CAUSE_INVALID_CELL = 39, + GSM0808_CAUSE_TRAFFIC_LOAD = 40, + GSM0808_CAUSE_PREEMPTION = 41, + GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48, + GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49, + GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50, + GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51, + GSM0808_CAUSE_LSA_NOT_ALLOWED = 52, + GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64, + GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80, + GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81, + GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82, + GSM0808_CAUSE_INCORRECT_VALUE = 83, + GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84, + GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85, + GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96, +}; + +/* GSM 08.08 3.2.2.11 Channel Type */ +enum gsm0808_chan_indicator { + GSM0808_CHAN_SPEECH = 1, + GSM0808_CHAN_DATA = 2, + GSM0808_CHAN_SIGN = 3, +}; + +enum gsm0808_chan_rate_type_data { + GSM0808_DATA_FULL_BM = 0x8, + GSM0808_DATA_HALF_LM = 0x9, + GSM0808_DATA_FULL_RPREF = 0xa, + GSM0808_DATA_HALF_PREF = 0xb, + GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_DATA_MULTI_MASK = 0x20, + GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30, +}; + +enum gsm0808_chan_rate_type_speech { + GSM0808_SPEECH_FULL_BM = 0x8, + GSM0808_SPEECH_HALF_LM = 0x9, + GSM0808_SPEECH_FULL_PREF= 0xa, + GSM0808_SPEECH_HALF_PREF= 0xb, + GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_SPEECH_PERM = 0xf, + GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f, +}; + +enum gsm0808_permitted_speech { + GSM0808_PERM_FR1 = 0x01, + GSM0808_PERM_FR2 = 0x11, + GSM0808_PERM_FR3 = 0x21, + GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4, + GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4, + GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4, +}; + +#endif diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h new file mode 100644 index 00000000..74a4083b --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h @@ -0,0 +1,546 @@ +#ifndef PROTO_GSM_08_58_H +#define PROTO_GSM_08_58_H + +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +struct abis_rsl_common_hdr { + uint8_t msg_discr; + uint8_t msg_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Chapter 8.3 */ +struct abis_rsl_rll_hdr { + struct abis_rsl_common_hdr c; + uint8_t ie_chan; + uint8_t chan_nr; + uint8_t ie_link_id; + uint8_t link_id; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Chapter 8.3 and 8.4 */ +struct abis_rsl_dchan_hdr { + struct abis_rsl_common_hdr c; + uint8_t ie_chan; + uint8_t chan_nr; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Chapter 8.5 */ +struct abis_rsl_cchan_hdr { + struct abis_rsl_common_hdr c; + uint8_t ie_chan; + uint8_t chan_nr; + uint8_t data[0]; +} __attribute__ ((packed)); + + +/* Chapter 9.1 */ +#define ABIS_RSL_MDISC_RLL 0x02 +#define ABIS_RSL_MDISC_DED_CHAN 0x08 +#define ABIS_RSL_MDISC_COM_CHAN 0x0c +#define ABIS_RSL_MDISC_TRX 0x10 +#define ABIS_RSL_MDISC_LOC 0x20 +#define ABIS_RSL_MDISC_IPACCESS 0x7e +#define ABIS_RSL_MDISC_TRANSP 0x01 + +#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01) + +/* Chapter 9.1 */ +enum abis_rsl_msgtype { + /* Radio Link Layer Management */ + RSL_MT_DATA_REQ = 0x01, + RSL_MT_DATA_IND, + RSL_MT_ERROR_IND, + RSL_MT_EST_REQ, + RSL_MT_EST_CONF, + RSL_MT_EST_IND, + RSL_MT_REL_REQ, + RSL_MT_REL_CONF, + RSL_MT_REL_IND, + RSL_MT_UNIT_DATA_REQ, + RSL_MT_UNIT_DATA_IND, /* 0x0b */ + RSL_MT_SUSP_REQ, /* non-standard elements */ + RSL_MT_SUSP_CONF, + RSL_MT_RES_REQ, + RSL_MT_RECON_REQ, /* 0x0f */ + + /* Common Channel Management / TRX Management */ + RSL_MT_BCCH_INFO = 0x11, + RSL_MT_CCCH_LOAD_IND, + RSL_MT_CHAN_RQD, + RSL_MT_DELETE_IND, + RSL_MT_PAGING_CMD, + RSL_MT_IMMEDIATE_ASSIGN_CMD, + RSL_MT_SMS_BC_REQ, + RSL_MT_CHAN_CONF, /* non-standard element */ + /* empty */ + RSL_MT_RF_RES_IND = 0x19, + RSL_MT_SACCH_FILL, + RSL_MT_OVERLOAD, + RSL_MT_ERROR_REPORT, + RSL_MT_SMS_BC_CMD, + RSL_MT_CBCH_LOAD_IND, + RSL_MT_NOT_CMD, /* 0x1f */ + + /* Dedicate Channel Management */ + RSL_MT_CHAN_ACTIV = 0x21, + RSL_MT_CHAN_ACTIV_ACK, + RSL_MT_CHAN_ACTIV_NACK, + RSL_MT_CONN_FAIL, + RSL_MT_DEACTIVATE_SACCH, + RSL_MT_ENCR_CMD, + RSL_MT_HANDO_DET, + RSL_MT_MEAS_RES, + RSL_MT_MODE_MODIFY_REQ, + RSL_MT_MODE_MODIFY_ACK, + RSL_MT_MODE_MODIFY_NACK, + RSL_MT_PHY_CONTEXT_REQ, + RSL_MT_PHY_CONTEXT_CONF, + RSL_MT_RF_CHAN_REL, + RSL_MT_MS_POWER_CONTROL, + RSL_MT_BS_POWER_CONTROL, /* 0x30 */ + RSL_MT_PREPROC_CONFIG, + RSL_MT_PREPROC_MEAS_RES, + RSL_MT_RF_CHAN_REL_ACK, + RSL_MT_SACCH_INFO_MODIFY, + RSL_MT_TALKER_DET, + RSL_MT_LISTENER_DET, + RSL_MT_REMOTE_CODEC_CONF_REP, + RSL_MT_RTD_REP, + RSL_MT_PRE_HANDO_NOTIF, + RSL_MT_MR_CODEC_MOD_REQ, + RSL_MT_MR_CODEC_MOD_ACK, + RSL_MT_MR_CODEC_MOD_NACK, + RSL_MT_MR_CODEC_MOD_PER, + RSL_MT_TFO_REP, + RSL_MT_TFO_MOD_REQ, /* 0x3f */ + RSL_MT_LOCATION_INFO = 0x41, + + /* ip.access specific RSL message types */ + RSL_MT_IPAC_DIR_RETR_ENQ = 0x40, + RSL_MT_IPAC_PDCH_ACT = 0x48, + RSL_MT_IPAC_PDCH_ACT_ACK, + RSL_MT_IPAC_PDCH_ACT_NACK, + RSL_MT_IPAC_PDCH_DEACT = 0x4b, + RSL_MT_IPAC_PDCH_DEACT_ACK, + RSL_MT_IPAC_PDCH_DEACT_NACK, + RSL_MT_IPAC_CONNECT_MUX = 0x50, + RSL_MT_IPAC_CONNECT_MUX_ACK, + RSL_MT_IPAC_CONNECT_MUX_NACK, + RSL_MT_IPAC_BIND_MUX = 0x53, + RSL_MT_IPAC_BIND_MUX_ACK, + RSL_MT_IPAC_BIND_MUX_NACK, + RSL_MT_IPAC_DISC_MUX = 0x56, + RSL_MT_IPAC_DISC_MUX_ACK, + RSL_MT_IPAC_DISC_MUX_NACK, + RSL_MT_IPAC_CRCX = 0x70, /* Bind to local BTS RTP port */ + RSL_MT_IPAC_CRCX_ACK, + RSL_MT_IPAC_CRCX_NACK, + RSL_MT_IPAC_MDCX = 0x73, + RSL_MT_IPAC_MDCX_ACK, + RSL_MT_IPAC_MDCX_NACK, + RSL_MT_IPAC_DLCX_IND = 0x76, + RSL_MT_IPAC_DLCX = 0x77, + RSL_MT_IPAC_DLCX_ACK, + RSL_MT_IPAC_DLCX_NACK, +}; + +/* Siemens vendor-specific */ +enum abis_rsl_msgtype_siemens { + RSL_MT_SIEMENS_MRPCI = 0x41, + RSL_MT_SIEMENS_INTRAC_HO_COND_IND = 0x42, + RSL_MT_SIEMENS_INTERC_HO_COND_IND = 0x43, + RSL_MT_SIEMENS_FORCED_HO_REQ = 0x44, + RSL_MT_SIEMENS_PREF_AREA_REQ = 0x45, + RSL_MT_SIEMENS_PREF_AREA = 0x46, + RSL_MT_SIEMENS_START_TRACE = 0x47, + RSL_MT_SIEMENS_START_TRACE_ACK = 0x48, + RSL_MT_SIEMENS_STOP_TRACE = 0x49, + RSL_MT_SIEMENS_TRMR = 0x4a, + RSL_MT_SIEMENS_HO_FAIL_IND = 0x4b, + RSL_MT_SIEMENS_STOP_TRACE_ACK = 0x4c, + RSL_MT_SIEMENS_UPLF = 0x4d, + RSL_MT_SIEMENS_UPLB = 0x4e, + RSL_MT_SIEMENS_SET_SYS_INFO_10 = 0x4f, + RSL_MT_SIEMENS_MODIF_COND_IND = 0x50, +}; + +/* Chapter 9.3 */ +enum abis_rsl_ie { + RSL_IE_CHAN_NR = 0x01, + RSL_IE_LINK_IDENT, + RSL_IE_ACT_TYPE, + RSL_IE_BS_POWER, + RSL_IE_CHAN_IDENT, + RSL_IE_CHAN_MODE, + RSL_IE_ENCR_INFO, + RSL_IE_FRAME_NUMBER, + RSL_IE_HANDO_REF, + RSL_IE_L1_INFO, + RSL_IE_L3_INFO, + RSL_IE_MS_IDENTITY, + RSL_IE_MS_POWER, + RSL_IE_PAGING_GROUP, + RSL_IE_PAGING_LOAD, + RSL_IE_PYHS_CONTEXT = 0x10, + RSL_IE_ACCESS_DELAY, + RSL_IE_RACH_LOAD, + RSL_IE_REQ_REFERENCE, + RSL_IE_RELEASE_MODE, + RSL_IE_RESOURCE_INFO, + RSL_IE_RLM_CAUSE, + RSL_IE_STARTNG_TIME, + RSL_IE_TIMING_ADVANCE, + RSL_IE_UPLINK_MEAS, + RSL_IE_CAUSE, + RSL_IE_MEAS_RES_NR, + RSL_IE_MSG_ID, + /* reserved */ + RSL_IE_SYSINFO_TYPE = 0x1e, + RSL_IE_MS_POWER_PARAM, + RSL_IE_BS_POWER_PARAM, + RSL_IE_PREPROC_PARAM, + RSL_IE_PREPROC_MEAS, + RSL_IE_IMM_ASS_INFO, /* Phase 1 (3.6.0), later Full below */ + RSL_IE_SMSCB_INFO = 0x24, + RSL_IE_MS_TIMING_OFFSET, + RSL_IE_ERR_MSG, + RSL_IE_FULL_BCCH_INFO, + RSL_IE_CHAN_NEEDED, + RSL_IE_CB_CMD_TYPE, + RSL_IE_SMSCB_MSG, + RSL_IE_FULL_IMM_ASS_INFO, + RSL_IE_SACCH_INFO, + RSL_IE_CBCH_LOAD_INFO, + RSL_IE_SMSCB_CHAN_INDICATOR, + RSL_IE_GROUP_CALL_REF, + RSL_IE_CHAN_DESC = 0x30, + RSL_IE_NCH_DRX_INFO, + RSL_IE_CMD_INDICATOR, + RSL_IE_EMLPP_PRIO, + RSL_IE_UIC, + RSL_IE_MAIN_CHAN_REF, + RSL_IE_MR_CONFIG, + RSL_IE_MR_CONTROL, + RSL_IE_SUP_CODEC_TYPES, + RSL_IE_CODEC_CONFIG, + RSL_IE_RTD, + RSL_IE_TFO_STATUS, + RSL_IE_LLP_APDU, + /* Siemens vendor-specific */ + RSL_IE_SIEMENS_MRPCI = 0x40, + RSL_IE_SIEMENS_PREF_AREA_TYPE = 0x43, + RSL_IE_SIEMENS_ININ_CELL_HO_PAR = 0x45, + RSL_IE_SIEMENS_TRACE_REF_NR = 0x46, + RSL_IE_SIEMENS_INT_TRACE_IDX = 0x47, + RSL_IE_SIEMENS_L2_HDR_INFO = 0x48, + RSL_IE_SIEMENS_HIGHEST_RATE = 0x4e, + RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f, + + /* ip.access */ + RSL_IE_IPAC_SRTP_CONFIG = 0xe0, + RSL_IE_IPAC_PROXY_UDP = 0xe1, + RSL_IE_IPAC_BSCMPL_TOUT = 0xe2, + RSL_IE_IPAC_REMOTE_IP = 0xf0, + RSL_IE_IPAC_REMOTE_PORT = 0xf1, + RSL_IE_IPAC_RTP_PAYLOAD = 0xf2, + RSL_IE_IPAC_LOCAL_PORT = 0xf3, + RSL_IE_IPAC_SPEECH_MODE = 0xf4, + RSL_IE_IPAC_LOCAL_IP = 0xf5, + RSL_IE_IPAC_CONN_STAT = 0xf6, + RSL_IE_IPAC_HO_C_PARMS = 0xf7, + RSL_IE_IPAC_CONN_ID = 0xf8, + RSL_IE_IPAC_RTP_CSD_FMT = 0xf9, + RSL_IE_IPAC_RTP_JIT_BUF = 0xfa, + RSL_IE_IPAC_RTP_COMPR = 0xfb, + RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc, + RSL_IE_IPAC_RTP_MPLEX = 0xfd, + RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe, +}; + +/* Chapter 9.3.1 */ +#define RSL_CHAN_NR_MASK 0xf8 +#define RSL_CHAN_Bm_ACCHs 0x08 +#define RSL_CHAN_Lm_ACCHs 0x10 /* .. 0x18 */ +#define RSL_CHAN_SDCCH4_ACCH 0x20 /* .. 0x38 */ +#define RSL_CHAN_SDCCH8_ACCH 0x40 /* ...0x78 */ +#define RSL_CHAN_BCCH 0x80 +#define RSL_CHAN_RACH 0x88 +#define RSL_CHAN_PCH_AGCH 0x90 + +/* Chapter 9.3.3 */ +#define RSL_ACT_TYPE_INITIAL 0x00 +#define RSL_ACT_TYPE_REACT 0x80 +#define RSL_ACT_INTRA_IMM_ASS 0x00 +#define RSL_ACT_INTRA_NORM_ASS 0x01 +#define RSL_ACT_INTER_ASYNC 0x02 +#define RSL_ACT_INTER_SYNC 0x03 +#define RSL_ACT_SECOND_ADD 0x04 +#define RSL_ACT_SECOND_MULTI 0x05 + +/* Chapter 9.3.6 */ +struct rsl_ie_chan_mode { + uint8_t dtx_dtu; + uint8_t spd_ind; + uint8_t chan_rt; + uint8_t chan_rate; +} __attribute__ ((packed)); +#define RSL_CMOD_DTXu 0x01 /* uplink */ +#define RSL_CMOD_DTXd 0x02 /* downlink */ +enum rsl_cmod_spd { + RSL_CMOD_SPD_SPEECH = 0x01, + RSL_CMOD_SPD_DATA = 0x02, + RSL_CMOD_SPD_SIGN = 0x03, +}; +#define RSL_CMOD_CRT_SDCCH 0x01 +#define RSL_CMOD_CRT_TCH_Bm 0x08 /* full-rate */ +#define RSL_CMOD_CRT_TCH_Lm 0x09 /* half-rate */ +/* FIXME: More CRT types */ +/* Speech */ +#define RSL_CMOD_SP_GSM1 0x01 +#define RSL_CMOD_SP_GSM2 0x11 +#define RSL_CMOD_SP_GSM3 0x21 +/* Data */ +#define RSL_CMOD_SP_NT_14k5 0x58 +#define RSL_CMOD_SP_NT_12k0 0x50 +#define RSL_CMOD_SP_NT_6k0 0x51 + +/* Chapter 9.3.5 */ +struct rsl_ie_chan_ident { + /* GSM 04.08 10.5.2.5 */ + struct { + uint8_t iei; + uint8_t chan_nr; /* enc_chan_nr */ + uint8_t oct3; + uint8_t oct4; + } chan_desc; +#if 0 /* spec says we need this but Abissim doesn't use it */ + struct { + uint8_t tag; + uint8_t len; + } mobile_alloc; +#endif +} __attribute__ ((packed)); + +/* Chapter 9.3.22 */ +#define RLL_CAUSE_T200_EXPIRED 0x01 +#define RLL_CAUSE_REEST_REQ 0x02 +#define RLL_CAUSE_UNSOL_UA_RESP 0x03 +#define RLL_CAUSE_UNSOL_DM_RESP 0x04 +#define RLL_CAUSE_UNSOL_DM_RESP_MF 0x05 +#define RLL_CAUSE_UNSOL_SPRV_RESP 0x06 +#define RLL_CAUSE_SEQ_ERR 0x07 +#define RLL_CAUSE_UFRM_INC_PARAM 0x08 +#define RLL_CAUSE_SFRM_INC_PARAM 0x09 +#define RLL_CAUSE_IFRM_INC_MBITS 0x0a +#define RLL_CAUSE_IFRM_INC_LEN 0x0b +#define RLL_CAUSE_FRM_UNIMPL 0x0c +#define RLL_CAUSE_SABM_MF 0x0d +#define RLL_CAUSE_SABM_INFO_NOTALL 0x0e + +/* Chapter 9.3.26 */ +#define RSL_ERRCLS_NORMAL 0x00 +#define RSL_ERRCLS_RESOURCE_UNAVAIL 0x20 +#define RSL_ERRCLS_SERVICE_UNAVAIL 0x30 +#define RSL_ERRCLS_SERVICE_UNIMPL 0x40 +#define RSL_ERRCLS_INVAL_MSG 0x50 +#define RSL_ERRCLS_PROTO_ERROR 0x60 +#define RSL_ERRCLS_INTERWORKING 0x70 + +/* normal event */ +#define RSL_ERR_RADIO_IF_FAIL 0x00 +#define RSL_ERR_RADIO_LINK_FAIL 0x01 +#define RSL_ERR_HANDOVER_ACC_FAIL 0x02 +#define RSL_ERR_TALKER_ACC_FAIL 0x03 +#define RSL_ERR_OM_INTERVENTION 0x07 +#define RSL_ERR_NORMAL_UNSPEC 0x0f +#define RSL_ERR_T_MSRFPCI_EXP 0x18 +/* resource unavailable */ +#define RSL_ERR_EQUIPMENT_FAIL 0x20 +#define RSL_ERR_RR_UNAVAIL 0x21 +#define RSL_ERR_TERR_CH_FAIL 0x22 +#define RSL_ERR_CCCH_OVERLOAD 0x23 +#define RSL_ERR_ACCH_OVERLOAD 0x24 +#define RSL_ERR_PROCESSOR_OVERLOAD 0x25 +#define RSL_ERR_RES_UNAVAIL 0x2f +/* service or option not available */ +#define RSL_ERR_TRANSC_UNAVAIL 0x30 +#define RSL_ERR_SERV_OPT_UNAVAIL 0x3f +/* service or option not implemented */ +#define RSL_ERR_ENCR_UNIMPL 0x40 +#define RSL_ERR_SERV_OPT_UNIMPL 0x4f +/* invalid message */ +#define RSL_ERR_RCH_ALR_ACTV_ALLOC 0x50 +#define RSL_ERR_INVALID_MESSAGE 0x5f +/* protocol error */ +#define RSL_ERR_MSG_DISCR 0x60 +#define RSL_ERR_MSG_TYPE 0x61 +#define RSL_ERR_MSG_SEQ 0x62 +#define RSL_ERR_IE_ERROR 0x63 +#define RSL_ERR_MAND_IE_ERROR 0x64 +#define RSL_ERR_OPT_IE_ERROR 0x65 +#define RSL_ERR_IE_NONEXIST 0x66 +#define RSL_ERR_IE_LENGTH 0x67 +#define RSL_ERR_IE_CONTENT 0x68 +#define RSL_ERR_PROTO 0x6f +/* interworking */ +#define RSL_ERR_INTERWORKING 0x7f + +/* Chapter 9.3.30 */ +#define RSL_SYSTEM_INFO_8 0x00 +#define RSL_SYSTEM_INFO_1 0x01 +#define RSL_SYSTEM_INFO_2 0x02 +#define RSL_SYSTEM_INFO_3 0x03 +#define RSL_SYSTEM_INFO_4 0x04 +#define RSL_SYSTEM_INFO_5 0x05 +#define RSL_SYSTEM_INFO_6 0x06 +#define RSL_SYSTEM_INFO_7 0x07 +#define RSL_SYSTEM_INFO_16 0x08 +#define RSL_SYSTEM_INFO_17 0x09 +#define RSL_SYSTEM_INFO_2bis 0x0a +#define RSL_SYSTEM_INFO_2ter 0x0b +#define RSL_SYSTEM_INFO_5bis 0x0d +#define RSL_SYSTEM_INFO_5ter 0x0e +#define RSL_SYSTEM_INFO_10 0x0f +#define REL_EXT_MEAS_ORDER 0x47 +#define RSL_MEAS_INFO 0x48 +#define RSL_SYSTEM_INFO_13 0x28 +#define RSL_SYSTEM_INFO_2quater 0x29 +#define RSL_SYSTEM_INFO_9 0x2a +#define RSL_SYSTEM_INFO_18 0x2b +#define RSL_SYSTEM_INFO_19 0x2c +#define RSL_SYSTEM_INFO_20 0x2d + +/* Chapter 9.3.40 */ +#define RSL_CHANNEED_ANY 0x00 +#define RSL_CHANNEED_SDCCH 0x01 +#define RSL_CHANNEED_TCH_F 0x02 +#define RSL_CHANNEED_TCH_ForH 0x03 + +/* Chapter 9.3.45 */ +struct rsl_ie_cb_cmd_type { + uint8_t last_block:2; + uint8_t spare:1; + uint8_t def_bcast:1; + uint8_t command:4; +} __attribute__ ((packed)); +/* ->command */ +#define RSL_CB_CMD_TYPE_NORMAL 0x00 +#define RSL_CB_CMD_TYPE_SCHEDULE 0x08 +#define RSL_CB_CMD_TYPE_DEFAULT 0x0e +#define RSL_CB_CMD_TYPE_NULL 0x0f +/* ->def_bcast */ +#define RSL_CB_CMD_DEFBCAST_NORMAL 0 +#define RSL_CB_CMD_DEFBCAST_NULL 1 +/* ->last_block */ +#define RSL_CB_CMD_LASTBLOCK_4 0 +#define RSL_CB_CMD_LASTBLOCK_1 1 +#define RSL_CB_CMD_LASTBLOCK_2 2 +#define RSL_CB_CMD_LASTBLOCK_3 3 + +/* Chapter 3.3.2.3 Brocast control channel */ +/* CCCH-CONF, NC is not combined */ +#define RSL_BCCH_CCCH_CONF_1_NC 0x00 +#define RSL_BCCH_CCCH_CONF_1_C 0x01 +#define RSL_BCCH_CCCH_CONF_2_NC 0x02 +#define RSL_BCCH_CCCH_CONF_3_NC 0x04 +#define RSL_BCCH_CCCH_CONF_4_NC 0x06 + +/* BS-PA-MFRMS */ +#define RSL_BS_PA_MFRMS_2 0x00 +#define RSL_BS_PA_MFRMS_3 0x01 +#define RSL_BS_PA_MFRMS_4 0x02 +#define RSL_BS_PA_MFRMS_5 0x03 +#define RSL_BS_PA_MFRMS_6 0x04 +#define RSL_BS_PA_MFRMS_7 0x05 +#define RSL_BS_PA_MFRMS_8 0x06 +#define RSL_BS_PA_MFRMS_9 0x07 + +/* RSL_IE_IPAC_RTP_PAYLOAD[2] */ +enum rsl_ipac_rtp_payload { + RSL_IPAC_RTP_GSM = 1, + RSL_IPAC_RTP_EFR, + RSL_IPAC_RTP_AMR, + RSL_IPAC_RTP_CSD, + RSL_IPAC_RTP_MUX, +}; + +/* RSL_IE_IPAC_SPEECH_MODE, lower four bits */ +enum rsl_ipac_speech_mode_s { + RSL_IPAC_SPEECH_GSM_FR = 0, /* GSM FR (Type 1, FS) */ + RSL_IPAC_SPEECH_GSM_EFR = 1, /* GSM EFR (Type 2, FS) */ + RSL_IPAC_SPEECH_GSM_AMR_FR = 2, /* GSM AMR/FR (Type 3, FS) */ + RSL_IPAC_SPEECH_GSM_HR = 3, /* GSM HR (Type 1, HS) */ + RSL_IPAC_SPEECH_GSM_AMR_HR = 5, /* GSM AMR/hr (Type 3, HS) */ + RSL_IPAC_SPEECH_AS_RTP = 0xf, /* As specified by RTP Payload IE */ +}; +/* RSL_IE_IPAC_SPEECH_MODE, upper four bits */ +enum rsl_ipac_speech_mode_m { + RSL_IPAC_SPEECH_M_RXTX = 0, /* Send and Receive */ + RSL_IPAC_SPEECH_M_RX = 1, /* Receive only */ + RSL_IPAC_SPEECH_M_TX = 2, /* Send only */ +}; + +/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */ +enum rsl_ipac_rtp_csd_format_d { + RSL_IPAC_RTP_CSD_EXT_TRAU = 0, + RSL_IPAC_RTP_CSD_NON_TRAU = 1, + RSL_IPAC_RTP_CSD_TRAU_BTS = 2, + RSL_IPAC_RTP_CSD_IWF_FREE = 3, +}; +/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */ +enum rsl_ipac_rtp_csd_format_ir { + RSL_IPAC_RTP_CSD_IR_8k = 0, + RSL_IPAC_RTP_CSD_IR_16k = 1, + RSL_IPAC_RTP_CSD_IR_32k = 2, + RSL_IPAC_RTP_CSD_IR_64k = 3, +}; + +/* Siemens vendor-specific RSL extensions */ +struct rsl_mrpci { + uint8_t power_class:3, + vgcs_capable:1, + vbs_capable:1, + gsm_phase:2; +} __attribute__ ((packed)); + +enum rsl_mrpci_pwrclass { + RSL_MRPCI_PWRC_1 = 0, + RSL_MRPCI_PWRC_2 = 1, + RSL_MRPCI_PWRC_3 = 2, + RSL_MRPCI_PWRC_4 = 3, + RSL_MRPCI_PWRC_5 = 4, +}; +enum rsl_mrpci_phase { + RSL_MRPCI_PHASE_1 = 0, + /* reserved */ + RSL_MRPCI_PHASE_2 = 2, + RSL_MRPCI_PHASE_2PLUS = 3, +}; + + +#endif /* PROTO_GSM_08_58_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h new file mode 100644 index 00000000..b8b00f3f --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h @@ -0,0 +1,713 @@ +#ifndef PROTO_GSM_12_21_H +#define PROTO_GSM_12_21_H + +/* GSM Network Management messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <osmocom/gsm/tlv.h> + +/* generic header in front of every OML message according to TS 08.59 */ +struct abis_om_hdr { + uint8_t mdisc; + uint8_t placement; + uint8_t sequence; + uint8_t length; + uint8_t data[0]; +} __attribute__ ((packed)); + +#define ABIS_OM_MDISC_FOM 0x80 +#define ABIS_OM_MDISC_MMI 0x40 +#define ABIS_OM_MDISC_TRAU 0x20 +#define ABIS_OM_MDISC_MANUF 0x10 +#define ABIS_OM_PLACEMENT_ONLY 0x80 +#define ABIS_OM_PLACEMENT_FIRST 0x40 +#define ABIS_OM_PLACEMENT_MIDDLE 0x20 +#define ABIS_OM_PLACEMENT_LAST 0x10 + +struct abis_om_obj_inst { + uint8_t bts_nr; + uint8_t trx_nr; + uint8_t ts_nr; +} __attribute__ ((packed)); + +struct abis_om_fom_hdr { + uint8_t msg_type; + uint8_t obj_class; + struct abis_om_obj_inst obj_inst; + uint8_t data[0]; +} __attribute__ ((packed)); + +#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr)) + +/* Section 9.1: Message Types */ +enum abis_nm_msgtype { + /* SW Download Management Messages */ + NM_MT_LOAD_INIT = 0x01, + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ, /* BTS->BSC */ + NM_MT_SW_ACT_REQ_ACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW, /* BSC->BTS */ + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, /* 0x10 */ + /* A-bis Interface Management Messages */ + NM_MT_ESTABLISH_TEI = 0x21, + NM_MT_ESTABLISH_TEI_ACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN, + NM_MT_CONN_TERR_SIGN_ACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN, + NM_MT_DISC_TERR_SIGN_ACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF, + NM_MT_CONN_TERR_TRAF_ACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF, + NM_MT_DISC_TERR_TRAF_ACK, + NM_MT_DISC_TERR_TRAF_NACK, + /* Transmission Management Messages */ + NM_MT_CONN_MDROP_LINK = 0x31, + NM_MT_CONN_MDROP_LINK_ACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK, + NM_MT_DISC_MDROP_LINK_ACK, + NM_MT_DISC_MDROP_LINK_NACK, + /* Air Interface Management Messages */ + NM_MT_SET_BTS_ATTR = 0x41, + NM_MT_SET_BTS_ATTR_ACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR, + NM_MT_SET_RADIO_ATTR_ACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR, + NM_MT_SET_CHAN_ATTR_ACK, + NM_MT_SET_CHAN_ATTR_NACK, + /* Test Management Messages */ + NM_MT_PERF_TEST = 0x51, + NM_MT_PERF_TEST_ACK, + NM_MT_PERF_TEST_NACK, + NM_MT_TEST_REP, + NM_MT_SEND_TEST_REP, + NM_MT_SEND_TEST_REP_ACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST, + NM_MT_STOP_TEST_ACK, + NM_MT_STOP_TEST_NACK, + /* State Management and Event Report Messages */ + NM_MT_STATECHG_EVENT_REP = 0x61, + NM_MT_FAILURE_EVENT_REP, + NM_MT_STOP_EVENT_REP, + NM_MT_STOP_EVENT_REP_ACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP, + NM_MT_REST_EVENT_REP_ACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE, + NM_MT_CHG_ADM_STATE_ACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ, + NM_MT_CHG_ADM_STATE_REQ_ACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS = 0x93, + NM_MT_REP_OUTST_ALARMS_ACK, + NM_MT_REP_OUTST_ALARMS_NACK, + /* Equipment Management Messages */ + NM_MT_CHANGEOVER = 0x71, + NM_MT_CHANGEOVER_ACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART, + NM_MT_OPSTART_ACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT, + NM_MT_REINIT_ACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */ + NM_MT_SET_SITE_OUT_ACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF = 0x90, + NM_MT_CHG_HW_CONF_ACK, + NM_MT_CHG_HW_CONF_NACK, + /* Measurement Management Messages */ + NM_MT_MEAS_RES_REQ = 0x8a, + NM_MT_MEAS_RES_RESP, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, + /* Other Messages */ + NM_MT_GET_ATTR = 0x81, + NM_MT_GET_ATTR_RESP, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES, + NM_MT_SET_ALARM_THRES_ACK, + NM_MT_SET_ALARM_THRES_NACK, +}; + +enum abis_nm_msgtype_bs11 { + NM_MT_BS11_RESET_RESOURCE = 0x74, + + NM_MT_BS11_BEGIN_DB_TX = 0xa3, + NM_MT_BS11_BEGIN_DB_TX_ACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX = 0xa6, + NM_MT_BS11_END_DB_TX_ACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ = 0xa9, + NM_MT_BS11_CREATE_OBJ_ACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ = 0xac, + NM_MT_BS11_DELETE_OBJ_ACK, + NM_MT_BS11_DELETE_OBJ_NACK, + + NM_MT_BS11_SET_ATTR = 0xd0, + NM_MT_BS11_SET_ATTR_ACK, + NM_MT_BS11_SET_ATTR_NACK, + NM_MT_BS11_LMT_SESSION = 0xdc, + + NM_MT_BS11_GET_STATE = 0xe3, + NM_MT_BS11_GET_STATE_ACK, + NM_MT_BS11_LMT_LOGON = 0xe5, + NM_MT_BS11_LMT_LOGON_ACK, + NM_MT_BS11_RESTART = 0xe7, + NM_MT_BS11_RESTART_ACK, + NM_MT_BS11_DISCONNECT = 0xe9, + NM_MT_BS11_DISCONNECT_ACK, + NM_MT_BS11_LMT_LOGOFF = 0xec, + NM_MT_BS11_LMT_LOGOFF_ACK, + NM_MT_BS11_RECONNECT = 0xf1, + NM_MT_BS11_RECONNECT_ACK, +}; + +enum abis_nm_msgtype_ipacc { + NM_MT_IPACC_RESTART = 0x87, + NM_MT_IPACC_RESTART_ACK, + NM_MT_IPACC_RESTART_NACK, + NM_MT_IPACC_RSL_CONNECT = 0xe0, + NM_MT_IPACC_RSL_CONNECT_ACK, + NM_MT_IPACC_RSL_CONNECT_NACK, + NM_MT_IPACC_RSL_DISCONNECT = 0xe3, + NM_MT_IPACC_RSL_DISCONNECT_ACK, + NM_MT_IPACC_RSL_DISCONNECT_NACK, + NM_MT_IPACC_CONN_TRAF = 0xe6, + NM_MT_IPACC_CONN_TRAF_ACK, + NM_MT_IPACC_CONN_TRAF_NACK, + NM_MT_IPACC_DEF_BOOT_SW = 0xec, + NM_MT_IPACC_DEF_BOOT_SW_ACK, + MN_MT_IPACC_DEF_BOOT_SW_NACK, + NM_MT_IPACC_SET_NVATTR = 0xef, + NM_MT_IPACC_SET_NVATTR_ACK, + NM_MT_IPACC_SET_NVATTR_NACK, + NM_MT_IPACC_GET_NVATTR = 0xf2, + NM_MT_IPACC_GET_NVATTR_ACK, + NM_MT_IPACC_GET_NVATTR_NACK, + NM_MT_IPACC_SET_ATTR = 0xf5, + NM_MT_IPACC_SET_ATTR_ACK, + NM_MT_IPACC_SET_ATTR_NACK, +}; + +enum abis_nm_bs11_cell_alloc { + NM_BS11_CANR_GSM = 0x00, + NM_BS11_CANR_DCS1800 = 0x01, +}; + +/* Section 9.2: Object Class */ +enum abis_nm_obj_class { + NM_OC_SITE_MANAGER = 0x00, + NM_OC_BTS, + NM_OC_RADIO_CARRIER, + NM_OC_CHANNEL, + NM_OC_BASEB_TRANSC, + /* RFU: 05-FE */ + + NM_OC_IPAC_E1_TRUNK = 0x0e, + NM_OC_IPAC_E1_PORT = 0x0f, + NM_OC_IPAC_E1_CHAN = 0x10, + NM_OC_IPAC_CLK_MODULE = 0x22, + + NM_OC_BS11_ADJC = 0xa0, + NM_OC_BS11_HANDOVER = 0xa1, + NM_OC_BS11_PWR_CTRL = 0xa2, + NM_OC_BS11_BTSE = 0xa3, /* LMT? */ + NM_OC_BS11_RACK = 0xa4, + NM_OC_BS11 = 0xa5, /* 01: ALCO */ + NM_OC_BS11_TEST = 0xa6, + NM_OC_BS11_ENVABTSE = 0xa8, + NM_OC_BS11_BPORT = 0xa9, + + NM_OC_GPRS_NSE = 0xf0, + NM_OC_GPRS_CELL = 0xf1, + NM_OC_GPRS_NSVC = 0xf2, + + NM_OC_NULL = 0xff, +}; + +/* Section 9.4: Attributes */ +enum abis_nm_attr { + NM_ATT_ABIS_CHANNEL = 0x01, + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_ADM_STATE, + NM_ATT_ARFCN_LIST, + NM_ATT_AUTON_REPORT, + NM_ATT_AVAIL_STATUS, + NM_ATT_BCCH_ARFCN, + NM_ATT_BSIC, + NM_ATT_BTS_AIR_TIMER, + NM_ATT_CCCH_L_I_P, + NM_ATT_CCCH_L_T, + NM_ATT_CHAN_COMB, + NM_ATT_CONN_FAIL_CRIT, + NM_ATT_DEST, + /* res */ + NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */ + NM_ATT_FILE_ID, + NM_ATT_FILE_VERSION, + NM_ATT_GSM_TIME, + NM_ATT_HSN, + NM_ATT_HW_CONFIG, + NM_ATT_HW_DESC, + NM_ATT_INTAVE_PARAM, + NM_ATT_INTERF_BOUND, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MAIO, + NM_ATT_MANUF_STATE, + NM_ATT_MANUF_THRESH, + NM_ATT_MANUF_ID, + NM_ATT_MAX_TA, + NM_ATT_MDROP_LINK, /* 0x20 */ + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_NY1, + NM_ATT_OPER_STATE, + NM_ATT_OVERL_PERIOD, + NM_ATT_PHYS_CONF, + NM_ATT_POWER_CLASS, + NM_ATT_POWER_THRESH, + NM_ATT_PROB_CAUSE, + NM_ATT_RACH_B_THRESH, + NM_ATT_LDAVG_SLOTS, + NM_ATT_RAD_SUBC, + NM_ATT_RF_MAXPOWR_R, + NM_ATT_SITE_INPUTS, + NM_ATT_SITE_OUTPUTS, + NM_ATT_SOURCE, /* 0x30 */ + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_T200, + NM_ATT_TEI, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_VSWR_THRESH, + NM_ATT_WINDOW_SIZE, + /* Res */ + NM_ATT_BS11_RSSI_OFFS = 0x3d, + NM_ATT_BS11_TXPWR = 0x3e, + NM_ATT_BS11_DIVERSITY = 0x3f, + /* Res */ + NM_ATT_TSC = 0x40, + NM_ATT_SW_CONFIG, + NM_ATT_SW_DESCR, + NM_ATT_SEVERITY, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_OUTST_ALARM, + NM_ATT_FILE_DATA, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, + + NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c, + NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f, + + NM_ATT_BS11_ESN_PCB_SERIAL = 0x55, + NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58, + + NM_ATT_BS11_ALL_TEST_CATG = 0x60, + NM_ATT_BS11_BTSLS_HOPPING, + NM_ATT_BS11_CELL_ALLOC_NR, + NM_ATT_BS11_CELL_GLOBAL_ID, + NM_ATT_BS11_ENA_INTERF_CLASS = 0x66, + NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67, + NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68, + NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69, + NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a, + NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b, + NM_ATT_BS11_ENA_RXLEV_HO = 0x6c, + NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d, + NM_ATT_BS11_FACCH_QUAL = 0x6e, + + NM_ATT_IPACC_DST_IP = 0x80, + NM_ATT_IPACC_DST_IP_PORT = 0x81, + NM_ATT_IPACC_SSRC = 0x82, + NM_ATT_IPACC_RTP_PAYLD_TYPE = 0x83, + NM_ATT_IPACC_BASEB_ID = 0x84, + NM_ATT_IPACC_STREAM_ID = 0x85, + NM_ATT_IPACC_NV_FLAGS = 0x86, + NM_ATT_IPACC_FREQ_CTRL = 0x87, + NM_ATT_IPACC_PRIM_OML_CFG = 0x88, + NM_ATT_IPACC_SEC_OML_CFG = 0x89, + NM_ATT_IPACC_IP_IF_CFG = 0x8a, /* IP interface */ + NM_ATT_IPACC_IP_GW_CFG = 0x8b, /* IP gateway */ + NM_ATT_IPACC_IN_SERV_TIME = 0x8c, + NM_ATT_IPACC_TRX_BTS_ASS = 0x8d, + NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */ + NM_ATT_IPACC_PAGING_CFG = 0x8f, + NM_ATT_IPACC_FILE_DATA = 0x90, + NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */ + NM_ATT_IPACC_PARENT_UNIT_ID = 0x92, + NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */ + NM_ATT_IPACC_SNMP_CFG = 0x94, + NM_ATT_IPACC_PRIM_OML_CFG_LIST = 0x95, + NM_ATT_IPACC_PRIM_OML_FB_TOUT = 0x96, + NM_ATT_IPACC_CUR_SW_CFG = 0x97, + NM_ATT_IPACC_TIMING_BUS = 0x98, + NM_ATT_IPACC_CGI = 0x99, + NM_ATT_IPACC_RAC = 0x9a, + NM_ATT_IPACC_OBJ_VERSION = 0x9b, + NM_ATT_IPACC_GPRS_PAGING_CFG = 0x9c, + NM_ATT_IPACC_NSEI = 0x9d, + NM_ATT_IPACC_BVCI = 0x9e, + NM_ATT_IPACC_NSVCI = 0x9f, + NM_ATT_IPACC_NS_CFG = 0xa0, + NM_ATT_IPACC_BSSGP_CFG = 0xa1, + NM_ATT_IPACC_NS_LINK_CFG = 0xa2, + NM_ATT_IPACC_RLC_CFG = 0xa3, + NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4, + NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5, + NM_ATT_IPACC_TIB_CONTROL = 0xa6, + NM_ATT_IPACC_SUPP_FEATURES = 0xa7, + NM_ATT_IPACC_CODING_SCHEMES = 0xa8, + NM_ATT_IPACC_RLC_CFG_2 = 0xa9, + NM_ATT_IPACC_HEARTB_TOUT = 0xaa, + NM_ATT_IPACC_UPTIME = 0xab, + NM_ATT_IPACC_RLC_CFG_3 = 0xac, + NM_ATT_IPACC_SSL_CFG = 0xad, + NM_ATT_IPACC_SEC_POSSIBLE = 0xae, + NM_ATT_IPACC_IML_SSL_STATE = 0xaf, + NM_ATT_IPACC_REVOC_DATE = 0xb0, + + + NM_ATT_BS11_RF_RES_IND_PER = 0x8f, + + NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90, + NM_ATT_BS11_ABIS_EXT_TIME = 0x91, + NM_ATT_BS11_TIMER_HO_REQUEST = 0x92, + NM_ATT_BS11_TIMER_NCELL = 0x93, + NM_ATT_BS11_TSYNC = 0x94, + NM_ATT_BS11_TTRAU = 0x95, + NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b, + NM_ATT_BS11_TRX_AREA = 0x9f, + + NM_ATT_BS11_BCCH_RECONF = 0xd7, + NM_ATT_BS11_BIT_ERR_THESH = 0xa0, + NM_ATT_BS11_BOOT_SW_VERS = 0xa1, + NM_ATT_BS11_CCLK_ACCURACY = 0xa3, + NM_ATT_BS11_CCLK_TYPE = 0xa4, + NM_ATT_BS11_INP_IMPEDANCE = 0xaa, + NM_ATT_BS11_L1_PROT_TYPE = 0xab, + NM_ATT_BS11_LINE_CFG = 0xac, + NM_ATT_BS11_LI_PORT_1 = 0xad, + NM_ATT_BS11_LI_PORT_2 = 0xae, + + NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0, + NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb, + NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc, + NM_ATT_BS11_SW_LOAD_STORED = 0xbd, + + NM_ATT_BS11_VENDOR_NAME = 0xc1, + NM_ATT_BS11_HOPPING_MODE = 0xc5, + NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6, + NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7, + NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8, + NM_ATT_BS11_LMT_USER_NAME = 0xc9, + + NM_ATT_BS11_L1_CONTROL_TS = 0xd8, + NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */ + NM_ATT_BS11_RADIO_MEAS_REP = 0xdd, + + NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8, + + NM_ATT_BS11_BTS_STATE = 0xf0, + NM_ATT_BS11_E1_STATE = 0xf1, + NM_ATT_BS11_PLL = 0xf2, + NM_ATT_BS11_RX_OFFSET = 0xf3, + NM_ATT_BS11_ANT_TYPE = 0xf4, + NM_ATT_BS11_PLL_MODE = 0xfc, + NM_ATT_BS11_PASSWORD = 0xfd, +}; +#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE + +/* Section 9.4.4: Administrative State */ +enum abis_nm_adm_state { + NM_STATE_LOCKED = 0x01, + NM_STATE_UNLOCKED = 0x02, + NM_STATE_SHUTDOWN = 0x03, + NM_STATE_NULL = 0xff, +}; + +/* Section 9.4.7: Administrative State */ +enum abis_nm_avail_state { + NM_AVSTATE_IN_TEST = 1, + NM_AVSTATE_POWER_OFF = 2, + NM_AVSTATE_OFF_LINE = 3, + NM_AVSTATE_DEPENDENCY = 5, + NM_AVSTATE_DEGRADED = 6, + NM_AVSTATE_NOT_INSTALLED= 7, + NM_AVSTATE_OK = 0xff, +}; + +enum abis_nm_op_state { + NM_OPSTATE_DISABLED = 1, + NM_OPSTATE_ENABLED = 2, + NM_OPSTATE_NULL = 0xff, +}; + +/* Section 9.4.13: Channel Combination */ +enum abis_nm_chan_comb { + NM_CHANC_TCHFull = 0x00, /* TCH/F + TCH/H + SACCH/TF */ + NM_CHANC_TCHHalf = 0x01, /* TCH/H(0,1) + FACCH/H(0,1) + + SACCH/TH(0,1) */ + NM_CHANC_TCHHalf2 = 0x02, /* TCH/H(0) + FACCH/H(0) + SACCH/TH(0) + + TCH/H(1) */ + NM_CHANC_SDCCH = 0x03, /* SDCCH/8 + SACCH/8 */ + NM_CHANC_mainBCCH = 0x04, /* FCCH + SCH + BCCH + CCCH */ + NM_CHANC_BCCHComb = 0x05, /* FCCH + SCH + BCCH + CCCH + SDCCH/4 + + SACCH/C4 */ + NM_CHANC_BCCH = 0x06, /* BCCH + CCCH */ + NM_CHANC_BCCH_CBCH = 0x07, /* CHANC_BCCHComb + CBCH */ + NM_CHANC_SDCCH_CBCH = 0x08, /* CHANC_SDCCH8 + CBCH */ + /* ip.access */ + NM_CHANC_IPAC_bPDCH = 0x0b, /* PBCCH + PCCCH + PDTCH/F + PACCH/F + + PTCCH/F */ + NM_CHANC_IPAC_cPDCH = 0x0c, /* PBCCH + PDTCH/F + PACCH/F + PTCCH/F */ + NM_CHANC_IPAC_PDCH = 0x0d, /* PDTCH/F + PACCH/F + PTCCH/F */ + NM_CHANC_IPAC_TCHFull_PDCH = 0x80, + NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81, +}; + +/* Section 9.4.16: Event Type */ +enum abis_nm_event_type { + NM_EVT_COMM_FAIL = 0x00, + NM_EVT_QOS_FAIL = 0x01, + NM_EVT_PROC_FAIL = 0x02, + NM_EVT_EQUIP_FAIL = 0x03, + NM_EVT_ENV_FAIL = 0x04, +}; + +/* Section: 9.4.63: Perceived Severity */ +enum abis_nm_severity { + NM_SEVER_CEASED = 0x00, + NM_SEVER_CRITICAL = 0x01, + NM_SEVER_MAJOR = 0x02, + NM_SEVER_MINOR = 0x03, + NM_SEVER_WARNING = 0x04, + NM_SEVER_INDETERMINATE = 0x05, +}; + +/* Section 9.4.43: Probable Cause Type */ +enum abis_nm_pcause_type { + NM_PCAUSE_T_X721 = 0x01, + NM_PCAUSE_T_GSM = 0x02, + NM_PCAUSE_T_MANUF = 0x03, +}; + +/* Section 9.4.36: NACK Causes */ +enum abis_nm_nack_cause { + /* General Nack Causes */ + NM_NACK_INCORR_STRUCT = 0x01, + NM_NACK_MSGTYPE_INVAL = 0x02, + NM_NACK_OBJCLASS_INVAL = 0x05, + NM_NACK_OBJCLASS_NOTSUPP = 0x06, + NM_NACK_BTSNR_UNKN = 0x07, + NM_NACK_TRXNR_UNKN = 0x08, + NM_NACK_OBJINST_UNKN = 0x09, + NM_NACK_ATTRID_INVAL = 0x0c, + NM_NACK_ATTRID_NOTSUPP = 0x0d, + NM_NACK_PARAM_RANGE = 0x0e, + NM_NACK_ATTRLIST_INCONSISTENT = 0x0f, + NM_NACK_SPEC_IMPL_NOTSUPP = 0x10, + NM_NACK_CANT_PERFORM = 0x11, + /* Specific Nack Causes */ + NM_NACK_RES_NOTIMPL = 0x19, + NM_NACK_RES_NOTAVAIL = 0x1a, + NM_NACK_FREQ_NOTAVAIL = 0x1b, + NM_NACK_TEST_NOTSUPP = 0x1c, + NM_NACK_CAPACITY_RESTR = 0x1d, + NM_NACK_PHYSCFG_NOTPERFORM = 0x1e, + NM_NACK_TEST_NOTINIT = 0x1f, + NM_NACK_PHYSCFG_NOTRESTORE = 0x20, + NM_NACK_TEST_NOSUCH = 0x21, + NM_NACK_TEST_NOSTOP = 0x22, + NM_NACK_MSGINCONSIST_PHYSCFG = 0x23, + NM_NACK_FILE_INCOMPLETE = 0x25, + NM_NACK_FILE_NOTAVAIL = 0x26, + NM_NACK_FILE_NOTACTIVATE = 0x27, + NM_NACK_REQ_NOT_GRANT = 0x28, + NM_NACK_WAIT = 0x29, + NM_NACK_NOTH_REPORT_EXIST = 0x2a, + NM_NACK_MEAS_NOTSUPP = 0x2b, + NM_NACK_MEAS_NOTSTART = 0x2c, +}; + +/* Section 9.4.1 */ +struct abis_nm_channel { + uint8_t attrib; + uint8_t bts_port; + uint8_t timeslot; + uint8_t subslot; +} __attribute__ ((packed)); + +/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */ +enum abis_bs11_objtype { + BS11_OBJ_ALCO = 0x01, + BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ + BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ + BS11_OBJ_CCLK = 0x04, + BS11_OBJ_GPSU = 0x06, + BS11_OBJ_LI = 0x07, + BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ +}; + +enum abis_bs11_trx_power { + BS11_TRX_POWER_GSM_2W = 0x06, + BS11_TRX_POWER_GSM_250mW= 0x07, + BS11_TRX_POWER_GSM_80mW = 0x08, + BS11_TRX_POWER_GSM_30mW = 0x09, + BS11_TRX_POWER_DCS_3W = 0x0a, + BS11_TRX_POWER_DCS_1W6 = 0x0b, + BS11_TRX_POWER_DCS_500mW= 0x0c, + BS11_TRX_POWER_DCS_160mW= 0x0d, +}; + +enum abis_bs11_li_pll_mode { + BS11_LI_PLL_LOCKED = 2, + BS11_LI_PLL_STANDALONE = 3, +}; + +enum abis_bs11_line_cfg { + BS11_LINE_CFG_STAR = 0x00, + BS11_LINE_CFG_MULTIDROP = 0x01, + BS11_LINE_CFG_LOOP = 0x02, +}; + +enum abis_bs11_phase { + BS11_STATE_SOFTWARE_RQD = 0x01, + BS11_STATE_LOAD_SMU_INTENDED = 0x11, + BS11_STATE_LOAD_SMU_SAFETY = 0x21, + BS11_STATE_LOAD_FAILED = 0x31, + BS11_STATE_LOAD_DIAGNOSTIC = 0x41, + BS11_STATE_WARM_UP = 0x51, + BS11_STATE_WARM_UP_2 = 0x52, + BS11_STATE_WAIT_MIN_CFG = 0x62, + BS11_STATE_MAINTENANCE = 0x72, + BS11_STATE_LOAD_MBCCU = 0x92, + BS11_STATE_WAIT_MIN_CFG_2 = 0xA2, + BS11_STATE_NORMAL = 0x03, + BS11_STATE_ABIS_LOAD = 0x13, +}; + +enum abis_nm_ipacc_test_no { + NM_IPACC_TESTNO_RLOOP_ANT = 0x01, + NM_IPACC_TESTNO_RLOOP_XCVR = 0x02, + NM_IPACC_TESTNO_FUNC_OBJ = 0x03, + NM_IPACC_TESTNO_CHAN_USAGE = 0x40, + NM_IPACC_TESTNO_BCCH_CHAN_USAGE = 0x41, + NM_IPACC_TESTNO_FREQ_SYNC = 0x42, + NM_IPACC_TESTNO_BCCH_INFO = 0x43, + NM_IPACC_TESTNO_TX_BEACON = 0x44, + NM_IPACC_TESTNO_SYSINFO_MONITOR = 0x45, + NM_IPACC_TESTNO_BCCCH_MONITOR = 0x46, +}; + +/* first byte after length inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_test_res { + NM_IPACC_TESTRES_SUCCESS = 0, + NM_IPACC_TESTRES_TIMEOUT = 1, + NM_IPACC_TESTRES_NO_CHANS = 2, + NM_IPACC_TESTRES_PARTIAL = 3, + NM_IPACC_TESTRES_STOPPED = 4, +}; + +/* internal IE inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_testres_ie { + NM_IPACC_TR_IE_FREQ_ERR_LIST = 3, + NM_IPACC_TR_IE_CHAN_USAGE = 4, + NM_IPACC_TR_IE_BCCH_INFO = 6, + NM_IPACC_TR_IE_RESULT_DETAILS = 8, + NM_IPACC_TR_IE_FREQ_ERR = 18, +}; + +enum ipac_eie { + NM_IPAC_EIE_ARFCN_WHITE = 0x01, + NM_IPAC_EIE_ARFCH_BLACK = 0x02, + NM_IPAC_EIE_FREQ_ERR_LIST = 0x03, + NM_IPAC_EIE_CHAN_USE_LIST = 0x04, + NM_IPAC_EIE_BCCH_INFO_TYPE = 0x05, + NM_IPAC_EIE_BCCH_INFO = 0x06, + NM_IPAC_EIE_CONFIG = 0x07, + NM_IPAC_EIE_RES_DETAILS = 0x08, + NM_IPAC_EIE_RXLEV_THRESH = 0x09, + NM_IPAC_EIE_FREQ_SYNC_OPTS = 0x0a, + NM_IPAC_EIE_MAC_ADDR = 0x0b, + NM_IPAC_EIE_HW_SW_COMPAT_NR = 0x0c, + NM_IPAC_EIE_MANUF_SER_NR = 0x0d, + NM_IPAC_EIE_OEM_ID = 0x0e, + NM_IPAC_EIE_DATE_TIME_MANUF = 0x0f, + NM_IPAC_EIE_DATE_TIME_CALIB = 0x10, + NM_IPAC_EIE_BEACON_INFO = 0x11, + NM_IPAC_EIE_FREQ_ERR = 0x12, + /* FIXME */ + NM_IPAC_EIE_FREQ_BANDS = 0x1c, + NM_IPAC_EIE_MAX_TA = 0x1d, + NM_IPAC_EIE_CIPH_ALGOS = 0x1e, + NM_IPAC_EIE_CHAN_TYPES = 0x1f, + NM_IPAC_EIE_CHAN_MODES = 0x20, + NM_IPAC_EIE_GPRS_CODING = 0x21, + NM_IPAC_EIE_RTP_FEATURES = 0x22, + NM_IPAC_EIE_RSL_FEATURES = 0x23, + NM_IPAC_EIE_BTS_HW_CLASS = 0x24, + NM_IPAC_EIE_BTS_ID = 0x25, +}; + +enum ipac_bcch_info_type { + IPAC_BINF_RXLEV = (1 << 8), + IPAC_BINF_RXQUAL = (1 << 9), + IPAC_BINF_FREQ_ERR_QUAL = (1 << 10), + IPAC_BINF_FRAME_OFFSET = (1 << 11), + IPAC_BINF_FRAME_NR_OFFSET = (1 << 12), + IPAC_BINF_BSIC = (1 << 13), + IPAC_BINF_CGI = (1 << 14), + IPAC_BINF_NEIGH_BA_SI2 = (1 << 15), + IPAC_BINF_NEIGH_BA_SI2bis = (1 << 0), + IPAC_BINF_NEIGH_BA_SI2ter = (1 << 1), + IPAC_BINF_CELL_ALLOC = (1 << 2), +}; + +#endif /* PROTO_GSM_12_21_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h new file mode 100644 index 00000000..27925725 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/ipaccess.h @@ -0,0 +1,93 @@ +#ifndef _OSMO_PROTO_IPACCESS_H +#define _OSMO_PROTO_IPACCESS_H + +#include <stdint.h> + +#define IPA_TCP_PORT_OML 3002 +#define IPA_TCP_PORT_RSL 3003 + +struct ipaccess_head { + uint16_t len; /* network byte order */ + uint8_t proto; + uint8_t data[0]; +} __attribute__ ((packed)); + +struct ipaccess_head_ext { + uint8_t proto; + uint8_t data[0]; +} __attribute__ ((packed)); + +enum ipaccess_proto { + IPAC_PROTO_RSL = 0x00, + IPAC_PROTO_IPACCESS = 0xfe, + IPAC_PROTO_SCCP = 0xfd, + IPAC_PROTO_OML = 0xff, + + + /* OpenBSC extensions */ + IPAC_PROTO_OSMO = 0xee, + IPAC_PROTO_MGCP_OLD = 0xfc, +}; + +enum ipaccess_proto_ext { + IPAC_PROTO_EXT_CTRL = 0x00, + IPAC_PROTO_EXT_MGCP = 0x01, + IPAC_PROTO_EXT_LAC = 0x02, +}; + +enum ipaccess_msgtype { + IPAC_MSGT_PING = 0x00, + IPAC_MSGT_PONG = 0x01, + IPAC_MSGT_ID_GET = 0x04, + IPAC_MSGT_ID_RESP = 0x05, + IPAC_MSGT_ID_ACK = 0x06, + + /* OpenBSC extension */ + IPAC_MSGT_SCCP_OLD = 0xff, +}; + +enum ipaccess_id_tags { + IPAC_IDTAG_SERNR = 0x00, + IPAC_IDTAG_UNITNAME = 0x01, + IPAC_IDTAG_LOCATION1 = 0x02, + IPAC_IDTAG_LOCATION2 = 0x03, + IPAC_IDTAG_EQUIPVERS = 0x04, + IPAC_IDTAG_SWVERSION = 0x05, + IPAC_IDTAG_IPADDR = 0x06, + IPAC_IDTAG_MACADDR = 0x07, + IPAC_IDTAG_UNIT = 0x08, +}; + +/* + * Firmware specific header + */ +struct sdp_firmware { + char magic[4]; + char more_magic[2]; + uint16_t more_more_magic; + uint32_t header_length; + uint32_t file_length; + char sw_part[20]; + char text1[64]; + char time[12]; + char date[14]; + char text2[10]; + char version[20]; + uint16_t table_offset; + /* stuff i don't know */ +} __attribute__((packed)); + +struct sdp_header_entry { + uint16_t something1; + char text1[64]; + char time[12]; + char date[14]; + char text2[10]; + char version[20]; + uint32_t length; + uint32_t addr1; + uint32_t addr2; + uint32_t start; +} __attribute__((packed)); + +#endif /* _OSMO_PROTO_IPACCESS_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/rsl.h b/src/shared/libosmocore/include/osmocom/gsm/rsl.h new file mode 100644 index 00000000..7e46330f --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/rsl.h @@ -0,0 +1,41 @@ +#ifndef _OSMOCORE_RSL_H +#define _OSMOCORE_RSL_H + +#include <stdint.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type); + +void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type); + +extern const struct tlv_definition rsl_att_tlvdef; +#define rsl_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) + +/* encode channel number as per Section 9.3.1 */ +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot); +/* decode channel number as per Section 9.3.1 */ +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot); +/* Turns channel number into a string */ +const char *rsl_chan_nr_str(uint8_t chan_nr); + + +const char *rsl_err_name(uint8_t err); +const char *rsl_rlm_cause_name(uint8_t err); + +/* Section 3.3.2.3 TS 05.02. I think this looks like a table */ +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); + +/* Push a RSL RLL header */ +void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); + +/* Push a RSL RLL header with L3_INFO IE */ +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); + +/* Allocate msgb and fill with simple RSL RLL header */ +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); +#endif /* _OSMOCORE_RSL_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h b/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h new file mode 100644 index 00000000..415509dc --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/rxlev_stat.h @@ -0,0 +1,22 @@ +#ifndef _OSMOCORE_RXLEV_STATS_H +#define _OSMOCORE_RXLEV_STATS_H + +#define NUM_RXLEVS 32 +#define NUM_ARFCNS 1024 + +struct rxlev_stats { + /* the maximum number of ARFCN's is 1024, and there are 32 RxLevels, + * so in we keep one 1024bit-bitvec for each RxLev */ + uint8_t rxlev_buckets[NUM_RXLEVS][NUM_ARFCNS/8]; +}; + +void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev); + +/* get the next ARFCN that has the specified Rxlev */ +int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn); + +void rxlev_stat_reset(struct rxlev_stats *st); + +void rxlev_stat_dump(const struct rxlev_stats *st); + +#endif /* _OSMOCORE_RXLEV_STATS_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h new file mode 100644 index 00000000..a66f3f19 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/sysinfo.h @@ -0,0 +1,40 @@ +#ifndef _OSMO_GSM_SYSINFO_H +#define _OSMO_GSM_SYSINFO_H + +#include <osmocom/gsm/protocol/gsm_04_08.h> + +enum osmo_sysinfo_type { + SYSINFO_TYPE_NONE, + SYSINFO_TYPE_1, + SYSINFO_TYPE_2, + SYSINFO_TYPE_3, + SYSINFO_TYPE_4, + SYSINFO_TYPE_5, + SYSINFO_TYPE_6, + SYSINFO_TYPE_7, + SYSINFO_TYPE_8, + SYSINFO_TYPE_9, + SYSINFO_TYPE_10, + SYSINFO_TYPE_13, + SYSINFO_TYPE_16, + SYSINFO_TYPE_17, + SYSINFO_TYPE_18, + SYSINFO_TYPE_19, + SYSINFO_TYPE_20, + SYSINFO_TYPE_2bis, + SYSINFO_TYPE_2ter, + SYSINFO_TYPE_2quater, + SYSINFO_TYPE_5bis, + SYSINFO_TYPE_5ter, + /* FIXME all the various bis and ter */ + _MAX_SYSINFO_TYPE +}; + +typedef uint8_t sysinfo_buf_t[GSM_MACBLOCK_LEN]; + +extern const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE]; + +uint8_t gsm_sitype2rsl(enum osmo_sysinfo_type si_type); +enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si); + +#endif /* _OSMO_GSM_SYSINFO_H */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/tlv.h b/src/shared/libosmocore/include/osmocom/gsm/tlv.h new file mode 100644 index 00000000..552af2bd --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/gsm/tlv.h @@ -0,0 +1,260 @@ +#ifndef _TLV_H +#define _TLV_H + +#include <stdint.h> +#include <string.h> + +#include <osmocom/core/msgb.h> + +/* Terminology / wording + tag length value (in bits) + + V - - 8 + LV - 8 N * 8 + TLV 8 8 N * 8 + TL16V 8 16 N * 8 + TLV16 8 8 N * 16 + TvLV 8 8/16 N * 8 + +*/ + +#define LV_GROSS_LEN(x) (x+1) +#define TLV_GROSS_LEN(x) (x+2) +#define TLV16_GROSS_LEN(x) ((2*x)+2) +#define TL16V_GROSS_LEN(x) (x+3) +#define L16TV_GROSS_LEN(x) (x+3) + +#define TVLV_MAX_ONEBYTE 0x7f + +static inline uint16_t TVLV_GROSS_LEN(uint16_t len) +{ + if (len <= TVLV_MAX_ONEBYTE) + return TLV_GROSS_LEN(len); + else + return TL16V_GROSS_LEN(len); +} + +/* TLV generation */ + +static inline uint8_t *lv_put(uint8_t *buf, uint8_t len, + const uint8_t *val) +{ + *buf++ = len; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len, + const uint8_t *val) +{ + *buf++ = tag; + *buf++ = len; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len, + const uint16_t *val) +{ + *buf++ = tag; + *buf++ = len; + memcpy(buf, val, len*2); + return buf + len*2; +} + +static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + *buf++ = tag; + *buf++ = len >> 8; + *buf++ = len & 0xff; + memcpy(buf, val, len); + return buf + len*2; +} + +static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *ret; + + if (len <= TVLV_MAX_ONEBYTE) { + ret = tlv_put(buf, tag, len, val); + buf[1] |= 0x80; + } else + ret = tl16v_put(buf, tag, len, val); + + return ret; +} + +static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val) +{ + uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len)); + return tlv16_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len)); + return tl16v_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len)); + return tvlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len)); + + *buf++ = len >> 8; + *buf++ = len & 0xff; + *buf++ = tag; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *v_put(uint8_t *buf, uint8_t val) +{ + *buf++ = val; + return buf; +} + +static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, + uint8_t val) +{ + *buf++ = tag; + *buf++ = val; + return buf; +} + +static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag, + unsigned int len, const uint8_t *val) +{ + *buf++ = tag; + memcpy(buf, val, len); + return buf + len; +} + +/* 'val' is still in host byte order! */ +static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, + uint16_t val) +{ + *buf++ = tag; + *buf++ = val >> 8; + *buf++ = val & 0xff; + return buf; +} + +static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len)); + return lv_put(buf, len, val); +} + +static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len)); + return tlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val) +{ + uint8_t *buf = msgb_put(msg, 2); + return tv_put(buf, tag, val); +} + +static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag, + unsigned int len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, 1+len); + return tv_fixed_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val) +{ + uint8_t *buf = msgb_put(msg, 1); + return v_put(buf, val); +} + +static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val) +{ + uint8_t *buf = msgb_put(msg, 3); + return tv16_put(buf, tag, val); +} + +static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len)); + return tlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val) +{ + uint8_t *buf = msgb_push(msg, 2); + return tv_put(buf, tag, val); +} + +static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val) +{ + uint8_t *buf = msgb_push(msg, 3); + return tv16_put(buf, tag, val); +} + +static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len)); + return tvlv_put(buf, tag, len, val); +} + +/* TLV parsing */ + +struct tlv_p_entry { + uint16_t len; + const uint8_t *val; +}; + +enum tlv_type { + TLV_TYPE_NONE, + TLV_TYPE_FIXED, + TLV_TYPE_T, + TLV_TYPE_TV, + TLV_TYPE_TLV, + TLV_TYPE_TL16V, + TLV_TYPE_TvLV, + TLV_TYPE_SINGLE_TV +}; + +struct tlv_def { + enum tlv_type type; + uint8_t fixed_len; +}; + +struct tlv_definition { + struct tlv_def def[0xff]; +}; + +struct tlv_parsed { + struct tlv_p_entry lv[0xff]; +}; + +extern struct tlv_definition tvlv_att_def; + +int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, + const struct tlv_definition *def, + const uint8_t *buf, int buf_len); +int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, + const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2); +/* take a master (src) tlvdev and fill up all empty slots in 'dst' */ +void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src); + +#define TLVP_PRESENT(x, y) ((x)->lv[y].val) +#define TLVP_LEN(x, y) (x)->lv[y].len +#define TLVP_VAL(x, y) (x)->lv[y].val + +#endif /* _TLV_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am new file mode 100644 index 00000000..83d00107 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/Makefile.am @@ -0,0 +1,4 @@ +osmovty_HEADERS = buffer.h command.h vector.h vty.h \ + telnet_interface.h logging.h misc.h + +osmovtydir = $(includedir)/osmocom/vty diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h new file mode 100644 index 00000000..c9467a91 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/buffer.h @@ -0,0 +1,102 @@ +/* + * Buffering to output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_BUFFER_H +#define _ZEBRA_BUFFER_H + +#include <sys/types.h> + +/* Create a new buffer. Memory will be allocated in chunks of the given + size. If the argument is 0, the library will supply a reasonable + default size suitable for buffering socket I/O. */ +struct buffer *buffer_new(void *ctx, size_t); + +/* Free all data in the buffer. */ +void buffer_reset(struct buffer *); + +/* This function first calls buffer_reset to release all buffered data. + Then it frees the struct buffer itself. */ +void buffer_free(struct buffer *); + +/* Add the given data to the end of the buffer. */ +extern void buffer_put(struct buffer *, const void *, size_t); +/* Add a single character to the end of the buffer. */ +extern void buffer_putc(struct buffer *, u_char); +/* Add a NUL-terminated string to the end of the buffer. */ +extern void buffer_putstr(struct buffer *, const char *); + +/* Combine all accumulated (and unflushed) data inside the buffer into a + single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note + that this function does not alter the state of the buffer, so the data + is still inside waiting to be flushed. */ +char *buffer_getstr(struct buffer *); + +/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */ +int buffer_empty(struct buffer *); + +typedef enum { + /* An I/O error occurred. The buffer should be destroyed and the + file descriptor should be closed. */ + BUFFER_ERROR = -1, + + /* The data was written successfully, and the buffer is now empty + (there is no pending data waiting to be flushed). */ + BUFFER_EMPTY = 0, + + /* There is pending data in the buffer waiting to be flushed. Please + try flushing the buffer when select indicates that the file descriptor + is writeable. */ + BUFFER_PENDING = 1 +} buffer_status_t; + +/* Try to write this data to the file descriptor. Any data that cannot + be written immediately is added to the buffer queue. */ +extern buffer_status_t buffer_write(struct buffer *, int fd, + const void *, size_t); + +/* This function attempts to flush some (but perhaps not all) of + the queued data to the given file descriptor. */ +extern buffer_status_t buffer_flush_available(struct buffer *, int fd); + +/* The following 2 functions (buffer_flush_all and buffer_flush_window) + are for use in lib/vty.c only. They should not be used elsewhere. */ + +/* Call buffer_flush_available repeatedly until either all data has been + flushed, or an I/O error has been encountered, or the operation would + block. */ +extern buffer_status_t buffer_flush_all(struct buffer *, int fd); + +/* Attempt to write enough data to the given fd to fill a window of the + given width and height (and remove the data written from the buffer). + + If !no_more, then a message saying " --More-- " is appended. + If erase is true, then first overwrite the previous " --More-- " message + with spaces. + + Any write error (including EAGAIN or EINTR) will cause this function + to return -1 (because the logic for handling the erase and more features + is too complicated to retry the write later). +*/ +extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width, + int height, int erase, int no_more); + +#endif /* _ZEBRA_BUFFER_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h new file mode 100644 index 00000000..caf04142 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/command.h @@ -0,0 +1,350 @@ +/* + * Zebra configuration command interface routine + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _ZEBRA_COMMAND_H +#define _ZEBRA_COMMAND_H + +#include <stdio.h> +#include <sys/types.h> +#include "vector.h" +#include "vty.h" + +/* Host configuration variable */ +struct host { + /* Host name of this router. */ + char *name; + + /* Password for vty interface. */ + char *password; + char *password_encrypt; + + /* Enable password */ + char *enable; + char *enable_encrypt; + + /* System wide terminal lines. */ + int lines; + + /* Log filename. */ + char *logfile; + + /* config file name of this host */ + char *config; + + /* Flags for services */ + int advanced; + int encrypt; + + /* Banner configuration. */ + const char *motd; + char *motdfile; + + const struct vty_app_info *app_info; +}; + +/* There are some command levels which called from command node. */ +enum node_type { + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ + CFG_LOG_NODE, /* Configure the logging */ + + VTY_NODE, /* Vty node. */ + + _LAST_OSMOVTY_NODE +}; + +/* Node which has some commands and prompt string and configuration + function pointer . */ +struct cmd_node { + /* Node index. */ + enum node_type node; + + /* Prompt character at vty interface. */ + const char *prompt; + + /* Is this node's configuration goes to vtysh ? */ + int vtysh; + + /* Node's configuration write function */ + int (*func) (struct vty *); + + /* Vector of this node's command list. */ + vector cmd_vector; +}; + +enum { + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, +}; + +/* Structure of command element. */ +struct cmd_element { + const char *string; /* Command specification by string. */ + int (*func) (struct cmd_element *, struct vty *, int, const char *[]); + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + vector strvec; /* Pointing out each description vector. */ + unsigned int cmdsize; /* Command index count. */ + char *config; /* Configuration string */ + vector subconfig; /* Sub configuration string */ + u_char attr; /* Command attributes */ +}; + +/* Command description structure. */ +struct desc { + const char *cmd; /* Command string. */ + const char *str; /* Command's description. */ +}; + +/* Return value of the commands. */ +#define CMD_SUCCESS 0 +#define CMD_WARNING 1 +#define CMD_ERR_NO_MATCH 2 +#define CMD_ERR_AMBIGUOUS 3 +#define CMD_ERR_INCOMPLETE 4 +#define CMD_ERR_EXEED_ARGC_MAX 5 +#define CMD_ERR_NOTHING_TODO 6 +#define CMD_COMPLETE_FULL_MATCH 7 +#define CMD_COMPLETE_MATCH 8 +#define CMD_COMPLETE_LIST_MATCH 9 +#define CMD_SUCCESS_DAEMON 10 + +/* Argc max counts. */ +#define CMD_ARGC_MAX 25 + +/* Turn off these macros when uisng cpp with extract.pl */ +#ifndef VTYSH_EXTRACT_PL + +/* helper defines for end-user DEFUN* macros */ +#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + static struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +/* global (non static) cmd_element */ +#define gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ + struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +#define DEFUN_CMD_FUNC_DECL(funcname) \ + static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \ + +#define DEFUN_CMD_FUNC_TEXT(funcname) \ + static int funcname \ + (struct cmd_element *self, struct vty *vty, int argc, const char *argv[]) + +/* DEFUN for vty command interafce. Little bit hacky ;-). */ +#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* global (non static) cmd_element */ +#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \ + +/* DEFUN_NOSH for commands that vtysh should ignore */ +#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFUN(funcname, cmdname, cmdstr, helpstr) + +/* DEFSH for vtysh. */ +#define DEFSH(daemon, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \ + +/* DEFUN + DEFSH */ +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +/* DEFUN + DEFSH with attributes */ +#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ + DEFUN_CMD_FUNC_TEXT(funcname) + +#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) + +#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) + +/* ALIAS macro which define existing command's alias. */ +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +/* global (non static) cmd_element */ +#define gALIAS(funcname, cmdname, cmdstr, helpstr) \ + gDEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) + +#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) + +#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0) + +#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0) + +#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) + +#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon) + +#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon) + +#endif /* VTYSH_EXTRACT_PL */ + +/* Some macroes */ +#define CMD_OPTION(S) ((S[0]) == '[') +#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) +#define CMD_VARARG(S) ((S[0]) == '.') +#define CMD_RANGE(S) ((S[0] == '<')) + +#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) +#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) +#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) +#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) + +/* Common descriptions. */ +#define SHOW_STR "Show running system information\n" +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define NO_STR "Negate a command or set its defaults\n" +#define CLEAR_STR "Reset functions\n" +#define RIP_STR "RIP information\n" +#define BGP_STR "BGP information\n" +#define OSPF_STR "OSPF information\n" +#define NEIGHBOR_STR "Specify neighbor router\n" +#define DEBUG_STR "Debugging functions (see also 'undebug')\n" +#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n" +#define ROUTER_STR "Enable a routing process\n" +#define AS_STR "AS number\n" +#define MBGP_STR "MBGP information\n" +#define MATCH_STR "Match values from routing table\n" +#define SET_STR "Set values in destination routing protocol\n" +#define OUT_STR "Filter outgoing routing updates\n" +#define IN_STR "Filter incoming routing updates\n" +#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n" +#define OSPF6_NUMBER_STR "Specify by number\n" +#define INTERFACE_STR "Interface infomation\n" +#define IFNAME_STR "Interface name(e.g. ep0)\n" +#define IP6_STR "IPv6 Information\n" +#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" +#define OSPF6_ROUTER_STR "Enable a routing process\n" +#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" +#define SECONDS_STR "<1-65535> Seconds\n" +#define ROUTE_STR "Routing Table\n" +#define PREFIX_LIST_STR "Build a prefix list\n" +#define OSPF6_DUMP_TYPE_LIST \ +"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +#define ISIS_STR "IS-IS information\n" +#define AREA_TAG_STR "[area tag]\n" + +#define CONF_BACKUP_EXT ".sav" + +/* IPv4 only machine should not accept IPv6 address for peer's IP + address. So we replace VTY command string like below. */ +#ifdef HAVE_IPV6 +#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " +#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n" +#else +#define NEIGHBOR_CMD "neighbor A.B.C.D " +#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " +#define NEIGHBOR_ADDR_STR "Neighbor address\n" +#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " +#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" +#endif /* HAVE_IPV6 */ + +/* Prototypes. */ +void install_node(struct cmd_node *, int (*)(struct vty *)); +void install_default(enum node_type); +void install_element(enum node_type, struct cmd_element *); +void install_element_ve(struct cmd_element *cmd); +void sort_node(); + +/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated + string with a space between each element (allocated using + XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ +char *argv_concat(const char **argv, int argc, int shift); + +vector cmd_make_strvec(const char *); +void cmd_free_strvec(vector); +vector cmd_describe_command(); +char **cmd_complete_command(); +const char *cmd_prompt(enum node_type); +int config_from_file(struct vty *, FILE *); +enum node_type node_parent(enum node_type); +int cmd_execute_command(vector, struct vty *, struct cmd_element **, int); +int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **); +void config_replace_string(struct cmd_element *, char *, ...); +void cmd_init(int); + +/* Export typical functions. */ +extern struct cmd_element config_exit_cmd; +extern struct cmd_element config_help_cmd; +extern struct cmd_element config_list_cmd; +char *host_config_file(); +void host_config_set(const char *); + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(int print_copyright); + +extern void *tall_vty_cmd_ctx; + +#endif /* _ZEBRA_COMMAND_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h new file mode 100644 index 00000000..e0011bf9 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/logging.h @@ -0,0 +1,12 @@ +#ifndef _VTY_LOGGING_H +#define _VTY_LOGGING_H + +#define LOGGING_STR "Configure log message to this terminal\n" +#define FILTER_STR "Filter log messages\n" + +struct log_info; +void logging_vty_add_cmds(const struct log_info *cat); +struct vty; +struct log_target *osmo_log_vty2tgt(struct vty *vty); + +#endif /* _VTY_LOGGING_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/misc.h b/src/shared/libosmocore/include/osmocom/vty/misc.h new file mode 100644 index 00000000..707f82fa --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/misc.h @@ -0,0 +1,10 @@ +#ifndef OSMO_VTY_MISC_H +#define OSMO_VTY_MISC_H + +#include <osmocom/vty/vty.h> +#include <osmocom/core/rate_ctr.h> + +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h new file mode 100644 index 00000000..1d8055e7 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h @@ -0,0 +1,40 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef TELNET_INTERFACE_H +#define TELNET_INTERFACE_H + +#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> + +#include <osmocom/vty/vty.h> + +struct telnet_connection { + struct llist_head entry; + void *priv; + struct osmo_fd fd; + struct vty *vty; + struct log_target *dbg; +}; + + +int telnet_init(void *tall_ctx, void *priv, int port); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h new file mode 100644 index 00000000..22a184d6 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/vector.h @@ -0,0 +1,64 @@ +/* + * Generic vector interface header. + * Copyright (C) 1997, 98 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_VECTOR_H +#define _ZEBRA_VECTOR_H + +/* struct for vector */ +struct _vector { + unsigned int active; /* number of active slots */ + unsigned int alloced; /* number of allocated slot */ + void **index; /* index to data */ +}; +typedef struct _vector *vector; + +#define VECTOR_MIN_SIZE 1 + +/* (Sometimes) usefull macros. This macro convert index expression to + array expression. */ +/* Reference slot at given index, caller must ensure slot is active */ +#define vector_slot(V,I) ((V)->index[(I)]) +/* Number of active slots. + * Note that this differs from vector_count() as it the count returned + * will include any empty slots + */ +#define vector_active(V) ((V)->active) + +/* Prototypes. */ +vector vector_init(unsigned int size); +void vector_ensure(vector v, unsigned int num); +int vector_empty_slot(vector v); +int vector_set(vector v, void *val); +int vector_set_index(vector v, unsigned int i, void *val); +void vector_unset(vector v, unsigned int i); +unsigned int vector_count(vector v); +void vector_only_wrapper_free(vector v); +void vector_only_index_free(void *index); +void vector_free(vector v); +vector vector_copy(vector v); + +void *vector_lookup(vector, unsigned int); +void *vector_lookup_ensure(vector, unsigned int); + +extern void *tall_vty_vec_ctx; + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h new file mode 100644 index 00000000..8035585d --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/vty.h @@ -0,0 +1,162 @@ +#ifndef _VTY_H +#define _VTY_H + +#include <stdio.h> +#include <stdarg.h> + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define VTY_PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +/* Does the I/O error indicate that the operation should be retried later? */ +#define ERRNO_IO_RETRY(EN) \ + (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +/* Vty read buffer size. */ +#define VTY_READ_BUFSIZ 512 + +#define VTY_BUFSIZ 512 +#define VTY_MAXHIST 20 + +/* Vty events */ +enum event { + VTY_SERV, + VTY_READ, + VTY_WRITE, + VTY_CLOSED, + VTY_TIMEOUT_RESET, +#ifdef VTYSH + VTYSH_SERV, + VTYSH_READ, + VTYSH_WRITE +#endif /* VTYSH */ +}; + +struct vty { + FILE *file; + + /* private data, specified by creator */ + void *priv; + + /* File descripter of this vty. */ + int fd; + + /* Is this vty connect to file or not */ + enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type; + + /* Node status of this vty */ + int node; + + /* Failure count */ + int fail; + + /* Output buffer. */ + struct buffer *obuf; + + /* Command input buffer */ + char *buf; + + /* Command cursor point */ + int cp; + + /* Command length */ + int length; + + /* Command max length. */ + int max; + + /* Histry of command */ + char *hist[VTY_MAXHIST]; + + /* History lookup current point */ + int hp; + + /* History insert end point */ + int hindex; + + /* For current referencing point of interface, route-map, + access-list etc... */ + void *index; + + /* For multiple level index treatment such as key chain and key. */ + void *index_sub; + + /* For escape character. */ + unsigned char escape; + + /* Current vty status. */ + enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status; + + /* IAC handling: was the last character received the IAC + * (interpret-as-command) escape character (and therefore the next + * character will be the command code)? Refer to Telnet RFC 854. */ + unsigned char iac; + + /* IAC SB (option subnegotiation) handling */ + unsigned char iac_sb_in_progress; + /* At the moment, we care only about the NAWS (window size) negotiation, + * and that requires just a 5-character buffer (RFC 1073): + * <NAWS char> <16-bit width> <16-bit height> */ +#define TELNET_NAWS_SB_LEN 5 + unsigned char sb_buf[TELNET_NAWS_SB_LEN]; + /* How many subnegotiation characters have we received? We just drop + * those that do not fit in the buffer. */ + size_t sb_len; + + /* Window width/height. */ + int width; + int height; + + /* Configure lines. */ + int lines; + + int monitor; + + /* In configure mode. */ + int config; +}; + +/* Small macro to determine newline is newline only or linefeed needed. */ +#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n") + +static inline char *vty_newline(struct vty *vty) +{ + return VTY_NEWLINE; +} + +struct vty_app_info { + const char *name; + const char *version; + const char *copyright; + void *tall_ctx; + enum node_type (*go_parent_cb)(struct vty *vty); + int (*is_config_node)(struct vty *vty, int node); +}; + +/* Prototypes. */ +void vty_init(struct vty_app_info *app_info); +int vty_read_config_file(const char *file_name, void *priv); +void vty_init_vtysh (void); +void vty_reset (void); +struct vty *vty_new (void); +struct vty *vty_create (int vty_sock, void *priv); +int vty_out (struct vty *, const char *, ...) VTY_PRINTF_ATTRIBUTE(2, 3); +int vty_out_newline(struct vty *); +int vty_read(struct vty *vty); +//void vty_time_print (struct vty *, int); +void vty_close (struct vty *); +char *vty_get_cwd (void); +void vty_log (const char *level, const char *proto, const char *fmt, va_list); +int vty_config_lock (struct vty *); +int vty_config_unlock (struct vty *); +int vty_shell (struct vty *); +int vty_shell_serv (struct vty *); +void vty_hello (struct vty *); +void *vty_current_index(struct vty *); +int vty_current_node(struct vty *vty); + +extern void *tall_vty_ctx; +#endif diff --git a/src/shared/libosmocore/libosmocodec.pc.in b/src/shared/libosmocore/libosmocodec.pc.in new file mode 100644 index 00000000..3030230b --- /dev/null +++ b/src/shared/libosmocore/libosmocodec.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom Codec related utilities Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmocodec +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in new file mode 100644 index 00000000..7c298693 --- /dev/null +++ b/src/shared/libosmocore/libosmocore.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom Core Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmocore +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/libosmogsm.pc.in b/src/shared/libosmocore/libosmogsm.pc.in new file mode 100644 index 00000000..753bb3a1 --- /dev/null +++ b/src/shared/libosmocore/libosmogsm.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom GSM Core Library +Description: GSM Core Library +Version: @VERSION@ +Libs: -L${libdir} -losmogsm +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in new file mode 100644 index 00000000..2cc0b5f8 --- /dev/null +++ b/src/shared/libosmocore/libosmovty.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom VTY Interface Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmovty +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/m4/DUMMY b/src/shared/libosmocore/m4/DUMMY new file mode 100644 index 00000000..fda557ad --- /dev/null +++ b/src/shared/libosmocore/m4/DUMMY @@ -0,0 +1 @@ +Dummply placeholder. diff --git a/src/shared/libosmocore/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am new file mode 100644 index 00000000..1ae3cff8 --- /dev/null +++ b/src/shared/libosmocore/src/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS=. vty codec gsm + +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=2:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +lib_LTLIBRARIES = libosmocore.la + +libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ + bitvec.c statistics.c \ + write_queue.c utils.c socket.c \ + logging.c logging_syslog.c rate_ctr.c \ + gsmtap_util.c crc16.c panic.c backtrace.c \ + process.c conv.c application.c + +if ENABLE_PLUGIN +libosmocore_la_SOURCES += plugin.c +libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -ldl +else +libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) +endif + +if ENABLE_TALLOC +libosmocore_la_SOURCES += talloc.c +endif + +if ENABLE_MSGFILE +libosmocore_la_SOURCES += msgfile.c +endif diff --git a/src/shared/libosmocore/src/application.c b/src/shared/libosmocore/src/application.c new file mode 100644 index 00000000..96b4204e --- /dev/null +++ b/src/shared/libosmocore/src/application.c @@ -0,0 +1,49 @@ +/* Utility functions to setup applications */ +/* + * (C) 2011 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/core/application.h> +#include <osmocom/core/logging.h> + +#include <signal.h> + +struct log_target *osmo_stderr_target; + +void osmo_init_ignore_signals(void) +{ + /* Signals that by default would terminate */ + signal(SIGPIPE, SIG_IGN); + signal(SIGALRM, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGIO, SIG_IGN); +} + +int osmo_init_logging(const struct log_info *log_info) +{ + log_init(log_info); + osmo_stderr_target = log_target_create_stderr(); + if (!osmo_stderr_target) + return -1; + + log_add_target(osmo_stderr_target); + log_set_all_filter(osmo_stderr_target, 1); + return 0; +} diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c new file mode 100644 index 00000000..8281fada --- /dev/null +++ b/src/shared/libosmocore/src/backtrace.c @@ -0,0 +1,50 @@ +/* + * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Nico Golde <nico@ngolde.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <osmocom/core/utils.h> +#include "config.h" + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +void osmo_generate_backtrace() +{ + int i, nptrs; + void *buffer[100]; + char **strings; + + nptrs = backtrace(buffer, ARRAY_SIZE(buffer)); + printf("backtrace() returned %d addresses\n", nptrs); + + strings = backtrace_symbols(buffer, nptrs); + if (!strings) + return; + + for (i = 1; i < nptrs; i++) + printf("%s\n", strings[i]); + + free(strings); +} +#endif diff --git a/src/shared/libosmocore/src/bits.c b/src/shared/libosmocore/src/bits.c new file mode 100644 index 00000000..ff5d176c --- /dev/null +++ b/src/shared/libosmocore/src/bits.c @@ -0,0 +1,98 @@ + +#include <stdint.h> + +#include <osmocom/core/bits.h> + +/* convert unpacked bits to packed bits, return length in bytes */ +int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits) +{ + unsigned int i; + uint8_t curbyte = 0; + pbit_t *outptr = out; + + for (i = 0; i < num_bits; i++) { + uint8_t bitnum = 7 - (i % 8); + + curbyte |= (in[i] << bitnum); + + if(i % 8 == 7){ + *outptr++ = curbyte; + curbyte = 0; + } + } + /* we have a non-modulo-8 bitcount */ + if (i % 8) + *outptr++ = curbyte; + + return outptr - out; +} + +/* convert packed bits to unpacked bits, return length in bytes */ +int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits) +{ + unsigned int i; + ubit_t *cur = out; + ubit_t *limit = out + num_bits; + + for (i = 0; i < (num_bits/8)+1; i++) { + pbit_t byte = in[i]; + *cur++ = (byte >> 7) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 6) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 5) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 4) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 3) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 2) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 1) & 1; + if (cur >= limit) + break; + *cur++ = (byte >> 0) & 1; + if (cur >= limit) + break; + } + return cur - out; +} + +/* convert unpacked bits to packed bits (extended options but slower), + * return length in bytes (max written ofs of output buffer + 1) */ +int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, + const ubit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + int i, op, bn; + for (i=0; i<num_bits; i++) { + op = out_ofs + i; + bn = lsb_mode ? (op&7) : (7-(op&7)); + if (in[in_ofs+i]) + out[op>>3] |= 1 << bn; + else + out[op>>3] &= ~(1 << bn); + } + return ((out_ofs + num_bits - 1) >> 3) + 1; +} + +/* convert packed bits to unpacked bits (extended options but slower), + * return length in bytes (max written ofs of output buffer + 1) */ +int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, + const pbit_t *in, unsigned int in_ofs, + unsigned int num_bits, int lsb_mode) +{ + int i, ip, bn; + for (i=0; i<num_bits; i++) { + ip = in_ofs + i; + bn = lsb_mode ? (ip&7) : (7-(ip&7)); + out[out_ofs+i] = !!(in[ip>>3] & (1<<bn)); + } + return out_ofs + num_bits; +} diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c new file mode 100644 index 00000000..4fd38349 --- /dev/null +++ b/src/shared/libosmocore/src/bitvec.c @@ -0,0 +1,233 @@ +/* bit vector utility routines */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <errno.h> +#include <stdint.h> + +#include <osmocom/core/bitvec.h> + +#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit) + +static inline unsigned int bytenum_from_bitnum(unsigned int bitnum) +{ + unsigned int bytenum = bitnum / 8; + + return bytenum; +} + +/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */ +static uint8_t bitval2mask(enum bit_value bit, uint8_t bitnum) +{ + int bitval; + + switch (bit) { + case ZERO: + bitval = (0 << bitnum); + break; + case ONE: + bitval = (1 << bitnum); + break; + case L: + bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum)); + break; + case H: + bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum)); + break; + default: + return 0; + } + return bitval; +} + +/* check if the bit is 0 or 1 for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + bitval = bitval2mask(ONE, bitnum); + + if (bv->data[bytenum] & bitval) + return ONE; + + return ZERO; +} + +/* check if the bit is L or H for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + bitval = bitval2mask(H, bitnum); + + if ((bv->data[bytenum] & (1 << bitnum)) == bitval) + return H; + + return L; +} + +/* get the Nth set bit inside the bit vector */ +unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n) +{ + unsigned int i, k = 0; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i) == ONE) { + k++; + if (k == n) + return i; + } + } + + return 0; +} + +/* set the bit at a given position inside a bitvec */ +int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr, + enum bit_value bit) +{ + unsigned int bytenum = bytenum_from_bitnum(bitnr); + unsigned int bitnum = 7 - (bitnr % 8); + uint8_t bitval; + + if (bytenum >= bv->data_len) + return -EINVAL; + + /* first clear the bit */ + bitval = bitval2mask(ONE, bitnum); + bv->data[bytenum] &= ~bitval; + + /* then set it to desired value */ + bitval = bitval2mask(bit, bitnum); + bv->data[bytenum] |= bitval; + + return 0; +} + +/* set the next bit inside a bitvec */ +int bitvec_set_bit(struct bitvec *bv, enum bit_value bit) +{ + int rc; + + rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit); + if (!rc) + bv->cur_bit++; + + return rc; +} + +/* get the next bit (low/high) inside a bitvec */ +int bitvec_get_bit_high(struct bitvec *bv) +{ + int rc; + + rc = bitvec_get_bit_pos_high(bv, bv->cur_bit); + if (rc >= 0) + bv->cur_bit++; + + return rc; +} + +/* set multiple bits (based on array of bitvals) at current pos */ +int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count) +{ + int i, rc; + + for (i = 0; i < count; i++) { + rc = bitvec_set_bit(bv, bits[i]); + if (rc) + return rc; + } + + return 0; +} + +/* set multiple bits (based on numeric value) at current pos */ +int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits) +{ + int i, rc; + + for (i = 0; i < num_bits; i++) { + int bit = 0; + if (ui & (1 << (num_bits - i - 1))) + bit = 1; + rc = bitvec_set_bit(bv, bit); + if (rc) + return rc; + } + + return 0; +} + +/* get multiple bits (based on numeric value) from current pos */ +int bitvec_get_uint(struct bitvec *bv, int num_bits) +{ + int i; + unsigned int ui = 0; + + for (i = 0; i < num_bits; i++) { + int bit = bitvec_get_bit_pos(bv, bv->cur_bit); + if (bit < 0) + return bit; + if (bit) + ui |= (1 << (num_bits - i - 1)); + bv->cur_bit++; + } + + return ui; +} + +/* pad all remaining bits up to num_bits */ +int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit) +{ + unsigned int i; + + for (i = bv->cur_bit; i <= up_to_bit; i++) + bitvec_set_bit(bv, L); + + return 0; +} + +/* find first bit set in bit vector */ +int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, + enum bit_value val) +{ + unsigned int i; + + for (i = n; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i) == val) + return i; + } + + return -1; +} diff --git a/src/shared/libosmocore/src/codec/Makefile.am b/src/shared/libosmocore/src/codec/Makefile.am new file mode 100644 index 00000000..5a54c42d --- /dev/null +++ b/src/shared/libosmocore/src/codec/Makefile.am @@ -0,0 +1,11 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=0:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +lib_LTLIBRARIES = libosmocodec.la + +libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c +libosmocodec_la_LDFALGS = -version-info $(LIBVERSION) diff --git a/src/shared/libosmocore/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c new file mode 100644 index 00000000..09fbeb5a --- /dev/null +++ b/src/shared/libosmocore/src/codec/gsm610.c @@ -0,0 +1,294 @@ +/* GSM 06.10 - GSM FR codec */ + +/* + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +/* GSM FR - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 2. + * It's also GSM 06.10 Table A.2.1a + * + * It converts between serial parameter output by the encoder and the + * order needed before channel encoding. + */ +uint16_t gsm610_bitorder[260] = { + 0, /* LARc0:5 */ + 47, /* Xmaxc0:5 */ + 103, /* Xmaxc1:5 */ + 159, /* Xmaxc2:5 */ + 215, /* Xmaxc3:5 */ + 1, /* LARc0:4 */ + 6, /* LARc1:5 */ + 12, /* LARc2:4 */ + 2, /* LARc0:3 */ + 7, /* LARc1:4 */ + 13, /* LARc2:3 */ + 17, /* LARc3:4 */ + 36, /* Nc0:6 */ + 92, /* Nc1:6 */ + 148, /* Nc2:6 */ + 204, /* Nc3:6 */ + 48, /* Xmaxc0:4 */ + 104, /* Xmaxc1:4 */ + 160, /* Xmaxc2:4 */ + 216, /* Xmaxc3:4 */ + 8, /* LARc1:3 */ + 22, /* LARc4:3 */ + 26, /* LARc5:3 */ + 37, /* Nc0:5 */ + 93, /* Nc1:5 */ + 149, /* Nc2:5 */ + 205, /* Nc3:5 */ + 38, /* Nc0:4 */ + 94, /* Nc1:4 */ + 150, /* Nc2:4 */ + 206, /* Nc3:4 */ + 39, /* Nc0:3 */ + 95, /* Nc1:3 */ + 151, /* Nc2:3 */ + 207, /* Nc3:3 */ + 40, /* Nc0:2 */ + 96, /* Nc1:2 */ + 152, /* Nc2:2 */ + 208, /* Nc3:2 */ + 49, /* Xmaxc0:3 */ + 105, /* Xmaxc1:3 */ + 161, /* Xmaxc2:3 */ + 217, /* Xmaxc3:3 */ + 3, /* LARc0:2 */ + 18, /* LARc3:3 */ + 30, /* LARc6:2 */ + 41, /* Nc0:1 */ + 97, /* Nc1:1 */ + 153, /* Nc2:1 */ + 209, /* Nc3:1 */ + 23, /* LARc4:2 */ + 27, /* LARc5:2 */ + 43, /* bc0:1 */ + 99, /* bc1:1 */ + 155, /* bc2:1 */ + 211, /* bc3:1 */ + 42, /* Nc0:0 */ + 98, /* Nc1:0 */ + 154, /* Nc2:0 */ + 210, /* Nc3:0 */ + 45, /* Mc0:1 */ + 101, /* Mc1:1 */ + 157, /* Mc2:1 */ + 213, /* Mc3:1 */ + 4, /* LARc0:1 */ + 9, /* LARc1:2 */ + 14, /* LARc2:2 */ + 33, /* LARc7:2 */ + 19, /* LARc3:2 */ + 24, /* LARc4:1 */ + 31, /* LARc6:1 */ + 44, /* bc0:0 */ + 100, /* bc1:0 */ + 156, /* bc2:0 */ + 212, /* bc3:0 */ + 50, /* Xmaxc0:2 */ + 106, /* Xmaxc1:2 */ + 162, /* Xmaxc2:2 */ + 218, /* Xmaxc3:2 */ + 53, /* xmc0_0:2 */ + 56, /* xmc0_1:2 */ + 59, /* xmc0_2:2 */ + 62, /* xmc0_3:2 */ + 65, /* xmc0_4:2 */ + 68, /* xmc0_5:2 */ + 71, /* xmc0_6:2 */ + 74, /* xmc0_7:2 */ + 77, /* xmc0_8:2 */ + 80, /* xmc0_9:2 */ + 83, /* xmc0_10:2 */ + 86, /* xmc0_11:2 */ + 89, /* xmc0_12:2 */ + 109, /* xmc1_0:2 */ + 112, /* xmc1_1:2 */ + 115, /* xmc1_2:2 */ + 118, /* xmc1_3:2 */ + 121, /* xmc1_4:2 */ + 124, /* xmc1_5:2 */ + 127, /* xmc1_6:2 */ + 130, /* xmc1_7:2 */ + 133, /* xmc1_8:2 */ + 136, /* xmc1_9:2 */ + 139, /* xmc1_10:2 */ + 142, /* xmc1_11:2 */ + 145, /* xmc1_12:2 */ + 165, /* xmc2_0:2 */ + 168, /* xmc2_1:2 */ + 171, /* xmc2_2:2 */ + 174, /* xmc2_3:2 */ + 177, /* xmc2_4:2 */ + 180, /* xmc2_5:2 */ + 183, /* xmc2_6:2 */ + 186, /* xmc2_7:2 */ + 189, /* xmc2_8:2 */ + 192, /* xmc2_9:2 */ + 195, /* xmc2_10:2 */ + 198, /* xmc2_11:2 */ + 201, /* xmc2_12:2 */ + 221, /* xmc3_0:2 */ + 224, /* xmc3_1:2 */ + 227, /* xmc3_2:2 */ + 230, /* xmc3_3:2 */ + 233, /* xmc3_4:2 */ + 236, /* xmc3_5:2 */ + 239, /* xmc3_6:2 */ + 242, /* xmc3_7:2 */ + 245, /* xmc3_8:2 */ + 248, /* xmc3_9:2 */ + 251, /* xmc3_10:2 */ + 254, /* xmc3_11:2 */ + 257, /* xmc3_12:2 */ + 46, /* Mc0:0 */ + 102, /* Mc1:0 */ + 158, /* Mc2:0 */ + 214, /* Mc3:0 */ + 51, /* Xmaxc0:1 */ + 107, /* Xmaxc1:1 */ + 163, /* Xmaxc2:1 */ + 219, /* Xmaxc3:1 */ + 54, /* xmc0_0:1 */ + 57, /* xmc0_1:1 */ + 60, /* xmc0_2:1 */ + 63, /* xmc0_3:1 */ + 66, /* xmc0_4:1 */ + 69, /* xmc0_5:1 */ + 72, /* xmc0_6:1 */ + 75, /* xmc0_7:1 */ + 78, /* xmc0_8:1 */ + 81, /* xmc0_9:1 */ + 84, /* xmc0_10:1 */ + 87, /* xmc0_11:1 */ + 90, /* xmc0_12:1 */ + 110, /* xmc1_0:1 */ + 113, /* xmc1_1:1 */ + 116, /* xmc1_2:1 */ + 119, /* xmc1_3:1 */ + 122, /* xmc1_4:1 */ + 125, /* xmc1_5:1 */ + 128, /* xmc1_6:1 */ + 131, /* xmc1_7:1 */ + 134, /* xmc1_8:1 */ + 137, /* xmc1_9:1 */ + 140, /* xmc1_10:1 */ + 143, /* xmc1_11:1 */ + 146, /* xmc1_12:1 */ + 166, /* xmc2_0:1 */ + 169, /* xmc2_1:1 */ + 172, /* xmc2_2:1 */ + 175, /* xmc2_3:1 */ + 178, /* xmc2_4:1 */ + 181, /* xmc2_5:1 */ + 184, /* xmc2_6:1 */ + 187, /* xmc2_7:1 */ + 190, /* xmc2_8:1 */ + 193, /* xmc2_9:1 */ + 196, /* xmc2_10:1 */ + 199, /* xmc2_11:1 */ + 202, /* xmc2_12:1 */ + 222, /* xmc3_0:1 */ + 225, /* xmc3_1:1 */ + 228, /* xmc3_2:1 */ + 231, /* xmc3_3:1 */ + 234, /* xmc3_4:1 */ + 237, /* xmc3_5:1 */ + 240, /* xmc3_6:1 */ + 243, /* xmc3_7:1 */ + 246, /* xmc3_8:1 */ + 249, /* xmc3_9:1 */ + 252, /* xmc3_10:1 */ + 255, /* xmc3_11:1 */ + 258, /* xmc3_12:1 */ + 5, /* LARc0:0 */ + 10, /* LARc1:1 */ + 15, /* LARc2:1 */ + 28, /* LARc5:1 */ + 32, /* LARc6:0 */ + 34, /* LARc7:1 */ + 35, /* LARc7:0 */ + 16, /* LARc2:0 */ + 20, /* LARc3:1 */ + 21, /* LARc3:0 */ + 25, /* LARc4:0 */ + 52, /* Xmaxc0:0 */ + 108, /* Xmaxc1:0 */ + 164, /* Xmaxc2:0 */ + 220, /* Xmaxc3:0 */ + 55, /* xmc0_0:0 */ + 58, /* xmc0_1:0 */ + 61, /* xmc0_2:0 */ + 64, /* xmc0_3:0 */ + 67, /* xmc0_4:0 */ + 70, /* xmc0_5:0 */ + 73, /* xmc0_6:0 */ + 76, /* xmc0_7:0 */ + 79, /* xmc0_8:0 */ + 82, /* xmc0_9:0 */ + 85, /* xmc0_10:0 */ + 88, /* xmc0_11:0 */ + 91, /* xmc0_12:0 */ + 111, /* xmc1_0:0 */ + 114, /* xmc1_1:0 */ + 117, /* xmc1_2:0 */ + 120, /* xmc1_3:0 */ + 123, /* xmc1_4:0 */ + 126, /* xmc1_5:0 */ + 129, /* xmc1_6:0 */ + 132, /* xmc1_7:0 */ + 135, /* xmc1_8:0 */ + 138, /* xmc1_9:0 */ + 141, /* xmc1_10:0 */ + 144, /* xmc1_11:0 */ + 147, /* xmc1_12:0 */ + 167, /* xmc2_0:0 */ + 170, /* xmc2_1:0 */ + 173, /* xmc2_2:0 */ + 176, /* xmc2_3:0 */ + 179, /* xmc2_4:0 */ + 182, /* xmc2_5:0 */ + 185, /* xmc2_6:0 */ + 188, /* xmc2_7:0 */ + 191, /* xmc2_8:0 */ + 194, /* xmc2_9:0 */ + 197, /* xmc2_10:0 */ + 200, /* xmc2_11:0 */ + 203, /* xmc2_12:0 */ + 223, /* xmc3_0:0 */ + 226, /* xmc3_1:0 */ + 229, /* xmc3_2:0 */ + 232, /* xmc3_3:0 */ + 235, /* xmc3_4:0 */ + 238, /* xmc3_5:0 */ + 241, /* xmc3_6:0 */ + 244, /* xmc3_7:0 */ + 247, /* xmc3_8:0 */ + 250, /* xmc3_9:0 */ + 253, /* xmc3_10:0 */ + 256, /* xmc3_11:0 */ + 259, /* xmc3_12:0 */ + 11, /* LARc1:0 */ + 29, /* LARc5:0 */ +}; diff --git a/src/shared/libosmocore/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c new file mode 100644 index 00000000..09aca502 --- /dev/null +++ b/src/shared/libosmocore/src/codec/gsm620.c @@ -0,0 +1,262 @@ +/* GSM 06.20 - GSM HR codec */ + +/* + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */ + /* This array encode mapping between GSM 05.03 Table 3a (bits + * ordering before channel coding on TCH) and GSM 06.20 Table B.1 + * (bit ordering on A-bis */ +uint16_t gsm620_unvoiced_bitorder[112] = { + 3, /* R0:1 */ + 25, /* LPC 3:7 */ + 52, /* GSP 0-1:2 */ + 71, /* GSP 0-2:2 */ + 90, /* GSP 0-3:2 */ + 109, /* GSP 0-4:2 */ + 15, /* LPC 1:0 */ + 23, /* LPC 2:1 */ + 22, /* LPC 2:2 */ + 21, /* LPC 2:3 */ + 20, /* LPC 2:4 */ + 19, /* LPC 2:5 */ + 31, /* LPC 3:1 */ + 30, /* LPC 3:2 */ + 29, /* LPC 3:3 */ + 28, /* LPC 3:4 */ + 27, /* LPC 3:5 */ + 26, /* LPC 3:6 */ + 61, /* Code 1-2:0 */ + 68, /* Code 2-2:0 */ + 67, /* Code 2-2:1 */ + 66, /* Code 2-2:2 */ + 65, /* Code 2-2:3 */ + 64, /* Code 2-2:4 */ + 63, /* Code 2-2:5 */ + 62, /* Code 2-2:6 */ + 80, /* Code 1-3:0 */ + 79, /* Code 1-3:1 */ + 78, /* Code 1-3:2 */ + 77, /* Code 1-3:3 */ + 76, /* Code 1-3:4 */ + 75, /* Code 1-3:5 */ + 74, /* Code 1-3:6 */ + 84, /* Code 2-3:3 */ + 83, /* Code 2-3:4 */ + 82, /* Code 2-3:5 */ + 81, /* Code 2-3:6 */ + 32, /* LPC 3:0 */ + 4, /* R0:0 */ + 33, /* INT-LPC:0 */ + 60, /* Code 1-2:1 */ + 59, /* Code 1-2:2 */ + 58, /* Code 1-2:3 */ + 57, /* Code 1-2:4 */ + 56, /* Code 1-2:5 */ + 55, /* Code 1-2:6 */ + 49, /* Code 2-1:0 */ + 48, /* Code 2-1:1 */ + 47, /* Code 2-1:2 */ + 46, /* Code 2-1:3 */ + 45, /* Code 2-1:4 */ + 44, /* Code 2-1:5 */ + 43, /* Code 2-1:6 */ + 42, /* Code 1-1:0 */ + 41, /* Code 1-1:1 */ + 40, /* Code 1-1:2 */ + 39, /* Code 1-1:3 */ + 38, /* Code 1-1:4 */ + 37, /* Code 1-1:5 */ + 36, /* Code 1-1:6 */ + 111, /* GSP 0-4:0 */ + 92, /* GSP 0-3:0 */ + 73, /* GSP 0-2:0 */ + 54, /* GSP 0-1:0 */ + 24, /* LPC 2:0 */ + 110, /* GSP 0-4:1 */ + 91, /* GSP 0-3:1 */ + 72, /* GSP 0-2:1 */ + 53, /* GSP 0-1:1 */ + 14, /* LPC 1:1 */ + 13, /* LPC 1:2 */ + 12, /* LPC 1:3 */ + 11, /* LPC 1:4 */ + 10, /* LPC 1:5 */ + 108, /* GSP 0-4:3 */ + 89, /* GSP 0-3:3 */ + 70, /* GSP 0-2:3 */ + 51, /* GSP 0-1:3 */ + 18, /* LPC 2:6 */ + 17, /* LPC 2:7 */ + 16, /* LPC 2:8 */ + 107, /* GSP 0-4:4 */ + 88, /* GSP 0-3:4 */ + 69, /* GSP 0-2:4 */ + 50, /* GSP 0-1:4 */ + 9, /* LPC 1:6 */ + 8, /* LPC 1:7 */ + 7, /* LPC 1:8 */ + 6, /* LPC 1:9 */ + 2, /* R0:2 */ + 5, /* LPC 1:10 */ + 1, /* R0:3 */ + 0, /* R0:4 */ + 35, /* Mode:0 */ + 34, /* Mode:1 */ + 106, /* Code 2-4:0 */ + 105, /* Code 2-4:1 */ + 104, /* Code 2-4:2 */ + 103, /* Code 2-4:3 */ + 102, /* Code 2-4:4 */ + 101, /* Code 2-4:5 */ + 100, /* Code 2-4:6 */ + 99, /* Code 1-4:0 */ + 98, /* Code 1-4:1 */ + 97, /* Code 1-4:2 */ + 96, /* Code 1-4:3 */ + 95, /* Code 1-4:4 */ + 94, /* Code 1-4:5 */ + 93, /* Code 1-4:6 */ + 87, /* Code 2-3:0 */ + 86, /* Code 2-3:1 */ + 85, /* Code 2-3:2 */ +}; + +/* GSM HR voiced (mode=1,2,3) frames - subjective importance bit ordering */ + /* This array encode mapping between GSM 05.03 Table 3b (bits + * ordering before channel coding on TCH) and GSM 06.20 Table B.2 + * (bit ordering on A-bis */ +uint16_t gsm620_voiced_bitorder[112] = { + 13, /* LPC 1:2 */ + 14, /* LPC 1:1 */ + 20, /* LPC 2:4 */ + 19, /* LPC 2:5 */ + 18, /* LPC 2:6 */ + 53, /* GSP 0-1:4 */ + 71, /* GSP 0-2:4 */ + 89, /* GSP 0-3:4 */ + 107, /* GSP 0-4:4 */ + 54, /* GSP 0-1:3 */ + 72, /* GSP 0-2:3 */ + 90, /* GSP 0-3:3 */ + 108, /* GSP 0-4:3 */ + 55, /* GSP 0-1:2 */ + 73, /* GSP 0-2:2 */ + 91, /* GSP 0-3:2 */ + 109, /* GSP 0-4:2 */ + 52, /* Code 1:0 */ + 51, /* Code 1:1 */ + 50, /* Code 1:2 */ + 49, /* Code 1:3 */ + 48, /* Code 1:4 */ + 47, /* Code 1:5 */ + 46, /* Code 1:6 */ + 45, /* Code 1:7 */ + 44, /* Code 1:8 */ + 65, /* Code 2:5 */ + 64, /* Code 2:6 */ + 63, /* Code 2:7 */ + 62, /* Code 2:8 */ + 70, /* Code 2:0 */ + 69, /* Code 2:1 */ + 68, /* Code 2:2 */ + 80, /* Code 3:8 */ + 66, /* Code 2:4 */ + 67, /* Code 2:3 */ + 56, /* GSP 0-1:1 */ + 74, /* GSP 0-2:1 */ + 92, /* GSP 0-3:1 */ + 110, /* GSP 0-4:1 */ + 57, /* GSP 0-1:0 */ + 75, /* GSP 0-2:0 */ + 93, /* GSP 0-3:0 */ + 111, /* GSP 0-4:0 */ + 33, /* INT-LPC:0 */ + 24, /* LPC 2:0 */ + 32, /* LPC 3:0 */ + 97, /* LAG 4:0 */ + 31, /* LPC 3:1 */ + 23, /* LPC 2:1 */ + 96, /* LAG 4:1 */ + 79, /* LAG 3:0 */ + 61, /* LAG 2:0 */ + 43, /* LAG 1:0 */ + 95, /* LAG 4:2 */ + 78, /* LAG 3:1 */ + 60, /* LAG 2:1 */ + 42, /* LAG 1:1 */ + 30, /* LPC 3:2 */ + 29, /* LPC 3:3 */ + 28, /* LPC 3:4 */ + 22, /* LPC 2:2 */ + 27, /* LPC 3:5 */ + 26, /* LPC 3:6 */ + 21, /* LPC 2:3 */ + 4, /* R0:0 */ + 25, /* LPC 3:7 */ + 15, /* LPC 1:0 */ + 94, /* LAG 4:3 */ + 77, /* LAG 3:2 */ + 59, /* LAG 2:2 */ + 41, /* LAG 1:2 */ + 3, /* R0:1 */ + 76, /* LAG 3:3 */ + 58, /* LAG 2:3 */ + 40, /* LAG 1:3 */ + 39, /* LAG 1:4 */ + 17, /* LPC 2:7 */ + 16, /* LPC 2:8 */ + 12, /* LPC 1:3 */ + 11, /* LPC 1:4 */ + 10, /* LPC 1:5 */ + 9, /* LPC 1:6 */ + 2, /* R0:2 */ + 38, /* LAG 1:5 */ + 37, /* LAG 1:6 */ + 36, /* LAG 1:7 */ + 8, /* LPC 1:7 */ + 7, /* LPC 1:8 */ + 6, /* LPC 1:9 */ + 5, /* LPC 1:10 */ + 1, /* R0:3 */ + 0, /* R0:4 */ + 35, /* Mode:0 */ + 34, /* Mode:1 */ + 106, /* Code 4:0 */ + 105, /* Code 4:1 */ + 104, /* Code 4:2 */ + 103, /* Code 4:3 */ + 102, /* Code 4:4 */ + 101, /* Code 4:5 */ + 100, /* Code 4:6 */ + 99, /* Code 4:7 */ + 98, /* Code 4:8 */ + 88, /* Code 3:0 */ + 87, /* Code 3:1 */ + 86, /* Code 3:2 */ + 85, /* Code 3:3 */ + 84, /* Code 3:4 */ + 83, /* Code 3:5 */ + 82, /* Code 3:6 */ + 81, /* Code 3:7 */ +}; diff --git a/src/shared/libosmocore/src/codec/gsm660.c b/src/shared/libosmocore/src/codec/gsm660.c new file mode 100644 index 00000000..4fff5ffc --- /dev/null +++ b/src/shared/libosmocore/src/codec/gsm660.c @@ -0,0 +1,256 @@ +/* GSM 06.60 - GSM EFR Codec */ + +/* + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +/* GSM EFR - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 6. + * + * It converts between serial parameter output (as described in + * GSM 06.60 Table 6 and GSM 05.03 Table 5) and the order needed + * before channel encoding. CRC poly and bit repetition must be + * applied prior to this table, as in GSM 05.03 3.1.1, to get 260 + * bits from a 244 bits raw EFR frame. + */ +uint16_t gsm660_bitorder[260] = { + 38, 39, 40, 41, 42, 43, /* 0 -> LTP-LAG 1: b8..b3 */ + 145, 146, 147, 148, 149, 150, /* 6 -> LTP-LAG 3: b8..b3 */ + 93, 94, /* 12 -> LTP-LAG 2: b5..b4 */ + 200, 201, /* 14 -> LTP-LAG 4: b5..b4 */ + 47, /* 16 -> LTP-GAIN 1: b3 */ + 88, /* 17 -> FCB-GAIN 1: b4 */ + 99, /* 18 -> LTP-GAIN 2: b3 */ + 140, /* 19 -> FCB-GAIN 2: b4 */ + 44, /* 20 -> LTP-LAG 1: b2 */ + 151, /* 21 -> LTP-LAG 3: b2 */ + 95, /* 22 -> LTP-LAG 2: b3 */ + 202, /* 23 -> LTP-LAG 4: b3 */ + 1, 2, /* 24 -> LPC 1: b5..b4 */ + 7, /* 26 -> LPC 2: b7 */ + 9, /* 27 -> LPC 2: b5 */ + 17, 18, /* 28 -> LPC 3: b6..b5 */ + 23, /* 30 -> LPC 3: b0 */ + 45, 46, /* 31 -> LTP-LAG 1: b1..b0 */ + 152, 153, /* 33 -> LTP-LAG 3: b1..b0 */ + 96, /* 35 -> LTP-LAG 2: b2 */ + 203, /* 36 -> LTP-LAG 4: b2 */ + 3, 4, /* 37 -> LPC 1: b3..b2 */ + 10, 11, /* 39 -> LPC 2: b4..b3 */ + 15, /* 41 -> LPC 3: b8 */ + 8, /* 42 -> LPC 2: b6 */ + 5, 6, /* 43 -> LPC 1: b1..b0 */ + 12, /* 45 -> LPC 2: b2 */ + 16, /* 46 -> LPC 3: b7 */ + 19, /* 47 -> LPC 3: b4 */ + 97, /* 48 -> LTP-LAG 2: b1 */ + 204, /* 49 -> LTP-LAG 4: b1 */ + 0, /* 50 -> LPC 1: b6 */ + 13, 14, /* 51 -> LPC 2: b1..b0 */ + 20, /* 53 -> LPC 3: b3 */ + 24, 25, /* 54 -> LPC 4: b7..b6 */ + 27, /* 56 -> LPC 4: b4 */ + 154, /* 57 -> LTP-GAIN 3: b3 */ + 206, /* 58 -> LTP-GAIN 4: b3 */ + 195, /* 59 -> FCB-GAIN 3: b4 */ + 247, /* 60 -> FCB-GAIN 4: b4 */ + 89, /* 61 -> FCB-GAIN 1: b3 */ + 141, /* 62 -> FCB-GAIN 2: b3 */ + 196, /* 63 -> FCB-GAIN 3: b3 */ + 248, /* 64 -> FCB-GAIN 4: b3 */ + 252, 253, 254, 255, 256, 257, 258, 259, /* 65 -> CRC-POLY: b7..b0 */ + 48, /* 73 -> LTP-GAIN 1: b2 */ + 100, /* 74 -> LTP-GAIN 2: b2 */ + 155, /* 75 -> LTP-GAIN 3: b2 */ + 207, /* 76 -> LTP-GAIN 4: b2 */ + 21, 22, /* 77 -> LPC 3: b2..b1 */ + 26, /* 79 -> LPC 4: b5 */ + 28, /* 80 -> LPC 4: b3 */ + 51, /* 81 -> PULSE 1_1: b3 */ + 55, /* 82 -> PULSE 1_2: b3 */ + 59, /* 83 -> PULSE 1_3: b3 */ + 63, /* 84 -> PULSE 1_4: b3 */ + 67, /* 85 -> PULSE 1_5: b3 */ + 103, /* 86 -> PULSE 2_1: b3 */ + 107, /* 87 -> PULSE 2_2: b3 */ + 111, /* 88 -> PULSE 2_3: b3 */ + 115, /* 89 -> PULSE 2_4: b3 */ + 119, /* 90 -> PULSE 2_5: b3 */ + 158, /* 91 -> PULSE 3_1: b3 */ + 162, /* 92 -> PULSE 3_2: b3 */ + 166, /* 93 -> PULSE 3_3: b3 */ + 170, /* 94 -> PULSE 3_4: b3 */ + 174, /* 95 -> PULSE 3_5: b3 */ + 210, /* 96 -> PULSE 4_1: b3 */ + 214, /* 97 -> PULSE 4_2: b3 */ + 218, /* 98 -> PULSE 4_3: b3 */ + 222, /* 99 -> PULSE 4_4: b3 */ + 226, /* 100 -> PULSE 4_5: b3 */ + 90, /* 101 -> FCB-GAIN 1: b2 */ + 142, /* 102 -> FCB-GAIN 2: b2 */ + 197, /* 103 -> FCB-GAIN 3: b2 */ + 249, /* 104 -> FCB-GAIN 4: b2 */ + 49, /* 105 -> LTP-GAIN 1: b1 */ + 101, /* 106 -> LTP-GAIN 2: b1 */ + 156, /* 107 -> LTP-GAIN 3: b1 */ + 208, /* 108 -> LTP-GAIN 4: b1 */ + 29, 30, 31, /* 109 -> LPC 4: b2..b0 */ + 32, 33, 34, 35, /* 112 -> LPC 5: b5..b2 */ + 98, /* 116 -> LTP-LAG 2: b0 */ + 205, /* 117 -> LTP-LAG 4: b0 */ + 52, /* 118 -> PULSE 1_1: b2 */ + 56, /* 119 -> PULSE 1_2: b2 */ + 60, /* 120 -> PULSE 1_3: b2 */ + 64, /* 121 -> PULSE 1_4: b2 */ + 68, /* 122 -> PULSE 1_5: b2 */ + 104, /* 123 -> PULSE 2_1: b2 */ + 108, /* 124 -> PULSE 2_2: b2 */ + 112, /* 125 -> PULSE 2_3: b2 */ + 116, /* 126 -> PULSE 2_4: b2 */ + 120, /* 127 -> PULSE 2_5: b2 */ + 159, /* 128 -> PULSE 3_1: b2 */ + 163, /* 129 -> PULSE 3_2: b2 */ + 167, /* 130 -> PULSE 3_3: b2 */ + 171, /* 131 -> PULSE 3_4: b2 */ + 175, /* 132 -> PULSE 3_5: b2 */ + 211, /* 133 -> PULSE 4_1: b2 */ + 215, /* 134 -> PULSE 4_2: b2 */ + 219, /* 135 -> PULSE 4_3: b2 */ + 223, /* 136 -> PULSE 4_4: b2 */ + 227, /* 137 -> PULSE 4_5: b2 */ + 53, /* 138 -> PULSE 1_1: b1 */ + 57, /* 139 -> PULSE 1_2: b1 */ + 61, /* 140 -> PULSE 1_3: b1 */ + 65, /* 141 -> PULSE 1_4: b1 */ + 105, /* 142 -> PULSE 2_1: b1 */ + 109, /* 143 -> PULSE 2_2: b1 */ + 113, /* 144 -> PULSE 2_3: b1 */ + 117, /* 145 -> PULSE 2_4: b1 */ + 160, /* 146 -> PULSE 3_1: b1 */ + 164, /* 147 -> PULSE 3_2: b1 */ + 168, /* 148 -> PULSE 3_3: b1 */ + 172, /* 149 -> PULSE 3_4: b1 */ + 212, /* 150 -> PULSE 4_1: b1 */ + 220, /* 151 -> PULSE 4_3: b1 */ + 224, /* 152 -> PULSE 4_4: b1 */ + 91, /* 153 -> FCB-GAIN 1: b1 */ + 143, /* 154 -> FCB-GAIN 2: b1 */ + 198, /* 155 -> FCB-GAIN 3: b1 */ + 250, /* 156 -> FCB-GAIN 4: b1 */ + 50, /* 157 -> LTP-GAIN 1: b0 */ + 102, /* 158 -> LTP-GAIN 2: b0 */ + 157, /* 159 -> LTP-GAIN 3: b0 */ + 209, /* 160 -> LTP-GAIN 4: b0 */ + 92, /* 161 -> FCB-GAIN 1: b0 */ + 144, /* 162 -> FCB-GAIN 2: b0 */ + 199, /* 163 -> FCB-GAIN 3: b0 */ + 251, /* 164 -> FCB-GAIN 4: b0 */ + 54, /* 165 -> PULSE 1_1: b0 */ + 58, /* 166 -> PULSE 1_2: b0 */ + 62, /* 167 -> PULSE 1_3: b0 */ + 66, /* 168 -> PULSE 1_4: b0 */ + 106, /* 169 -> PULSE 2_1: b0 */ + 110, /* 170 -> PULSE 2_2: b0 */ + 114, /* 171 -> PULSE 2_3: b0 */ + 118, /* 172 -> PULSE 2_4: b0 */ + 161, /* 173 -> PULSE 3_1: b0 */ + 165, /* 174 -> PULSE 3_2: b0 */ + 169, /* 175 -> PULSE 3_3: b0 */ + 173, /* 176 -> PULSE 3_4: b0 */ + 213, /* 177 -> PULSE 4_1: b0 */ + 221, /* 178 -> PULSE 4_3: b0 */ + 225, /* 179 -> PULSE 4_4: b0 */ + 36, 37, /* 180 -> LPC 5: b1..b0 */ + 69, /* 182 -> PULSE 1_5: b1 */ + 71, 72, /* 183 -> PULSE 1_5: b1..b1 */ + 121, /* 185 -> PULSE 2_5: b1 */ + 123, 124, /* 186 -> PULSE 2_5: b1..b1 */ + 176, /* 188 -> PULSE 3_5: b1 */ + 178, 179, /* 189 -> PULSE 3_5: b1..b1 */ + 228, /* 191 -> PULSE 4_5: b1 */ + 230, 231, /* 192 -> PULSE 4_5: b1..b1 */ + 216, 217, /* 194 -> PULSE 4_2: b1..b0 */ + 70, /* 196 -> PULSE 1_5: b0 */ + 122, /* 197 -> PULSE 2_5: b0 */ + 177, /* 198 -> PULSE 3_5: b0 */ + 229, /* 199 -> PULSE 4_5: b0 */ + 73, /* 200 -> PULSE 1_6: b2 */ + 76, /* 201 -> PULSE 1_7: b2 */ + 79, /* 202 -> PULSE 1_8: b2 */ + 82, /* 203 -> PULSE 1_9: b2 */ + 85, /* 204 -> PULSE 1_10: b2 */ + 125, /* 205 -> PULSE 2_6: b2 */ + 128, /* 206 -> PULSE 2_7: b2 */ + 131, /* 207 -> PULSE 2_8: b2 */ + 134, /* 208 -> PULSE 2_9: b2 */ + 137, /* 209 -> PULSE 2_10: b2 */ + 180, /* 210 -> PULSE 3_6: b2 */ + 183, /* 211 -> PULSE 3_7: b2 */ + 186, /* 212 -> PULSE 3_8: b2 */ + 189, /* 213 -> PULSE 3_9: b2 */ + 192, /* 214 -> PULSE 3_10: b2 */ + 232, /* 215 -> PULSE 4_6: b2 */ + 235, /* 216 -> PULSE 4_7: b2 */ + 238, /* 217 -> PULSE 4_8: b2 */ + 241, /* 218 -> PULSE 4_9: b2 */ + 244, /* 219 -> PULSE 4_10: b2 */ + 74, /* 220 -> PULSE 1_6: b1 */ + 77, /* 221 -> PULSE 1_7: b1 */ + 80, /* 222 -> PULSE 1_8: b1 */ + 83, /* 223 -> PULSE 1_9: b1 */ + 86, /* 224 -> PULSE 1_10: b1 */ + 126, /* 225 -> PULSE 2_6: b1 */ + 129, /* 226 -> PULSE 2_7: b1 */ + 132, /* 227 -> PULSE 2_8: b1 */ + 135, /* 228 -> PULSE 2_9: b1 */ + 138, /* 229 -> PULSE 2_10: b1 */ + 181, /* 230 -> PULSE 3_6: b1 */ + 184, /* 231 -> PULSE 3_7: b1 */ + 187, /* 232 -> PULSE 3_8: b1 */ + 190, /* 233 -> PULSE 3_9: b1 */ + 193, /* 234 -> PULSE 3_10: b1 */ + 233, /* 235 -> PULSE 4_6: b1 */ + 236, /* 236 -> PULSE 4_7: b1 */ + 239, /* 237 -> PULSE 4_8: b1 */ + 242, /* 238 -> PULSE 4_9: b1 */ + 245, /* 239 -> PULSE 4_10: b1 */ + 75, /* 240 -> PULSE 1_6: b0 */ + 78, /* 241 -> PULSE 1_7: b0 */ + 81, /* 242 -> PULSE 1_8: b0 */ + 84, /* 243 -> PULSE 1_9: b0 */ + 87, /* 244 -> PULSE 1_10: b0 */ + 127, /* 245 -> PULSE 2_6: b0 */ + 130, /* 246 -> PULSE 2_7: b0 */ + 133, /* 247 -> PULSE 2_8: b0 */ + 136, /* 248 -> PULSE 2_9: b0 */ + 139, /* 249 -> PULSE 2_10: b0 */ + 182, /* 250 -> PULSE 3_6: b0 */ + 185, /* 251 -> PULSE 3_7: b0 */ + 188, /* 252 -> PULSE 3_8: b0 */ + 191, /* 253 -> PULSE 3_9: b0 */ + 194, /* 254 -> PULSE 3_10: b0 */ + 234, /* 255 -> PULSE 4_6: b0 */ + 237, /* 256 -> PULSE 4_7: b0 */ + 240, /* 257 -> PULSE 4_8: b0 */ + 243, /* 258 -> PULSE 4_9: b0 */ + 246, /* 259 -> PULSE 4_10: b0 */ +}; diff --git a/src/shared/libosmocore/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c new file mode 100644 index 00000000..e5b9bd49 --- /dev/null +++ b/src/shared/libosmocore/src/codec/gsm690.c @@ -0,0 +1,210 @@ +/* GSM 06.90 - GSM AMR Codec */ + +/* + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +/* + * These table map between the raw encoder parameter output and + * the format used before channel coding. Both in GSM and in various + * file/network format (same tables used in several specs). + */ + +/* AMR 12.2 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 7 + * It's also TS 26.101 Table B.8 + */ +uint16_t gsm690_12_2_bitorder[244] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 23, 15, 16, 17, 18, + 19, 20, 21, 22, 24, 25, 26, 27, 28, 38, + 141, 39, 142, 40, 143, 41, 144, 42, 145, 43, + 146, 44, 147, 45, 148, 46, 149, 47, 97, 150, + 200, 48, 98, 151, 201, 49, 99, 152, 202, 86, + 136, 189, 239, 87, 137, 190, 240, 88, 138, 191, + 241, 91, 194, 92, 195, 93, 196, 94, 197, 95, + 198, 29, 30, 31, 32, 33, 34, 35, 50, 100, + 153, 203, 89, 139, 192, 242, 51, 101, 154, 204, + 55, 105, 158, 208, 90, 140, 193, 243, 59, 109, + 162, 212, 63, 113, 166, 216, 67, 117, 170, 220, + 36, 37, 54, 53, 52, 58, 57, 56, 62, 61, + 60, 66, 65, 64, 70, 69, 68, 104, 103, 102, + 108, 107, 106, 112, 111, 110, 116, 115, 114, 120, + 119, 118, 157, 156, 155, 161, 160, 159, 165, 164, + 163, 169, 168, 167, 173, 172, 171, 207, 206, 205, + 211, 210, 209, 215, 214, 213, 219, 218, 217, 223, + 222, 221, 73, 72, 71, 76, 75, 74, 79, 78, + 77, 82, 81, 80, 85, 84, 83, 123, 122, 121, + 126, 125, 124, 129, 128, 127, 132, 131, 130, 135, + 134, 133, 176, 175, 174, 179, 178, 177, 182, 181, + 180, 185, 184, 183, 188, 187, 186, 226, 225, 224, + 229, 228, 227, 232, 231, 230, 235, 234, 233, 238, + 237, 236, 96, 199, +}; + +/* AMR 10.2 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 8 + * It's also TS 26.101 Table B.7 + */ +uint16_t gsm690_10_2_bitorder[204] = { + 7, 6, 5, 4, 3, 2, 1, 0, 16, 15, + 14, 13, 12, 11, 10, 9, 8, 26, 27, 28, + 29, 30, 31, 115, 116, 117, 118, 119, 120, 72, + 73, 161, 162, 65, 68, 69, 108, 111, 112, 154, + 157, 158, 197, 200, 201, 32, 33, 121, 122, 74, + 75, 163, 164, 66, 109, 155, 198, 19, 23, 21, + 22, 18, 17, 20, 24, 25, 37, 36, 35, 34, + 80, 79, 78, 77, 126, 125, 124, 123, 169, 168, + 167, 166, 70, 67, 71, 113, 110, 114, 159, 156, + 160, 202, 199, 203, 76, 165, 81, 82, 92, 91, + 93, 83, 95, 85, 84, 94, 101, 102, 96, 104, + 86, 103, 87, 97, 127, 128, 138, 137, 139, 129, + 141, 131, 130, 140, 147, 148, 142, 150, 132, 149, + 133, 143, 170, 171, 181, 180, 182, 172, 184, 174, + 173, 183, 190, 191, 185, 193, 175, 192, 176, 186, + 38, 39, 49, 48, 50, 40, 52, 42, 41, 51, + 58, 59, 53, 61, 43, 60, 44, 54, 194, 179, + 189, 196, 177, 195, 178, 187, 188, 151, 136, 146, + 153, 134, 152, 135, 144, 145, 105, 90, 100, 107, + 88, 106, 89, 98, 99, 62, 47, 57, 64, 45, + 63, 46, 55, 56, +}; + +/* AMR 7.95 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 9 + * It's also TS 26.101 Table B.6 + */ +uint16_t gsm690_7_95_bitorder[159] = { + 8, 7, 6, 5, 4, 3, 2, 14, 16, 9, + 10, 12, 13, 15, 11, 17, 20, 22, 24, 23, + 19, 18, 21, 56, 88, 122, 154, 57, 89, 123, + 155, 58, 90, 124, 156, 52, 84, 118, 150, 53, + 85, 119, 151, 27, 93, 28, 94, 29, 95, 30, + 96, 31, 97, 61, 127, 62, 128, 63, 129, 59, + 91, 125, 157, 32, 98, 64, 130, 1, 0, 25, + 26, 33, 99, 34, 100, 65, 131, 66, 132, 54, + 86, 120, 152, 60, 92, 126, 158, 55, 87, 121, + 153, 117, 116, 115, 46, 78, 112, 144, 43, 75, + 109, 141, 40, 72, 106, 138, 36, 68, 102, 134, + 114, 149, 148, 147, 146, 83, 82, 81, 80, 51, + 50, 49, 48, 47, 45, 44, 42, 39, 35, 79, + 77, 76, 74, 71, 67, 113, 111, 110, 108, 105, + 101, 145, 143, 142, 140, 137, 133, 41, 73, 107, + 139, 37, 69, 103, 135, 38, 70, 104, 136, +}; + +/* AMR 7.4 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 10 + * It's also TS 26.101 Table B.5 + */ +uint16_t gsm690_7_4_bitorder[148] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 26, 87, 27, + 88, 28, 89, 29, 90, 30, 91, 51, 80, 112, + 141, 52, 81, 113, 142, 54, 83, 115, 144, 55, + 84, 116, 145, 58, 119, 59, 120, 21, 22, 23, + 17, 18, 19, 31, 60, 92, 121, 56, 85, 117, + 146, 20, 24, 25, 50, 79, 111, 140, 57, 86, + 118, 147, 49, 78, 110, 139, 48, 77, 53, 82, + 114, 143, 109, 138, 47, 76, 108, 137, 32, 33, + 61, 62, 93, 94, 122, 123, 41, 42, 43, 44, + 45, 46, 70, 71, 72, 73, 74, 75, 102, 103, + 104, 105, 106, 107, 131, 132, 133, 134, 135, 136, + 34, 63, 95, 124, 35, 64, 96, 125, 36, 65, + 97, 126, 37, 66, 98, 127, 38, 67, 99, 128, + 39, 68, 100, 129, 40, 69, 101, 130, +}; + +/* AMR 6.7 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 11 + * It's also TS 26.101 Table B.4 + */ +uint16_t gsm690_6_7_bitorder[134] = { + 0, 1, 4, 3, 5, 6, 13, 7, 2, 8, + 9, 11, 15, 12, 14, 10, 28, 82, 29, 83, + 27, 81, 26, 80, 30, 84, 16, 55, 109, 56, + 110, 31, 85, 57, 111, 48, 73, 102, 127, 32, + 86, 51, 76, 105, 130, 52, 77, 106, 131, 58, + 112, 33, 87, 19, 23, 53, 78, 107, 132, 21, + 22, 18, 17, 20, 24, 25, 50, 75, 104, 129, + 47, 72, 101, 126, 54, 79, 108, 133, 46, 71, + 100, 125, 128, 103, 74, 49, 45, 70, 99, 124, + 42, 67, 96, 121, 39, 64, 93, 118, 38, 63, + 92, 117, 35, 60, 89, 114, 34, 59, 88, 113, + 44, 69, 98, 123, 43, 68, 97, 122, 41, 66, + 95, 120, 40, 65, 94, 119, 37, 62, 91, 116, + 36, 61, 90, 115, +}; + +/* AMR 5.9 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 12 + * It's also TS 26.101 Table B.3 + */ +uint16_t gsm690_5_9_bitorder[118] = { + 0, 1, 4, 5, 3, 6, 7, 2, 13, 15, + 8, 9, 11, 12, 14, 10, 16, 28, 74, 29, + 75, 27, 73, 26, 72, 30, 76, 51, 97, 50, + 71, 96, 117, 31, 77, 52, 98, 49, 70, 95, + 116, 53, 99, 32, 78, 33, 79, 48, 69, 94, + 115, 47, 68, 93, 114, 46, 67, 92, 113, 19, + 21, 23, 22, 18, 17, 20, 24, 111, 43, 89, + 110, 64, 65, 44, 90, 25, 45, 66, 91, 112, + 54, 100, 40, 61, 86, 107, 39, 60, 85, 106, + 36, 57, 82, 103, 35, 56, 81, 102, 34, 55, + 80, 101, 42, 63, 88, 109, 41, 62, 87, 108, + 38, 59, 84, 105, 37, 58, 83, 104, +}; + +/* AMR 5.15 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 13 + * It's also TS 26.101 Table B.2 + */ +uint16_t gsm690_5_15_bitorder[103] = { + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, + 13, 12, 11, 10, 9, 8, 23, 24, 25, 26, + 27, 46, 65, 84, 45, 44, 43, 64, 63, 62, + 83, 82, 81, 102, 101, 100, 42, 61, 80, 99, + 28, 47, 66, 85, 18, 41, 60, 79, 98, 29, + 48, 67, 17, 20, 22, 40, 59, 78, 97, 21, + 30, 49, 68, 86, 19, 16, 87, 39, 38, 58, + 57, 77, 35, 54, 73, 92, 76, 96, 95, 36, + 55, 74, 93, 32, 51, 33, 52, 70, 71, 89, + 90, 31, 50, 69, 88, 37, 56, 75, 94, 34, + 53, 72, 91, +}; + +/* AMR 4.75 kbits - subjective importance bit ordering */ + /* This array encodes GSM 05.03 Table 14 + * It's also TS 26.101 Table B.1 + */ +uint16_t gsm690_4_75_bitorder[95] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 23, 24, 25, 26, + 27, 28, 48, 49, 61, 62, 82, 83, 47, 46, + 45, 44, 81, 80, 79, 78, 17, 18, 20, 22, + 77, 76, 75, 74, 29, 30, 43, 42, 41, 40, + 38, 39, 16, 19, 21, 50, 51, 59, 60, 63, + 64, 72, 73, 84, 85, 93, 94, 32, 33, 35, + 36, 53, 54, 56, 57, 66, 67, 69, 70, 87, + 88, 90, 91, 34, 55, 68, 89, 37, 58, 71, + 92, 31, 52, 65, 86, +}; diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c new file mode 100644 index 00000000..70bdffba --- /dev/null +++ b/src/shared/libosmocore/src/conv.c @@ -0,0 +1,496 @@ +/* + * conv.c + * + * Generic convolutional encoding / decoding + * + * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <alloca.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/conv.h> + + +/* ------------------------------------------------------------------------ */ +/* Encoding */ +/* ------------------------------------------------------------------------ */ + +void +osmo_conv_encode_init(struct osmo_conv_encoder *encoder, + const struct osmo_conv_code *code) +{ + memset(encoder, 0x00, sizeof(struct osmo_conv_encoder)); + encoder->code = code; +} + +static inline int +_conv_encode_do_output(struct osmo_conv_encoder *encoder, + uint8_t out, ubit_t *output) +{ + const struct osmo_conv_code *code = encoder->code; + int o_idx = 0; + int j; + + if (code->puncture) { + for (j=0; j<code->N; j++) + { + int bit_no = code->N - j - 1; + int r_idx = encoder->i_idx * code->N + j; + + if (code->puncture[encoder->p_idx] == r_idx) + encoder->p_idx++; + else + output[o_idx++] = (out >> bit_no) & 1; + } + } else { + for (j=0; j<code->N; j++) + { + int bit_no = code->N - j - 1; + output[o_idx++] = (out >> bit_no) & 1; + } + } + + return o_idx; +} + +int +osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, + const ubit_t *input, ubit_t *output, int n) +{ + const struct osmo_conv_code *code = encoder->code; + uint8_t state; + int i; + int o_idx; + + o_idx = 0; + state = encoder->state; + + for (i=0; i<n; i++) { + int bit = input[i]; + uint8_t out; + + out = code->next_output[state][bit]; + state = code->next_state[state][bit]; + + o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); + + encoder->i_idx++; + } + + encoder->state = state; + + return o_idx; +} + +int +osmo_conv_encode_finish(struct osmo_conv_encoder *encoder, + ubit_t *output) +{ + const struct osmo_conv_code *code = encoder->code; + uint8_t state; + int n; + int i; + int o_idx; + + n = code->K - 1; + + o_idx = 0; + state = encoder->state; + + for (i=0; i<n; i++) { + uint8_t out; + + if (code->next_term_output) { + out = code->next_term_output[state]; + state = code->next_term_state[state]; + } else { + out = code->next_output[state][0]; + state = code->next_state[state][0]; + } + + o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); + + encoder->i_idx++; + } + + encoder->state = state; + + return o_idx; +} + +int +osmo_conv_encode(const struct osmo_conv_code *code, + const ubit_t *input, ubit_t *output) +{ + struct osmo_conv_encoder encoder; + int l; + + osmo_conv_encode_init(&encoder, code); + l = osmo_conv_encode_raw(&encoder, input, output, code->len); + l += osmo_conv_encode_finish(&encoder, &output[l]); + + return l; +} + + +/* ------------------------------------------------------------------------ */ +/* Decoding (viterbi) */ +/* ------------------------------------------------------------------------ */ + +#define MAX_AE 0x00ffffff + +void +osmo_conv_decode_init(struct osmo_conv_decoder *decoder, + const struct osmo_conv_code *code, int len) +{ + int n_states; + + /* Init */ + if (len <= 0) + len = code->len; + + n_states = 1 << (code->K - 1); + + memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); + + decoder->code = code; + decoder->n_states = n_states; + decoder->len = len; + + /* Allocate arrays */ + decoder->ae = malloc(sizeof(unsigned int) * n_states); + decoder->ae_next = malloc(sizeof(unsigned int) * n_states); + + decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1)); + + /* Classic reset */ + osmo_conv_decode_reset(decoder); +} + +void +osmo_conv_decode_reset(struct osmo_conv_decoder *decoder) +{ + int i; + + /* Reset indexes */ + decoder->o_idx = 0; + decoder->p_idx = 0; + + /* Initial error (only state 0 is valid) */ + decoder->ae[0] = 0; + for (i=1; i<decoder->n_states; i++) { + decoder->ae[i] = MAX_AE; + } +} + +void +osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder) +{ + free(decoder->ae); + free(decoder->ae_next); + free(decoder->state_history); + + memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); +} + +int +osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, + const sbit_t *input, int n) +{ + const struct osmo_conv_code *code = decoder->code; + + int i, s, b, j; + + int n_states; + unsigned int *ae; + unsigned int *ae_next; + uint8_t *state_history; + sbit_t *in_sym; + + int i_idx, p_idx; + + /* Prepare */ + n_states = decoder->n_states; + + ae = decoder->ae; + ae_next = decoder->ae_next; + state_history = &decoder->state_history[n_states * decoder->o_idx]; + + in_sym = alloca(sizeof(sbit_t) * code->N); + + i_idx = 0; + p_idx = decoder->p_idx; + + /* Scan the treillis */ + for (i=0; i<n; i++) + { + /* Reset next accumulated error */ + for (s=0; s<n_states; s++) { + ae_next[s] = MAX_AE; + } + + /* Get input */ + if (code->puncture) { + /* Hard way ... */ + for (j=0; j<code->N; j++) { + int idx = ((decoder->o_idx + i) * code->N) + j; + if (idx == code->puncture[p_idx]) { + in_sym[j] = 0; /* Undefined */ + p_idx++; + } else { + in_sym[j] = input[i_idx]; + i_idx++; + } + } + } else { + /* Easy, just copy N bits */ + memcpy(in_sym, &input[i_idx], code->N); + i_idx += code->N; + } + + /* Scan all state */ + for (s=0; s<n_states; s++) + { + /* Scan possible input bits */ + for (b=0; b<2; b++) + { + int nae, ov, e; + uint8_t m; + + /* Next output and state */ + uint8_t out = code->next_output[s][b]; + uint8_t state = code->next_state[s][b]; + + /* New error for this path */ + nae = ae[s]; /* start from last error */ + m = 1 << (code->N - 1); /* mask for 'out' bit selection */ + + for (j=0; j<code->N; j++) { + ov = (out & m) ? -127 : 127; /* sbit_t value for it */ + e = ((int)in_sym[j]) - ov; /* raw error for this bit */ + nae += (e * e) >> 9; /* acc the squared/scaled value */ + m >>= 1; /* next mask bit */ + } + + /* Is it survivor ? */ + if (ae_next[state] > nae) { + ae_next[state] = nae; + state_history[(n_states * i) + state] = s; + } + } + } + + /* Copy accumulated error */ + memcpy(ae, ae_next, sizeof(unsigned int) * n_states); + } + + /* Update decoder state */ + decoder->p_idx = p_idx; + decoder->o_idx += n; + + return i_idx; +} + +int +osmo_conv_decode_finish(struct osmo_conv_decoder *decoder, + const sbit_t *input) +{ + const struct osmo_conv_code *code = decoder->code; + + int i, s, j; + + int n_states; + unsigned int *ae; + unsigned int *ae_next; + uint8_t *state_history; + sbit_t *in_sym; + + int i_idx, p_idx; + + /* Prepare */ + n_states = decoder->n_states; + + ae = decoder->ae; + ae_next = decoder->ae_next; + state_history = &decoder->state_history[n_states * decoder->o_idx]; + + in_sym = alloca(sizeof(sbit_t) * code->N); + + i_idx = 0; + p_idx = decoder->p_idx; + + /* Scan the treillis */ + for (i=0; i<code->K-1; i++) + { + /* Reset next accumulated error */ + for (s=0; s<n_states; s++) { + ae_next[s] = MAX_AE; + } + + /* Get input */ + if (code->puncture) { + /* Hard way ... */ + for (j=0; j<code->N; j++) { + int idx = ((decoder->o_idx + i) * code->N) + j; + if (idx == code->puncture[p_idx]) { + in_sym[j] = 0; /* Undefined */ + p_idx++; + } else { + in_sym[j] = input[i_idx]; + i_idx++; + } + } + } else { + /* Easy, just copy N bits */ + memcpy(in_sym, &input[i_idx], code->N); + i_idx += code->N; + } + + /* Scan all state */ + for (s=0; s<n_states; s++) + { + int nae, ov, e; + uint8_t m; + + /* Next output and state */ + uint8_t out; + uint8_t state; + + if (code->next_term_output) { + out = code->next_term_output[s]; + state = code->next_term_state[s]; + } else { + out = code->next_output[s][0]; + state = code->next_state[s][0]; + } + + /* New error for this path */ + nae = ae[s]; /* start from last error */ + m = 1 << (code->N - 1); /* mask for 'out' bit selection */ + + for (j=0; j<code->N; j++) { + int is = (int)in_sym[j]; + if (is) { + ov = (out & m) ? -127 : 127; /* sbit_t value for it */ + e = is - ov; /* raw error for this bit */ + nae += (e * e) >> 9; /* acc the squared/scaled value */ + } + m >>= 1; /* next mask bit */ + } + + /* Is it survivor ? */ + if (ae_next[state] > nae) { + ae_next[state] = nae; + state_history[(n_states * i) + state] = s; + } + } + + /* Copy accumulated error */ + memcpy(ae, ae_next, sizeof(unsigned int) * n_states); + } + + /* Update decoder state */ + decoder->p_idx = p_idx; + decoder->o_idx += code->K - 1; + + return i_idx; +} + +int +osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, + ubit_t *output, int has_finish) +{ + const struct osmo_conv_code *code = decoder->code; + + int min_ae; + uint8_t min_state, cur_state; + int i, s, n; + + uint8_t *sh_ptr; + + /* Find state with least error */ + min_ae = MAX_AE; + min_state = 0xff; + + for (s=0; s<decoder->n_states; s++) + { + if (decoder->ae[s] < min_ae) { + min_ae = decoder->ae[s]; + min_state = s; + } + } + + if (min_state == 0xff) + return -1; + + /* Traceback */ + cur_state = min_state; + + n = decoder->o_idx; + + sh_ptr = &decoder->state_history[decoder->n_states * (n-1)]; + + /* No output for the K-1 termination input bits */ + if (has_finish) { + for (i=0; i<code->K-1; i++) { + cur_state = sh_ptr[cur_state]; + sh_ptr -= decoder->n_states; + } + n -= code->K - 1; + } + + /* Generate output backward */ + for (i=n-1; i>=0; i--) + { + min_state = cur_state; + cur_state = sh_ptr[cur_state]; + + sh_ptr -= decoder->n_states; + + if (code->next_state[cur_state][0] == min_state) + output[i] = 0; + else + output[i] = 1; + } + + return min_ae; +} + +int +osmo_conv_decode(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output) +{ + struct osmo_conv_decoder decoder; + int rv, l; + + osmo_conv_decode_init(&decoder, code, 0); + + l = osmo_conv_decode_scan(&decoder, input, code->len); + l = osmo_conv_decode_finish(&decoder, &input[l]); + + rv = osmo_conv_decode_get_output(&decoder, output, 1); + + osmo_conv_decode_deinit(&decoder); + + return rv; +} diff --git a/src/shared/libosmocore/src/crc16.c b/src/shared/libosmocore/src/crc16.c new file mode 100644 index 00000000..2741cf53 --- /dev/null +++ b/src/shared/libosmocore/src/crc16.c @@ -0,0 +1,62 @@ +/* + * This was copied from the linux kernel and adjusted for our types. + */ +/* + * crc16.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <osmocom/core/crc16.h> + +/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ +uint16_t const osmo_crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +/** + * crc16 - compute the CRC-16 for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer + * + * Returns the updated CRC value. + */ +uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len) +{ + while (len--) + crc = osmo_crc16_byte(crc, *buffer++); + return crc; +} diff --git a/src/shared/libosmocore/src/gsm/Makefile.am b/src/shared/libosmocore/src/gsm/Makefile.am new file mode 100644 index 00000000..55b772f1 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/Makefile.am @@ -0,0 +1,14 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=0:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +lib_LTLIBRARIES = libosmogsm.la + +libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \ + rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ + gprs_cipher_core.c gsm0480.c abis_nm.c +libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION) +libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la diff --git a/src/shared/libosmocore/src/gsm/a5.c b/src/shared/libosmocore/src/gsm/a5.c new file mode 100644 index 00000000..d3f2b2c3 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/a5.c @@ -0,0 +1,311 @@ +/* + * a5.c + * + * Full reimplementation of A5/1,2 (split and threadsafe) + * + * The logic behind the algorithm is taken from "A pedagogical implementation + * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by + * Marc Briceno, Ian Goldberg, and David Wagner. + * + * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <string.h> + +#include <osmocom/gsm/a5.h> + +void +osmo_a5(int n, uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) +{ + switch (n) + { + case 0: + if (dl) + memset(dl, 0x00, 114); + if (ul) + memset(ul, 0x00, 114); + break; + + case 1: + osmo_a5_1(key, fn, dl, ul); + break; + + case 2: + osmo_a5_2(key, fn, dl, ul); + break; + + default: + /* a5/[3..7] not supported here/yet */ + break; + } +} + + +/* ------------------------------------------------------------------------ */ +/* A5/1&2 common stuff */ +/* ------------------------------------------------------------------------ */ + +#define A5_R1_LEN 19 +#define A5_R2_LEN 22 +#define A5_R3_LEN 23 +#define A5_R4_LEN 17 /* A5/2 only */ + +#define A5_R1_MASK ((1<<A5_R1_LEN)-1) +#define A5_R2_MASK ((1<<A5_R2_LEN)-1) +#define A5_R3_MASK ((1<<A5_R3_LEN)-1) +#define A5_R4_MASK ((1<<A5_R4_LEN)-1) + +#define A5_R1_TAPS 0x072000 /* x^19 + x^5 + x^2 + x + 1 */ +#define A5_R2_TAPS 0x300000 /* x^22 + x + 1 */ +#define A5_R3_TAPS 0x700080 /* x^23 + x^15 + x^2 + x + 1 */ +#define A5_R4_TAPS 0x010800 /* x^17 + x^5 + 1 */ + +static inline uint32_t +_a5_12_parity(uint32_t x) +{ + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; +} + +static inline uint32_t +_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3) +{ + return (!!v1 + !!v2 + !!v3) >= 2; +} + +static inline uint32_t +_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps) +{ + return ((r << 1) & mask) | _a5_12_parity(r & taps); +} + + +/* ------------------------------------------------------------------------ */ +/* A5/1 */ +/* ------------------------------------------------------------------------ */ + +#define A51_R1_CLKBIT 0x000100 +#define A51_R2_CLKBIT 0x000400 +#define A51_R3_CLKBIT 0x000400 + +static inline void +_a5_1_clock(uint32_t r[], int force) +{ + int cb[3], maj; + + cb[0] = !!(r[0] & A51_R1_CLKBIT); + cb[1] = !!(r[1] & A51_R2_CLKBIT); + cb[2] = !!(r[2] & A51_R3_CLKBIT); + + maj = _a5_12_majority(cb[0], cb[1], cb[2]); + + if (force || (maj == cb[0])) + r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS); + + if (force || (maj == cb[1])) + r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS); + + if (force || (maj == cb[2])) + r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS); +} + +static inline uint8_t +_a5_1_get_output(uint32_t r[]) +{ + return (r[0] >> (A5_R1_LEN-1)) ^ + (r[1] >> (A5_R2_LEN-1)) ^ + (r[2] >> (A5_R3_LEN-1)); +} + +void +osmo_a5_1(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) +{ + uint32_t r[3] = {0, 0, 0}; + uint32_t fn_count; + uint32_t b; + int i; + + /* Key load */ + for (i=0; i<64; i++) + { + b = ( key[7 - (i>>3)] >> (i&7) ) & 1; + + _a5_1_clock(r, 1); + + r[0] ^= b; + r[1] ^= b; + r[2] ^= b; + } + + /* Frame count load */ + fn_count = osmo_a5_fn_count(fn); + + for (i=0; i<22; i++) + { + b = (fn_count >> i) & 1; + + _a5_1_clock(r, 1); + + r[0] ^= b; + r[1] ^= b; + r[2] ^= b; + } + + /* Mix */ + for (i=0; i<100; i++) + { + _a5_1_clock(r, 0); + } + + /* Output */ + for (i=0; i<114; i++) { + _a5_1_clock(r, 0); + if (dl) + dl[i] = _a5_1_get_output(r); + } + + for (i=0; i<114; i++) { + _a5_1_clock(r, 0); + if (ul) + ul[i] = _a5_1_get_output(r); + } +} + + +/* ------------------------------------------------------------------------ */ +/* A5/2 */ +/* ------------------------------------------------------------------------ */ + +#define A52_R4_CLKBIT0 0x000400 +#define A52_R4_CLKBIT1 0x000008 +#define A52_R4_CLKBIT2 0x000080 + +static inline void +_a5_2_clock(uint32_t r[], int force) +{ + int cb[3], maj; + + cb[0] = !!(r[3] & A52_R4_CLKBIT0); + cb[1] = !!(r[3] & A52_R4_CLKBIT1); + cb[2] = !!(r[3] & A52_R4_CLKBIT2); + + maj = (cb[0] + cb[1] + cb[2]) >= 2; + + if (force || (maj == cb[0])) + r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS); + + if (force || (maj == cb[1])) + r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS); + + if (force || (maj == cb[2])) + r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS); + + r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS); +} + +static inline uint8_t +_a5_2_get_output(uint32_t r[], uint8_t *db) +{ + uint8_t cb, tb; + + tb = (r[0] >> (A5_R1_LEN-1)) ^ + (r[1] >> (A5_R2_LEN-1)) ^ + (r[2] >> (A5_R3_LEN-1)); + + cb = *db; + + *db = ( tb ^ + _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^ + _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^ + _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000) + ); + + return cb; +} + +void +osmo_a5_2(uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) +{ + uint32_t r[4] = {0, 0, 0, 0}; + uint32_t fn_count; + uint32_t b; + uint8_t db = 0, o; + int i; + + /* Key load */ + for (i=0; i<64; i++) + { + b = ( key[7 - (i>>3)] >> (i&7) ) & 1; + + _a5_2_clock(r, 1); + + r[0] ^= b; + r[1] ^= b; + r[2] ^= b; + r[3] ^= b; + } + + /* Frame count load */ + fn_count = osmo_a5_fn_count(fn); + + for (i=0; i<22; i++) + { + b = (fn_count >> i) & 1; + + _a5_2_clock(r, 1); + + r[0] ^= b; + r[1] ^= b; + r[2] ^= b; + r[3] ^= b; + } + + r[0] |= 1 << 15; + r[1] |= 1 << 16; + r[2] |= 1 << 18; + r[3] |= 1 << 10; + + /* Mix */ + for (i=0; i<100; i++) + { + _a5_2_clock(r, 0); + } + + _a5_2_get_output(r, &db); + + + /* Output */ + for (i=0; i<114; i++) { + _a5_2_clock(r, 0); + o = _a5_2_get_output(r, &db); + if (dl) + dl[i] = o; + } + + for (i=0; i<114; i++) { + _a5_2_clock(r, 0); + o = _a5_2_get_output(r, &db); + if (ul) + ul[i] = o; + } +} diff --git a/src/shared/libosmocore/src/gsm/abis_nm.c b/src/shared/libosmocore/src/gsm/abis_nm.c new file mode 100644 index 00000000..a82194fd --- /dev/null +++ b/src/shared/libosmocore/src/gsm/abis_nm.c @@ -0,0 +1,428 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_12_21.h> +#include <osmocom/gsm/abis_nm.h> + +/* unidirectional messages from BTS to BSC */ +const enum abis_nm_msgtype abis_nm_reports[4] = { + NM_MT_SW_ACTIVATED_REP, + NM_MT_TEST_REP, + NM_MT_STATECHG_EVENT_REP, + NM_MT_FAILURE_EVENT_REP, +}; + +/* messages without ACK/NACK */ +const enum abis_nm_msgtype abis_nm_no_ack_nack[3] = { + NM_MT_MEAS_RES_REQ, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, +}; + +/* Messages related to software load */ +const enum abis_nm_msgtype abis_nm_sw_load_msgs[9] = { + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + //NM_MT_SW_ACT_REQ, + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, +}; + +const enum abis_nm_msgtype abis_nm_nacks[33] = { + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF_NACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK_NACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR_NACK, + NM_MT_PERF_TEST_NACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST_NACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS_NACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF_NACK, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES_NACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ_NACK, +}; + +static const struct value_string nack_names[] = { + { NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" }, + { NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" }, + { NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" }, + { NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" }, + { NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" }, + { NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" }, + { NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" }, + { NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" }, + { NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" }, + { NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" }, + { NM_MT_PERF_TEST_NACK, "PERFORM TEST" }, + { NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" }, + { NM_MT_STOP_TEST_NACK, "STOP TEST" }, + { NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" }, + { NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" }, + { NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" }, + { NM_MT_CHG_ADM_STATE_REQ_NACK, + "CHANGE ADMINISTRATIVE STATE REQUEST" }, + { NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" }, + { NM_MT_CHANGEOVER_NACK, "CHANGEOVER" }, + { NM_MT_OPSTART_NACK, "OPSTART" }, + { NM_MT_REINIT_NACK, "REINIT" }, + { NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" }, + { NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" }, + { NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" }, + { NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" }, + { NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" }, + { NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" }, + { NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" }, + { NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" }, + { 0, NULL } +}; + +const char *abis_nm_nack_name(uint8_t nack) +{ + return get_value_string(nack_names, nack); +} + +/* Chapter 9.4.36 */ +static const struct value_string nack_cause_names[] = { + /* General Nack Causes */ + { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, + { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, + { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, + { NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" }, + { NM_NACK_BTSNR_UNKN, "BTS no. unknown" }, + { NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" }, + { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, + { NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" }, + { NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" }, + { NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" }, + { NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" }, + { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" }, + { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, + /* Specific Nack Causes */ + { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, + { NM_NACK_RES_NOTAVAIL, "Resource not available" }, + { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, + { NM_NACK_TEST_NOTSUPP, "Test not supported" }, + { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, + { NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" }, + { NM_NACK_TEST_NOTINIT, "Test not initiated" }, + { NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" }, + { NM_NACK_TEST_NOSUCH, "No such test" }, + { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, + { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" }, + { NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" }, + { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, + { NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" }, + { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, + { NM_NACK_WAIT, "Wait" }, + { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, + { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, + { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, + { 0, NULL } +}; + +const char *abis_nm_nack_cause_name(uint8_t cause) +{ + return get_value_string(nack_cause_names, cause); +} + +/* Chapter 9.4.16: Event Type */ +static const struct value_string event_type_names[] = { + { NM_EVT_COMM_FAIL, "communication failure" }, + { NM_EVT_QOS_FAIL, "quality of service failure" }, + { NM_EVT_PROC_FAIL, "processing failure" }, + { NM_EVT_EQUIP_FAIL, "equipment failure" }, + { NM_EVT_ENV_FAIL, "environment failure" }, + { 0, NULL } +}; + +const char *abis_nm_event_type_name(uint8_t cause) +{ + return get_value_string(event_type_names, cause); +} + +/* Chapter 9.4.63: Perceived Severity */ +static const struct value_string severity_names[] = { + { NM_SEVER_CEASED, "failure ceased" }, + { NM_SEVER_CRITICAL, "critical failure" }, + { NM_SEVER_MAJOR, "major failure" }, + { NM_SEVER_MINOR, "minor failure" }, + { NM_SEVER_WARNING, "warning level failure" }, + { NM_SEVER_INDETERMINATE, "indeterminate failure" }, + { 0, NULL } +}; + +const char *abis_nm_severity_name(uint8_t cause) +{ + return get_value_string(severity_names, cause); +} + +/* Attributes that the BSC can set, not only get, according to Section 9.4 */ +const enum abis_nm_attr abis_nm_att_settable[] = { + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_DEST, + NM_ATT_EVENT_TYPE, + NM_ATT_FILE_DATA, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MDROP_LINK, + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_OUTST_ALARM, + NM_ATT_PHYS_CONF, + NM_ATT_PROB_CAUSE, + NM_ATT_RAD_SUBC, + NM_ATT_SOURCE, + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_WINDOW_SIZE, + NM_ATT_SEVERITY, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, +}; + +const struct tlv_definition abis_nm_att_tlvdef = { + .def = { + [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, + [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, + [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, + [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, + [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BSIC] = { TLV_TYPE_TV }, + [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, + [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, + [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, + [NM_ATT_DEST] = { TLV_TYPE_TL16V }, + [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_HSN] = { TLV_TYPE_TV }, + [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, + [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, + [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, + [NM_ATT_MAIO] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, + [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, + [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, + [NM_ATT_NY1] = { TLV_TYPE_TV }, + [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, + [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, + [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, + [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, + [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, + [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, + [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, + [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, + [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, + [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, + [NM_ATT_TEI] = { TLV_TYPE_TV }, + [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, + [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, + [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, + [NM_ATT_TSC] = { TLV_TYPE_TV }, + [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, + [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, + [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, + [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, + }, +}; + +const struct value_string abis_nm_obj_class_names[] = { + { NM_OC_SITE_MANAGER, "SITE-MANAGER" }, + { NM_OC_BTS, "BTS" }, + { NM_OC_RADIO_CARRIER, "RADIO-CARRIER" }, + { NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" }, + { NM_OC_CHANNEL, "CHANNEL" }, + { NM_OC_BS11_ADJC, "ADJC" }, + { NM_OC_BS11_HANDOVER, "HANDOVER" }, + { NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" }, + { NM_OC_BS11_BTSE, "BTSE" }, + { NM_OC_BS11_RACK, "RACK" }, + { NM_OC_BS11_TEST, "TEST" }, + { NM_OC_BS11_ENVABTSE, "ENVABTSE" }, + { NM_OC_BS11_BPORT, "BPORT" }, + { NM_OC_GPRS_NSE, "GPRS-NSE" }, + { NM_OC_GPRS_CELL, "GPRS-CELL" }, + { NM_OC_GPRS_NSVC, "GPRS-NSVC" }, + { NM_OC_BS11, "SIEMENSHW" }, + { 0, NULL } +}; + +const char *abis_nm_opstate_name(uint8_t os) +{ + switch (os) { + case NM_OPSTATE_DISABLED: + return "Disabled"; + case NM_OPSTATE_ENABLED: + return "Enabled"; + case NM_OPSTATE_NULL: + return "NULL"; + default: + return "RFU"; + } +} + +/* Chapter 9.4.7 */ +static const struct value_string avail_names[] = { + { 0, "In test" }, + { 1, "Failed" }, + { 2, "Power off" }, + { 3, "Off line" }, + /* Not used */ + { 5, "Dependency" }, + { 6, "Degraded" }, + { 7, "Not installed" }, + { 0xff, "OK" }, + { 0, NULL } +}; + +const char *abis_nm_avail_name(uint8_t avail) +{ + return get_value_string(avail_names, avail); +} + +static struct value_string test_names[] = { + /* FIXME: standard test names */ + { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, + { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, + { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, + { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" }, + { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, + { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" }, + { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" }, + { 0, NULL } +}; + +const char *abis_nm_test_name(uint8_t test) +{ + return get_value_string(test_names, test); +} + +const struct value_string abis_nm_adm_state_names[] = { + { NM_STATE_LOCKED, "Locked" }, + { NM_STATE_UNLOCKED, "Unlocked" }, + { NM_STATE_SHUTDOWN, "Shutdown" }, + { NM_STATE_NULL, "NULL" }, + { 0, NULL } +}; + +void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh) +{ + DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", + get_value_string(abis_nm_obj_class_names, foh->obj_class), + foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); +} + +static const enum abis_nm_chan_comb chcomb4pchan[] = { + [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, + [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb, + [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, + [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, + [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, + [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH, + [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH, + /* FIXME: bounds check */ +}; + +int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) +{ + if (pchan < ARRAY_SIZE(chcomb4pchan)) + return chcomb4pchan[pchan]; + + return -EINVAL; +} + +enum abis_nm_chan_comb abis_nm_pchan4chcomb(uint8_t chcomb) +{ + int i; + for (i = 0; i < ARRAY_SIZE(chcomb4pchan); i++) { + if (chcomb4pchan[i] == chcomb) + return i; + } + return GSM_PCHAN_NONE; +} diff --git a/src/shared/libosmocore/src/gsm/comp128.c b/src/shared/libosmocore/src/gsm/comp128.c new file mode 100644 index 00000000..5d5680c7 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/comp128.c @@ -0,0 +1,230 @@ +/* + * COMP128 implementation + * + * + * This code is inspired by original code from : + * Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>, + * and David Wagner <daw@cs.berkeley.edu> + * + * But it has been fully rewritten from various PDFs found online describing + * the algorithm because the licence of the code referenced above was unclear. + * A comment snippet from the original code is included below, it describes + * where the doc came from and how the algorithm was reverse engineered. + * + * + * (C) 2009 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* + * --- SNIP --- + * + * This code derived from a leaked document from the GSM standards. + * Some missing pieces were filled in by reverse-engineering a working SIM. + * We have verified that this is the correct COMP128 algorithm. + * + * The first page of the document identifies it as + * _Technical Information: GSM System Security Study_. + * 10-1617-01, 10th June 1988. + * The bottom of the title page is marked + * Racal Research Ltd. + * Worton Drive, Worton Grange Industrial Estate, + * Reading, Berks. RG2 0SB, England. + * Telephone: Reading (0734) 868601 Telex: 847152 + * The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy! + * + * Note: There are three typos in the spec (discovered by + * reverse-engineering). + * First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read + * "z = (2 * x[m] + x[n]) mod 2^(9-j)". + * Second, the "k" loop in the "Form bits from bytes" section is severely + * botched: the k index should run only from 0 to 3, and clearly the range + * on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8, + * to be consistent with the subsequent section). + * Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as + * claimed in the document. (And the document doesn't specify how Kc is + * derived, but that was also easily discovered with reverse engineering.) + * All of these typos have been corrected in the following code. + * + * --- /SNIP --- + */ + +#include <string.h> +#include <stdint.h> + +/* The compression tables (just copied ...) */ +static const uint8_t table_0[512] = { + 102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188, + 109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161, + 252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70, + 67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116, + 247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225, + 182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48, + 149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176, + 250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121, + 61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196, + 56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231, + 174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255, + 239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82, + 104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5, + 57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226, + 184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23, + 80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119, + 177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246, + 213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108, + 37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59, + 26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207, + 194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215, + 243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245, + 90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137, + 27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32, + 103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172, + 197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210, + 165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125, + 54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192, + 170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198, + 254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147, + 218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154, + 159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253, +}, table_1[256] = { + 19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43, + 27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5, + 35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6, + 53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20, + 90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78, + 76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51, + 57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67, + 12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75, + 95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29, + 82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114, + 82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74, + 113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73, + 118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83, + 104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126, + 31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52, + 101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35, +}, table_2[128] = { + 52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43, + 37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18, + 55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59, + 62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56, + 48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61, + 29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0, + 20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27, + 31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7, +}, table_3[64] = { + 1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31, + 28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9, + 20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10, + 17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19, +}, table_4[32] = { + 15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8, + 10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12, +}; + +static const uint8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 }; + + +static inline void +_comp128_compression_round(uint8_t *x, int n, const uint8_t *tbl) +{ + int i, j, m, a, b, y, z; + m = 4 - n; + for (i=0; i<(1<<n); i++) + for (j=0; j<(1<<m); j++) { + a = j + i * (2<<m); + b = a + (1<<m); + y = (x[a] + (x[b]<<1)) & ((32<<m)-1); + z = ((x[a]<<1) + x[b]) & ((32<<m)-1); + x[a] = tbl[y]; + x[b] = tbl[z]; + } +} + +static inline void +_comp128_compression(uint8_t *x) +{ + int n; + for (n=0; n<5; n++) + _comp128_compression_round(x, n, _comp128_table[n]); +} + +static inline void +_comp128_bitsfrombytes(uint8_t *x, uint8_t *bits) +{ + int i; + memset(bits, 0x00, 128); + for (i=0; i<128; i++) + if (x[i>>2] & (1<<(3-(i&3)))) + bits[i] = 1; +} + +static inline void +_comp128_permutation(uint8_t *x, uint8_t *bits) +{ + int i; + memset(&x[16], 0x00, 16); + for (i=0; i<128; i++) + x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7)); +} + +void +comp128(uint8_t *ki, uint8_t *rand, uint8_t *sres, uint8_t *kc) +{ + int i; + uint8_t x[32], bits[128]; + + /* x[16-31] = RAND */ + memcpy(&x[16], rand, 16); + + /* Round 1-7 */ + for (i=0; i<7; i++) { + /* x[0-15] = Ki */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* FormBitFromBytes */ + _comp128_bitsfrombytes(x, bits); + + /* Permutation */ + _comp128_permutation(x, bits); + } + + /* Round 8 (final) */ + /* x[0-15] = Ki */ + memcpy(x, ki, 16); + + /* Compression */ + _comp128_compression(x); + + /* Output stage */ + for (i=0; i<8; i+=2) + sres[i>>1] = x[i]<<4 | x[i+1]; + + for (i=0; i<12; i+=2) + kc[i>>1] = (x[i + 18] << 6) | + (x[i + 19] << 2) | + (x[i + 20] >> 2); + + kc[6] = (x[30]<<6) | (x[31]<<2); + kc[7] = 0; +} + diff --git a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c new file mode 100644 index 00000000..7884be01 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c @@ -0,0 +1,99 @@ +/* GPRS LLC cipher core infrastructure */ + +/* (C) 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <errno.h> +#include <stdint.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/plugin.h> + +#include <osmocom/crypt/gprs_cipher.h> + +static LLIST_HEAD(gprs_ciphers); + +static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM]; + +/* register a cipher with the core */ +int gprs_cipher_register(struct gprs_cipher_impl *ciph) +{ + if (ciph->algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + llist_add_tail(&ciph->list, &gprs_ciphers); + + /* check if we want to select this implementation over others */ + if (!selected_ciphers[ciph->algo] || + (selected_ciphers[ciph->algo]->priority > ciph->priority)) + selected_ciphers[ciph->algo] = ciph; + + return 0; +} + +/* load all available GPRS cipher plugins */ +int gprs_cipher_load(const char *path) +{ + /* load all plugins available from path */ + return osmo_plugin_load_all(path); +} + +/* function to be called by core code */ +int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, + uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir) +{ + if (algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + if (!selected_ciphers[algo]) + return -EINVAL; + + if (len > GSM0464_CIPH_MAX_BLOCK) + return -ERANGE; + + /* run the actual cipher from the plugin */ + return selected_ciphers[algo]->run(out, len, kc, iv, dir); +} + +int gprs_cipher_supported(enum gprs_ciph_algo algo) +{ + if (algo > ARRAY_SIZE(selected_ciphers)) + return -ERANGE; + + if (selected_ciphers[algo]) + return 1; + + return 0; +} + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc) +{ + uint32_t sx = ((1<<27) * sapi) + (1<<31); + + return (iov_ui ^ sx) + lfn + oc; +} + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc) +{ + return iov_i + lfn + oc; +} diff --git a/src/shared/libosmocore/src/gsm/gsm0480.c b/src/shared/libosmocore/src/gsm/gsm0480.c new file mode 100644 index 00000000..b9b3ed97 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm0480.c @@ -0,0 +1,461 @@ +/* Format functions for GSM 04.80 */ + +/* + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009 by Mike Haben <michael.haben@btinternet.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/gsm/gsm0480.h> +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/core/logging.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_04_80.h> + +#include <string.h> + +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) +{ + uint8_t *data = msgb_push(msgb, 2); + + data[0] = tag; + data[1] = msgb->len - 2; + return data; +} + +static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, + uint8_t value) +{ + uint8_t *data = msgb_push(msgb, 3); + + data[0] = tag; + data[1] = 1; + data[2] = value; + return data; +} + +/* wrap an invoke around it... the other way around + * + * 1.) Invoke Component tag + * 2.) Invoke ID Tag + * 3.) Operation + * 4.) Data + */ +int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id) +{ + /* 3. operation */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op); + + /* 2. invoke id tag */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id); + + /* 1. component tag */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE); + + return 0; +} + +/* wrap the GSM 04.08 Facility IE around it */ +int gsm0480_wrap_facility(struct msgb *msg) +{ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + return 0; +} + +struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text) +{ + struct msgb *msg; + uint8_t *seq_len_ptr, *ussd_len_ptr, *data; + int len; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + /* SEQUENCE { */ + msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); + seq_len_ptr = msgb_put(msg, 1); + + /* DCS { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x0F); + /* } DCS */ + + /* USSD-String { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + ussd_len_ptr = msgb_put(msg, 1); + data = msgb_put(msg, 0); + len = gsm_7bit_encode(data, text); + msgb_put(msg, len); + ussd_len_ptr[0] = len; + /* USSD-String } */ + + /* alertingPattern { */ + msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, alertPattern); + /* } alertingPattern */ + + seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3; + /* } SEQUENCE */ + + return msg; +} + +struct msgb *gsm0480_create_notifySS(const char *text) +{ + struct msgb *msg; + uint8_t *data, *tmp_len; + uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr; + int len; + + len = strlen(text); + if (len < 1 || len > 160) + return NULL; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); + seq_len_ptr = msgb_put(msg, 1); + + /* ss_code for CNAP { */ + msgb_put_u8(msg, 0x81); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x19); + /* } ss_code */ + + + /* nameIndicator { */ + msgb_put_u8(msg, 0xB4); + nam_len_ptr = msgb_put(msg, 1); + + /* callingName { */ + msgb_put_u8(msg, 0xA0); + opt_len_ptr = msgb_put(msg, 1); + msgb_put_u8(msg, 0xA0); + cal_len_ptr = msgb_put(msg, 1); + + /* namePresentationAllowed { */ + /* add the DCS value */ + msgb_put_u8(msg, 0x80); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0x0F); + + /* add the lengthInCharacters */ + msgb_put_u8(msg, 0x81); + msgb_put_u8(msg, 1); + msgb_put_u8(msg, strlen(text)); + + /* add the actual string */ + msgb_put_u8(msg, 0x82); + tmp_len = msgb_put(msg, 1); + data = msgb_put(msg, 0); + len = gsm_7bit_encode(data, text); + tmp_len[0] = len; + msgb_put(msg, len); + + /* }; namePresentationAllowed */ + + cal_len_ptr[0] = 3 + 3 + 2 + len; + opt_len_ptr[0] = cal_len_ptr[0] + 2; + /* }; callingName */ + + nam_len_ptr[0] = opt_len_ptr[0] + 2; + /* ); nameIndicator */ + + /* write the lengths... */ + seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2; + + return msg; +} + +/* Forward declarations */ +static int parse_ussd(const struct gsm48_hdr *hdr, + uint16_t len, struct ussd_request *req); +static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len, + struct ussd_request *req); +static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, + struct ussd_request *req); +static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, + struct ussd_request *req); +static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, + struct ussd_request *req); + +/* Decode a mobile-originated USSD-request message */ +int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, + struct ussd_request *req) +{ + int rc = 0; + + if (len < sizeof(*hdr) + 2) { + LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n"); + return 0; + } + + if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) { + req->transaction_id = hdr->proto_discr & 0x70; + rc = parse_ussd(hdr, len, req); + } + + if (!rc) + LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n"); + + return rc; +} + +static int parse_ussd(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req) +{ + int rc = 1; + uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */ + + switch (msg_type) { + case GSM0480_MTYPE_RELEASE_COMPLETE: + LOGP(0, LOGL_DEBUG, "USS Release Complete\n"); + /* could also parse out the optional Cause/Facility data */ + req->text[0] = 0xFF; + break; + case GSM0480_MTYPE_REGISTER: + case GSM0480_MTYPE_FACILITY: + rc &= parse_ussd_info_elements(&hdr->data[0], len - sizeof(*hdr), req); + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n", + hdr->msg_type); + rc = 0; + break; + } + + return rc; +} + +static int parse_ussd_info_elements(const uint8_t *ussd_ie, uint16_t len, + struct ussd_request *req) +{ + int rc = -1; + /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ + uint8_t iei; + uint8_t iei_length; + + iei = ussd_ie[0]; + iei_length = ussd_ie[1]; + + /* If the data does not fit, report an error */ + if (len - 2 < iei_length) + return 0; + + switch (iei) { + case GSM48_IE_CAUSE: + break; + case GSM0480_IE_FACILITY: + rc = parse_facility_ie(ussd_ie+2, iei_length, req); + break; + case GSM0480_IE_SS_VERSION: + break; + default: + LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n", + iei); + rc = 0; + break; + } + + return rc; +} + +static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset = 0; + + while (offset + 2 <= length) { + /* Component Type tag - table 3.7 */ + uint8_t component_type = facility_ie[offset]; + uint8_t component_length = facility_ie[offset+1]; + + /* size check */ + if (offset + 2 + component_length > length) { + LOGP(0, LOGL_ERROR, "Component does not fit.\n"); + return 0; + } + + switch (component_type) { + case GSM0480_CTYPE_INVOKE: + rc &= parse_ss_invoke(facility_ie+2, + component_length, + req); + break; + case GSM0480_CTYPE_RETURN_RESULT: + break; + case GSM0480_CTYPE_RETURN_ERROR: + break; + case GSM0480_CTYPE_REJECT: + break; + default: + LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility " + "Component Type 0x%02x\n", component_type); + rc = 0; + break; + } + offset += (component_length+2); + }; + + return rc; +} + +/* Parse an Invoke component - see table 3.3 */ +static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, + struct ussd_request *req) +{ + int rc = 1; + uint8_t offset; + + if (length < 3) + return 0; + + /* mandatory part */ + if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag " + "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]); + } + + offset = invoke_data[1] + 2; + req->invoke_id = invoke_data[2]; + + /* look ahead once */ + if (offset + 1 > length) + return 0; + + /* optional part */ + if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID) + offset += invoke_data[offset+1] + 2; /* skip over it */ + + /* mandatory part */ + if (invoke_data[offset] == GSM0480_OPERATION_CODE) { + if (offset + 2 > length) + return 0; + uint8_t operation_code = invoke_data[offset+2]; + switch (operation_code) { + case GSM0480_OP_CODE_PROCESS_USS_REQ: + rc = parse_process_uss_req(invoke_data + offset + 3, + length - offset - 3, + req); + break; + default: + LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x " + "is not yet handled\n", operation_code); + rc = 0; + break; + } + } else { + LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x " + "(expecting Operation Code tag)\n", + invoke_data[0]); + rc = 0; + } + + return rc; +} + +/* Parse the parameters of a Process UnstructuredSS Request */ +static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, + struct ussd_request *req) +{ + int rc = 0; + int num_chars; + uint8_t dcs; + + + /* we need at least that much */ + if (length < 8) + return 0; + + + if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) { + if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) { + dcs = uss_req_data[4]; + if ((dcs == 0x0F) && + (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) { + num_chars = (uss_req_data[6] * 8) / 7; + /* Prevent a mobile-originated buffer-overrun! */ + if (num_chars > MAX_LEN_USSD_STRING) + num_chars = MAX_LEN_USSD_STRING; + gsm_7bit_decode(req->text, + &(uss_req_data[7]), num_chars); + rc = 1; + } + } + } + return rc; +} + +struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text) +{ + struct msgb *msg; + struct gsm48_hdr *gh; + uint8_t *ptr8; + int response_len; + + msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); + if (!msg) + return NULL; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + response_len = gsm_7bit_encode(ptr8, text); + msgb_put(msg, response_len); + + /* Then wrap it as an Octet String */ + msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); + + /* Pre-pend the DCS octet string */ + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); + + /* Then wrap these as a Sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + GSM0480_OP_CODE_PROCESS_USS_REQ); + + /* Wrap the operation code and IA5 string as a sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); + + /* Wrap this up as a Return Result component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS | trans_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return msg; +} diff --git a/src/shared/libosmocore/src/gsm/gsm0808.c b/src/shared/libosmocore/src/gsm/gsm0808.c new file mode 100644 index 00000000..1640adc8 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm0808.c @@ -0,0 +1,369 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009,2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/gsm/gsm0808.h> +#include <osmocom/gsm/protocol/gsm_08_08.h> +#include <osmocom/gsm/gsm48.h> + +#include <arpa/inet.h> + +#define BSSMAP_MSG_SIZE 512 +#define BSSMAP_MSG_HEADROOM 128 + +static void put_data_16(uint8_t *data, const uint16_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, uint16_t _ci) +{ + uint8_t *data; + uint8_t *ci; + struct msgb* msg; + struct gsm48_loc_area_id *lai; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap cmpl l3"); + if (!msg) + return NULL; + + /* create the bssmap header */ + msg->l3h = msgb_put(msg, 2); + msg->l3h[0] = 0x0; + + /* create layer 3 header */ + data = msgb_put(msg, 1); + data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3; + + /* create the cell header */ + data = msgb_put(msg, 3); + data[0] = GSM0808_IE_CELL_IDENTIFIER; + data[1] = 1 + sizeof(*lai) + 2; + data[2] = CELL_IDENT_WHOLE_GLOBAL; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, cc, nc, lac); + + ci = msgb_put(msg, 2); + put_data_16(ci, htons(_ci)); + + /* copy the layer3 data */ + data = msgb_put(msg, msgb_l3len(msg_l3) + 2); + data[0] = GSM0808_IE_LAYER_3_INFORMATION; + data[1] = msgb_l3len(msg_l3); + memcpy(&data[2], msg_l3->l3h, data[1]); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + + return msg; +} + +struct msgb *gsm0808_create_reset(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: reset"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0x04; + msg->l3h[2] = 0x30; + msg->l3h[3] = 0x04; + msg->l3h[4] = 0x01; + msg->l3h[5] = 0x20; + return msg; +} + +struct msgb *gsm0808_create_clear_complete(void) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 1; + msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE; + + return msg; +} + +struct msgb *gsm0808_create_clear_command(uint8_t reason) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear command"); + if (!msg) + return NULL; + + msg->l3h = msgb_tv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 4); + msgb_v_put(msg, BSS_MAP_MSG_CLEAR_CMD); + msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &reason); + return msg; +} + +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "cipher-complete"); + if (!msg) + return NULL; + + /* send response with BSS override for A5/1... cheating */ + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE; + + /* include layer3 in case we have at least two octets */ + if (layer3 && msgb_l3len(layer3) > 2) { + msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2); + msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS; + msg->l4h[1] = msgb_l3len(layer3); + memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3)); + } + + /* and the optional BSS message */ + msg->l4h = msgb_put(msg, 2); + msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + msg->l4h[1] = alg_id; + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_cipher_reject(uint8_t cause) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear complete"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 2; + msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT; + msg->l3h[3] = cause; + + return msg; +} + +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "classmark-update"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE; + + msg->l4h = msgb_put(msg, length); + memcpy(msg->l4h, classmark_data, length); + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id) +{ + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: sapi 'n' reject"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 5); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 3; + msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT; + msg->l3h[3] = link_id; + msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED; + + return msg; +} + +struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode) +{ + uint8_t *data; + + struct msgb *msg = msgb_alloc(35, "bssmap: ass compl"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 3); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE; + + /* write 3.2.2.22 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = rr_cause; + + /* write cirtcuit identity code 3.2.2.2 */ + /* write cell identifier 3.2.2.17 */ + /* write chosen channel 3.2.2.33 when BTS picked it */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_CHANNEL; + data[1] = chosen_channel; + + /* write chosen encryption algorithm 3.2.2.44 */ + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_CHOSEN_ENCR_ALG; + data[1] = encr_alg_id; + + /* write circuit pool 3.2.2.45 */ + /* write speech version chosen: 3.2.2.51 when BTS picked it */ + if (speech_mode != 0) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_SPEECH_VERSION; + data[1] = speech_mode; + } + + /* write LSA identifier 3.2.2.15 */ + + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause) +{ + uint8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: ass fail"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 6); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 0xff; + msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = cause; + + /* RR cause 3.2.2.22 */ + if (rr_cause) { + data = msgb_put(msg, 2); + data[0] = GSM0808_IE_RR_CAUSE; + data[1] = *rr_cause; + } + + /* Circuit pool 3.22.45 */ + /* Circuit pool list 3.2.2.46 */ + + /* update the size */ + msg->l3h[1] = msgb_l3len(msg) - 2; + return msg; +} + +struct msgb *gsm0808_create_clear_rqst(uint8_t cause) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "bssmap: clear rqst"); + if (!msg) + return NULL; + + msg->l3h = msgb_put(msg, 2 + 4); + msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT; + msg->l3h[1] = 4; + + msg->l3h[2] = BSS_MAP_MSG_CLEAR_RQST; + msg->l3h[3] = GSM0808_IE_CAUSE; + msg->l3h[4] = 1; + msg->l3h[5] = cause; + return msg; +} + +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id) +{ + uint8_t *hh = msgb_push(msg, 3); + hh[0] = BSSAP_MSG_DTAP; + hh[1] = link_id; + hh[2] = msg->len - 3; +} + +struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id) +{ + struct dtap_header *header; + uint8_t *data; + struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, + "dtap"); + if (!msg) + return NULL; + + /* DTAP header */ + msg->l3h = msgb_put(msg, sizeof(*header)); + header = (struct dtap_header *) &msg->l3h[0]; + header->type = BSSAP_MSG_DTAP; + header->link_id = link_id; + header->length = msgb_l3len(msg_l3); + + /* Payload */ + data = msgb_put(msg, header->length); + memcpy(data, msg_l3->l3h, header->length); + + return msg; +} + +static const struct tlv_definition bss_att_tlvdef = { + .def = { + [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_TMSI] = { TLV_TYPE_TLV }, + [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV }, + [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV }, + [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV }, + [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 }, + [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV }, + [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV }, + [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV }, + [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV }, + [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T }, + [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV }, + [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV }, + [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TLV }, + [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, + [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV }, + [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV }, + [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV }, + [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV }, + [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV }, + }, +}; + +const struct tlv_definition *gsm0808_att_tlvdef() +{ + return &bss_att_tlvdef; +} diff --git a/src/shared/libosmocore/src/gsm/gsm48.c b/src/shared/libosmocore/src/gsm/gsm48.c new file mode 100644 index 00000000..d0345892 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm48.c @@ -0,0 +1,415 @@ +/* GSM Mobile Radio Interface Layer 3 messages + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2008, 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <arpa/inet.h> + +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> + +const struct tlv_definition gsm48_att_tlvdef = { + .def = { + [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, + [GSM48_IE_UTC] = { TLV_TYPE_TV }, + [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, + + [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_CAUSE] = { TLV_TYPE_TLV }, + [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV }, + [GSM48_IE_ALERT] = { TLV_TYPE_TLV }, + [GSM48_IE_FACILITY] = { TLV_TYPE_TLV }, + [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV }, + [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV }, + [GSM48_IE_NOTIFY] = { TLV_TYPE_TV }, + [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV }, + [GSM48_IE_SIGNAL] = { TLV_TYPE_TV }, + [GSM48_IE_CONN_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CONN_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV }, + [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV }, + [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV }, + [GSM48_IE_USER_USER] = { TLV_TYPE_TLV }, + [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV }, + [GSM48_IE_MORE_DATA] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T }, + [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T }, + [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_CIR] = { TLV_TYPE_T }, + [GSM48_IE_REPEAT_SEQ] = { TLV_TYPE_T }, + /* FIXME: more elements */ + }, +}; + +/* RR elements */ +const struct tlv_definition gsm48_rr_att_tlvdef = { + .def = { + /* NOTE: Don't add IE 17 = MOBILE_ID here, it already used. */ + [GSM48_IE_VGCS_TARGET] = { TLV_TYPE_TLV }, + [GSM48_IE_FRQSHORT_AFTER] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_MUL_RATE_CFG] = { TLV_TYPE_TLV }, + [GSM48_IE_FREQ_L_AFTER] = { TLV_TYPE_TLV }, + [GSM48_IE_MSLOT_DESC] = { TLV_TYPE_TLV }, + [GSM48_IE_CHANMODE_2] = { TLV_TYPE_TV }, + [GSM48_IE_FRQSHORT_BEFORE] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_CHANMODE_3] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_4] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_5] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_6] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_7] = { TLV_TYPE_TV }, + [GSM48_IE_CHANMODE_8] = { TLV_TYPE_TV }, + [GSM48_IE_FREQ_L_BEFORE] = { TLV_TYPE_TLV }, + [GSM48_IE_CH_DESC_1_BEFORE] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_CH_DESC_2_BEFORE] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_F_CH_SEQ_BEFORE] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_CLASSMARK3] = { TLV_TYPE_TLV }, + [GSM48_IE_MA_BEFORE] = { TLV_TYPE_TLV }, + [GSM48_IE_RR_PACKET_UL] = { TLV_TYPE_TLV }, + [GSM48_IE_RR_PACKET_DL] = { TLV_TYPE_TLV }, + [GSM48_IE_CELL_CH_DESC] = { TLV_TYPE_FIXED, 16 }, + [GSM48_IE_CHANMODE_1] = { TLV_TYPE_TV }, + [GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV }, + [GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 }, + [GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV }, + [GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV }, + [GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV }, + [GSM48_IE_BA_LIST_PREF] = { TLV_TYPE_TLV }, + [GSM48_IE_MOB_OVSERV_DIF] = { TLV_TYPE_TLV }, + [GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV }, + [GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, + [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV }, + }, +}; + +/* MM elements */ +const struct tlv_definition gsm48_mm_att_tlvdef = { + .def = { + [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV }, + [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV }, + [GSM48_IE_UTC] = { TLV_TYPE_TV }, + [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 }, + [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV }, + + [GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 }, + [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV }, + [GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T }, + [GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T }, + }, +}; + +static const struct value_string rr_cause_names[] = { + { GSM48_RR_CAUSE_NORMAL, "Normal event" }, + { GSM48_RR_CAUSE_ABNORMAL_UNSPEC, "Abnormal release, unspecified" }, + { GSM48_RR_CAUSE_ABNORMAL_UNACCT, "Abnormal release, channel unacceptable" }, + { GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" }, + { GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" }, + { GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" }, + { GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" }, + { GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" }, + { GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" }, + { GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" }, + { GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" }, + { GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" }, + { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" }, + { GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" }, + { GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" }, + { GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" }, + { GSM48_RR_CAUSE_PROT_ERROR_UNSPC, "Protocol error unspecified" }, + { 0, NULL }, +}; + +/* FIXME: convert to value_string */ +static const char *cc_state_names[32] = { + "NULL", + "INITIATED", + "MM_CONNECTION_PEND", + "MO_CALL_PROC", + "CALL_DELIVERED", + "illegal state 5", + "CALL_PRESENT", + "CALL_RECEIVED", + "CONNECT_REQUEST", + "MO_TERM_CALL_CONF", + "ACTIVE", + "DISCONNECT_REQ", + "DISCONNECT_IND", + "illegal state 13", + "illegal state 14", + "illegal state 15", + "illegal state 16", + "illegal state 17", + "illegal state 18", + "RELEASE_REQ", + "illegal state 20", + "illegal state 21", + "illegal state 22", + "illegal state 23", + "illegal state 24", + "illegal state 25", + "MO_ORIG_MODIFY", + "MO_TERM_MODIFY", + "CONNECT_IND", + "illegal state 29", + "illegal state 30", + "illegal state 31", +}; + +const char *gsm48_cc_state_name(uint8_t state) +{ + if (state < ARRAY_SIZE(cc_state_names)) + return cc_state_names[state]; + + return "invalid"; +} + +static const struct value_string cc_msg_names[] = { + { GSM48_MT_CC_ALERTING, "ALERTING" }, + { GSM48_MT_CC_CALL_PROC, "CALL_PROC" }, + { GSM48_MT_CC_PROGRESS, "PROGRESS" }, + { GSM48_MT_CC_ESTAB, "ESTAB" }, + { GSM48_MT_CC_SETUP, "SETUP" }, + { GSM48_MT_CC_ESTAB_CONF, "ESTAB_CONF" }, + { GSM48_MT_CC_CONNECT, "CONNECT" }, + { GSM48_MT_CC_CALL_CONF, "CALL_CONF" }, + { GSM48_MT_CC_START_CC, "START_CC" }, + { GSM48_MT_CC_RECALL, "RECALL" }, + { GSM48_MT_CC_EMERG_SETUP, "EMERG_SETUP" }, + { GSM48_MT_CC_CONNECT_ACK, "CONNECT_ACK" }, + { GSM48_MT_CC_USER_INFO, "USER_INFO" }, + { GSM48_MT_CC_MODIFY_REJECT, "MODIFY_REJECT" }, + { GSM48_MT_CC_MODIFY, "MODIFY" }, + { GSM48_MT_CC_HOLD, "HOLD" }, + { GSM48_MT_CC_HOLD_ACK, "HOLD_ACK" }, + { GSM48_MT_CC_HOLD_REJ, "HOLD_REJ" }, + { GSM48_MT_CC_RETR, "RETR" }, + { GSM48_MT_CC_RETR_ACK, "RETR_ACK" }, + { GSM48_MT_CC_RETR_REJ, "RETR_REJ" }, + { GSM48_MT_CC_MODIFY_COMPL, "MODIFY_COMPL" }, + { GSM48_MT_CC_DISCONNECT, "DISCONNECT" }, + { GSM48_MT_CC_RELEASE_COMPL, "RELEASE_COMPL" }, + { GSM48_MT_CC_RELEASE, "RELEASE" }, + { GSM48_MT_CC_STOP_DTMF, "STOP_DTMF" }, + { GSM48_MT_CC_STOP_DTMF_ACK, "STOP_DTMF_ACK" }, + { GSM48_MT_CC_STATUS_ENQ, "STATUS_ENQ" }, + { GSM48_MT_CC_START_DTMF, "START_DTMF" }, + { GSM48_MT_CC_START_DTMF_ACK, "START_DTMF_ACK" }, + { GSM48_MT_CC_START_DTMF_REJ, "START_DTMF_REJ" }, + { GSM48_MT_CC_CONG_CTRL, "CONG_CTRL" }, + { GSM48_MT_CC_FACILITY, "FACILITY" }, + { GSM48_MT_CC_STATUS, "STATUS" }, + { GSM48_MT_CC_NOTIFY, "NOTFIY" }, + { 0, NULL } +}; + +const char *gsm48_cc_msg_name(uint8_t msgtype) +{ + return get_value_string(cc_msg_names, msgtype); +} + +const char *rr_cause_name(uint8_t cause) +{ + return get_value_string(rr_cause_names, cause); +} + +static void to_bcd(uint8_t *bcd, uint16_t val) +{ + bcd[2] = val % 10; + val = val / 10; + bcd[1] = val % 10; + val = val / 10; + bcd[0] = val % 10; + val = val / 10; +} + +void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, + uint16_t mnc, uint16_t lac) +{ + uint8_t bcd[3]; + + to_bcd(bcd, mcc); + lai48->digits[0] = bcd[0] | (bcd[1] << 4); + lai48->digits[1] = bcd[2]; + + to_bcd(bcd, mnc); + /* FIXME: do we need three-digit MNC? See Table 10.5.3 */ + if (mnc > 99) { + lai48->digits[1] |= bcd[2] << 4; + lai48->digits[2] = bcd[0] | (bcd[1] << 4); + } else { + lai48->digits[1] |= 0xf << 4; + lai48->digits[2] = bcd[1] | (bcd[2] << 4); + } + + lai48->lac = htons(lac); +} + +int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi) +{ + uint32_t *tptr = (uint32_t *) &buf[3]; + + buf[0] = GSM48_IE_MOBILE_ID; + buf[1] = GSM48_TMSI_LEN; + buf[2] = 0xf0 | GSM_MI_TYPE_TMSI; + *tptr = htonl(tmsi); + + return 7; +} + +int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi) +{ + unsigned int length = strlen(imsi), i, off = 0; + uint8_t odd = (length & 0x1) == 1; + + buf[0] = GSM48_IE_MOBILE_ID; + buf[2] = osmo_char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3); + + /* if the length is even we will fill half of the last octet */ + if (odd) + buf[1] = (length + 1) >> 1; + else + buf[1] = (length + 2) >> 1; + + for (i = 1; i < buf[1]; ++i) { + uint8_t lower, upper; + + lower = osmo_char2bcd(imsi[++off]); + if (!odd && off + 1 == length) + upper = 0x0f; + else + upper = osmo_char2bcd(imsi[++off]) & 0x0f; + + buf[2 + i] = (upper << 4) | lower; + } + + return 2 + buf[1]; +} + +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi, + const int mi_len) +{ + int i; + uint8_t mi_type; + char *str_cur = string; + uint32_t tmsi; + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + + switch (mi_type) { + case GSM_MI_TYPE_NONE: + break; + case GSM_MI_TYPE_TMSI: + /* Table 10.5.4.3, reverse generate_mid_from_tmsi */ + if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) { + memcpy(&tmsi, &mi[1], 4); + tmsi = ntohl(tmsi); + return snprintf(string, str_len, "%u", tmsi); + } + break; + case GSM_MI_TYPE_IMSI: + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + *str_cur++ = osmo_bcd2char(mi[0] >> 4); + + for (i = 1; i < mi_len; i++) { + if (str_cur + 2 >= string + str_len) + return str_cur - string; + *str_cur++ = osmo_bcd2char(mi[i] & 0xf); + /* skip last nibble in last input byte when GSM_EVEN */ + if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD)) + *str_cur++ = osmo_bcd2char(mi[i] >> 4); + } + break; + default: + break; + } + *str_cur++ = '\0'; + + return str_cur - string; +} + +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) +{ + raid->mcc = (buf[0] & 0xf) * 100; + raid->mcc += (buf[0] >> 4) * 10; + raid->mcc += (buf[1] & 0xf) * 1; + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if ((buf[1] >> 4) == 0xf) { + raid->mnc = (buf[2] & 0xf) * 10; + raid->mnc += (buf[2] >> 4) * 1; + } else { + raid->mnc = (buf[2] & 0xf) * 100; + raid->mnc += (buf[2] >> 4) * 10; + raid->mnc += (buf[1] >> 4) * 1; + } + + raid->lac = ntohs(*(uint16_t *)(buf + 3)); + raid->rac = buf[5]; +} + +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) +{ + uint16_t mcc = raid->mcc; + uint16_t mnc = raid->mnc; + + buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4); + buf[1] = (mcc % 10); + + /* I wonder who came up with the stupidity of encoding the MNC + * differently depending on how many digits its decimal number has! */ + if (mnc < 100) { + buf[1] |= 0xf0; + buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4); + } else { + buf[1] |= (mnc % 10) << 4; + buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4); + } + + *(uint16_t *)(buf+3) = htons(raid->lac); + + buf[5] = raid->rac; + + return 6; +} diff --git a/src/shared/libosmocore/src/gsm/gsm48_ie.c b/src/shared/libosmocore/src/gsm/gsm48_ie.c new file mode 100644 index 00000000..efcf2816 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm48_ie.c @@ -0,0 +1,1095 @@ +/* GSM Mobile Radio Interface Layer 3 messages + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/mncc.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm48_ie.h> + +static const char bcd_num_digits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '*', '#', 'a', 'b', 'c', '\0' +}; + +/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */ +int gsm48_decode_bcd_number(char *output, int output_len, + const uint8_t *bcd_lv, int h_len) +{ + uint8_t in_len = bcd_lv[0]; + int i; + + for (i = 1 + h_len; i <= in_len; i++) { + /* lower nibble */ + output_len--; + if (output_len <= 1) + break; + *output++ = bcd_num_digits[bcd_lv[i] & 0xf]; + + /* higher nibble */ + output_len--; + if (output_len <= 1) + break; + *output++ = bcd_num_digits[bcd_lv[i] >> 4]; + } + if (output_len >= 1) + *output++ = '\0'; + + return 0; +} + +/* convert a single ASCII character to call-control BCD */ +static int asc_to_bcd(const char asc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) { + if (bcd_num_digits[i] == asc) + return i; + } + return -EINVAL; +} + +/* convert a ASCII phone number to 'called/calling/connect party BCD number' */ +int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, + int h_len, const char *input) +{ + int in_len = strlen(input); + int i; + uint8_t *bcd_cur = bcd_lv + 1 + h_len; + + /* two digits per byte, plus type byte */ + bcd_lv[0] = in_len/2 + h_len; + if (in_len % 2) + bcd_lv[0]++; + + if (bcd_lv[0] > max_len) + return -EIO; + + for (i = 0; i < in_len; i++) { + int rc = asc_to_bcd(input[i]); + if (rc < 0) + return rc; + if (i % 2 == 0) + *bcd_cur = rc; + else + *bcd_cur++ |= (rc << 4); + } + /* append padding nibble in case of odd length */ + if (i % 2) + *bcd_cur++ |= 0xf0; + + /* return how many bytes we used */ + return (bcd_cur - bcd_lv); +} + +/* decode 'bearer capability' */ +int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + int i, s; + + if (in_len < 1) + return -EINVAL; + + bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */ + + /* octet 3 */ + bcap->transfer = lv[1] & 0x07; + bcap->mode = (lv[1] & 0x08) >> 3; + bcap->coding = (lv[1] & 0x10) >> 4; + bcap->radio = (lv[1] & 0x60) >> 5; + + if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) { + i = 1; + s = 0; + while(!(lv[i] & 0x80)) { + i++; /* octet 3a etc */ + if (in_len < i) + return 0; + bcap->speech_ver[s++] = lv[i] & 0x0f; + bcap->speech_ver[s] = -1; /* end of list */ + if (i == 2) /* octet 3a */ + bcap->speech_ctm = (lv[i] & 0x20) >> 5; + if (s == 7) /* maximum speech versions + end of list */ + return 0; + } + } else { + i = 1; + while (!(lv[i] & 0x80)) { + i++; /* octet 3a etc */ + if (in_len < i) + return 0; + /* ignore them */ + } + /* FIXME: implement OCTET 4+ parsing */ + } + + return 0; +} + +/* encode 'bearer capability' */ +int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only, + const struct gsm_mncc_bearer_cap *bcap) +{ + uint8_t lv[32 + 1]; + int i = 1, s; + + lv[1] = bcap->transfer; + lv[1] |= bcap->mode << 3; + lv[1] |= bcap->coding << 4; + lv[1] |= bcap->radio << 5; + + if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) { + for (s = 0; bcap->speech_ver[s] >= 0; s++) { + i++; /* octet 3a etc */ + lv[i] = bcap->speech_ver[s]; + if (i == 2) /* octet 3a */ + lv[i] |= bcap->speech_ctm << 5; + } + lv[i] |= 0x80; /* last IE of octet 3 etc */ + } else { + /* FIXME: implement OCTET 4+ encoding */ + } + + lv[0] = i; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1); + + return 0; +} + +/* decode 'call control cap' */ +int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + /* octet 3 */ + ccap->dtmf = lv[1] & 0x01; + ccap->pcp = (lv[1] & 0x02) >> 1; + + return 0; +} + +/* encode 'call control cap' */ +int gsm48_encode_cccap(struct msgb *msg, + const struct gsm_mncc_cccap *ccap) +{ + uint8_t lv[2]; + + lv[0] = 1; + lv[1] = 0; + if (ccap->dtmf) + lv [1] |= 0x01; + if (ccap->pcp) + lv [1] |= 0x02; + + msgb_tlv_put(msg, GSM48_IE_CC_CAP, lv[0], lv+1); + + return 0; +} + +/* decode 'called party BCD number' */ +int gsm48_decode_called(struct gsm_mncc_number *called, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + /* octet 3 */ + called->plan = lv[1] & 0x0f; + called->type = (lv[1] & 0x70) >> 4; + + /* octet 4..N */ + gsm48_decode_bcd_number(called->number, sizeof(called->number), lv, 1); + + return 0; +} + +/* encode 'called party BCD number' */ +int gsm48_encode_called(struct msgb *msg, + const struct gsm_mncc_number *called) +{ + uint8_t lv[18]; + int ret; + + /* octet 3 */ + lv[1] = 0x80; /* no extension */ + lv[1] |= called->plan; + lv[1] |= called->type << 4; + + /* octet 4..N, octet 2 */ + ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number); + if (ret < 0) + return ret; + + msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1); + + return 0; +} + +/* decode callerid of various IEs */ +int gsm48_decode_callerid(struct gsm_mncc_number *callerid, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + int i = 1; + + if (in_len < 1) + return -EINVAL; + + /* octet 3 */ + callerid->plan = lv[1] & 0x0f; + callerid->type = (lv[1] & 0x70) >> 4; + + /* octet 3a */ + if (!(lv[1] & 0x80)) { + callerid->screen = lv[2] & 0x03; + callerid->present = (lv[2] & 0x60) >> 5; + i = 2; + } + + /* octet 4..N */ + gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i); + + return 0; +} + +/* encode callerid of various IEs */ +int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len, + const struct gsm_mncc_number *callerid) +{ + uint8_t lv[max_len - 1]; + int h_len = 1; + int ret; + + /* octet 3 */ + lv[1] = callerid->plan; + lv[1] |= callerid->type << 4; + + if (callerid->present || callerid->screen) { + /* octet 3a */ + lv[2] = callerid->screen; + lv[2] |= callerid->present << 5; + lv[2] |= 0x80; + h_len++; + } else + lv[1] |= 0x80; + + /* octet 4..N, octet 2 */ + ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number); + if (ret < 0) + return ret; + + msgb_tlv_put(msg, ie, lv[0], lv+1); + + return 0; +} + +/* decode 'cause' */ +int gsm48_decode_cause(struct gsm_mncc_cause *cause, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + int i; + + if (in_len < 2) + return -EINVAL; + + cause->diag_len = 0; + + /* octet 3 */ + cause->location = lv[1] & 0x0f; + cause->coding = (lv[1] & 0x60) >> 5; + + i = 1; + if (!(lv[i] & 0x80)) { + i++; /* octet 3a */ + if (in_len < i+1) + return 0; + cause->rec = 1; + cause->rec_val = lv[i] & 0x7f; + } + i++; + + /* octet 4 */ + cause->value = lv[i] & 0x7f; + i++; + + if (in_len < i) /* no diag */ + return 0; + + if (in_len - (i-1) > 32) /* maximum 32 octets */ + return 0; + + /* octet 5-N */ + memcpy(cause->diag, lv + i, in_len - (i-1)); + cause->diag_len = in_len - (i-1); + + return 0; +} + +/* encode 'cause' */ +int gsm48_encode_cause(struct msgb *msg, int lv_only, + const struct gsm_mncc_cause *cause) +{ + uint8_t lv[32+4]; + int i; + + if (cause->diag_len > 32) + return -EINVAL; + + /* octet 3 */ + lv[1] = cause->location; + lv[1] |= cause->coding << 5; + + i = 1; + if (cause->rec) { + i++; /* octet 3a */ + lv[i] = cause->rec_val; + } + lv[i] |= 0x80; /* end of octet 3 */ + + /* octet 4 */ + i++; + lv[i] = 0x80 | cause->value; + + /* octet 5-N */ + if (cause->diag_len) { + memcpy(lv + i, cause->diag, cause->diag_len); + i += cause->diag_len; + } + + lv[0] = i; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1); + + return 0; +} + +/* decode 'calling number' */ +int gsm48_decode_calling(struct gsm_mncc_number *calling, + const uint8_t *lv) +{ + return gsm48_decode_callerid(calling, lv); +} + +/* encode 'calling number' */ +int gsm48_encode_calling(struct msgb *msg, + const struct gsm_mncc_number *calling) +{ + return gsm48_encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling); +} + +/* decode 'connected number' */ +int gsm48_decode_connected(struct gsm_mncc_number *connected, + const uint8_t *lv) +{ + return gsm48_decode_callerid(connected, lv); +} + +/* encode 'connected number' */ +int gsm48_encode_connected(struct msgb *msg, + const struct gsm_mncc_number *connected) +{ + return gsm48_encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected); +} + +/* decode 'redirecting number' */ +int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting, + const uint8_t *lv) +{ + return gsm48_decode_callerid(redirecting, lv); +} + +/* encode 'redirecting number' */ +int gsm48_encode_redirecting(struct msgb *msg, + const struct gsm_mncc_number *redirecting) +{ + return gsm48_encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting); +} + +/* decode 'facility' */ +int gsm48_decode_facility(struct gsm_mncc_facility *facility, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + if (in_len > sizeof(facility->info)) + return -EINVAL; + + memcpy(facility->info, lv+1, in_len); + facility->len = in_len; + + return 0; +} + +/* encode 'facility' */ +int gsm48_encode_facility(struct msgb *msg, int lv_only, + const struct gsm_mncc_facility *facility) +{ + uint8_t lv[GSM_MAX_FACILITY + 1]; + + if (facility->len < 1 || facility->len > GSM_MAX_FACILITY) + return -EINVAL; + + memcpy(lv+1, facility->info, facility->len); + lv[0] = facility->len; + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1); + + return 0; +} + +/* decode 'notify' */ +int gsm48_decode_notify(int *notify, const uint8_t *v) +{ + *notify = v[0] & 0x7f; + + return 0; +} + +/* encode 'notify' */ +int gsm48_encode_notify(struct msgb *msg, int notify) +{ + msgb_v_put(msg, notify | 0x80); + + return 0; +} + +/* decode 'signal' */ +int gsm48_decode_signal(int *signal, const uint8_t *v) +{ + *signal = v[0]; + + return 0; +} + +/* encode 'signal' */ +int gsm48_encode_signal(struct msgb *msg, int signal) +{ + msgb_tv_put(msg, GSM48_IE_SIGNAL, signal); + + return 0; +} + +/* decode 'keypad' */ +int gsm48_decode_keypad(int *keypad, const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 1) + return -EINVAL; + + *keypad = lv[1] & 0x7f; + + return 0; +} + +/* encode 'keypad' */ +int gsm48_encode_keypad(struct msgb *msg, int keypad) +{ + msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad); + + return 0; +} + +/* decode 'progress' */ +int gsm48_decode_progress(struct gsm_mncc_progress *progress, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 2) + return -EINVAL; + + progress->coding = (lv[1] & 0x60) >> 5; + progress->location = lv[1] & 0x0f; + progress->descr = lv[2] & 0x7f; + + return 0; +} + +/* encode 'progress' */ +int gsm48_encode_progress(struct msgb *msg, int lv_only, + const struct gsm_mncc_progress *p) +{ + uint8_t lv[3]; + + lv[0] = 2; + lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf); + lv[2] = 0x80 | (p->descr & 0x7f); + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1); + + return 0; +} + +/* decode 'user-user' */ +int gsm48_decode_useruser(struct gsm_mncc_useruser *uu, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + char *info = uu->info; + int info_len = sizeof(uu->info); + int i; + + if (in_len < 1) + return -EINVAL; + + uu->proto = lv[1]; + + for (i = 2; i <= in_len; i++) { + info_len--; + if (info_len <= 1) + break; + *info++ = lv[i]; + } + if (info_len >= 1) + *info++ = '\0'; + + return 0; +} + +/* encode 'useruser' */ +int gsm48_encode_useruser(struct msgb *msg, int lv_only, + const struct gsm_mncc_useruser *uu) +{ + uint8_t lv[GSM_MAX_USERUSER + 2]; + + if (strlen(uu->info) > GSM_MAX_USERUSER) + return -EINVAL; + + lv[0] = 1 + strlen(uu->info); + lv[1] = uu->proto; + memcpy(lv + 2, uu->info, strlen(uu->info)); + if (lv_only) + msgb_lv_put(msg, lv[0], lv+1); + else + msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1); + + return 0; +} + +/* decode 'ss version' */ +int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + + if (in_len < 1 || in_len < sizeof(ssv->info)) + return -EINVAL; + + memcpy(ssv->info, lv + 1, in_len); + ssv->len = in_len; + + return 0; +} + +/* encode 'ss version' */ +int gsm48_encode_ssversion(struct msgb *msg, + const struct gsm_mncc_ssversion *ssv) +{ + uint8_t lv[GSM_MAX_SSVERSION + 1]; + + if (ssv->len > GSM_MAX_SSVERSION) + return -EINVAL; + + lv[0] = ssv->len; + memcpy(lv + 1, ssv->info, ssv->len); + msgb_tlv_put(msg, GSM48_IE_SS_VERS, lv[0], lv+1); + + return 0; +} + +/* decode 'more data' does not require a function, because it has no value */ + +/* encode 'more data' */ +int gsm48_encode_more(struct msgb *msg) +{ + uint8_t *ie; + + ie = msgb_put(msg, 1); + ie[0] = GSM48_IE_MORE_DATA; + + return 0; +} + +/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ +int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, + uint8_t len, uint8_t mask, uint8_t frqt) +{ + int i; + + /* NOTES: + * + * The Range format uses "SMOD" computation. + * e.g. "n SMOD m" equals "((n - 1) % m) + 1" + * A cascade of multiple SMOD computations is simpified: + * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1" + * + * The Range format uses 16 octets of data in SYSTEM INFORMATION. + * When used in dedicated messages, the length can be less. + * In this case the ranges are decoded for all frequencies that + * fit in the block of given length. + */ + + /* tabula rasa */ + for (i = 0; i < 1024; i++) + f[i].mask &= ~frqt; + + /* 00..XXX. */ + if ((cd[0] & 0xc0 & mask) == 0x00) { + /* Bit map 0 format */ + if (len < 16) + return -EINVAL; + for (i = 1; i <= 124; i++) + if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7)))) + f[i].mask |= frqt; + + return 0; + } + + /* 10..0XX. */ + if ((cd[0] & 0xc8 & mask) == 0x80) { + /* Range 1024 format */ + uint16_t w[17]; /* 1..16 */ + struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd; + + if (len < 2) + return -EINVAL; + memset(w, 0, sizeof(w)); + if (r->f0) + f[0].mask |= frqt; + w[1] = (r->w1_hi << 8) | r->w1_lo; + if (len >= 4) + w[2] = (r->w2_hi << 1) | r->w2_lo; + if (len >= 5) + w[3] = (r->w3_hi << 2) | r->w3_lo; + if (len >= 6) + w[4] = (r->w4_hi << 2) | r->w4_lo; + if (len >= 7) + w[5] = (r->w5_hi << 2) | r->w5_lo; + if (len >= 8) + w[6] = (r->w6_hi << 2) | r->w6_lo; + if (len >= 9) + w[7] = (r->w7_hi << 2) | r->w7_lo; + if (len >= 10) + w[8] = (r->w8_hi << 1) | r->w8_lo; + if (len >= 10) + w[9] = r->w9; + if (len >= 11) + w[10] = r->w10; + if (len >= 12) + w[11] = (r->w11_hi << 6) | r->w11_lo; + if (len >= 13) + w[12] = (r->w12_hi << 5) | r->w12_lo; + if (len >= 14) + w[13] = (r->w13_hi << 4) | r->w13_lo; + if (len >= 15) + w[14] = (r->w14_hi << 3) | r->w14_lo; + if (len >= 16) + w[15] = (r->w15_hi << 2) | r->w15_lo; + if (len >= 16) + w[16] = r->w16; + if (w[1]) + f[w[1]].mask |= frqt; + if (w[2]) + f[((w[1] - 512 + w[2] - 1) % 1023) + 1].mask |= frqt; + if (w[3]) + f[((w[1] + w[3] - 1) % 1023) + 1].mask |= frqt; + if (w[4]) + f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[5]) + f[((w[1] + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[6]) + f[((w[1] - 512 + ((w[2] + w[6] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[7]) + f[((w[1] + ((w[3] + w[7] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[8]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[9]) + f[((w[1] + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[10]) + f[((w[1] - 512 + ((w[2] + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[11]) + f[((w[1] + ((w[3] + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[12]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] + w[12] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[13]) + f[((w[1] + ((w[3] - 256 + ((w[5] + w[13] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[14]) + f[((w[1] - 512 + ((w[2] + ((w[6] + w[14] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[15]) + f[((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[16]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].mask |= frqt; + + return 0; + } + /* 10..100. */ + if ((cd[0] & 0xce & mask) == 0x88) { + /* Range 512 format */ + uint16_t w[18]; /* 1..17 */ + struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd; + + if (len < 4) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = (r->w1_hi << 2) | r->w1_lo; + if (len >= 5) + w[2] = (r->w2_hi << 2) | r->w2_lo; + if (len >= 6) + w[3] = (r->w3_hi << 2) | r->w3_lo; + if (len >= 7) + w[4] = (r->w4_hi << 1) | r->w4_lo; + if (len >= 7) + w[5] = r->w5; + if (len >= 8) + w[6] = r->w6; + if (len >= 9) + w[7] = (r->w7_hi << 6) | r->w7_lo; + if (len >= 10) + w[8] = (r->w8_hi << 4) | r->w8_lo; + if (len >= 11) + w[9] = (r->w9_hi << 2) | r->w9_lo; + if (len >= 11) + w[10] = r->w10; + if (len >= 12) + w[11] = r->w11; + if (len >= 13) + w[12] = (r->w12_hi << 4) | r->w12_lo; + if (len >= 14) + w[13] = (r->w13_hi << 2) | r->w13_lo; + if (len >= 14) + w[14] = r->w14; + if (len >= 15) + w[15] = r->w15; + if (len >= 16) + w[16] = (r->w16_hi << 3) | r->w16_lo; + if (len >= 16) + w[17] = r->w17; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 511) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 256 + ((w[2] + w[6] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..101. */ + if ((cd[0] & 0xce & mask) == 0x8a) { + /* Range 256 format */ + uint16_t w[22]; /* 1..21 */ + struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd; + + if (len < 4) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = (r->w1_hi << 1) | r->w1_lo; + if (len >= 4) + w[2] = r->w2; + if (len >= 5) + w[3] = r->w3; + if (len >= 6) + w[4] = (r->w4_hi << 5) | r->w4_lo; + if (len >= 7) + w[5] = (r->w5_hi << 3) | r->w5_lo; + if (len >= 8) + w[6] = (r->w6_hi << 1) | r->w6_lo; + if (len >= 8) + w[7] = r->w7; + if (len >= 9) + w[8] = (r->w8_hi << 4) | r->w8_lo; + if (len >= 10) + w[9] = (r->w9_hi << 1) | r->w9_lo; + if (len >= 10) + w[10] = r->w10; + if (len >= 11) + w[11] = (r->w11_hi << 3) | r->w11_lo; + if (len >= 11) + w[12] = r->w12; + if (len >= 12) + w[13] = r->w13; + if (len >= 13) + w[14] = r->w15; + if (len >= 13) + w[15] = (r->w14_hi << 2) | r->w14_lo; + if (len >= 14) + w[16] = (r->w16_hi << 3) | r->w16_lo; + if (len >= 14) + w[17] = r->w17; + if (len >= 15) + w[18] = r->w19; + if (len >= 15) + w[19] = (r->w18_hi << 3) | r->w18_lo; + if (len >= 16) + w[20] = (r->w20_hi << 3) | r->w20_lo; + if (len >= 16) + w[21] = r->w21; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 255) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 128 + ((w[2] + w[6] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[18]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[19]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[20]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[21]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..110. */ + if ((cd[0] & 0xce & mask) == 0x8c) { + /* Range 128 format */ + uint16_t w[29]; /* 1..28 */ + struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd; + + if (len < 3) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = r->w1; + if (len >= 4) + w[2] = r->w2; + if (len >= 5) + w[3] = (r->w3_hi << 4) | r->w3_lo; + if (len >= 6) + w[4] = (r->w4_hi << 1) | r->w4_lo; + if (len >= 6) + w[5] = r->w5; + if (len >= 7) + w[6] = (r->w6_hi << 3) | r->w6_lo; + if (len >= 7) + w[7] = r->w7; + if (len >= 8) + w[8] = r->w8; + if (len >= 8) + w[9] = r->w9; + if (len >= 9) + w[10] = r->w10; + if (len >= 9) + w[11] = r->w11; + if (len >= 10) + w[12] = r->w12; + if (len >= 10) + w[13] = r->w13; + if (len >= 11) + w[14] = r->w14; + if (len >= 11) + w[15] = r->w15; + if (len >= 12) + w[16] = r->w16; + if (len >= 12) + w[17] = r->w17; + if (len >= 13) + w[18] = (r->w18_hi << 1) | r->w18_lo; + if (len >= 13) + w[19] = r->w19; + if (len >= 13) + w[20] = r->w20; + if (len >= 14) + w[21] = (r->w21_hi << 2) | r->w21_lo; + if (len >= 14) + w[22] = r->w22; + if (len >= 14) + w[23] = r->w23; + if (len >= 15) + w[24] = r->w24; + if (len >= 15) + w[25] = r->w25; + if (len >= 16) + w[26] = (r->w26_hi << 1) | r->w26_lo; + if (len >= 16) + w[27] = r->w27; + if (len >= 16) + w[28] = r->w28; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 127) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 64 + ((w[2] + w[6] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[18]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[19]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[20]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[21]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[22]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[23]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[24]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[25]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[26]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[27]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[28]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..111. */ + if ((cd[0] & 0xce & mask) == 0x8e) { + /* Variable bitmap format (can be any length >= 3) */ + uint16_t orig = 0; + struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd; + + if (len < 3) + return -EINVAL; + orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + f[orig].mask |= frqt; + for (i = 1; 2 + (i >> 3) < len; i++) + if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7)))) + f[(orig + i) % 1024].mask |= frqt; + + return 0; + } + + return 0; +} diff --git a/src/shared/libosmocore/src/gsm/gsm_utils.c b/src/shared/libosmocore/src/gsm/gsm_utils.c new file mode 100644 index 00000000..377705d9 --- /dev/null +++ b/src/shared/libosmocore/src/gsm/gsm_utils.c @@ -0,0 +1,465 @@ +/* + * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Nico Golde <nico@ngolde.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +//#include <openbsc/gsm_data.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm_utils.h> + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> + +#include "../../config.h" + +/* ETSI GSM 03.38 6.2.1 and 6.2.1.1 default alphabet + * Greek symbols at hex positions 0x10 and 0x12-0x1a + * left out as they can't be handled with a char and + * since most phones don't display or write these + * characters this would only needlessly make the code + * more complex +*/ +static unsigned char gsm_7bit_alphabet[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0xff, 0x0d, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x3c, 0x2f, 0x3e, 0x14, 0x11, 0xff, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x28, 0x40, 0x29, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xff, 0x01, 0xff, + 0x03, 0xff, 0x7b, 0x7d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x7e, 0x5d, 0xff, 0x7c, 0xff, 0xff, 0xff, + 0xff, 0x5b, 0x0e, 0x1c, 0x09, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5d, + 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0x1e, 0x7f, + 0xff, 0xff, 0xff, 0x7b, 0x0f, 0x1d, 0xff, 0x04, 0x05, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, + 0xff, 0x7d, 0x08, 0xff, 0xff, 0xff, 0x7c, 0xff, 0x0c, 0x06, 0xff, 0xff, 0x7e, 0xff, 0xff +}; + +/* GSM 03.38 6.2.1 Character lookup for decoding */ +static int gsm_septet_lookup(uint8_t ch) +{ + int i = 0; + for(; i < sizeof(gsm_7bit_alphabet); i++){ + if(gsm_7bit_alphabet[i] == ch) + return i; + } + return -1; +} + +/* GSM 03.38 6.2.1 Character unpacking */ +int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length) +{ + int i = 0; + int l = 0; + int septet_l = (length * 8) / 7; + uint8_t *rtext = calloc(septet_l, sizeof(uint8_t)); + uint8_t tmp; + + /* FIXME: We need to account for user data headers here */ + i += l; + for (; i < septet_l; i++){ + rtext[i] = + ((user_data[(i * 7 + 7) >> 3] << + (7 - ((i * 7 + 7) & 7))) | + (user_data[(i * 7) >> 3] >> + ((i * 7) & 7))) & 0x7f; + } + + for(i = 0; i < septet_l; i++){ + /* this is an extension character */ + if(rtext[i] == 0x1b && i + 1 < length){ + tmp = rtext[i+1]; + *(text++) = gsm_7bit_alphabet[0x7f + tmp]; + i++; + continue; + } + + *(text++) = gsm_septet_lookup(rtext[i]); + } + + *text = '\0'; + free(rtext); + + return i; +} + +/* GSM 03.38 6.2.1 Prepare character packing */ +static int gsm_septet_encode(uint8_t *result, const char *data) +{ + int i, y = 0; + uint8_t ch; + for(i = 0; i < strlen(data); i++){ + ch = data[i]; + switch(ch){ + /* fall-through for extension characters */ + case 0x0c: + case 0x5e: + case 0x7b: + case 0x7d: + case 0x5c: + case 0x5b: + case 0x7e: + case 0x5d: + case 0x7c: + result[y++] = 0x1b; + default: + result[y] = gsm_7bit_alphabet[ch]; + break; + } + y++; + } + + return y; +} + +/* GSM 03.38 6.2.1 Character packing */ +int gsm_7bit_encode(uint8_t *result, const char *data) +{ + int i,y,z = 0; + /* prepare for the worst case, every character expanding to two bytes */ + uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t)); + uint8_t cb, nb; + int shift = 0; + + y = gsm_septet_encode(rdata, data); + + for(i = 0; i < y; i++) { + if(shift == 7 && i + 1 < y){ + shift = 0; + continue; + } + + cb = (rdata[i] & 0x7f) >> shift; + if(i + 1 < y){ + nb = (rdata[i + 1] & 0x7f) << (7 - shift); + cb = cb | nb; + } + + result[z++] = cb; + + shift++; + } + + free(rdata); + return z; +} + +/* determine power control level for given dBm value, as indicated + * by the tables in chapter 4.1.1 of GSM TS 05.05 */ +int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm) +{ + switch (band) { + case GSM_BAND_450: + case GSM_BAND_480: + case GSM_BAND_750: + case GSM_BAND_900: + case GSM_BAND_810: + case GSM_BAND_850: + if (dbm >= 39) + return 0; + else if (dbm < 5) + return 19; + else { + /* we are guaranteed to have (5 <= dbm < 39) */ + return 2 + ((39 - dbm) / 2); + } + break; + case GSM_BAND_1800: + if (dbm >= 36) + return 29; + else if (dbm >= 34) + return 30; + else if (dbm >= 32) + return 31; + else if (dbm == 31) + return 0; + else { + /* we are guaranteed to have (0 <= dbm < 31) */ + return (30 - dbm) / 2; + } + break; + case GSM_BAND_1900: + if (dbm >= 33) + return 30; + else if (dbm >= 32) + return 31; + else if (dbm == 31) + return 0; + else { + /* we are guaranteed to have (0 <= dbm < 31) */ + return (30 - dbm) / 2; + } + break; + } + return -EINVAL; +} + +int ms_pwr_dbm(enum gsm_band band, uint8_t lvl) +{ + lvl &= 0x1f; + + switch (band) { + case GSM_BAND_450: + case GSM_BAND_480: + case GSM_BAND_750: + case GSM_BAND_900: + case GSM_BAND_810: + case GSM_BAND_850: + if (lvl < 2) + return 39; + else if (lvl < 20) + return 39 - ((lvl - 2) * 2) ; + else + return 5; + break; + case GSM_BAND_1800: + if (lvl < 16) + return 30 - (lvl * 2); + else if (lvl < 29) + return 0; + else + return 36 - ((lvl - 29) * 2); + break; + case GSM_BAND_1900: + if (lvl < 16) + return 30 - (lvl * 2); + else if (lvl < 30) + return -EINVAL; + else + return 33 - (lvl - 30); + break; + } + return -EINVAL; +} + +/* According to TS 08.05 Chapter 8.1.4 */ +int rxlev2dbm(uint8_t rxlev) +{ + if (rxlev > 63) + rxlev = 63; + + return -110 + rxlev; +} + +/* According to TS 08.05 Chapter 8.1.4 */ +uint8_t dbm2rxlev(int dbm) +{ + int rxlev = dbm + 110; + + if (rxlev > 63) + rxlev = 63; + else if (rxlev < 0) + rxlev = 0; + + return rxlev; +} + +const char *gsm_band_name(enum gsm_band band) +{ + switch (band) { + case GSM_BAND_450: + return "GSM450"; + case GSM_BAND_480: + return "GSM480"; + case GSM_BAND_750: + return "GSM750"; + case GSM_BAND_810: + return "GSM810"; + case GSM_BAND_850: + return "GSM850"; + case GSM_BAND_900: + return "GSM900"; + case GSM_BAND_1800: + return "DCS1800"; + case GSM_BAND_1900: + return "PCS1900"; + } + return "invalid"; +} + +enum gsm_band gsm_band_parse(const char* mhz) +{ + while (*mhz && !isdigit(*mhz)) + mhz++; + + if (*mhz == '\0') + return -EINVAL; + + switch (strtol(mhz, NULL, 10)) { + case 450: + return GSM_BAND_450; + case 480: + return GSM_BAND_480; + case 750: + return GSM_BAND_750; + case 810: + return GSM_BAND_810; + case 850: + return GSM_BAND_850; + case 900: + return GSM_BAND_900; + case 1800: + return GSM_BAND_1800; + case 1900: + return GSM_BAND_1900; + default: + return -EINVAL; + } +} + +enum gsm_band gsm_arfcn2band(uint16_t arfcn) +{ + int is_pcs = arfcn & ARFCN_PCS; + + arfcn &= ~ARFCN_FLAG_MASK; + + if (is_pcs) + return GSM_BAND_1900; + else if (arfcn <= 124) + return GSM_BAND_900; + else if (arfcn >= 955 && arfcn <= 1023) + return GSM_BAND_900; + else if (arfcn >= 128 && arfcn <= 251) + return GSM_BAND_850; + else if (arfcn >= 512 && arfcn <= 885) + return GSM_BAND_1800; + else if (arfcn >= 259 && arfcn <= 293) + return GSM_BAND_450; + else if (arfcn >= 306 && arfcn <= 340) + return GSM_BAND_480; + else if (arfcn >= 350 && arfcn <= 425) + return GSM_BAND_810; + else if (arfcn >= 438 && arfcn <= 511) + return GSM_BAND_750; + else + return GSM_BAND_1800; +} + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink) +{ + uint16_t freq10_ul; + uint16_t freq10_dl; + int is_pcs = arfcn & ARFCN_PCS; + + arfcn &= ~ARFCN_FLAG_MASK; + + if (is_pcs) { + /* DCS 1900 */ + arfcn &= ~ARFCN_PCS; + freq10_ul = 18502 + 2 * (arfcn-512); + freq10_dl = freq10_ul + 800; + } else if (arfcn <= 124) { + /* Primary GSM + ARFCN 0 of E-GSM */ + freq10_ul = 8900 + 2 * arfcn; + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 955 && arfcn <= 1023) { + /* E-GSM and R-GSM */ + freq10_ul = 8900 + 2 * (arfcn - 1024); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 128 && arfcn <= 251) { + /* GSM 850 */ + freq10_ul = 8242 + 2 * (arfcn - 128); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 512 && arfcn <= 885) { + /* DCS 1800 */ + freq10_ul = 17102 + 2 * (arfcn - 512); + freq10_dl = freq10_ul + 950; + } else if (arfcn >= 259 && arfcn <= 293) { + /* GSM 450 */ + freq10_ul = 4506 + 2 * (arfcn - 259); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 306 && arfcn <= 340) { + /* GSM 480 */ + freq10_ul = 4790 + 2 * (arfcn - 306); + freq10_dl = freq10_ul + 100; + } else if (arfcn >= 350 && arfcn <= 425) { + /* GSM 810 */ + freq10_ul = 8060 + 2 * (arfcn - 350); + freq10_dl = freq10_ul + 450; + } else if (arfcn >= 438 && arfcn <= 511) { + /* GSM 750 */ + freq10_ul = 7472 + 2 * (arfcn - 438); + freq10_dl = freq10_ul + 300; + } else + return 0xffff; + + if (uplink) + return freq10_ul; + else + return freq10_dl; +} + +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn) +{ + time->fn = fn; + time->t1 = time->fn / (26*51); + time->t2 = time->fn % 26; + time->t3 = time->fn % 51; + time->tc = (time->fn / 51) % 8; +} + +uint32_t gsm_gsmtime2fn(struct gsm_time *time) +{ + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + return (51 * ((time->t3 - time->t2 + 26) % 26) + time->t3 + (26 * 51 * time->t1)); +} + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli) +{ + if ((tlli & 0xc0000000) == 0xc0000000) + return TLLI_LOCAL; + else if ((tlli & 0xc0000000) == 0x80000000) + return TLLI_FOREIGN; + else if ((tlli & 0xf8000000) == 0x78000000) + return TLLI_RANDOM; + else if ((tlli & 0xf8000000) == 0x70000000) + return TLLI_AUXILIARY; + + return TLLI_RESERVED; +} + +uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type) +{ + uint32_t tlli; + switch (type) { + case TLLI_LOCAL: + tlli = p_tmsi | 0xc0000000; + break; + case TLLI_FOREIGN: + tlli = (p_tmsi & 0x3fffffff) | 0x80000000; + break; + default: + tlli = 0; + break; + } + return tlli; +} diff --git a/src/shared/libosmocore/src/gsm/rsl.c b/src/shared/libosmocore/src/gsm/rsl.c new file mode 100644 index 00000000..12ac0cfb --- /dev/null +++ b/src/shared/libosmocore/src/gsm/rsl.c @@ -0,0 +1,371 @@ +/* GSM Radio Signalling Link messages on the A-bis interface + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2008-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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <errno.h> + +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/rsl.h> + +#define RSL_ALLOC_SIZE 200 +#define RSL_ALLOC_HEADROOM 56 + +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type) +{ + dh->c.msg_discr = ABIS_RSL_MDISC_RLL; + dh->c.msg_type = msg_type; + dh->ie_chan = RSL_IE_CHAN_NR; + dh->ie_link_id = RSL_IE_LINK_IDENT; +} + +void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type) +{ + ch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; + ch->c.msg_type = msg_type; + ch->ie_chan = RSL_IE_CHAN_NR; +} + +const struct tlv_definition rsl_att_tlvdef = { + .def = { + [RSL_IE_CHAN_NR] = { TLV_TYPE_TV }, + [RSL_IE_LINK_IDENT] = { TLV_TYPE_TV }, + [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV }, + [RSL_IE_BS_POWER] = { TLV_TYPE_TV }, + [RSL_IE_CHAN_IDENT] = { TLV_TYPE_TLV }, + [RSL_IE_CHAN_MODE] = { TLV_TYPE_TLV }, + [RSL_IE_ENCR_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_FRAME_NUMBER] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_HANDO_REF] = { TLV_TYPE_TV }, + [RSL_IE_L1_INFO] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_L3_INFO] = { TLV_TYPE_TL16V }, + [RSL_IE_MS_IDENTITY] = { TLV_TYPE_TLV }, + [RSL_IE_MS_POWER] = { TLV_TYPE_TV }, + [RSL_IE_PAGING_GROUP] = { TLV_TYPE_TV }, + [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_PYHS_CONTEXT] = { TLV_TYPE_TLV }, + [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV }, + [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV }, + [RSL_IE_REQ_REFERENCE] = { TLV_TYPE_FIXED, 3 }, + [RSL_IE_RELEASE_MODE] = { TLV_TYPE_TV }, + [RSL_IE_RESOURCE_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV }, + [RSL_IE_STARTNG_TIME] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_TIMING_ADVANCE] = { TLV_TYPE_TV }, + [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV }, + [RSL_IE_CAUSE] = { TLV_TYPE_TLV }, + [RSL_IE_MEAS_RES_NR] = { TLV_TYPE_TV }, + [RSL_IE_MSG_ID] = { TLV_TYPE_TV }, + [RSL_IE_SYSINFO_TYPE] = { TLV_TYPE_TV }, + [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV }, + [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV }, + [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV }, + [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV }, + [RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 }, + [RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV }, + [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV }, + [RSL_IE_FULL_BCCH_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_CHAN_NEEDED] = { TLV_TYPE_TV }, + [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV }, + [RSL_IE_SMSCB_MSG] = { TLV_TYPE_TLV }, + [RSL_IE_FULL_IMM_ASS_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_CBCH_LOAD_INFO] = { TLV_TYPE_TV }, + [RSL_IE_SMSCB_CHAN_INDICATOR] = { TLV_TYPE_TV }, + [RSL_IE_GROUP_CALL_REF] = { TLV_TYPE_TLV }, + [RSL_IE_CHAN_DESC] = { TLV_TYPE_TLV }, + [RSL_IE_NCH_DRX_INFO] = { TLV_TYPE_TLV }, + [RSL_IE_CMD_INDICATOR] = { TLV_TYPE_TLV }, + [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV }, + [RSL_IE_UIC] = { TLV_TYPE_TLV }, + [RSL_IE_MAIN_CHAN_REF] = { TLV_TYPE_TV }, + [RSL_IE_MR_CONFIG] = { TLV_TYPE_TLV }, + [RSL_IE_MR_CONTROL] = { TLV_TYPE_TV }, + [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV }, + [RSL_IE_CODEC_CONFIG] = { TLV_TYPE_TLV }, + [RSL_IE_RTD] = { TLV_TYPE_TV }, + [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV }, + [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV }, + [RSL_IE_SIEMENS_MRPCI] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_PROXY_UDP] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_IPAC_BSCMPL_TOUT] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 }, + [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_IPAC_RTP_PAYLOAD] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_IPAC_SPEECH_MODE] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 }, + [RSL_IE_IPAC_CONN_ID] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_IPAC_RTP_CSD_FMT] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_RTP_JIT_BUF] = { TLV_TYPE_FIXED, 2 }, + [RSL_IE_IPAC_RTP_COMPR] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_RTP_PAYLOAD2] = { TLV_TYPE_TV }, + [RSL_IE_IPAC_RTP_MPLEX] = { TLV_TYPE_FIXED, 8 }, + [RSL_IE_IPAC_RTP_MPLEX_ID] = { TLV_TYPE_TV }, + }, +}; + +/* encode channel number as per Section 9.3.1 */ +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot) +{ + uint8_t ret; + + ret = (timeslot & 0x07) | type; + + switch (type) { + case RSL_CHAN_Lm_ACCHs: + subch &= 0x01; + break; + case RSL_CHAN_SDCCH4_ACCH: + subch &= 0x03; + break; + case RSL_CHAN_SDCCH8_ACCH: + subch &= 0x07; + break; + default: + /* no subchannels allowed */ + subch = 0x00; + break; + } + ret |= (subch << 3); + + return ret; +} + +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot) +{ + *timeslot = chan_nr & 0x7; + + if ((chan_nr & 0xf8) == RSL_CHAN_Bm_ACCHs) { + *type = RSL_CHAN_Bm_ACCHs; + *subch = 0; + } else if ((chan_nr & 0xf0) == RSL_CHAN_Lm_ACCHs) { + *type = RSL_CHAN_Lm_ACCHs; + *subch = (chan_nr >> 3) & 0x1; + } else if ((chan_nr & 0xe0) == RSL_CHAN_SDCCH4_ACCH) { + *type = RSL_CHAN_SDCCH4_ACCH; + *subch = (chan_nr >> 3) & 0x3; + } else if ((chan_nr & 0xc0) == RSL_CHAN_SDCCH8_ACCH) { + *type = RSL_CHAN_SDCCH8_ACCH; + *subch = (chan_nr >> 3) & 0x7; + } else if ((chan_nr & 0xf8) == RSL_CHAN_BCCH) { + *type = RSL_CHAN_BCCH; + *subch = 0; + } else if ((chan_nr & 0xf8) == RSL_CHAN_RACH) { + *type = RSL_CHAN_RACH; + *subch = 0; + } else if ((chan_nr & 0xf8) == RSL_CHAN_PCH_AGCH) { + *type = RSL_CHAN_PCH_AGCH; + *subch = 0; + } else + return -EINVAL; + + return 0; +} + +const char *rsl_chan_nr_str(uint8_t chan_nr) +{ + static char str[20]; + int ts = chan_nr & 7; + uint8_t cbits = chan_nr >> 3; + + if (cbits == 0x01) + sprintf(str, "TCH/F on TS%d", ts); + else if ((cbits & 0x1e) == 0x02) + sprintf(str, "TCH/H(%u) on TS%d", cbits & 0x01, ts); + else if ((cbits & 0x1c) == 0x04) + sprintf(str, "SDCCH/4(%u) on TS%d", cbits & 0x03, ts); + else if ((cbits & 0x18) == 0x08) + sprintf(str, "SDCCH/8(%u) on TS%d", cbits & 0x07, ts); + else if (cbits == 0x10) + sprintf(str, "BCCH on TS%d", ts); + else if (cbits == 0x11) + sprintf(str, "RACH on TS%d", ts); + else if (cbits == 0x12) + sprintf(str, "PCH/AGCH on TS%d", ts); + else + sprintf(str, "UNKNOWN on TS%d", ts); + + return str; +} + +static const struct value_string rsl_err_vals[] = { + { RSL_ERR_RADIO_IF_FAIL, "Radio Interface Failure" }, + { RSL_ERR_RADIO_LINK_FAIL, "Radio Link Failure" }, + { RSL_ERR_HANDOVER_ACC_FAIL, "Handover Access Failure" }, + { RSL_ERR_TALKER_ACC_FAIL, "Talker Access Failure" }, + { RSL_ERR_OM_INTERVENTION, "O&M Intervention" }, + { RSL_ERR_NORMAL_UNSPEC, "Normal event, unspecified" }, + { RSL_ERR_T_MSRFPCI_EXP, "Siemens: T_MSRFPCI Expired" }, + { RSL_ERR_EQUIPMENT_FAIL, "Equipment Failure" }, + { RSL_ERR_RR_UNAVAIL, "Radio Resource not available" }, + { RSL_ERR_TERR_CH_FAIL, "Terrestrial Channel Failure" }, + { RSL_ERR_CCCH_OVERLOAD, "CCCH Overload" }, + { RSL_ERR_ACCH_OVERLOAD, "ACCH Overload" }, + { RSL_ERR_PROCESSOR_OVERLOAD, "Processor Overload" }, + { RSL_ERR_RES_UNAVAIL, "Resource not available, unspecified" }, + { RSL_ERR_TRANSC_UNAVAIL, "Transcoding not available" }, + { RSL_ERR_SERV_OPT_UNAVAIL, "Service or Option not available" }, + { RSL_ERR_ENCR_UNIMPL, "Encryption algorithm not implemented" }, + { RSL_ERR_SERV_OPT_UNIMPL, "Service or Option not implemented" }, + { RSL_ERR_RCH_ALR_ACTV_ALLOC, "Radio channel already activated" }, + { RSL_ERR_INVALID_MESSAGE, "Invalid Message, unspecified" }, + { RSL_ERR_MSG_DISCR, "Message Discriminator Error" }, + { RSL_ERR_MSG_TYPE, "Message Type Error" }, + { RSL_ERR_MSG_SEQ, "Message Sequence Error" }, + { RSL_ERR_IE_ERROR, "General IE error" }, + { RSL_ERR_MAND_IE_ERROR, "Mandatory IE error" }, + { RSL_ERR_OPT_IE_ERROR, "Optional IE error" }, + { RSL_ERR_IE_NONEXIST, "IE non-existent" }, + { RSL_ERR_IE_LENGTH, "IE length error" }, + { RSL_ERR_IE_CONTENT, "IE content error" }, + { RSL_ERR_PROTO, "Protocol error, unspecified" }, + { RSL_ERR_INTERWORKING, "Interworking error, unspecified" }, + { 0, NULL } +}; + +const char *rsl_err_name(uint8_t err) +{ + return get_value_string(rsl_err_vals, err); +} + +static const struct value_string rsl_rlm_cause_strs[] = { + { RLL_CAUSE_T200_EXPIRED, "Timer T200 expired (N200+1) times" }, + { RLL_CAUSE_REEST_REQ, "Re-establishment request" }, + { RLL_CAUSE_UNSOL_UA_RESP, "Unsolicited UA response" }, + { RLL_CAUSE_UNSOL_DM_RESP, "Unsolicited DM response" }, + { RLL_CAUSE_UNSOL_DM_RESP_MF, "Unsolicited DM response, multiple frame" }, + { RLL_CAUSE_UNSOL_SPRV_RESP, "Unsolicited supervisory response" }, + { RLL_CAUSE_SEQ_ERR, "Sequence Error" }, + { RLL_CAUSE_UFRM_INC_PARAM, "U-Frame with incorrect parameters" }, + { RLL_CAUSE_SFRM_INC_PARAM, "S-Frame with incorrect parameters" }, + { RLL_CAUSE_IFRM_INC_MBITS, "I-Frame with incorrect use of M bit" }, + { RLL_CAUSE_IFRM_INC_LEN, "I-Frame with incorrect length" }, + { RLL_CAUSE_FRM_UNIMPL, "Fraeme not implemented" }, + { RLL_CAUSE_SABM_MF, "SABM command, multiple frame established state" }, + { RLL_CAUSE_SABM_INFO_NOTALL, "SABM frame with information not allowed in this state" }, + { 0, NULL }, +}; + +const char *rsl_rlm_cause_name(uint8_t err) +{ + return get_value_string(rsl_rlm_cause_strs, err); +} + +/* Section 3.3.2.3 TS 05.02. I think this looks like a table */ +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf) +{ + switch (ccch_conf) { + case RSL_BCCH_CCCH_CONF_1_NC: + return 1; + case RSL_BCCH_CCCH_CONF_1_C: + return 1; + case RSL_BCCH_CCCH_CONF_2_NC: + return 2; + case RSL_BCCH_CCCH_CONF_3_NC: + return 3; + case RSL_BCCH_CCCH_CONF_4_NC: + return 4; + default: + return -1; + } +} + +/* Section 3.3.2.3 TS 05.02 */ +int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf) +{ + switch (ccch_conf) { + case RSL_BCCH_CCCH_CONF_1_NC: + return 0; + case RSL_BCCH_CCCH_CONF_1_C: + return 1; + case RSL_BCCH_CCCH_CONF_2_NC: + return 0; + case RSL_BCCH_CCCH_CONF_3_NC: + return 0; + case RSL_BCCH_CCCH_CONF_4_NC: + return 0; + default: + return -1; + } +} + +/* Push a RSL RLL header */ +void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent) +{ + struct abis_rsl_rll_hdr *rh; + + rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh)); + rsl_init_rll_hdr(rh, msg_type); + if (transparent) + rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; + rh->chan_nr = chan_nr; + rh->link_id = link_id; + + /* set the l2 header pointer */ + msg->l2h = (uint8_t *)rh; +} + +/* Push a RSL RLL header with L3_INFO IE */ +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent) +{ + uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg); + + /* construct a RSLms RLL message (DATA INDICATION, UNIT DATA + * INDICATION) and send it off via RSLms */ + + /* Push the L3 IE tag and lengh */ + msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); + + /* Then push the RSL header */ + rsl_rll_push_hdr(msg, msg_type, chan_nr, link_id, transparent); +} + +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent) +{ + struct abis_rsl_rll_hdr *rh; + struct msgb *msg; + + msg = msgb_alloc_headroom(RSL_ALLOC_SIZE+RSL_ALLOC_HEADROOM, + RSL_ALLOC_HEADROOM, "rsl_rll_simple"); + + if (!msg) + return NULL; + + /* put the RSL header */ + rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); + rsl_init_rll_hdr(rh, msg_type); + if (transparent) + rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; + rh->chan_nr = chan_nr; + rh->link_id = link_id; + + /* set the l2 header pointer */ + msg->l2h = (uint8_t *)rh; + + return msg; +} diff --git a/src/shared/libosmocore/src/gsm/rxlev_stat.c b/src/shared/libosmocore/src/gsm/rxlev_stat.c new file mode 100644 index 00000000..d226861e --- /dev/null +++ b/src/shared/libosmocore/src/gsm/rxlev_stat.c @@ -0,0 +1,82 @@ +/* Rx Level statistics */ + +/* (C) 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> + +#include <osmocom/core/bitvec.h> +#include <osmocom/gsm/rxlev_stat.h> + +void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev) +{ + struct bitvec bv; + + if (rxlev >= NUM_RXLEVS) + rxlev = NUM_RXLEVS-1; + + bv.data_len = NUM_ARFCNS/8; + bv.data = st->rxlev_buckets[rxlev]; + + bitvec_set_bit_pos(&bv, arfcn, ONE); +} + +/* get the next ARFCN that has the specified Rxlev */ +int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn) +{ + struct bitvec bv; + + if (rxlev >= NUM_RXLEVS) + rxlev = NUM_RXLEVS-1; + + bv.data_len = NUM_ARFCNS/8; + + if (arfcn < 0) + arfcn = -1; + + bv.data = (uint8_t *) st->rxlev_buckets[rxlev]; + + return bitvec_find_bit_pos(&bv, arfcn+1, ONE); +} + +void rxlev_stat_reset(struct rxlev_stats *st) +{ + memset(st, 0, sizeof(*st)); +} + +void rxlev_stat_dump(const struct rxlev_stats *st) +{ + int i; + + for (i = NUM_RXLEVS-1; i >= 0; i--) { + int16_t arfcn = -1; + + printf("ARFCN with RxLev %u: ", i); + while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { + printf("%u ", arfcn); + } + printf("\n"); + } +} diff --git a/src/shared/libosmocore/src/gsm/sysinfo.c b/src/shared/libosmocore/src/gsm/sysinfo.c new file mode 100644 index 00000000..0dbff3ae --- /dev/null +++ b/src/shared/libosmocore/src/gsm/sysinfo.c @@ -0,0 +1,131 @@ +/* GSM 04.08 System Information (SI) encoding and decoding + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <netinet/in.h> + +#include <osmocom/core/bitvec.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/sysinfo.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +/* verify the sizes of the system information type structs */ + +/* rest octets are not part of the struct */ +osmo_static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); +osmo_static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); +osmo_static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); +osmo_static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); +osmo_static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); +osmo_static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); + +/* bs11 forgot the l2 len, 0-6 rest octets */ +osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); +osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); + +osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); + +static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = { + [SYSINFO_TYPE_1] = RSL_SYSTEM_INFO_1, + [SYSINFO_TYPE_2] = RSL_SYSTEM_INFO_2, + [SYSINFO_TYPE_3] = RSL_SYSTEM_INFO_3, + [SYSINFO_TYPE_4] = RSL_SYSTEM_INFO_4, + [SYSINFO_TYPE_5] = RSL_SYSTEM_INFO_5, + [SYSINFO_TYPE_6] = RSL_SYSTEM_INFO_6, + [SYSINFO_TYPE_7] = RSL_SYSTEM_INFO_7, + [SYSINFO_TYPE_8] = RSL_SYSTEM_INFO_8, + [SYSINFO_TYPE_9] = RSL_SYSTEM_INFO_9, + [SYSINFO_TYPE_10] = RSL_SYSTEM_INFO_10, + [SYSINFO_TYPE_13] = RSL_SYSTEM_INFO_13, + [SYSINFO_TYPE_16] = RSL_SYSTEM_INFO_16, + [SYSINFO_TYPE_17] = RSL_SYSTEM_INFO_17, + [SYSINFO_TYPE_18] = RSL_SYSTEM_INFO_18, + [SYSINFO_TYPE_19] = RSL_SYSTEM_INFO_19, + [SYSINFO_TYPE_20] = RSL_SYSTEM_INFO_20, + [SYSINFO_TYPE_2bis] = RSL_SYSTEM_INFO_2bis, + [SYSINFO_TYPE_2ter] = RSL_SYSTEM_INFO_2ter, + [SYSINFO_TYPE_2quater] = RSL_SYSTEM_INFO_2quater, + [SYSINFO_TYPE_5bis] = RSL_SYSTEM_INFO_5bis, + [SYSINFO_TYPE_5ter] = RSL_SYSTEM_INFO_5ter, +}; + +static const uint8_t rsl2sitype[0xff] = { + [RSL_SYSTEM_INFO_1] = SYSINFO_TYPE_1, + [RSL_SYSTEM_INFO_2] = SYSINFO_TYPE_2, + [RSL_SYSTEM_INFO_3] = SYSINFO_TYPE_3, + [RSL_SYSTEM_INFO_4] = SYSINFO_TYPE_4, + [RSL_SYSTEM_INFO_5] = SYSINFO_TYPE_5, + [RSL_SYSTEM_INFO_6] = SYSINFO_TYPE_6, + [RSL_SYSTEM_INFO_7] = SYSINFO_TYPE_7, + [RSL_SYSTEM_INFO_8] = SYSINFO_TYPE_8, + [RSL_SYSTEM_INFO_9] = SYSINFO_TYPE_9, + [RSL_SYSTEM_INFO_10] = SYSINFO_TYPE_10, + [RSL_SYSTEM_INFO_13] = SYSINFO_TYPE_13, + [RSL_SYSTEM_INFO_16] = SYSINFO_TYPE_16, + [RSL_SYSTEM_INFO_17] = SYSINFO_TYPE_17, + [RSL_SYSTEM_INFO_18] = SYSINFO_TYPE_18, + [RSL_SYSTEM_INFO_19] = SYSINFO_TYPE_19, + [RSL_SYSTEM_INFO_20] = SYSINFO_TYPE_20, + [RSL_SYSTEM_INFO_2bis] = SYSINFO_TYPE_2bis, + [RSL_SYSTEM_INFO_2ter] = SYSINFO_TYPE_2ter, + [RSL_SYSTEM_INFO_2quater] = SYSINFO_TYPE_2quater, + [RSL_SYSTEM_INFO_5bis] = SYSINFO_TYPE_5bis, + [RSL_SYSTEM_INFO_5ter] = SYSINFO_TYPE_5ter, +}; + +const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = { + { SYSINFO_TYPE_1, "1" }, + { SYSINFO_TYPE_2, "2" }, + { SYSINFO_TYPE_3, "3" }, + { SYSINFO_TYPE_4, "4" }, + { SYSINFO_TYPE_5, "5" }, + { SYSINFO_TYPE_6, "6" }, + { SYSINFO_TYPE_7, "7" }, + { SYSINFO_TYPE_8, "8" }, + { SYSINFO_TYPE_9, "9" }, + { SYSINFO_TYPE_10, "10" }, + { SYSINFO_TYPE_13, "13" }, + { SYSINFO_TYPE_16, "16" }, + { SYSINFO_TYPE_17, "17" }, + { SYSINFO_TYPE_18, "18" }, + { SYSINFO_TYPE_19, "19" }, + { SYSINFO_TYPE_20, "20" }, + { SYSINFO_TYPE_2bis, "2bis" }, + { SYSINFO_TYPE_2ter, "2ter" }, + { SYSINFO_TYPE_2quater, "2quater" }, + { SYSINFO_TYPE_5bis, "5bis" }, + { SYSINFO_TYPE_5ter, "5ter" }, + { 0, NULL } +}; + +uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type) +{ + return sitype2rsl[si_type]; +} + +enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si) +{ + return rsl2sitype[rsl_si]; +} diff --git a/src/shared/libosmocore/src/gsm/tlv_parser.c b/src/shared/libosmocore/src/gsm/tlv_parser.c new file mode 100644 index 00000000..1e4c6b5f --- /dev/null +++ b/src/shared/libosmocore/src/gsm/tlv_parser.c @@ -0,0 +1,179 @@ +#include <stdio.h> +#include <stdint.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> + +struct tlv_definition tvlv_att_def; + +int tlv_dump(struct tlv_parsed *dec) +{ + int i; + + for (i = 0; i <= 0xff; i++) { + if (!dec->lv[i].val) + continue; + printf("T=%02x L=%d\n", i, dec->lv[i].len); + } + return 0; +} + +/* o_tag: output: tag found + * o_len: output: length of the data + * o_val: output: pointer to the data + * def: input: a structure defining the valid TLV tags / configurations + * buf: input: the input data buffer to be parsed + * buf_len: input: the length of the input data buffer + * + * Also, returns the number of bytes consumed by the TLV entry + */ +int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, + const struct tlv_definition *def, + const uint8_t *buf, int buf_len) +{ + uint8_t tag; + int len; + + tag = *buf; + *o_tag = tag; + + /* single octet TV IE */ + if (def->def[tag & 0xf0].type == TLV_TYPE_SINGLE_TV) { + *o_tag = tag & 0xf0; + *o_val = buf; + *o_len = 1; + return 1; + } + + /* FIXME: use tables for knwon IEI */ + switch (def->def[tag].type) { + case TLV_TYPE_T: + /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */ + *o_val = buf; + *o_len = 0; + len = 1; + break; + case TLV_TYPE_TV: + *o_val = buf+1; + *o_len = 1; + len = 2; + break; + case TLV_TYPE_FIXED: + *o_val = buf+1; + *o_len = def->def[tag].fixed_len; + len = def->def[tag].fixed_len + 1; + break; + case TLV_TYPE_TLV: + /* GSM TS 04.07 11.2.4: Type 4 TLV */ + if (buf + 1 > buf + buf_len) + return -1; + *o_val = buf+2; + *o_len = *(buf+1); + len = *o_len + 2; + if (len > buf_len) + return -2; + break; + case TLV_TYPE_TvLV: + if (*(buf+1) & 0x80) { + /* like TLV, but without highest bit of len */ + if (buf + 1 > buf + buf_len) + return -1; + *o_val = buf+2; + *o_len = *(buf+1) & 0x7f; + len = *o_len + 2; + if (len > buf_len) + return -2; + break; + } + /* like TL16V, fallthrough */ + case TLV_TYPE_TL16V: + if (2 > buf_len) + return -1; + *o_val = buf+3; + *o_len = *(buf+1) << 8 | *(buf+2); + len = *o_len + 3; + if (len > buf_len) + return -2; + break; + default: + return -3; + } + + return len; +} + +/* dec: output: a caller-allocated pointer to a struct tlv_parsed, + * def: input: a structure defining the valid TLV tags / configurations + * buf: input: the input data buffer to be parsed + * buf_len: input: the length of the input data buffer + * lv_tag: input: an initial LV tag at the start of the buffer + * lv_tag2: input: a second initial LV tag following lv_tag + */ +int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, + const uint8_t *buf, int buf_len, uint8_t lv_tag, + uint8_t lv_tag2) +{ + int ofs = 0, num_parsed = 0; + uint16_t len; + + memset(dec, 0, sizeof(*dec)); + + if (lv_tag) { + if (ofs > buf_len) + return -1; + dec->lv[lv_tag].val = &buf[ofs+1]; + dec->lv[lv_tag].len = buf[ofs]; + len = dec->lv[lv_tag].len + 1; + if (ofs + len > buf_len) + return -2; + num_parsed++; + ofs += len; + } + if (lv_tag2) { + if (ofs > buf_len) + return -1; + dec->lv[lv_tag2].val = &buf[ofs+1]; + dec->lv[lv_tag2].len = buf[ofs]; + len = dec->lv[lv_tag2].len + 1; + if (ofs + len > buf_len) + return -2; + num_parsed++; + ofs += len; + } + + while (ofs < buf_len) { + int rv; + uint8_t tag; + const uint8_t *val; + + rv = tlv_parse_one(&tag, &len, &val, def, + &buf[ofs], buf_len-ofs); + if (rv < 0) + return rv; + dec->lv[tag].val = val; + dec->lv[tag].len = len; + ofs += rv; + num_parsed++; + } + //tlv_dump(dec); + return num_parsed; +} + +/* take a master (src) tlvdev and fill up all empty slots in 'dst' */ +void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dst->def); i++) { + if (src->def[i].type == TLV_TYPE_NONE) + continue; + if (dst->def[i].type == TLV_TYPE_NONE) + dst->def[i] = src->def[i]; + } +} + +static __attribute__((constructor)) void on_dso_load_tlv(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(tvlv_att_def.def); i++) + tvlv_att_def.def[i].type = TLV_TYPE_TvLV; +} diff --git a/src/shared/libosmocore/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c new file mode 100644 index 00000000..3d20bfc2 --- /dev/null +++ b/src/shared/libosmocore/src/gsmtap_util.c @@ -0,0 +1,281 @@ +/* GSMTAP support code in libmsomcore */ +/* + * (C) 2010-2011 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "../config.h" + +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/rsl.h> + +#include <sys/types.h> + +#include <arpa/inet.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) +{ + uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; + + switch (rsl_chantype) { + case RSL_CHAN_Bm_ACCHs: + ret = GSMTAP_CHANNEL_TCH_F; + break; + case RSL_CHAN_Lm_ACCHs: + ret = GSMTAP_CHANNEL_TCH_H; + break; + case RSL_CHAN_SDCCH4_ACCH: + ret = GSMTAP_CHANNEL_SDCCH4; + break; + case RSL_CHAN_SDCCH8_ACCH: + ret = GSMTAP_CHANNEL_SDCCH8; + break; + case RSL_CHAN_BCCH: + ret = GSMTAP_CHANNEL_BCCH; + break; + case RSL_CHAN_RACH: + ret = GSMTAP_CHANNEL_RACH; + break; + case RSL_CHAN_PCH_AGCH: + /* it could also be AGCH... */ + ret = GSMTAP_CHANNEL_PCH; + break; + } + + if (link_id & 0x40) + ret |= GSMTAP_CHANNEL_ACCH; + + return ret; +} + +/* receive a message from L1/L2 and put it in GSMTAP */ +struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, + uint8_t ss, uint32_t fn, int8_t signal_dbm, + uint8_t snr, const uint8_t *data, unsigned int len) +{ + struct msgb *msg; + struct gsmtap_hdr *gh; + uint8_t *dst; + + msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx"); + if (!msg) + return NULL; + + gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->version = GSMTAP_VERSION; + gh->hdr_len = sizeof(*gh)/4; + gh->type = GSMTAP_TYPE_UM; + gh->timeslot = ts; + gh->sub_slot = ss; + gh->arfcn = htons(arfcn); + gh->snr_db = snr; + gh->signal_dbm = signal_dbm; + gh->frame_number = htonl(fn); + gh->sub_type = chan_type; + gh->antenna_nr = 0; + + dst = msgb_put(msg, len); + memcpy(dst, data, len); + + return msg; +} + +#ifdef HAVE_SYS_SOCKET_H + +#include <sys/socket.h> +#include <netinet/in.h> + +/* Open a GSMTAP source (sending) socket, conncet it to host/port and + * return resulting fd */ +int gsmtap_source_init_fd(const char *host, uint16_t port) +{ + if (port == 0) + port = GSMTAP_UDP_PORT; + if (host == NULL) + host = "localhost"; + + return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, 0); +} + +int gsmtap_source_add_sink_fd(int gsmtap_fd) +{ + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + int rc; + + rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len); + if (rc < 0) + return rc; + + if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { + rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, IPPROTO_UDP, 1); + if (rc >= 0) + return rc; + } + + return -ENODEV; +} + +int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg) +{ + if (!gti) + return -ENODEV; + + if (gti->ofd_wq_mode) + return osmo_wqueue_enqueue(>i->wq, msg); + else { + /* try immediate send and return error if any */ + int rc; + + rc = write(gsmtap_inst_fd(gti), msg->data, msg->len); + if (rc <= 0) { + return rc; + } else if (rc >= msg->len) { + msgb_free(msg); + return 0; + } else { + /* short write */ + return -EIO; + } + } +} + +/* receive a message from L1/L2 and put it in GSMTAP */ +int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, + uint8_t chan_type, uint8_t ss, uint32_t fn, + int8_t signal_dbm, uint8_t snr, const uint8_t *data, + unsigned int len) +{ + struct msgb *msg; + + if (!gti) + return -ENODEV; + + msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm, + snr, data, len); + if (!msg) + return -ENOMEM; + + return gsmtap_sendmsg(gti, msg); +} + +/* Callback from select layer if we can write to the socket */ +static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + int rc; + + rc = write(ofd->fd, msg->data, msg->len); + if (rc < 0) { + perror("writing msgb to gsmtap fd"); + msgb_free(msg); + return rc; + } + if (rc != msg->len) { + perror("short write to gsmtap fd"); + msgb_free(msg); + return -EIO; + } + + msgb_free(msg); + return 0; +} + +/* Callback from select layer if we can read from the sink socket */ +static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) +{ + int rc; + uint8_t buf[4096]; + + if (!(flags & BSC_FD_READ)) + return 0; + + rc = read(fd->fd, buf, sizeof(buf)); + if (rc < 0) { + perror("reading from gsmtap sink fd"); + return rc; + } + /* simply discard any data arriving on the socket */ + + return 0; +} + +/* Add a local sink to an existing GSMTAP source instance */ +int gsmtap_source_add_sink(struct gsmtap_inst *gti) +{ + int fd; + + fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti)); + if (fd < 0) + return fd; + + if (gti->ofd_wq_mode) { + struct osmo_fd *sink_ofd; + + sink_ofd = >i->sink_ofd; + sink_ofd->fd = fd; + sink_ofd->when = BSC_FD_READ; + sink_ofd->cb = gsmtap_sink_fd_cb; + + osmo_fd_register(sink_ofd); + } + + return fd; +} + +/* like gsmtap_init2() but integrated with libosmocore select.c */ +struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, + int ofd_wq_mode) +{ + struct gsmtap_inst *gti; + int fd; + + fd = gsmtap_source_init_fd(host, port); + if (fd < 0) + return NULL; + + gti = talloc_zero(NULL, struct gsmtap_inst); + gti->ofd_wq_mode = ofd_wq_mode; + gti->wq.bfd.fd = fd; + gti->sink_ofd.fd = -1; + + if (ofd_wq_mode) { + osmo_wqueue_init(>i->wq, 64); + gti->wq.write_cb = &gsmtap_wq_w_cb; + + osmo_fd_register(>i->wq.bfd); + } + + return gti; +} + +#endif /* HAVE_SYS_SOCKET_H */ diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c new file mode 100644 index 00000000..3c9dc03f --- /dev/null +++ b/src/shared/libosmocore/src/logging.c @@ -0,0 +1,562 @@ +/* Debugging/Logging support code */ + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2008 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "../config.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#include <time.h> +#include <errno.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> + +#include <osmocom/vty/logging.h> /* for LOGGING_STR. */ + +const struct log_info *osmo_log_info; + +static struct log_context log_context; +static void *tall_log_ctx = NULL; +LLIST_HEAD(osmo_log_target_list); + +#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/ + +static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = { + { 0, "EVERYTHING" }, + { LOGL_DEBUG, "DEBUG" }, + { LOGL_INFO, "INFO" }, + { LOGL_NOTICE, "NOTICE" }, + { LOGL_ERROR, "ERROR" }, + { LOGL_FATAL, "FATAL" }, + { 0, NULL }, +}; + +/* You have to keep this in sync with the structure loglevel_strs. */ +const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = { + "Log simply everything", + "Log debug messages and higher levels", + "Log informational messages and higher levels", + "Log noticable messages and higher levels", + "Log error messages and higher levels", + "Log only fatal messages", + NULL, +}; + +int log_parse_level(const char *lvl) +{ + return get_string_value(loglevel_strs, lvl); +} + +const char *log_level_str(unsigned int lvl) +{ + return get_value_string(loglevel_strs, lvl); +} + +int log_parse_category(const char *category) +{ + int i; + + for (i = 0; i < osmo_log_info->num_cat; ++i) { + if (!strcasecmp(osmo_log_info->cat[i].name+1, category)) + return i; + } + + return -EINVAL; +} + +/* + * Parse the category mask. + * The format can be this: category1:category2:category3 + * or category1,2:category2,3:... + */ +void log_parse_category_mask(struct log_target* target, const char *_mask) +{ + int i = 0; + char *mask = strdup(_mask); + char *category_token = NULL; + + /* Disable everything to enable it afterwards */ + for (i = 0; i < ARRAY_SIZE(target->categories); ++i) + target->categories[i].enabled = 0; + + category_token = strtok(mask, ":"); + do { + for (i = 0; i < osmo_log_info->num_cat; ++i) { + char* colon = strstr(category_token, ","); + int length = strlen(category_token); + + if (colon) + length = colon - category_token; + + if (strncasecmp(osmo_log_info->cat[i].name, + category_token, length) == 0) { + int level = 0; + + if (colon) + level = atoi(colon+1); + + target->categories[i].enabled = 1; + target->categories[i].loglevel = level; + } + } + } while ((category_token = strtok(NULL, ":"))); + + free(mask); +} + +static const char* color(int subsys) +{ + if (subsys < osmo_log_info->num_cat) + return osmo_log_info->cat[subsys].color; + + return NULL; +} + +static void _output(struct log_target *target, unsigned int subsys, + unsigned int level, char *file, int line, int cont, + const char *format, va_list ap) +{ + char buf[4096]; + int ret, len = 0, offset = 0, rem = sizeof(buf); + + /* are we using color */ + if (target->use_color) { + const char *c = color(subsys); + if (c) { + ret = snprintf(buf + offset, rem, "%s", color(subsys)); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + } + if (!cont) { + if (target->print_timestamp) { + char *timestr; + time_t tm; + tm = time(NULL); + timestr = ctime(&tm); + timestr[strlen(timestr)-1] = '\0'; + ret = snprintf(buf + offset, rem, "%s ", timestr); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ", + subsys, file, line); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + ret = vsnprintf(buf + offset, rem, format, ap); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + ret = snprintf(buf + offset, rem, "%s", + target->use_color ? "\033[0;m" : ""); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); +err: + buf[sizeof(buf)-1] = '\0'; + target->output(target, level, buf); +} + + +static void _logp(unsigned int subsys, int level, char *file, int line, + int cont, const char *format, va_list ap) +{ + struct log_target *tar; + + llist_for_each_entry(tar, &osmo_log_target_list, entry) { + struct log_category *category; + int output = 0; + va_list bp; + + category = &tar->categories[subsys]; + /* subsystem is not supposed to be logged */ + if (!category->enabled) + continue; + + /* Check the global log level */ + if (tar->loglevel != 0 && level < tar->loglevel) + continue; + + /* Check the category log level */ + if (tar->loglevel == 0 && category->loglevel != 0 && + level < category->loglevel) + continue; + + /* Apply filters here... if that becomes messy we will + * need to put filters in a list and each filter will + * say stop, continue, output */ + if ((tar->filter_map & LOG_FILTER_ALL) != 0) + output = 1; + else if (osmo_log_info->filter_fn) + output = osmo_log_info->filter_fn(&log_context, + tar); + if (!output) + continue; + + /* According to the manpage, vsnprintf leaves the value of ap + * in undefined state. Since _output uses vsnprintf and it may + * be called several times, we have to pass a copy of ap. */ + va_copy(bp, ap); + _output(tar, subsys, level, file, line, cont, format, ap); + va_end(bp); + } +} + +void logp(unsigned int subsys, char *file, int line, int cont, + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap); + va_end(ap); +} + +void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + _logp(subsys, level, file, line, cont, format, ap); + va_end(ap); +} + +void log_add_target(struct log_target *target) +{ + llist_add_tail(&target->entry, &osmo_log_target_list); +} + +void log_del_target(struct log_target *target) +{ + llist_del(&target->entry); +} + +void log_reset_context(void) +{ + memset(&log_context, 0, sizeof(log_context)); +} + +int log_set_context(uint8_t ctx_nr, void *value) +{ + if (ctx_nr > LOG_MAX_CTX) + return -EINVAL; + + log_context.ctx[ctx_nr] = value; + + return 0; +} + +void log_set_all_filter(struct log_target *target, int all) +{ + if (all) + target->filter_map |= LOG_FILTER_ALL; + else + target->filter_map &= ~LOG_FILTER_ALL; +} + +void log_set_use_color(struct log_target *target, int use_color) +{ + target->use_color = use_color; +} + +void log_set_print_timestamp(struct log_target *target, int print_timestamp) +{ + target->print_timestamp = print_timestamp; +} + +void log_set_log_level(struct log_target *target, int log_level) +{ + target->loglevel = log_level; +} + +void log_set_category_filter(struct log_target *target, int category, + int enable, int level) +{ + if (category >= osmo_log_info->num_cat) + return; + target->categories[category].enabled = !!enable; + target->categories[category].loglevel = level; +} + +static void _file_output(struct log_target *target, unsigned int level, + const char *log) +{ + fprintf(target->tgt_file.out, "%s", log); + fflush(target->tgt_file.out); +} + +struct log_target *log_target_create(void) +{ + struct log_target *target; + unsigned int i; + + target = talloc_zero(tall_log_ctx, struct log_target); + if (!target) + return NULL; + + INIT_LLIST_HEAD(&target->entry); + + /* initialize the per-category enabled/loglevel from defaults */ + for (i = 0; i < osmo_log_info->num_cat; i++) { + struct log_category *cat = &target->categories[i]; + cat->enabled = osmo_log_info->cat[i].enabled; + cat->loglevel = osmo_log_info->cat[i].loglevel; + } + + /* global settings */ + target->use_color = 1; + target->print_timestamp = 0; + + /* global log level */ + target->loglevel = 0; + return target; +} + +struct log_target *log_target_create_stderr(void) +{ +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#ifdef stderr + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->type = LOG_TGT_TYPE_STDERR; + target->tgt_file.out = stderr; + target->output = _file_output; + return target; +#else + return NULL; +#endif /* stderr */ +} + +struct log_target *log_target_create_file(const char *fname) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->type = LOG_TGT_TYPE_FILE; + target->tgt_file.out = fopen(fname, "a"); + if (!target->tgt_file.out) + return NULL; + + target->output = _file_output; + + target->tgt_file.fname = talloc_strdup(target, fname); + + return target; +} + +struct log_target *log_target_find(int type, const char *fname) +{ + struct log_target *tgt; + + llist_for_each_entry(tgt, &osmo_log_target_list, entry) { + if (tgt->type != type) + continue; + if (tgt->type == LOG_TGT_TYPE_FILE) { + if (!strcmp(fname, tgt->tgt_file.fname)) + return tgt; + } else + return tgt; + } + return NULL; +} + +void log_target_destroy(struct log_target *target) +{ + + /* just in case, to make sure we don't have any references */ + log_del_target(target); + + if (target->output == &_file_output) { +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#ifdef stderr + /* don't close stderr */ + if (target->tgt_file.out != stderr) +#endif + { + fclose(target->tgt_file.out); + target->tgt_file.out = NULL; + } + } + + talloc_free(target); +} + +/* close and re-open a log file (for log file rotation) */ +int log_target_file_reopen(struct log_target *target) +{ + fclose(target->tgt_file.out); + + target->tgt_file.out = fopen(target->tgt_file.fname, "a"); + if (!target->tgt_file.out) + return -errno; + + /* we assume target->output already to be set */ + + return 0; +} + +/* This generates the logging command string for VTY. */ +const char *log_vty_command_string(const struct log_info *info) +{ + int len = 0, offset = 0, ret, i, rem; + int size = strlen("logging level () ()") + 1; + char *str; + + for (i = 0; i < info->num_cat; i++) + size += strlen(info->cat[i].name) + 1; + + for (i = 0; i < LOGLEVEL_DEFS; i++) + size += strlen(loglevel_strs[i].str) + 1; + + rem = size; + str = talloc_zero_size(tall_log_ctx, size); + if (!str) + return NULL; + + ret = snprintf(str + offset, rem, "logging level (all|"); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + for (i = 0; i < info->num_cat; i++) { + int j, name_len = strlen(info->cat[i].name)+1; + char name[name_len]; + + for (j = 0; j < name_len; j++) + name[j] = tolower(info->cat[i].name[j]); + + name[name_len-1] = '\0'; + ret = snprintf(str + offset, rem, "%s|", name+1); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + offset--; /* to remove the trailing | */ + rem++; + + ret = snprintf(str + offset, rem, ") ("); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + for (i = 0; i < LOGLEVEL_DEFS; i++) { + int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1; + char loglevel_str[loglevel_str_len]; + + for (j = 0; j < loglevel_str_len; j++) + loglevel_str[j] = tolower(loglevel_strs[i].str[j]); + + loglevel_str[loglevel_str_len-1] = '\0'; + ret = snprintf(str + offset, rem, "%s|", loglevel_str); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + offset--; /* to remove the trailing | */ + rem++; + + ret = snprintf(str + offset, rem, ")"); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); +err: + str[size-1] = '\0'; + return str; +} + +/* This generates the logging command description for VTY. */ +const char *log_vty_command_description(const struct log_info *info) +{ + char *str; + int i, ret, len = 0, offset = 0, rem; + unsigned int size = + strlen(LOGGING_STR + "Set the log level for a specified category\n") + 1; + + for (i = 0; i < info->num_cat; i++) + size += strlen(info->cat[i].description) + 1; + + for (i = 0; i < LOGLEVEL_DEFS; i++) + size += strlen(loglevel_descriptions[i]) + 1; + + size += strlen("Global setting for all subsystems") + 1; + rem = size; + str = talloc_zero_size(tall_log_ctx, size); + if (!str) + return NULL; + + ret = snprintf(str + offset, rem, LOGGING_STR + "Set the log level for a specified category\n"); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + ret = snprintf(str + offset, rem, + "Global setting for all subsystems\n"); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + + for (i = 0; i < info->num_cat; i++) { + ret = snprintf(str + offset, rem, "%s\n", + info->cat[i].description); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } + for (i = 0; i < LOGLEVEL_DEFS; i++) { + ret = snprintf(str + offset, rem, "%s\n", + loglevel_descriptions[i]); + if (ret < 0) + goto err; + OSMO_SNPRINTF_RET(ret, rem, offset, len); + } +err: + str[size-1] = '\0'; + return str; +} + +void log_init(const struct log_info *cat) +{ + tall_log_ctx = talloc_named_const(NULL, 1, "logging"); + osmo_log_info = cat; +} diff --git a/src/shared/libosmocore/src/logging_syslog.c b/src/shared/libosmocore/src/logging_syslog.c new file mode 100644 index 00000000..4f043b1d --- /dev/null +++ b/src/shared/libosmocore/src/logging_syslog.c @@ -0,0 +1,78 @@ +/* Syslog logging support code */ + +/* (C) 2011 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "../config.h" + +#ifdef HAVE_SYSLOG_H + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> + +static const int logp2syslog_level(unsigned int level) +{ + if (level >= LOGL_FATAL) + return LOG_CRIT; + else if (level >= LOGL_ERROR) + return LOG_ERR; + else if (level >= LOGL_NOTICE) + return LOG_NOTICE; + else if (level >= LOGL_INFO) + return LOG_INFO; + else + return LOG_DEBUG; +} + +static void _syslog_output(struct log_target *target, + unsigned int level, const char *log) +{ + syslog(logp2syslog_level(level), "%s", log); +} + +struct log_target *log_target_create_syslog(const char *ident, int option, + int facility) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->tgt_syslog.facility = facility; + target->type = LOG_TGT_TYPE_SYSLOG; + target->output = _syslog_output; + + openlog(ident, option, facility); + + return target; +} + +#endif /* HAVE_SYSLOG_H */ diff --git a/src/shared/libosmocore/src/msgb.c b/src/shared/libosmocore/src/msgb.c new file mode 100644 index 00000000..d2c167aa --- /dev/null +++ b/src/shared/libosmocore/src/msgb.c @@ -0,0 +1,100 @@ +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocom/core/msgb.h> +//#include <openbsc/gsm_data.h> +#include <osmocom/core/talloc.h> +//#include <openbsc/debug.h> + +void *tall_msgb_ctx; + +struct msgb *msgb_alloc(uint16_t size, const char *name) +{ + struct msgb *msg; + + msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name); + + if (!msg) { + //LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n"); + return NULL; + } + + msg->data_len = size; + msg->len = 0; + msg->data = msg->_data; + msg->head = msg->_data; + msg->tail = msg->_data; + + return msg; +} + +void msgb_free(struct msgb *m) +{ + talloc_free(m); +} + +void msgb_enqueue(struct llist_head *queue, struct msgb *msg) +{ + llist_add_tail(&msg->list, queue); +} + +struct msgb *msgb_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct msgb, list); +} + +void msgb_reset(struct msgb *msg) +{ + msg->len = 0; + msg->data = msg->_data; + msg->head = msg->_data; + msg->tail = msg->_data; + + msg->trx = NULL; + msg->lchan = NULL; + msg->l2h = NULL; + msg->l3h = NULL; + msg->l4h = NULL; + + memset(&msg->cb, 0, sizeof(msg->cb)); +} + +uint8_t *msgb_data(const struct msgb *msg) +{ + return msg->data; +} + +uint16_t msgb_length(const struct msgb *msg) +{ + return msg->len; +} diff --git a/src/shared/libosmocore/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c new file mode 100644 index 00000000..d2b180d7 --- /dev/null +++ b/src/shared/libosmocore/src/msgfile.c @@ -0,0 +1,123 @@ +/* + * Parse a simple file with messages, e.g used for USSD messages + * + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/core/msgfile.h> +#include <osmocom/core/talloc.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +static struct osmo_config_entry * +alloc_entry(struct osmo_config_list *entries, + const char *mcc, const char *mnc, + const char *option, const char *text) +{ + struct osmo_config_entry *entry = + talloc_zero(entries, struct osmo_config_entry); + if (!entry) + return NULL; + + entry->mcc = talloc_strdup(entry, mcc); + entry->mnc = talloc_strdup(entry, mnc); + entry->option = talloc_strdup(entry, option); + entry->text = talloc_strdup(entry, text); + + llist_add_tail(&entry->list, &entries->entry); + return entry; +} + +static struct osmo_config_list *alloc_entries(void *ctx) +{ + struct osmo_config_list *entries; + + entries = talloc_zero(ctx, struct osmo_config_list); + if (!entries) + return NULL; + + INIT_LLIST_HEAD(&entries->entry); + return entries; +} + +/* + * split a line like 'foo:Text'. + */ +static void handle_line(struct osmo_config_list *entries, char *line) +{ + int i; + const int len = strlen(line); + + char *items[3]; + int last_item = 0; + + /* Skip comments from the file */ + if (line[0] == '#') + return; + + for (i = 0; i < len; ++i) { + if (line[i] == '\n' || line[i] == '\r') + line[i] = '\0'; + else if (line[i] == ':' && last_item < 3) { + line[i] = '\0'; + + items[last_item++] = &line[i + 1]; + } + } + + if (last_item == 3) { + alloc_entry(entries, &line[0] , items[0], items[1], items[2]); + return; + } + + /* nothing found */ +} + +struct osmo_config_list *osmo_config_list_parse(void *ctx, const char *filename) +{ + struct osmo_config_list *entries; + size_t n; + char *line; + FILE *file; + + file = fopen(filename, "r"); + if (!file) + return NULL; + + entries = alloc_entries(ctx); + if (!entries) { + fclose(file); + return NULL; + } + + n = 2342; + line = NULL; + while (getline(&line, &n, file) != -1) { + handle_line(entries, line); + free(line); + line = NULL; + } + + fclose(file); + return entries; +} diff --git a/src/shared/libosmocore/src/panic.c b/src/shared/libosmocore/src/panic.c new file mode 100644 index 00000000..d4452264 --- /dev/null +++ b/src/shared/libosmocore/src/panic.c @@ -0,0 +1,74 @@ +/* Panic handling */ +/* + * (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/core/panic.h> +#include <osmocom/core/backtrace.h> + +#include "../config.h" + + +static osmo_panic_handler_t osmo_panic_handler = (void*)0; + + +#ifndef PANIC_INFLOOP + +#include <stdio.h> +#include <stdlib.h> + +static void osmo_panic_default(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + osmo_generate_backtrace(); + abort(); +} + +#else + +static void osmo_panic_default(const char *fmt, va_list args) +{ + while (1); +} + +#endif + + +void osmo_panic(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (osmo_panic_handler) + osmo_panic_handler(fmt, args); + else + osmo_panic_default(fmt, args); + + va_end(args); +} + + +void osmo_set_panic_handler(osmo_panic_handler_t h) +{ + osmo_panic_handler = h; +} + diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c new file mode 100644 index 00000000..998bca35 --- /dev/null +++ b/src/shared/libosmocore/src/plugin.c @@ -0,0 +1,62 @@ +/* plugin infrastructure */ + +/* (C) 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "../config.h" + +#if HAVE_DLFCN_H + +#include <dirent.h> +#include <dlfcn.h> +#include <stdio.h> +#include <errno.h> +#include <limits.h> + +#include <osmocom/core/plugin.h> + +int osmo_plugin_load_all(const char *directory) +{ + unsigned int num = 0; + char fname[PATH_MAX]; + DIR *dir; + struct dirent *entry; + + dir = opendir(directory); + if (!dir) + return -errno; + + while ((entry = readdir(dir))) { + snprintf(fname, sizeof(fname), "%s/%s", directory, + entry->d_name); + if (dlopen(fname, RTLD_NOW)) + num++; + } + + closedir(dir); + + return num; +} +#else +int osmo_plugin_load_all(const char *directory) +{ + return 0; +} +#endif /* HAVE_DLFCN_H */ diff --git a/src/shared/libosmocore/src/process.c b/src/shared/libosmocore/src/process.c new file mode 100644 index 00000000..7f4f12cb --- /dev/null +++ b/src/shared/libosmocore/src/process.c @@ -0,0 +1,73 @@ +/* Process handling support code */ + +/* (C) 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> + +int osmo_daemonize(void) +{ + int rc; + pid_t pid, sid; + + /* Check if parent PID == init, in which case we are already a daemon */ + if (getppid() == 1) + return -EEXIST; + + /* Fork from the parent process */ + pid = fork(); + if (pid < 0) { + /* some error happened */ + return pid; + } + + if (pid > 0) { + /* if we have received a positive PID, then we are the parent + * and can exit */ + exit(0); + } + + /* FIXME: do we really want this? */ + umask(0); + + /* Create a new session and set process group ID */ + sid = setsid(); + if (sid < 0) + return sid; + + /* Change to the /tmp directory, which prevents the CWD from being locked + * and unable to remove it */ + rc = chdir("/tmp"); + if (rc < 0) + return rc; + + /* Redirect stdio to /dev/null */ +/* since C89/C99 says stderr is a macro, we can safely do this! */ +#ifdef stderr + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); +#endif + + return 0; +} diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c new file mode 100644 index 00000000..6d771a44 --- /dev/null +++ b/src/shared/libosmocore/src/rate_ctr.c @@ -0,0 +1,161 @@ +/* utility routines for keeping conters about events and the event rates */ + +/* (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <string.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> + +static LLIST_HEAD(rate_ctr_groups); + +static void *tall_rate_ctr_ctx; + +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx) +{ + unsigned int size; + struct rate_ctr_group *group; + + size = sizeof(struct rate_ctr_group) + + desc->num_ctr * sizeof(struct rate_ctr); + + if (!ctx) + ctx = tall_rate_ctr_ctx; + + group = talloc_zero_size(ctx, size); + if (!group) + return NULL; + + group->desc = desc; + group->idx = idx; + + llist_add(&group->list, &rate_ctr_groups); + + return group; +} + +void rate_ctr_group_free(struct rate_ctr_group *grp) +{ + llist_del(&grp->list); + talloc_free(grp); +} + +void rate_ctr_add(struct rate_ctr *ctr, int inc) +{ + ctr->current += inc; +} + +static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) +{ + /* calculate rate over last interval */ + ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last; + /* save current counter for next interval */ + ctr->intv[intv].last = ctr->current; + + /* update the rate of the next bigger interval. This will + * be overwritten when that next larger interval expires */ + if (intv + 1 < ARRAY_SIZE(ctr->intv)) + ctr->intv[intv+1].rate += ctr->intv[intv].rate; +} + +static struct osmo_timer_list rate_ctr_timer; +static uint64_t timer_ticks; + +/* The one-second interval has expired */ +static void rate_ctr_group_intv(struct rate_ctr_group *grp) +{ + unsigned int i; + + for (i = 0; i < grp->desc->num_ctr; i++) { + struct rate_ctr *ctr = &grp->ctr[i]; + + interval_expired(ctr, RATE_CTR_INTV_SEC); + if ((timer_ticks % 60) == 0) + interval_expired(ctr, RATE_CTR_INTV_MIN); + if ((timer_ticks % (60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_HOUR); + if ((timer_ticks % (24*60*60)) == 0) + interval_expired(ctr, RATE_CTR_INTV_DAY); + } +} + +static void rate_ctr_timer_cb(void *data) +{ + struct rate_ctr_group *ctrg; + + /* Increment number of ticks before we calculate intervals, + * as a counter value of 0 would already wrap all counters */ + timer_ticks++; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) + rate_ctr_group_intv(ctrg); + + osmo_timer_schedule(&rate_ctr_timer, 1, 0); +} + +int rate_ctr_init(void *tall_ctx) +{ + tall_rate_ctr_ctx = tall_ctx; + rate_ctr_timer.cb = rate_ctr_timer_cb; + osmo_timer_schedule(&rate_ctr_timer, 1, 0); + + return 0; +} + +struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx) +{ + struct rate_ctr_group *ctrg; + + llist_for_each_entry(ctrg, &rate_ctr_groups, list) { + if (!ctrg->desc) + continue; + + if (!strcmp(ctrg->desc->group_name_prefix, name) && + ctrg->idx == idx) { + return ctrg; + } + } + return NULL; +} + +const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name) +{ + int i; + const struct rate_ctr_desc *ctr_desc; + + if (!ctrg->desc) + return NULL; + + for (i = 0; i < ctrg->desc->num_ctr; i++) { + ctr_desc = &ctrg->desc->ctr_desc[i]; + + if (!strcmp(ctr_desc->name, name)) { + return &ctrg->ctr[i]; + } + } + return NULL; +} diff --git a/src/shared/libosmocore/src/select.c b/src/shared/libosmocore/src/select.c new file mode 100644 index 00000000..4ea95362 --- /dev/null +++ b/src/shared/libosmocore/src/select.c @@ -0,0 +1,144 @@ +/* select filedescriptor handling, taken from: + * userspace logging daemon for the iptables ULOG target + * of the linux 2.4 netfilter subsystem. + * + * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <fcntl.h> +#include <stdio.h> + +#include <osmocom/core/select.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> + +#include "../config.h" + +#ifdef HAVE_SYS_SELECT_H + +static int maxfd = 0; +static LLIST_HEAD(osmo_fds); +static int unregistered_count; + +int osmo_fd_register(struct osmo_fd *fd) +{ + int flags; + + /* make FD nonblocking */ + flags = fcntl(fd->fd, F_GETFL); + if (flags < 0) + return flags; + flags |= O_NONBLOCK; + flags = fcntl(fd->fd, F_SETFL, flags); + if (flags < 0) + return flags; + + /* Register FD */ + if (fd->fd > maxfd) + maxfd = fd->fd; + +#ifdef BSC_FD_CHECK + struct osmo_fd *entry; + llist_for_each_entry(entry, &osmo_fds, list) { + if (entry == fd) { + fprintf(stderr, "Adding a osmo_fd that is already in the list.\n"); + return 0; + } + } +#endif + + llist_add_tail(&fd->list, &osmo_fds); + + return 0; +} + +void osmo_fd_unregister(struct osmo_fd *fd) +{ + unregistered_count++; + llist_del(&fd->list); +} + +int osmo_select_main(int polling) +{ + struct osmo_fd *ufd, *tmp; + fd_set readset, writeset, exceptset; + int work = 0, rc; + struct timeval no_time = {0, 0}; + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + /* prepare read and write fdsets */ + llist_for_each_entry(ufd, &osmo_fds, list) { + if (ufd->when & BSC_FD_READ) + FD_SET(ufd->fd, &readset); + + if (ufd->when & BSC_FD_WRITE) + FD_SET(ufd->fd, &writeset); + + if (ufd->when & BSC_FD_EXCEPT) + FD_SET(ufd->fd, &exceptset); + } + + osmo_timers_check(); + + if (!polling) + osmo_timers_prepare(); + rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest()); + if (rc < 0) + return 0; + + /* fire timers */ + osmo_timers_update(); + + /* call registered callback functions */ +restart: + unregistered_count = 0; + llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) { + int flags = 0; + + if (FD_ISSET(ufd->fd, &readset)) { + flags |= BSC_FD_READ; + FD_CLR(ufd->fd, &readset); + } + + if (FD_ISSET(ufd->fd, &writeset)) { + flags |= BSC_FD_WRITE; + FD_CLR(ufd->fd, &writeset); + } + + if (FD_ISSET(ufd->fd, &exceptset)) { + flags |= BSC_FD_EXCEPT; + FD_CLR(ufd->fd, &exceptset); + } + + if (flags) { + work = 1; + ufd->cb(ufd, flags); + } + /* ugly, ugly hack. If more than one filedescriptors were + * unregistered, they might have been consecutive and + * llist_for_each_entry_safe() is no longer safe */ + /* this seems to happen with the last element of the list as well */ + if (unregistered_count >= 1) + goto restart; + } + return work; +} + +#endif /* _HAVE_SYS_SELECT_H */ diff --git a/src/shared/libosmocore/src/signal.c b/src/shared/libosmocore/src/signal.c new file mode 100644 index 00000000..bc339bb9 --- /dev/null +++ b/src/shared/libosmocore/src/signal.c @@ -0,0 +1,87 @@ +/* Generic signalling/notification infrastructure */ +/* (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/core/signal.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/linuxlist.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +void *tall_sigh_ctx; +static LLIST_HEAD(signal_handler_list); + +struct signal_handler { + struct llist_head entry; + unsigned int subsys; + osmo_signal_cbfn *cbfn; + void *data; +}; + + +int osmo_signal_register_handler(unsigned int subsys, + osmo_signal_cbfn *cbfn, void *data) +{ + struct signal_handler *sig_data; + + sig_data = talloc(tall_sigh_ctx, struct signal_handler); + if (!sig_data) + return -ENOMEM; + + memset(sig_data, 0, sizeof(*sig_data)); + + sig_data->subsys = subsys; + sig_data->data = data; + sig_data->cbfn = cbfn; + + /* FIXME: check if we already have a handler for this subsys/cbfn/data */ + + llist_add_tail(&sig_data->entry, &signal_handler_list); + + return 0; +} + +void osmo_signal_unregister_handler(unsigned int subsys, + osmo_signal_cbfn *cbfn, void *data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->cbfn == cbfn && handler->data == data + && subsys == handler->subsys) { + llist_del(&handler->entry); + talloc_free(handler); + break; + } + } +} + + +void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, + void *signal_data) +{ + struct signal_handler *handler; + + llist_for_each_entry(handler, &signal_handler_list, entry) { + if (handler->subsys != subsys) + continue; + (*handler->cbfn)(subsys, signal, handler->data, signal_data); + } +} diff --git a/src/shared/libosmocore/src/socket.c b/src/shared/libosmocore/src/socket.c new file mode 100644 index 00000000..901b8cdf --- /dev/null +++ b/src/shared/libosmocore/src/socket.c @@ -0,0 +1,181 @@ +#include "../config.h" + +#ifdef HAVE_SYS_SOCKET_H + +#include <osmocom/core/logging.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <netdb.h> +#include <ifaddrs.h> + +int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, + const char *host, uint16_t port, int connect0_bind1) +{ + struct addrinfo hints, *result, *rp; + int sfd, rc, on = 1; + char portbuf[16]; + + sprintf(portbuf, "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = type; + hints.ai_flags = 0; + hints.ai_protocol = proto; + + rc = getaddrinfo(host, portbuf, &hints, &result); + if (rc != 0) { + perror("getaddrinfo returned NULL"); + return -EINVAL; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + if (connect0_bind1 == 0) { + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } else { + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + } + close(sfd); + } + freeaddrinfo(result); + + if (rp == NULL) { + perror("unable to connect/bind socket"); + return -ENODEV; + } + + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + /* Make sure to call 'listen' on a bound, connection-oriented sock */ + if (connect0_bind1 == 1) { + switch (type) { + case SOCK_STREAM: + case SOCK_SEQPACKET: + listen(sfd, 10); + break; + } + } + return sfd; +} + +int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, + const char *host, uint16_t port, int connect0_bind1) +{ + int sfd, rc; + + sfd = osmo_sock_init(family, type, proto, host, port, connect0_bind1); + if (sfd < 0) + return sfd; + + ofd->fd = sfd; + ofd->when = BSC_FD_READ; + + rc = osmo_fd_register(ofd); + if (rc < 0) { + close(sfd); + return rc; + } + + return sfd; +} + +int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, + uint8_t proto, int connect0_bind1) +{ + char host[NI_MAXHOST]; + uint16_t port; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int s, sa_len; + + /* determine port and host from ss */ + switch (ss->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *) ss; + sa_len = sizeof(struct sockaddr_in); + port = ntohs(sin->sin_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *) ss; + sa_len = sizeof(struct sockaddr_in6); + port = ntohs(sin6->sin6_port); + break; + default: + return -EINVAL; + } + + s = getnameinfo(ss, sa_len, host, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST); + if (s != 0) { + perror("getnameinfo failed"); + return s; + } + + return osmo_sock_init(ss->sa_family, type, proto, host, + port, connect0_bind1); +} + +static int sockaddr_equal(const struct sockaddr *a, + const struct sockaddr *b, unsigned int len) +{ + struct sockaddr_in *sin_a, *sin_b; + struct sockaddr_in6 *sin6_a, *sin6_b; + + if (a->sa_family != b->sa_family) + return 0; + + switch (a->sa_family) { + case AF_INET: + sin_a = (struct sockaddr_in *)a; + sin_b = (struct sockaddr_in *)b; + if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, + sizeof(struct in_addr))) + return 1; + break; + case AF_INET6: + sin6_a = (struct sockaddr_in6 *)a; + sin6_b = (struct sockaddr_in6 *)b; + if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, + sizeof(struct in6_addr))) + return 1; + break; + } + return 0; +} + +/* determine if the given address is a local address */ +int osmo_sockaddr_is_local(struct sockaddr *addr, socklen_t addrlen) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) { + perror("getifaddrs"); + return -EIO; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) + return 1; + } + + return 0; +} + +#endif /* HAVE_SYS_SOCKET_H */ diff --git a/src/shared/libosmocore/src/statistics.c b/src/shared/libosmocore/src/statistics.c new file mode 100644 index 00000000..e28541ba --- /dev/null +++ b/src/shared/libosmocore/src/statistics.c @@ -0,0 +1,76 @@ +/* utility routines for keeping some statistics */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <string.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/statistics.h> + +static LLIST_HEAD(counters); + +void *tall_ctr_ctx; + +struct osmo_counter *osmo_counter_alloc(const char *name) +{ + struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter); + + if (!ctr) + return NULL; + + ctr->name = name; + llist_add_tail(&ctr->list, &counters); + + return ctr; +} + +void osmo_counter_free(struct osmo_counter *ctr) +{ + llist_del(&ctr->list); + talloc_free(ctr); +} + +int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), + void *data) +{ + struct osmo_counter *ctr; + int rc = 0; + + llist_for_each_entry(ctr, &counters, list) { + rc = handle_counter(ctr, data); + if (rc < 0) + return rc; + } + + return rc; +} + +struct osmo_counter *osmo_counter_get_by_name(const char *name) +{ + struct osmo_counter *ctr; + + llist_for_each_entry(ctr, &counters, list) { + if (!strcmp(ctr->name, name)) + return ctr; + } + return NULL; +} diff --git a/src/shared/libosmocore/src/talloc.c b/src/shared/libosmocore/src/talloc.c new file mode 100644 index 00000000..d3a0690f --- /dev/null +++ b/src/shared/libosmocore/src/talloc.c @@ -0,0 +1,1804 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#ifdef _SAMBA_BUILD_ +#include "version.h" +#if (SAMBA_VERSION_MAJOR<4) +#include "includes.h" +/* This is to circumvent SAMBA3's paranoid malloc checker. Here in this file + * we trust ourselves... */ +#ifdef malloc +#undef malloc +#endif +#ifdef realloc +#undef realloc +#endif +#define _TALLOC_SAMBA3 +#endif /* (SAMBA_VERSION_MAJOR<4) */ +#endif /* _SAMBA_BUILD_ */ + +#ifndef _TALLOC_SAMBA3 +//#include "replace.h" +#include <unistd.h> +#include <stdio.h> +#include <stdbool.h> +#define __USE_GNU +#include <string.h> +#undef __USE_GNU +#include <osmocom/core/talloc.h> +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif /* not _TALLOC_SAMBA3 */ + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ +#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +#ifdef __APPLE__ +/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */ +size_t strnlen(const char *s, size_t n) +{ + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); +} +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + unsigned flags; + + /* + * "pool" has dual use: + * + * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" + * marks the end of the currently allocated area. + * + * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" + * is a pointer to the struct talloc_chunk of the pool that it was + * allocated from. This way children can quickly find the pool to chew + * from. + */ + void *pool; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +static void (*talloc_abort_fn)(const char *reason); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + talloc_abort_fn = abort_fn; +} + +static void talloc_abort(const char *reason) +{ + if (!talloc_abort_fn) { + TALLOC_ABORT(reason); + } + + talloc_abort_fn(reason); +} + +static void talloc_abort_double_free(void) +{ + talloc_abort("Bad talloc magic value - double free"); +} + +static void talloc_abort_unknown_value(void) +{ + talloc_abort("Bad talloc magic value - unknown value"); +} + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + talloc_abort_double_free(); + } else { + talloc_abort_unknown_value(); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + A pool carries an in-pool object count count in the first 16 bytes. + bytes. This is done to support talloc_steal() to a parent outside of the + pool. The count includes the pool itself, so a talloc_free() on a pool will + only destroy the pool if the count has dropped to zero. A talloc_free() of a + pool member will reduce the count, and eventually also call free(3) on the + pool memory. + + The object count is not put into "struct talloc_chunk" because it is only + relevant for talloc pools and the alignment to 16 bytes would increase the + memory footprint of each talloc chunk by those 16 bytes. +*/ + +#define TALLOC_POOL_HDR_SIZE 16 + +static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) +{ + return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); +} + +/* + Allocate from a pool +*/ + +static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, + size_t size) +{ + struct talloc_chunk *pool_ctx = NULL; + size_t space_left; + struct talloc_chunk *result; + size_t chunk_size; + + if (parent == NULL) { + return NULL; + } + + if (parent->flags & TALLOC_FLAG_POOL) { + pool_ctx = parent; + } + else if (parent->flags & TALLOC_FLAG_POOLMEM) { + pool_ctx = (struct talloc_chunk *)parent->pool; + } + + if (pool_ctx == NULL) { + return NULL; + } + + space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) + - ((char *)pool_ctx->pool); + + /* + * Align size to 16 bytes + */ + chunk_size = ((size + 15) & ~15); + + if (space_left < chunk_size) { + return NULL; + } + + result = (struct talloc_chunk *)pool_ctx->pool; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(result, size); +#endif + + pool_ctx->pool = (void *)((char *)result + chunk_size); + + result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; + result->pool = pool_ctx; + + *talloc_pool_objectcount(pool_ctx) += 1; + + return result; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc = NULL; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + if (context != NULL) { + tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), + TC_HDR_SIZE+size); + } + + if (tc == NULL) { + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + tc->flags = TALLOC_MAGIC; + tc->pool = NULL; + } + + tc->size = size; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + * Create a talloc pool + */ + +void *talloc_pool(const void *context, size_t size) +{ + void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); + struct talloc_chunk *tc; + + if (unlikely(result == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(result); + + tc->flags |= TALLOC_FLAG_POOL; + tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; + + *talloc_pool_objectcount(tc) = 1; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); +#endif + + return result; +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + + if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { + struct talloc_chunk *pool; + unsigned int *pool_object_count; + + pool = (tc->flags & TALLOC_FLAG_POOL) + ? tc : (struct talloc_chunk *)tc->pool; + + pool_object_count = talloc_pool_objectcount(pool); + + if (*pool_object_count == 0) { + talloc_abort("Pool object count zero!"); + } + + *pool_object_count -= 1; + + if (*pool_object_count == 0) { + free(pool); + } + } + else { + free(tc); + } + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + +static void talloc_abort_type_missmatch(const char *location, + const char *name, + const char *expected) +{ + const char *reason; + + reason = talloc_asprintf(NULL, + "%s: Type mismatch: name[%s] expected[%s]", + location, + name?name:"NULL", + expected); + if (!reason) { + reason = "Type mismatch"; + } + + talloc_abort(reason); +} + +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) +{ + const char *pname; + + if (unlikely(ptr == NULL)) { + talloc_abort_type_missmatch(location, NULL, name); + return NULL; + } + + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + + talloc_abort_type_missmatch(location, pname, name); + return NULL; +} + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + if ((tc->flags & TALLOC_FLAG_POOL) + && (*talloc_pool_objectcount(tc) == 1)) { + tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS( + tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); +#endif + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + return _talloc_free(ptr); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + bool malloced = false; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* don't let anybody try to realloc a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { + return NULL; + } + + /* don't shrink if we have less than 1k to gain */ + if ((size < tc->size) && ((tc->size - size) < 1024)) { + tc->size = size; + return ptr; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + if (tc->flags & TALLOC_FLAG_POOLMEM) { + + new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); + *talloc_pool_objectcount((struct talloc_chunk *) + (tc->pool)) -= 1; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + } + } + else { + new_ptr = realloc(tc, size + TC_HDR_SIZE); + } +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (malloced) { + tc->flags &= ~TALLOC_FLAG_POOLMEM; + } + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) +{ + char *ret; + + ret = (char *)__talloc(t, len + 1); + if (unlikely(!ret)) return NULL; + + memcpy(ret, p, len); + ret[len] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strlen(p)); +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strnlen(p, n)); +} + +static inline char *__talloc_strlendup_append(char *s, size_t slen, + const char *a, size_t alen) +{ + char *ret; + + ret = talloc_realloc(NULL, s, char, slen + alen + 1); + if (unlikely(!ret)) return NULL; + + /* append the string and the trailing \0 */ + memcpy(&ret[slen], a, alen); + ret[slen+alen] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + * Appends at the end of the string. + */ +char *talloc_strdup_append(char *s, const char *a) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strdup_append_buffer(char *s, const char *a) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strlen(a)); +} + +/* + * Appends at the end of the string. + */ +char *talloc_strndup_append(char *s, const char *a, size_t n) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); +} + +#ifndef HAVE_VA_COPY +#ifdef HAVE___VA_COPY +#define va_copy(dest, src) __va_copy(dest, src) +#else +#define va_copy(dest, src) (dest) = (src) +#endif +#endif + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + char c; + + /* this call looks strange, but it makes it work on older solaris boxes */ + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + if (unlikely(len < 0)) { + return NULL; + } + + ret = (char *)__talloc(t, len+1); + if (unlikely(!ret)) return NULL; + + va_copy(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(ret, ret); + return ret; +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(3,0); + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) +{ + ssize_t alen; + va_list ap2; + char c; + + va_copy(ap2, ap); + alen = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (alen <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, slen + alen + 1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s + slen, alen + 1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(s, s); + return s; +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. Appends at the end + * of the string. + **/ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Always appends at the + * end of the talloc'ed buffer, not the end of the string. + **/ +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_vaslenprintf_append(s, slen, fmt, ap); +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a buffer. + */ +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c new file mode 100644 index 00000000..ec752123 --- /dev/null +++ b/src/shared/libosmocore/src/timer.c @@ -0,0 +1,186 @@ +/* + * (C) 2008,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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <assert.h> +#include <string.h> +#include <osmocom/core/timer.h> + +static LLIST_HEAD(timer_list); +static struct timeval s_nearest_time; +static struct timeval s_select_time; + +#define MICRO_SECONDS 1000000LL + +#define TIME_SMALLER(left, right) \ + (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec) + +void osmo_timer_add(struct osmo_timer_list *timer) +{ + struct osmo_timer_list *list_timer; + + /* TODO: Optimize and remember the closest item... */ + timer->active = 1; + + /* this might be called from within update_timers */ + llist_for_each_entry(list_timer, &timer_list, entry) + if (timer == list_timer) + return; + + timer->in_list = 1; + llist_add(&timer->entry, &timer_list); +} + +void +osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds) +{ + struct timeval current_time; + + gettimeofday(¤t_time, NULL); + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + currentTime += seconds * MICRO_SECONDS + microseconds; + timer->timeout.tv_sec = currentTime / MICRO_SECONDS; + timer->timeout.tv_usec = currentTime % MICRO_SECONDS; + osmo_timer_add(timer); +} + +void osmo_timer_del(struct osmo_timer_list *timer) +{ + if (timer->in_list) { + timer->active = 0; + timer->in_list = 0; + llist_del(&timer->entry); + } +} + +int osmo_timer_pending(struct osmo_timer_list *timer) +{ + return timer->active; +} + +/* + * if we have a nearest time return the delta between the current + * time and the time of the nearest timer. + * If the nearest timer timed out return NULL and then we will + * dispatch everything after the select + */ +struct timeval *osmo_timers_nearest() +{ + struct timeval current_time; + + if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) + return NULL; + + if (gettimeofday(¤t_time, NULL) == -1) + return NULL; + + unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + + if (nearestTime < currentTime) { + s_select_time.tv_sec = 0; + s_select_time.tv_usec = 0; + } else { + s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + } + + return &s_select_time; +} + +/* + * Find the nearest time and update s_nearest_time + */ +void osmo_timers_prepare() +{ + struct osmo_timer_list *timer, *nearest_timer = NULL; + llist_for_each_entry(timer, &timer_list, entry) { + if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) { + nearest_timer = timer; + } + } + + if (nearest_timer) { + s_nearest_time = nearest_timer->timeout; + } else { + memset(&s_nearest_time, 0, sizeof(struct timeval)); + } +} + +/* + * fire all timers... and remove them + */ +int osmo_timers_update() +{ + struct timeval current_time; + struct osmo_timer_list *timer, *tmp; + int work = 0; + + gettimeofday(¤t_time, NULL); + + /* + * The callbacks might mess with our list and in this case + * even llist_for_each_entry_safe is not safe to use. To allow + * del_timer, add_timer, schedule_timer to be called from within + * the callback we jump through some loops. + * + * First we set the handled flag of each active timer to zero, + * then we iterate over the list and execute the callbacks. As the + * list might have been changed (specially the next) from within + * the callback we have to start over again. Once every callback + * is dispatched we will remove the non-active from the list. + * + * TODO: If this is a performance issue we can poison a global + * variable in add_timer and del_timer and only then restart. + */ + llist_for_each_entry(timer, &timer_list, entry) { + timer->handled = 0; + } + +restart: + llist_for_each_entry(timer, &timer_list, entry) { + if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) { + timer->handled = 1; + timer->active = 0; + (*timer->cb)(timer->data); + work = 1; + goto restart; + } + } + + llist_for_each_entry_safe(timer, tmp, &timer_list, entry) { + timer->handled = 0; + if (!timer->active) { + osmo_timer_del(timer); + } + } + + return work; +} + +int osmo_timers_check(void) +{ + struct osmo_timer_list *timer; + int i = 0; + + llist_for_each_entry(timer, &timer_list, entry) { + i++; + } + return i; +} diff --git a/src/shared/libosmocore/src/utils.c b/src/shared/libosmocore/src/utils.c new file mode 100644 index 00000000..3ee14abd --- /dev/null +++ b/src/shared/libosmocore/src/utils.c @@ -0,0 +1,158 @@ + +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/utils.h> + +static char namebuf[255]; +const char *get_value_string(const struct value_string *vs, uint32_t val) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (vs[i].value == val) + return vs[i].str; + } + + snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val); + return namebuf; +} + +int get_string_value(const struct value_string *vs, const char *str) +{ + int i; + + for (i = 0;; i++) { + if (vs[i].value == 0 && vs[i].str == NULL) + break; + if (!strcasecmp(vs[i].str, str)) + return vs[i].value; + } + return -EINVAL; +} + +char osmo_bcd2char(uint8_t bcd) +{ + if (bcd < 0xa) + return '0' + bcd; + else + return 'A' + (bcd - 0xa); +} + +/* only works for numbers in ascii */ +uint8_t osmo_char2bcd(char c) +{ + return c - 0x30; +} + +int osmo_hexparse(const char *str, uint8_t *b, int max_len) + +{ + int i, l, v; + + l = strlen(str); + if ((l&1) || ((l>>1) > max_len)) + return -1; + + memset(b, 0x00, max_len); + + for (i=0; i<l; i++) { + char c = str[i]; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'f') + v = 10 + (c - 'a'); + else if (c >= 'A' && c <= 'F') + v = 10 + (c - 'A'); + else + return -1; + b[i>>1] |= v << (i&1 ? 0 : 4); + } + + return i>>1; +} + +static char hexd_buff[4096]; + +static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim) +{ + int i; + char *cur = hexd_buff; + + hexd_buff[0] = 0; + for (i = 0; i < len; i++) { + int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); + int rc = snprintf(cur, len_remain, "%02x%s", buf[i], delim); + if (rc <= 0) + break; + cur += rc; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + +char *osmo_ubit_dump(const uint8_t *bits, unsigned int len) +{ + int i; + + if (len > sizeof(hexd_buff)-1) + len = sizeof(hexd_buff)-1; + memset(hexd_buff, 0, sizeof(hexd_buff)); + + for (i = 0; i < len; i++) { + char outch; + switch (bits[i]) { + case 0: + outch = '0'; + break; + case 0xff: + outch = '?'; + break; + case 1: + outch = '1'; + break; + default: + outch = 'E'; + break; + } + hexd_buff[i] = outch; + } + hexd_buff[sizeof(hexd_buff)-1] = 0; + return hexd_buff; +} + +char *osmo_hexdump(const unsigned char *buf, int len) +{ + return _osmo_hexdump(buf, len, " "); +} + +char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) +{ + return _osmo_hexdump(buf, len, ""); +} + +#include "../config.h" +#ifdef HAVE_CTYPE_H +#include <ctype.h> +void osmo_str2lower(char *out, const char *in) +{ + unsigned int i; + + for (i = 0; i < strlen(in); i++) + out[i] = tolower(in[i]); + out[strlen(in)] = '\0'; +} + +void osmo_str2upper(char *out, const char *in) +{ + unsigned int i; + + for (i = 0; i < strlen(in); i++) + out[i] = toupper(in[i]); + out[strlen(in)] = '\0'; +} +#endif /* HAVE_CTYPE_H */ diff --git a/src/shared/libosmocore/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am new file mode 100644 index 00000000..b7be6f84 --- /dev/null +++ b/src/shared/libosmocore/src/vty/Makefile.am @@ -0,0 +1,15 @@ +# This is _NOT_ the library release version, it's an API version. +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification +LIBVERSION=0:0:0 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS = -fPIC -Wall + +if ENABLE_VTY +lib_LTLIBRARIES = libosmovty.la + +libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ + telnet_interface.c logging_vty.c +libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) +libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la +endif diff --git a/src/shared/libosmocore/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c new file mode 100644 index 00000000..e385f9fd --- /dev/null +++ b/src/shared/libosmocore/src/vty/buffer.c @@ -0,0 +1,463 @@ +/* + * Buffering of output and input. + * Copyright (C) 1998 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stddef.h> +#include <sys/uio.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> + +/* Buffer master. */ +struct buffer { + /* Data list. */ + struct buffer_data *head; + struct buffer_data *tail; + + /* Size of each buffer_data chunk. */ + size_t size; +}; + +/* Data container. */ +struct buffer_data { + struct buffer_data *next; + + /* Location to add new data. */ + size_t cp; + + /* Pointer to data not yet flushed. */ + size_t sp; + + /* Actual data stream (variable length). */ + unsigned char data[0]; /* real dimension is buffer->size */ +}; + +/* It should always be true that: 0 <= sp <= cp <= size */ + +/* Default buffer size (used if none specified). It is rounded up to the + next page boundery. */ +#define BUFFER_SIZE_DEFAULT 4096 + +#define BUFFER_DATA_FREE(D) talloc_free((D)) + +/* Make new buffer. */ +struct buffer *buffer_new(void *ctx, size_t size) +{ + struct buffer *b; + + b = talloc_zero(ctx, struct buffer); + + if (size) + b->size = size; + else { + static size_t default_size; + if (!default_size) { + long pgsz = sysconf(_SC_PAGESIZE); + default_size = + ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz); + } + b->size = default_size; + } + + return b; +} + +/* Free buffer. */ +void buffer_free(struct buffer *b) +{ + buffer_reset(b); + talloc_free(b); +} + +/* Make string clone. */ +char *buffer_getstr(struct buffer *b) +{ + size_t totlen = 0; + struct buffer_data *data; + char *s; + char *p; + + for (data = b->head; data; data = data->next) + totlen += data->cp - data->sp; + if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr"))) + return NULL; + p = s; + for (data = b->head; data; data = data->next) { + memcpy(p, data->data + data->sp, data->cp - data->sp); + p += data->cp - data->sp; + } + *p = '\0'; + return s; +} + +/* Return 1 if buffer is empty. */ +int buffer_empty(struct buffer *b) +{ + return (b->head == NULL); +} + +/* Clear and free all allocated data. */ +void buffer_reset(struct buffer *b) +{ + struct buffer_data *data; + struct buffer_data *next; + + for (data = b->head; data; data = next) { + next = data->next; + BUFFER_DATA_FREE(data); + } + b->head = b->tail = NULL; +} + +/* Add buffer_data to the end of buffer. */ +static struct buffer_data *buffer_add(struct buffer *b) +{ + struct buffer_data *d; + + d = _talloc_zero(b, + offsetof(struct buffer_data, data[b->size]), + "buffer_add"); + if (!d) + return NULL; + d->cp = d->sp = 0; + d->next = NULL; + + if (b->tail) + b->tail->next = d; + else + b->head = d; + b->tail = d; + + return d; +} + +/* Write data to buffer. */ +void buffer_put(struct buffer *b, const void *p, size_t size) +{ + struct buffer_data *data = b->tail; + const char *ptr = p; + + /* We use even last one byte of data buffer. */ + while (size) { + size_t chunk; + + /* If there is no data buffer add it. */ + if (data == NULL || data->cp == b->size) + data = buffer_add(b); + + chunk = + ((size <= + (b->size - data->cp)) ? size : (b->size - data->cp)); + memcpy((data->data + data->cp), ptr, chunk); + size -= chunk; + ptr += chunk; + data->cp += chunk; + } +} + +/* Insert character into the buffer. */ +void buffer_putc(struct buffer *b, u_char c) +{ + buffer_put(b, &c, 1); +} + +/* Put string to the buffer. */ +void buffer_putstr(struct buffer *b, const char *c) +{ + buffer_put(b, c, strlen(c)); +} + +/* Keep flushing data to the fd until the buffer is empty or an error is + encountered or the operation would block. */ +buffer_status_t buffer_flush_all(struct buffer *b, int fd) +{ + buffer_status_t ret; + struct buffer_data *head; + size_t head_sp; + + if (!b->head) + return BUFFER_EMPTY; + head_sp = (head = b->head)->sp; + /* Flush all data. */ + while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) { + if ((b->head == head) && (head_sp == head->sp) + && (errno != EINTR)) + /* No data was flushed, so kernel buffer must be full. */ + return ret; + head_sp = (head = b->head)->sp; + } + + return ret; +} + +#if 0 +/* Flush enough data to fill a terminal window of the given scene (used only + by vty telnet interface). */ +buffer_status_t +buffer_flush_window(struct buffer * b, int fd, int width, int height, + int erase_flag, int no_more_flag) +{ + int nbytes; + int iov_alloc; + int iov_index; + struct iovec *iov; + struct iovec small_iov[3]; + char more[] = " --More-- "; + char erase[] = + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 + }; + struct buffer_data *data; + int column; + + if (!b->head) + return BUFFER_EMPTY; + + if (height < 1) { + zlog_warn + ("%s called with non-positive window height %d, forcing to 1", + __func__, height); + height = 1; + } else if (height >= 2) + height--; + if (width < 1) { + zlog_warn + ("%s called with non-positive window width %d, forcing to 1", + __func__, width); + width = 1; + } + + /* For erase and more data add two to b's buffer_data count. */ + if (b->head->next == NULL) { + iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]); + iov = small_iov; + } else { + iov_alloc = ((height * (width + 2)) / b->size) + 10; + iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov)); + } + iov_index = 0; + + /* Previously print out is performed. */ + if (erase_flag) { + iov[iov_index].iov_base = erase; + iov[iov_index].iov_len = sizeof erase; + iov_index++; + } + + /* Output data. */ + column = 1; /* Column position of next character displayed. */ + for (data = b->head; data && (height > 0); data = data->next) { + size_t cp; + + cp = data->sp; + while ((cp < data->cp) && (height > 0)) { + /* Calculate lines remaining and column position after displaying + this character. */ + if (data->data[cp] == '\r') + column = 1; + else if ((data->data[cp] == '\n') || (column == width)) { + column = 1; + height--; + } else + column++; + cp++; + } + iov[iov_index].iov_base = (char *)(data->data + data->sp); + iov[iov_index++].iov_len = cp - data->sp; + data->sp = cp; + + if (iov_index == iov_alloc) + /* This should not ordinarily happen. */ + { + iov_alloc *= 2; + if (iov != small_iov) { + zlog_warn("%s: growing iov array to %d; " + "width %d, height %d, size %lu", + __func__, iov_alloc, width, height, + (u_long) b->size); + iov = + XREALLOC(MTYPE_TMP, iov, + iov_alloc * sizeof(*iov)); + } else { + /* This should absolutely never occur. */ + zlog_err + ("%s: corruption detected: iov_small overflowed; " + "head %p, tail %p, head->next %p", + __func__, b->head, b->tail, b->head->next); + iov = + XMALLOC(MTYPE_TMP, + iov_alloc * sizeof(*iov)); + memcpy(iov, small_iov, sizeof(small_iov)); + } + } + } + + /* In case of `more' display need. */ + if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) { + iov[iov_index].iov_base = more; + iov[iov_index].iov_len = sizeof more; + iov_index++; + } +#ifdef IOV_MAX + /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g. + example: Solaris2.6 are defined IOV_MAX size at 16. */ + { + struct iovec *c_iov = iov; + nbytes = 0; /* Make sure it's initialized. */ + + while (iov_index > 0) { + int iov_size; + + iov_size = + ((iov_index > IOV_MAX) ? IOV_MAX : iov_index); + if ((nbytes = writev(fd, c_iov, iov_size)) < 0) { + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); + break; + } + + /* move pointer io-vector */ + c_iov += iov_size; + iov_index -= iov_size; + } + } +#else /* IOV_MAX */ + if ((nbytes = writev(fd, iov, iov_index)) < 0) + zlog_warn("%s: writev to fd %d failed: %s", + __func__, fd, safe_strerror(errno)); +#endif /* IOV_MAX */ + + /* Free printed buffer data. */ + while (b->head && (b->head->sp == b->head->cp)) { + struct buffer_data *del; + if (!(b->head = (del = b->head)->next)) + b->tail = NULL; + BUFFER_DATA_FREE(del); + } + + if (iov != small_iov) + XFREE(MTYPE_TMP, iov); + + return (nbytes < 0) ? BUFFER_ERROR : + (b->head ? BUFFER_PENDING : BUFFER_EMPTY); +} +#endif + +/* This function (unlike other buffer_flush* functions above) is designed +to work with non-blocking sockets. It does not attempt to write out +all of the queued data, just a "big" chunk. It returns 0 if it was +able to empty out the buffers completely, 1 if more flushing is +required later, or -1 on a fatal write error. */ +buffer_status_t buffer_flush_available(struct buffer * b, int fd) +{ + +/* These are just reasonable values to make sure a significant amount of +data is written. There's no need to go crazy and try to write it all +in one shot. */ +#ifdef IOV_MAX +#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX) +#else +#define MAX_CHUNKS 16 +#endif +#define MAX_FLUSH 131072 + + struct buffer_data *d; + size_t written; + struct iovec iov[MAX_CHUNKS]; + size_t iovcnt = 0; + size_t nbyte = 0; + + for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH); + d = d->next, iovcnt++) { + iov[iovcnt].iov_base = d->data + d->sp; + nbyte += (iov[iovcnt].iov_len = d->cp - d->sp); + } + + if (!nbyte) + /* No data to flush: should we issue a warning message? */ + return BUFFER_EMPTY; + + /* only place where written should be sign compared */ + if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) { + if (ERRNO_IO_RETRY(errno)) + /* Calling code should try again later. */ + return BUFFER_PENDING; + return BUFFER_ERROR; + } + + /* Free printed buffer data. */ + while (written > 0) { + struct buffer_data *d; + if (!(d = b->head)) + break; + if (written < d->cp - d->sp) { + d->sp += written; + return BUFFER_PENDING; + } + + written -= (d->cp - d->sp); + if (!(b->head = d->next)) + b->tail = NULL; + BUFFER_DATA_FREE(d); + } + + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; + +#undef MAX_CHUNKS +#undef MAX_FLUSH +} + +buffer_status_t +buffer_write(struct buffer * b, int fd, const void *p, size_t size) +{ + ssize_t nbytes; + +#if 0 + /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */ + + if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR)) + return BUFFER_ERROR; +#endif + if (b->head) + /* Buffer is not empty, so do not attempt to write the new data. */ + nbytes = 0; + else if ((nbytes = write(fd, p, size)) < 0) { + if (ERRNO_IO_RETRY(errno)) + nbytes = 0; + else + return BUFFER_ERROR; + } + /* Add any remaining data to the buffer. */ + { + size_t written = nbytes; + if (written < size) + buffer_put(b, ((const char *)p) + written, + size - written); + } + return b->head ? BUFFER_PENDING : BUFFER_EMPTY; +} diff --git a/src/shared/libosmocore/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c new file mode 100644 index 00000000..5dc1dd45 --- /dev/null +++ b/src/shared/libosmocore/src/vty/command.c @@ -0,0 +1,3229 @@ +/* + $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $ + + Command interpreter routine for virtual terminal [aka TeletYpe] + Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + +This file is part of GNU Zebra. + +GNU Zebra is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published +by the Free Software Foundation; either version 2, or (at your +option) any later version. + +GNU Zebra is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Zebra; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#define _XOPEN_SOURCE +#include <unistd.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <osmocom/vty/vector.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> + +#include <osmocom/core/talloc.h> + +#define CONFIGFILE_MASK 022 + +void *tall_vty_cmd_ctx; + +/* Command vector which includes some level of command lists. Normally + each daemon maintains each own cmdvec. */ +vector cmdvec; + +/* Host information structure. */ +struct host host; + +/* Standard command node structures. */ +struct cmd_node auth_node = { + AUTH_NODE, + "Password: ", +}; + +struct cmd_node view_node = { + VIEW_NODE, + "%s> ", +}; + +struct cmd_node auth_enable_node = { + AUTH_ENABLE_NODE, + "Password: ", +}; + +struct cmd_node enable_node = { + ENABLE_NODE, + "%s# ", +}; + +struct cmd_node config_node = { + CONFIG_NODE, + "%s(config)# ", + 1 +}; + +/* Default motd string. */ +const char *default_motd = ""; + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(int print_copyright) +{ + printf("%s version %s\n", host.app_info->name, host.app_info->version); + if (print_copyright) + printf("\n%s\n", host.app_info->copyright); +} + +/* Utility function to concatenate argv argument into a single string + with inserting ' ' character between each argument. */ +char *argv_concat(const char **argv, int argc, int shift) +{ + int i; + size_t len; + char *str; + char *p; + + len = 0; + for (i = shift; i < argc; i++) + len += strlen(argv[i]) + 1; + if (!len) + return NULL; + p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat"); + for (i = shift; i < argc; i++) { + size_t arglen; + memcpy(p, argv[i], (arglen = strlen(argv[i]))); + p += arglen; + *p++ = ' '; + } + *(p - 1) = '\0'; + return str; +} + +/* Install top node of command vector. */ +void install_node(struct cmd_node *node, int (*func) (struct vty *)) +{ + vector_set_index(cmdvec, node->node, node); + node->func = func; + node->cmd_vector = vector_init(VECTOR_MIN_SIZE); +} + +/* Compare two command's string. Used in sort_node (). */ +static int cmp_node(const void *p, const void *q) +{ + struct cmd_element *a = *(struct cmd_element **)p; + struct cmd_element *b = *(struct cmd_element **)q; + + return strcmp(a->string, b->string); +} + +static int cmp_desc(const void *p, const void *q) +{ + struct desc *a = *(struct desc **)p; + struct desc *b = *(struct desc **)q; + + return strcmp(a->cmd, b->cmd); +} + +static int is_config(struct vty *vty) +{ + if (vty->node <= CONFIG_NODE) + return 0; + else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE) + return 1; + else if (host.app_info->is_config_node) + return host.app_info->is_config_node(vty, vty->node); + else + return vty->node > CONFIG_NODE; +} + +/* Sort each node's command element according to command string. */ +void sort_node() +{ + unsigned int i, j; + struct cmd_node *cnode; + vector descvec; + struct cmd_element *cmd_element; + + for (i = 0; i < vector_active(cmdvec); i++) + if ((cnode = vector_slot(cmdvec, i)) != NULL) { + vector cmd_vector = cnode->cmd_vector; + qsort(cmd_vector->index, vector_active(cmd_vector), + sizeof(void *), cmp_node); + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && vector_active(cmd_element->strvec)) { + descvec = + vector_slot(cmd_element->strvec, + vector_active + (cmd_element->strvec) - + 1); + qsort(descvec->index, + vector_active(descvec), + sizeof(void *), cmp_desc); + } + } +} + +/* Breaking up string into each command piece. I assume given + character is separated by a space character. Return value is a + vector which includes char ** data element. */ +vector cmd_make_strvec(const char *string) +{ + const char *cp, *start; + char *token; + int strlen; + vector strvec; + + if (string == NULL) + return NULL; + + cp = string; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + if (*cp == '!' || *cp == '#') + return NULL; + + /* Prepare return vector. */ + strvec = vector_init(VECTOR_MIN_SIZE); + + /* Copy each command piece and set into vector. */ + while (1) { + start = cp; + while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') && + *cp != '\0') + cp++; + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + vector_set(strvec, token); + + while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') && + *cp != '\0') + cp++; + + if (*cp == '\0') + return strvec; + } +} + +/* Free allocated string vector. */ +void cmd_free_strvec(vector v) +{ + unsigned int i; + char *cp; + + if (!v) + return; + + for (i = 0; i < vector_active(v); i++) + if ((cp = vector_slot(v, i)) != NULL) + talloc_free(cp); + + vector_free(v); +} + +/* Fetch next description. Used in cmd_make_descvec(). */ +static char *cmd_desc_str(const char **string) +{ + const char *cp, *start; + char *token; + int strlen; + + cp = *string; + + if (cp == NULL) + return NULL; + + /* Skip white spaces. */ + while (isspace((int)*cp) && *cp != '\0') + cp++; + + /* Return if there is only white spaces */ + if (*cp == '\0') + return NULL; + + start = cp; + + while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') + cp++; + + strlen = cp - start; + token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str"); + memcpy(token, start, strlen); + *(token + strlen) = '\0'; + + *string = cp; + + return token; +} + +/* New string vector. */ +static vector cmd_make_descvec(const char *string, const char *descstr) +{ + int multiple = 0; + const char *sp; + char *token; + int len; + const char *cp; + const char *dp; + vector allvec; + vector strvec = NULL; + struct desc *desc; + + cp = string; + dp = descstr; + + if (cp == NULL) + return NULL; + + allvec = vector_init(VECTOR_MIN_SIZE); + + while (1) { + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + if (*cp == ')') { + multiple = 0; + cp++; + } + if (*cp == '|') { + if (!multiple) { + fprintf(stderr, "Command parse error!: %s\n", + string); + exit(1); + } + cp++; + } + + while (isspace((int)*cp) && *cp != '\0') + cp++; + + if (*cp == '(') { + multiple = 1; + cp++; + } + + if (*cp == '\0') + return allvec; + + sp = cp; + + while (! + (isspace((int)*cp) || *cp == '\r' || *cp == '\n' + || *cp == ')' || *cp == '|') && *cp != '\0') + cp++; + + len = cp - sp; + + token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec"); + memcpy(token, sp, len); + *(token + len) = '\0'; + + desc = talloc_zero(tall_vty_cmd_ctx, struct desc); + desc->cmd = token; + desc->str = cmd_desc_str(&dp); + + if (multiple) { + if (multiple == 1) { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + multiple++; + } else { + strvec = vector_init(VECTOR_MIN_SIZE); + vector_set(allvec, strvec); + } + vector_set(strvec, desc); + } +} + +/* Count mandantory string vector size. This is to determine inputed + command has enough command length. */ +static int cmd_cmdsize(vector strvec) +{ + unsigned int i; + int size = 0; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(strvec); i++) + if ((descvec = vector_slot(strvec, i)) != NULL) { + if ((vector_active(descvec)) == 1 + && (desc = vector_slot(descvec, 0)) != NULL) { + if (desc->cmd == NULL || CMD_OPTION(desc->cmd)) + return size; + else + size++; + } else + size++; + } + return size; +} + +/* Return prompt character of specified node. */ +const char *cmd_prompt(enum node_type node) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, node); + return cnode->prompt; +} + +/* Install a command into a node. */ +void install_element(enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + cnode = vector_slot(cmdvec, ntype); + + if (cnode == NULL) { + fprintf(stderr, + "Command node %d doesn't exist, please check it\n", + ntype); + exit(1); + } + + vector_set(cnode->cmd_vector, cmd); + + cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc); + cmd->cmdsize = cmd_cmdsize(cmd->strvec); +} + +/* Install a command into VIEW and ENABLE node */ +void install_element_ve(struct cmd_element *cmd) +{ + install_element(VIEW_NODE, cmd); + install_element(ENABLE_NODE, cmd); +} + +#ifdef VTY_CRYPT_PW +static unsigned char itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +static char *zencrypt(const char *passwd) +{ + char salt[6]; + struct timeval tv; + char *crypt(const char *, const char *); + + gettimeofday(&tv, 0); + + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + salt[5] = '\0'; + + return crypt(passwd, salt); +} +#endif + +/* This function write configuration of this host. */ +static int config_write_host(struct vty *vty) +{ + if (host.name) + vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE); + + if (host.encrypt) { + if (host.password_encrypt) + vty_out(vty, "password 8 %s%s", host.password_encrypt, + VTY_NEWLINE); + if (host.enable_encrypt) + vty_out(vty, "enable password 8 %s%s", + host.enable_encrypt, VTY_NEWLINE); + } else { + if (host.password) + vty_out(vty, "password %s%s", host.password, + VTY_NEWLINE); + if (host.enable) + vty_out(vty, "enable password %s%s", host.enable, + VTY_NEWLINE); + } + + if (host.advanced) + vty_out(vty, "service advanced-vty%s", VTY_NEWLINE); + + if (host.encrypt) + vty_out(vty, "service password-encryption%s", VTY_NEWLINE); + + if (host.lines >= 0) + vty_out(vty, "service terminal-length %d%s", host.lines, + VTY_NEWLINE); + + if (host.motdfile) + vty_out(vty, "banner motd file %s%s", host.motdfile, + VTY_NEWLINE); + else if (!host.motd) + vty_out(vty, "no banner motd%s", VTY_NEWLINE); + + return 1; +} + +/* Utility function for getting command vector. */ +static vector cmd_node_vector(vector v, enum node_type ntype) +{ + struct cmd_node *cnode = vector_slot(v, ntype); + return cnode->cmd_vector; +} + +/* Completion match types. */ +enum match_type { + no_match, + extend_match, + ipv4_prefix_match, + ipv4_match, + ipv6_prefix_match, + ipv6_match, + range_match, + vararg_match, + partly_match, + exact_match +}; + +static enum match_type cmd_ipv4_match(const char *str) +{ + const char *sp; + int dots = 0, nums = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0') { + if (*str == '.') { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +static enum match_type cmd_ipv4_prefix_match(const char *str) +{ + const char *sp; + int dots = 0; + char buf[4]; + + if (str == NULL) + return partly_match; + + for (;;) { + memset(buf, 0, sizeof(buf)); + sp = str; + while (*str != '\0' && *str != '/') { + if (*str == '.') { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy(buf, sp, str - sp); + if (atoi(buf) > 255) + return no_match; + + if (dots == 3) { + if (*str == '/') { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') { + if (!isdigit((int)*str)) + return no_match; + + str++; + } + + if (atoi(sp) > 32) + return no_match; + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +#ifdef HAVE_IPV6 + +static enum match_type cmd_ipv6_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + const char *sp = NULL; + struct sockaddr_in6 sin6_dummy; + int ret; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_ADDR_STR) != strlen(str)) + return no_match; + + /* use inet_pton that has a better support, + * for example inet_pton can support the automatic addresses: + * ::1.2.3.4 + */ + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + + if (ret == 1) + return exact_match; + + while (*str != '\0') { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0') + colons++; + sp = str + 1; + state = STATE_ADDR; + } + + double_colon++; + nums++; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '\0') { + if (str - sp > 3) + return no_match; + + nums++; + state = STATE_COLON; + } + if (*(str + 1) == '.') + state = STATE_DOT; + break; + case STATE_DOT: + state = STATE_ADDR; + break; + default: + break; + } + + if (nums > 8) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + +#if 0 + if (nums < 11) + return partly_match; +#endif /* 0 */ + + return exact_match; +} + +static enum match_type cmd_ipv6_prefix_match(const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + const char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) { + switch (state) { + case STATE_START: + if (*str == ':') { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } else { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + state = STATE_DOT; + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol(str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + +/* I don't know why mask < 13 makes command match partly. + Forgive me to make this comments. I Want to set static default route + because of lack of function to originate default in ospf6d; sorry + yasu + if (mask < 13) + return partly_match; +*/ + + return exact_match; +} + +#endif /* HAVE_IPV6 */ + +#define DECIMAL_STRLEN_MAX 10 + +static int cmd_range_match(const char *range, const char *str) +{ + char *p; + char buf[DECIMAL_STRLEN_MAX + 1]; + char *endptr = NULL; + + if (str == NULL) + return 1; + + if (range[1] == '-') { + signed long min = 0, max = 0, val; + + val = strtol(str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range += 2; + p = strchr(range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + min = -strtol(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr(range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + max = strtol(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + } else { + unsigned long min, max, val; + + val = strtoul(str, &endptr, 10); + if (*endptr != '\0') + return 0; + + range++; + p = strchr(range, '-'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + min = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + range = p + 1; + p = strchr(range, '>'); + if (p == NULL) + return 0; + if (p - range > DECIMAL_STRLEN_MAX) + return 0; + strncpy(buf, range, p - range); + buf[p - range] = '\0'; + max = strtoul(buf, &endptr, 10); + if (*endptr != '\0') + return 0; + + if (val < min || val > max) + return 0; + } + + return 1; +} + +/* Make completion match and return match type flag. */ +static enum match_type +cmd_filter_by_completion(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command)) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command)) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command)) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command)) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else + /* Check is this point's argument optional ? */ + if (CMD_OPTION(str) + || + CMD_VARIABLE(str)) { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else + if (strncmp + (command, str, + strlen(command)) == + 0) { + if (strcmp(command, str) + == 0) + match_type = + exact_match; + else { + if (match_type < + partly_match) + match_type + = + partly_match; + } + matched++; + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Filter vector by command character with index. */ +static enum match_type +cmd_filter_by_string(char *command, vector v, unsigned int index) +{ + unsigned int i; + const char *str; + struct cmd_element *cmd_element; + enum match_type match_type; + vector descvec; + struct desc *desc; + + match_type = no_match; + + /* If command and cmd_element string does not match set NULL to vector */ + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + /* If given index is bigger than max string vector of command, + set NULL */ + if (index >= vector_active(cmd_element->strvec)) + vector_slot(v, i) = NULL; + else { + unsigned int j; + int matched = 0; + + descvec = + vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + str = desc->cmd; + + if (CMD_VARARG(str)) { + if (match_type < + vararg_match) + match_type = + vararg_match; + matched++; + } else if (CMD_RANGE(str)) { + if (cmd_range_match + (str, command)) { + if (match_type < + range_match) + match_type + = + range_match; + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6(str)) { + if (cmd_ipv6_match + (command) == + exact_match) { + if (match_type < + ipv6_match) + match_type + = + ipv6_match; + matched++; + } + } else if (CMD_IPV6_PREFIX(str)) { + if (cmd_ipv6_prefix_match(command) == exact_match) { + if (match_type < + ipv6_prefix_match) + match_type + = + ipv6_prefix_match; + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4(str)) { + if (cmd_ipv4_match + (command) == + exact_match) { + if (match_type < + ipv4_match) + match_type + = + ipv4_match; + matched++; + } + } else if (CMD_IPV4_PREFIX(str)) { + if (cmd_ipv4_prefix_match(command) == exact_match) { + if (match_type < + ipv4_prefix_match) + match_type + = + ipv4_prefix_match; + matched++; + } + } else if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + { + if (match_type < + extend_match) + match_type = + extend_match; + matched++; + } else { + if (strcmp(command, str) + == 0) { + match_type = + exact_match; + matched++; + } + } + } + if (!matched) + vector_slot(v, i) = NULL; + } + } + return match_type; +} + +/* Check ambiguous match */ +static int +is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) +{ + unsigned int i; + unsigned int j; + const char *str = NULL; + struct cmd_element *cmd_element; + const char *matched = NULL; + vector descvec; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((cmd_element = vector_slot(v, i)) != NULL) { + int match = 0; + + descvec = vector_slot(cmd_element->strvec, index); + + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + enum match_type ret; + + str = desc->cmd; + + switch (type) { + case exact_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strcmp(command, str) == 0) + match++; + break; + case partly_match: + if (! + (CMD_OPTION(str) + || CMD_VARIABLE(str)) +&& strncmp(command, str, strlen(command)) == 0) { + if (matched + && strcmp(matched, + str) != 0) + return 1; /* There is ambiguous match. */ + else + matched = str; + match++; + } + break; + case range_match: + if (cmd_range_match + (str, command)) { + if (matched + && strcmp(matched, + str) != 0) + return 1; + else + matched = str; + match++; + } + break; +#ifdef HAVE_IPV6 + case ipv6_match: + if (CMD_IPV6(str)) + match++; + break; + case ipv6_prefix_match: + if ((ret = + cmd_ipv6_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; +#endif /* HAVE_IPV6 */ + case ipv4_match: + if (CMD_IPV4(str)) + match++; + break; + case ipv4_prefix_match: + if ((ret = + cmd_ipv4_prefix_match + (command)) != no_match) { + if (ret == partly_match) + return 2; /* There is incomplete match. */ + + match++; + } + break; + case extend_match: + if (CMD_OPTION(str) + || CMD_VARIABLE(str)) + match++; + break; + case no_match: + default: + break; + } + } + if (!match) + vector_slot(v, i) = NULL; + } + return 0; +} + +/* If src matches dst return dst string, otherwise return NULL */ +static const char *cmd_entry_function(const char *src, const char *dst) +{ + /* Skip variable arguments. */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) || + CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst)) + return NULL; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + /* Matched with input string. */ + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + + return NULL; +} + +/* If src matches dst return dst string, otherwise return NULL */ +/* This version will return the dst string always if it is + CMD_VARIABLE for '?' key processing */ +static const char *cmd_entry_function_desc(const char *src, const char *dst) +{ + if (CMD_VARARG(dst)) + return dst; + + if (CMD_RANGE(dst)) { + if (cmd_range_match(dst, src)) + return dst; + else + return NULL; + } +#ifdef HAVE_IPV6 + if (CMD_IPV6(dst)) { + if (cmd_ipv6_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV6_PREFIX(dst)) { + if (cmd_ipv6_prefix_match(src)) + return dst; + else + return NULL; + } +#endif /* HAVE_IPV6 */ + + if (CMD_IPV4(dst)) { + if (cmd_ipv4_match(src)) + return dst; + else + return NULL; + } + + if (CMD_IPV4_PREFIX(dst)) { + if (cmd_ipv4_prefix_match(src)) + return dst; + else + return NULL; + } + + /* Optional or variable commands always match on '?' */ + if (CMD_OPTION(dst) || CMD_VARIABLE(dst)) + return dst; + + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp(src, dst, strlen(src)) == 0) + return dst; + else + return NULL; +} + +/* Check same string element existence. If it isn't there return + 1. */ +static int cmd_unique_string(vector v, const char *str) +{ + unsigned int i; + char *match; + + for (i = 0; i < vector_active(v); i++) + if ((match = vector_slot(v, i)) != NULL) + if (strcmp(match, str) == 0) + return 0; + return 1; +} + +/* Compare string to description vector. If there is same string + return 1 else return 0. */ +static int desc_unique_string(vector v, const char *str) +{ + unsigned int i; + struct desc *desc; + + for (i = 0; i < vector_active(v); i++) + if ((desc = vector_slot(v, i)) != NULL) + if (strcmp(desc->cmd, str) == 0) + return 1; + return 0; +} + +static int cmd_try_do_shortcut(enum node_type node, char *first_word) +{ + if (first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && 0 == strcmp("do", first_word)) + return 1; + return 0; +} + +/* '?' describe command support. */ +static vector +cmd_describe_command_real(vector vline, struct vty *vty, int *status) +{ + unsigned int i; + vector cmd_vector; +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + int ret; + enum match_type match; + char *command; + static struct desc desc_cr = { "<cr>", "" }; + + /* Set index. */ + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* Make copy vector of current node's command vector. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + /* Prepare match vector */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Filter commands. */ + /* Only words precedes current word will be checked in this loop. */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + match = + cmd_filter_by_completion(command, cmd_vector, i); + + if (match == vararg_match) { + struct cmd_element *cmd_element; + vector descvec; + unsigned int j, k; + + for (j = 0; j < vector_active(cmd_vector); j++) + if ((cmd_element = + vector_slot(cmd_vector, j)) != NULL + && + (vector_active + (cmd_element->strvec))) { + descvec = + vector_slot(cmd_element-> + strvec, + vector_active + (cmd_element-> + strvec) - 1); + for (k = 0; + k < vector_active(descvec); + k++) { + struct desc *desc = + vector_slot(descvec, + k); + vector_set(matchvec, + desc); + } + } + + vector_set(matchvec, &desc_cr); + vector_free(cmd_vector); + + return matchvec; + } + + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } else if (ret == 2) { + vector_free(cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + } + + /* Prepare match vector */ + /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + + /* Make sure that cmd_vector is filtered based on current word */ + command = vector_slot(vline, index); + if (command) + match = cmd_filter_by_completion(command, cmd_vector, index); + + /* Make description vector. */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) { + const char *string = NULL; + vector strvec = cmd_element->strvec; + + /* if command is NULL, index may be equal to vector_active */ + if (command && index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + /* Check if command is completed. */ + if (command == NULL + && index == vector_active(strvec)) { + string = "<cr>"; + if (!desc_unique_string + (matchvec, string)) + vector_set(matchvec, &desc_cr); + } else { + unsigned int j; + vector descvec = + vector_slot(strvec, index); + struct desc *desc; + + for (j = 0; j < vector_active(descvec); + j++) + if ((desc = + vector_slot(descvec, j))) { + string = + cmd_entry_function_desc + (command, + desc->cmd); + if (string) { + /* Uniqueness check */ + if (!desc_unique_string(matchvec, string)) + vector_set + (matchvec, + desc); + } + } + } + } + } + vector_free(cmd_vector); + + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + *status = CMD_ERR_NO_MATCH; + } else + *status = CMD_SUCCESS; + + return matchvec; +} + +vector cmd_describe_command(vector vline, struct vty * vty, int *status) +{ + vector ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_describe_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_describe_command_real(vline, vty, status); +} + +/* Check LCD of matched command. */ +static int cmd_lcd(char **matched) +{ + int i; + int j; + int lcd = -1; + char *s1, *s2; + char c1, c2; + + if (matched[0] == NULL || matched[1] == NULL) + return 0; + + for (i = 1; matched[i] != NULL; i++) { + s1 = matched[i - 1]; + s2 = matched[i]; + + for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) + if (c1 != c2) + break; + + if (lcd < 0) + lcd = j; + else { + if (lcd > j) + lcd = j; + } + } + return lcd; +} + +/* Command line completion support. */ +static char **cmd_complete_command_real(vector vline, struct vty *vty, + int *status) +{ + unsigned int i; + vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); +#define INIT_MATCHVEC_SIZE 10 + vector matchvec; + struct cmd_element *cmd_element; + unsigned int index; + char **match_str; + struct desc *desc; + vector descvec; + char *command; + int lcd; + + if (vector_active(vline) == 0) { + *status = CMD_ERR_NO_MATCH; + return NULL; + } else + index = vector_active(vline) - 1; + + /* First, filter by preceeding command string */ + for (i = 0; i < index; i++) + if ((command = vector_slot(vline, i))) { + enum match_type match; + int ret; + + /* First try completion match, if there is exactly match return 1 */ + match = + cmd_filter_by_completion(command, cmd_vector, i); + + /* If there is exact match then filter ambiguous match else check + ambiguousness. */ + if ((ret = + is_cmd_ambiguous(command, cmd_vector, i, + match)) == 1) { + vector_free(cmd_vector); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } + /* + else if (ret == 2) + { + vector_free (cmd_vector); + *status = CMD_ERR_NO_MATCH; + return NULL; + } + */ + } + + /* Prepare match vector. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + + /* Now we got into completion */ + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + const char *string; + vector strvec = cmd_element->strvec; + + /* Check field length */ + if (index >= vector_active(strvec)) + vector_slot(cmd_vector, i) = NULL; + else { + unsigned int j; + + descvec = vector_slot(strvec, index); + for (j = 0; j < vector_active(descvec); j++) + if ((desc = vector_slot(descvec, j))) { + if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd))) + if (cmd_unique_string (matchvec, string)) + vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string)); + } + } + } + + /* We don't need cmd_vector any more. */ + vector_free(cmd_vector); + + /* No matched command */ + if (vector_slot(matchvec, 0) == NULL) { + vector_free(matchvec); + + /* In case of 'command \t' pattern. Do you need '?' command at + the end of the line. */ + if (vector_slot(vline, index) == '\0') + *status = CMD_ERR_NOTHING_TODO; + else + *status = CMD_ERR_NO_MATCH; + return NULL; + } + + /* Only one matched */ + if (vector_slot(matchvec, 1) == NULL) { + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_FULL_MATCH; + return match_str; + } + /* Make it sure last element is NULL. */ + vector_set(matchvec, NULL); + + /* Check LCD of matched strings. */ + if (vector_slot(vline, index) != NULL) { + lcd = cmd_lcd((char **)matchvec->index); + + if (lcd) { + int len = strlen(vector_slot(vline, index)); + + if (len < lcd) { + char *lcdstr; + + lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1, + "complete-lcdstr"); + memcpy(lcdstr, matchvec->index[0], lcd); + lcdstr[lcd] = '\0'; + + /* match_str = (char **) &lcdstr; */ + + /* Free matchvec. */ + for (i = 0; i < vector_active(matchvec); i++) { + if (vector_slot(matchvec, i)) + talloc_free(vector_slot(matchvec, i)); + } + vector_free(matchvec); + + /* Make new matchvec. */ + matchvec = vector_init(INIT_MATCHVEC_SIZE); + vector_set(matchvec, lcdstr); + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + + *status = CMD_COMPLETE_MATCH; + return match_str; + } + } + } + + match_str = (char **)matchvec->index; + vector_only_wrapper_free(matchvec); + *status = CMD_COMPLETE_LIST_MATCH; + return match_str; +} + +char **cmd_complete_command(vector vline, struct vty *vty, int *status) +{ + char **ret; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + enum node_type onode; + vector shifted_vline; + unsigned int index; + + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_complete_command_real(shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + return cmd_complete_command_real(vline, vty, status); +} + +/* return parent node */ +/* MUST eventually converge on CONFIG_NODE */ +enum node_type vty_go_parent(struct vty *vty) +{ + assert(vty->node > CONFIG_NODE); + + if (host.app_info->go_parent_cb) + host.app_info->go_parent_cb(vty); + else + vty->node = CONFIG_NODE; + + return vty->node; +} + +/* Execute command by argument vline vector. */ +static int +cmd_execute_command_real(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + enum match_type match = 0; + int varflag; + char *command; + + /* Make copy of command elements. */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = + cmd_filter_by_completion(command, cmd_vector, + index); + + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } else if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + + for (i = 0; i < vector_active(cmd_vector); i++) + if ((cmd_element = vector_slot(cmd_vector, i))) { + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; +#if 0 + printf("DEBUG: %s\n", cmd_element->string); +#endif + matched_count++; + } else { + incomplete_count++; + } + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Execute matched command. */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +int +cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd, + int vtysh) +{ + int ret, saved_ret, tried = 0; + enum node_type onode; + void *oindex; + + onode = vty->node; + oindex = vty->index; + + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { + vector shifted_vline; + unsigned int index; + + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ + + shifted_vline = vector_init(vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active(vline); index++) { + vector_set_index(shifted_vline, index - 1, + vector_lookup(vline, index)); + } + + ret = cmd_execute_command_real(shifted_vline, vty, cmd); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } + + saved_ret = ret = cmd_execute_command_real(vline, vty, cmd); + + if (vtysh) + return saved_ret; + + /* Go to parent for config nodes to attempt to find the right command */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && is_config(vty)) { + vty_go_parent(vty); + ret = cmd_execute_command_real(vline, vty, cmd); + tried = 1; + if (ret == CMD_SUCCESS || ret == CMD_WARNING) { + /* succesfull command, leave the node as is */ + return ret; + } + } + /* no command succeeded, reset the vty to the original node and + return the error for this node */ + if (tried) { + vty->node = onode; + vty->index = oindex; + } + return saved_ret; +} + +/* Execute command by argument readline. */ +int +cmd_execute_command_strict(vector vline, struct vty *vty, + struct cmd_element **cmd) +{ + unsigned int i; + unsigned int index; + vector cmd_vector; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + int argc; + const char *argv[CMD_ARGC_MAX]; + int varflag; + enum match_type match = 0; + char *command; + + /* Make copy of command element */ + cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); + + for (index = 0; index < vector_active(vline); index++) + if ((command = vector_slot(vline, index))) { + int ret; + + match = cmd_filter_by_string(vector_slot(vline, index), + cmd_vector, index); + + /* If command meets '.VARARG' then finish matching. */ + if (match == vararg_match) + break; + + ret = + is_cmd_ambiguous(command, cmd_vector, index, match); + if (ret == 1) { + vector_free(cmd_vector); + return CMD_ERR_AMBIGUOUS; + } + if (ret == 2) { + vector_free(cmd_vector); + return CMD_ERR_NO_MATCH; + } + } + + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; + for (i = 0; i < vector_active(cmd_vector); i++) + if (vector_slot(cmd_vector, i) != NULL) { + cmd_element = vector_slot(cmd_vector, i); + + if (match == vararg_match + || index >= cmd_element->cmdsize) { + matched_element = cmd_element; + matched_count++; + } else + incomplete_count++; + } + + /* Finish of using cmd_vector. */ + vector_free(cmd_vector); + + /* To execute command, matched_count must be 1. */ + if (matched_count == 0) { + if (incomplete_count) + return CMD_ERR_INCOMPLETE; + else + return CMD_ERR_NO_MATCH; + } + + if (matched_count > 1) + return CMD_ERR_AMBIGUOUS; + + /* Argument treatment */ + varflag = 0; + argc = 0; + + for (i = 0; i < vector_active(vline); i++) { + if (varflag) + argv[argc++] = vector_slot(vline, i); + else { + vector descvec = + vector_slot(matched_element->strvec, i); + + if (vector_active(descvec) == 1) { + struct desc *desc = vector_slot(descvec, 0); + + if (CMD_VARARG(desc->cmd)) + varflag = 1; + + if (varflag || CMD_VARIABLE(desc->cmd) + || CMD_OPTION(desc->cmd)) + argv[argc++] = vector_slot(vline, i); + } else + argv[argc++] = vector_slot(vline, i); + } + + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXEED_ARGC_MAX; + } + + /* For vtysh execution. */ + if (cmd) + *cmd = matched_element; + + if (matched_element->daemon) + return CMD_SUCCESS_DAEMON; + + /* Now execute matched command */ + return (*matched_element->func) (matched_element, vty, argc, argv); +} + +/* Configration make from file. */ +int config_from_file(struct vty *vty, FILE * fp) +{ + int ret; + vector vline; + + while (fgets(vty->buf, VTY_BUFSIZ, fp)) { + vline = cmd_make_strvec(vty->buf); + + /* In case of comment line */ + if (vline == NULL) + continue; + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict(vline, vty, NULL); + + /* Try again with setting node to CONFIG_NODE */ + while (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO + && vty->node != CONFIG_NODE && is_config(vty)) { + vty_go_parent(vty); + ret = cmd_execute_command_strict(vline, vty, NULL); + } + + cmd_free_strvec(vline); + + if (ret != CMD_SUCCESS && ret != CMD_WARNING + && ret != CMD_ERR_NOTHING_TODO) + return ret; + } + return CMD_SUCCESS; +} + +/* Configration from terminal */ +DEFUN(config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" "Configuration terminal\n") +{ + if (vty_config_lock(vty)) + vty->node = CONFIG_NODE; + else { + vty_out(vty, "VTY configuration is locked by other VTY%s", + VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +/* Enable command */ +DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") +{ + /* If enable password is NULL, change to ENABLE_NODE */ + if ((host.enable == NULL && host.enable_encrypt == NULL) || + vty->type == VTY_SHELL_SERV) + vty->node = ENABLE_NODE; + else + vty->node = AUTH_ENABLE_NODE; + + return CMD_SUCCESS; +} + +/* Disable command */ +DEFUN(disable, + config_disable_cmd, "disable", "Turn off privileged mode command\n") +{ + if (vty->node == ENABLE_NODE) + vty->node = VIEW_NODE; + return CMD_SUCCESS; +} + +/* Down vty node level. */ +gDEFUN(config_exit, + config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + if (0) //vty_shell (vty)) + exit(0); + else + vty->status = VTY_CLOSE; + break; + case CONFIG_NODE: + vty->node = ENABLE_NODE; + vty_config_unlock(vty); + break; + case VTY_NODE: + vty->node = CONFIG_NODE; + break; + case CFG_LOG_NODE: + vty->node = CONFIG_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ + gDEFUN(config_end, + config_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CFG_LOG_NODE: + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* Show version. */ +DEFUN(show_version, + show_version_cmd, "show version", SHOW_STR "Displays program version\n") +{ + vty_out(vty, "%s %s (%s).%s", host.app_info->name, + host.app_info->version, + host.app_info->name ? host.app_info->name : "", VTY_NEWLINE); + vty_out(vty, "%s%s", host.app_info->copyright, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +gDEFUN(config_help, + config_help_cmd, "help", "Description of the interactive help system\n") +{ + vty_out(vty, + "This VTY provides advanced help features. When you need help,%s\ +anytime at the command line please press '?'.%s\ +%s\ +If nothing matches, the help list will be empty and you must backup%s\ + until entering a '?' shows the available options.%s\ +Two styles of help are provided:%s\ +1. Full help is available when you are ready to enter a%s\ +command argument (e.g. 'show ?') and describes each possible%s\ +argument.%s\ +2. Partial help is provided when an abbreviated argument is entered%s\ + and you want to know what arguments match the input%s\ + (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Help display function for all node. */ +gDEFUN(config_list, config_list_cmd, "list", "Print command list\n") +{ + unsigned int i; + struct cmd_node *cnode = vector_slot(cmdvec, vty->node); + struct cmd_element *cmd; + + for (i = 0; i < vector_active(cnode->cmd_vector); i++) + if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL + && !(cmd->attr == CMD_ATTR_DEPRECATED + || cmd->attr == CMD_ATTR_HIDDEN)) + vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Write current configuration into file. */ +DEFUN(config_write_file, + config_write_file_cmd, + "write file", + "Write running configuration to memory, network, or terminal\n" + "Write to configuration file\n") +{ + unsigned int i; + int fd; + struct cmd_node *node; + char *config_file; + char *config_file_tmp = NULL; + char *config_file_sav = NULL; + struct vty *file_vty; + + /* Check and see if we are operating under vtysh configuration */ + if (host.config == NULL) { + vty_out(vty, "Can't save to configuration file, using vtysh.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get filename. */ + config_file = host.config; + + config_file_sav = + _talloc_zero(tall_vty_cmd_ctx, + strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1, + "config_file_sav"); + strcpy(config_file_sav, config_file); + strcat(config_file_sav, CONF_BACKUP_EXT); + + config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8, + "config_file_tmp"); + sprintf(config_file_tmp, "%s.XXXXXX", config_file); + + /* Open file to configuration write. */ + fd = mkstemp(config_file_tmp); + if (fd < 0) { + vty_out(vty, "Can't open configuration file %s.%s", + config_file_tmp, VTY_NEWLINE); + talloc_free(config_file_tmp); + talloc_free(config_file_sav); + return CMD_WARNING; + } + + /* Make vty for configuration file. */ + file_vty = vty_new(); + file_vty->fd = fd; + file_vty->type = VTY_FILE; + + /* Config file header print. */ + vty_out(file_vty, "!\n! %s (%s) configuration saved from vty\n!", + host.app_info->name, host.app_info->version); + //vty_time_print (file_vty, 1); + vty_out(file_vty, "!\n"); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (file_vty)) + vty_out(file_vty, "!\n"); + } + vty_close(file_vty); + + if (unlink(config_file_sav) != 0) + if (errno != ENOENT) { + vty_out(vty, + "Can't unlink backup configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file, config_file_sav) != 0) { + vty_out(vty, "Can't backup old configuration file %s.%s", + config_file_sav, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + sync(); + if (unlink(config_file) != 0) { + vty_out(vty, "Can't unlink configuration file %s.%s", + config_file, VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + if (link(config_file_tmp, config_file) != 0) { + vty_out(vty, "Can't save configuration file %s.%s", config_file, + VTY_NEWLINE); + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + unlink(config_file_tmp); + return CMD_WARNING; + } + unlink(config_file_tmp); + sync(); + + talloc_free(config_file_sav); + talloc_free(config_file_tmp); + + if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) { + vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, strerror(errno), errno, VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE); + return CMD_SUCCESS; +} + +ALIAS(config_write_file, + config_write_cmd, + "write", "Write running configuration to memory, network, or terminal\n") + + ALIAS(config_write_file, + config_write_memory_cmd, + "write memory", + "Write running configuration to memory, network, or terminal\n" + "Write configuration to the file (same as write file)\n") + + ALIAS(config_write_file, + copy_runningconfig_startupconfig_cmd, + "copy running-config startup-config", + "Copy configuration\n" + "Copy running config to... \n" + "Copy running config to startup config (same as write file)\n") + +/* Write current configuration into the terminal. */ + DEFUN(config_write_terminal, + config_write_terminal_cmd, + "write terminal", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n") +{ + unsigned int i; + struct cmd_node *node; + + if (vty->type == VTY_SHELL_SERV) { + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func + && node->vtysh) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + } else { + vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active(cmdvec); i++) + if ((node = vector_slot(cmdvec, i)) && node->func) { + if ((*node->func) (vty)) + vty_out(vty, "!%s", VTY_NEWLINE); + } + vty_out(vty, "end%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +/* Write current configuration into the terminal. */ +ALIAS(config_write_terminal, + show_running_config_cmd, + "show running-config", SHOW_STR "running configuration\n") + +/* Write startup configuration into the terminal. */ + DEFUN(show_startup_config, + show_startup_config_cmd, + "show startup-config", SHOW_STR "Contentes of startup configuration\n") +{ + char buf[BUFSIZ]; + FILE *confp; + + confp = fopen(host.config, "r"); + if (confp == NULL) { + vty_out(vty, "Can't open configuration file [%s]%s", + host.config, VTY_NEWLINE); + return CMD_WARNING; + } + + while (fgets(buf, BUFSIZ, confp)) { + char *cp = buf; + + while (*cp != '\r' && *cp != '\n' && *cp != '\0') + cp++; + *cp = '\0'; + + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + + fclose(confp); + + return CMD_SUCCESS; +} + +/* Hostname configuration */ +DEFUN(config_hostname, + hostname_cmd, + "hostname WORD", + "Set system's network name\n" "This system's network name\n") +{ + if (!isalpha((int)*argv[0])) { + vty_out(vty, "Please specify string starting with alphabet%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.name) + talloc_free(host.name); + + host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(config_no_hostname, + no_hostname_cmd, + "no hostname [HOSTNAME]", + NO_STR "Reset system's network name\n" "Host name of this router\n") +{ + if (host.name) + talloc_free(host.name); + host.name = NULL; + return CMD_SUCCESS; +} + +/* VTY interface password set. */ +DEFUN(config_password, password_cmd, + "password (8|) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN line password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc == 2) { + if (*argv[0] == '8') { + if (host.password) + talloc_free(host.password); + host.password = NULL; + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.password) + talloc_free(host.password); + host.password = NULL; + +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_password, password_text_cmd, + "password LINE", + "Assign the terminal connection password\n" + "The UNENCRYPTED (cleartext) line password\n") + +/* VTY enable password set. */ + DEFUN(config_enable_password, enable_password_cmd, + "enable password (8|) WORD", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" "The HIDDEN 'enable' password string\n") +{ + /* Argument check. */ + if (argc == 0) { + vty_out(vty, "Please specify password.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Crypt type is specified. */ + if (argc == 2) { + if (*argv[0] == '8') { + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]); + + return CMD_SUCCESS; + } else { + vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (!isalnum((int)*argv[0])) { + vty_out(vty, + "Please specify string starting with alphanumeric%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + /* Plain password input. */ +#ifdef VTY_CRYPT_PW + if (host.encrypt) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0])); + } else +#endif + host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS(config_enable_password, + enable_password_text_cmd, + "enable password LINE", + "Modify enable password parameters\n" + "Assign the privileged level password\n" + "The UNENCRYPTED (cleartext) 'enable' password\n") + +/* VTY enable password delete. */ + DEFUN(no_config_enable_password, no_enable_password_cmd, + "no enable password", + NO_STR + "Modify enable password parameters\n" + "Assign the privileged level password\n") +{ + if (host.enable) + talloc_free(host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} + +#ifdef VTY_CRYPT_PW +DEFUN(service_password_encrypt, + service_password_encrypt_cmd, + "service password-encryption", + "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 1; + + if (host.password) { + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password)); + } + if (host.enable) { + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable)); + } + + return CMD_SUCCESS; +} + +DEFUN(no_service_password_encrypt, + no_service_password_encrypt_cmd, + "no service password-encryption", + NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n") +{ + if (!host.encrypt) + return CMD_SUCCESS; + + host.encrypt = 0; + + if (host.password_encrypt) + talloc_free(host.password_encrypt); + host.password_encrypt = NULL; + + if (host.enable_encrypt) + talloc_free(host.enable_encrypt); + host.enable_encrypt = NULL; + + return CMD_SUCCESS; +} +#endif + +DEFUN(config_terminal_length, config_terminal_length_cmd, + "terminal length <0-512>", + "Set terminal line parameters\n" + "Set number of lines on a screen\n" + "Number of lines on screen (0 for no pausing)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + vty->lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(config_terminal_no_length, config_terminal_no_length_cmd, + "terminal no length", + "Set terminal line parameters\n" + NO_STR "Set number of lines on a screen\n") +{ + vty->lines = -1; + return CMD_SUCCESS; +} + +DEFUN(service_terminal_length, service_terminal_length_cmd, + "service terminal-length <0-512>", + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + int lines; + char *endptr = NULL; + + lines = strtol(argv[0], &endptr, 10); + if (lines < 0 || lines > 512 || *endptr != '\0') { + vty_out(vty, "length is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + host.lines = lines; + + return CMD_SUCCESS; +} + +DEFUN(no_service_terminal_length, no_service_terminal_length_cmd, + "no service terminal-length [<0-512>]", + NO_STR + "Set up miscellaneous service\n" + "System wide terminal length configuration\n" + "Number of lines of VTY (0 means no line control)\n") +{ + host.lines = -1; + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(do_echo, + echo_cmd, + "echo .MESSAGE", + "Echo a message back to the vty\n" "The message to echo\n") +{ + char *message; + + vty_out(vty, "%s%s", + ((message = + argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +#if 0 +DEFUN(config_logmsg, + config_logmsg_cmd, + "logmsg " LOG_LEVELS " .MESSAGE", + "Send a message to enabled logging destinations\n" + LOG_LEVEL_DESC "The message to send\n") +{ + int level; + char *message; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog(NULL, level, + ((message = argv_concat(argv, argc, 1)) ? message : "")); + if (message) + talloc_free(message); + return CMD_SUCCESS; +} + +DEFUN(show_logging, + show_logging_cmd, + "show logging", SHOW_STR "Show current logging configuration\n") +{ + struct zlog *zl = zlog_default; + + vty_out(vty, "Syslog logging: "); + if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, facility %s, ident %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Stdout logging: "); + if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Monitor logging: "); + if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "File logging: "); + if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp) + vty_out(vty, "disabled"); + else + vty_out(vty, "level %s, filename %s", + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Protocol name: %s%s", + zlog_proto_names[zl->protocol], VTY_NEWLINE); + vty_out(vty, "Record priority: %s%s", + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout, + config_log_stdout_cmd, + "log stdout", "Logging control\n" "Set stdout logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_stdout_level, + config_log_stdout_level_cmd, + "log stdout " LOG_LEVELS, + "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_STDOUT, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_stdout, + no_config_log_stdout_cmd, + "no log stdout [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor, + config_log_monitor_cmd, + "log monitor", + "Logging control\n" "Set terminal line (monitor) logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_monitor_level, + config_log_monitor_level_cmd, + "log monitor " LOG_LEVELS, + "Logging control\n" + "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_MONITOR, level); + return CMD_SUCCESS; +} + +DEFUN(no_config_log_monitor, + no_config_log_monitor_cmd, + "no log monitor [LEVEL]", + NO_STR + "Logging control\n" + "Disable terminal line (monitor) logging\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +static int set_log_file(struct vty *vty, const char *fname, int loglevel) +{ + int ret; + char *p = NULL; + const char *fullpath; + + /* Path detection. */ + if (!IS_DIRECTORY_SEP(*fname)) { + char cwd[MAXPATHLEN + 1]; + cwd[MAXPATHLEN] = '\0'; + + if (getcwd(cwd, MAXPATHLEN) == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + + if ((p = _talloc_zero(tall_vcmd_ctx, + strlen(cwd) + strlen(fname) + 2), + "set_log_file") + == NULL) { + zlog_err("config_log_file: Unable to alloc mem!"); + return CMD_WARNING; + } + sprintf(p, "%s/%s", cwd, fname); + fullpath = p; + } else + fullpath = fname; + + ret = zlog_set_file(NULL, fullpath, loglevel); + + if (p) + talloc_free(p); + + if (!ret) { + vty_out(vty, "can't open logfile %s\n", fname); + return CMD_WARNING; + } + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname); + + return CMD_SUCCESS; +} + +DEFUN(config_log_file, + config_log_file_cmd, + "log file FILENAME", + "Logging control\n" "Logging to file\n" "Logging filename\n") +{ + return set_log_file(vty, argv[0], zlog_default->default_lvl); +} + +DEFUN(config_log_file_level, + config_log_file_level_cmd, + "log file FILENAME " LOG_LEVELS, + "Logging control\n" + "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[1])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[0], level); +} + +DEFUN(no_config_log_file, + no_config_log_file_cmd, + "no log file [FILENAME]", + NO_STR + "Logging control\n" "Cancel logging to file\n" "Logging file name\n") +{ + zlog_reset_file(NULL); + + if (host.logfile) + talloc_free(host.logfile); + + host.logfile = NULL; + + return CMD_SUCCESS; +} + +ALIAS(no_config_log_file, + no_config_log_file_level_cmd, + "no log file FILENAME LEVEL", + NO_STR + "Logging control\n" + "Cancel logging to file\n" "Logging file name\n" "Logging level\n") + + DEFUN(config_log_syslog, + config_log_syslog_cmd, + "log syslog", "Logging control\n" "Set syslog logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; +} + +DEFUN(config_log_syslog_level, + config_log_syslog_level_cmd, + "log syslog " LOG_LEVELS, + "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) +{ + int level; + + if ((level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_syslog_facility, + config_log_syslog_facility_cmd, + "log syslog facility " LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_syslog, + no_config_log_syslog_cmd, + "no log syslog [LEVEL]", + NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n") +{ + zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + return CMD_SUCCESS; +} + +ALIAS(no_config_log_syslog, + no_config_log_syslog_facility_cmd, + "no log syslog facility " LOG_FACILITIES, + NO_STR + "Logging control\n" + "Logging goes to syslog\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) + + DEFUN(config_log_facility, + config_log_facility_cmd, + "log facility " LOG_FACILITIES, + "Logging control\n" + "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) +{ + int facility; + + if ((facility = facility_match(argv[0])) < 0) + return CMD_ERR_NO_MATCH; + zlog_default->facility = facility; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_facility, + no_config_log_facility_cmd, + "no log facility [FACILITY]", + NO_STR + "Logging control\n" + "Reset syslog facility to default (daemon)\n" "Syslog facility\n") +{ + zlog_default->facility = LOG_DAEMON; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(config_log_trap, + config_log_trap_cmd, + "log trap " LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) +{ + int new_level; + int i; + + if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + + zlog_default->default_lvl = new_level; + for (i = 0; i < ZLOG_NUM_DESTS; i++) + if (zlog_default->maxlvl[i] != ZLOG_DISABLED) + zlog_default->maxlvl[i] = new_level; + return CMD_SUCCESS; +} + +DEFUN_DEPRECATED(no_config_log_trap, + no_config_log_trap_cmd, + "no log trap [LEVEL]", + NO_STR + "Logging control\n" + "Permit all logging information\n" "Logging level\n") +{ + zlog_default->default_lvl = LOG_DEBUG; + return CMD_SUCCESS; +} + +DEFUN(config_log_record_priority, + config_log_record_priority_cmd, + "log record-priority", + "Logging control\n" + "Log the priority of the message within the message\n") +{ + zlog_default->record_priority = 1; + return CMD_SUCCESS; +} + +DEFUN(no_config_log_record_priority, + no_config_log_record_priority_cmd, + "no log record-priority", + NO_STR + "Logging control\n" + "Do not log the priority of the message within the message\n") +{ + zlog_default->record_priority = 0; + return CMD_SUCCESS; +} +#endif + +DEFUN(banner_motd_file, + banner_motd_file_cmd, + "banner motd file [FILE]", + "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") +{ + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(banner_motd_default, + banner_motd_default_cmd, + "banner motd default", + "Set banner string\n" "Strings for motd\n" "Default string\n") +{ + host.motd = default_motd; + return CMD_SUCCESS; +} + +DEFUN(no_banner_motd, + no_banner_motd_cmd, + "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n") +{ + host.motd = NULL; + if (host.motdfile) + talloc_free(host.motdfile); + host.motdfile = NULL; + return CMD_SUCCESS; +} + +/* Set config filename. Called from vty.c */ +void host_config_set(const char *filename) +{ + host.config = talloc_strdup(tall_vty_cmd_ctx, filename); +} + +void install_default(enum node_type node) +{ + install_element(node, &config_help_cmd); + install_element(node, &config_list_cmd); + + install_element(node, &config_write_terminal_cmd); + install_element(node, &config_write_file_cmd); + install_element(node, &config_write_memory_cmd); + install_element(node, &config_write_cmd); + install_element(node, &show_running_config_cmd); +} + +/* Initialize command interface. Install basic nodes and commands. */ +void cmd_init(int terminal) +{ + /* Allocate initial top vector of commands. */ + cmdvec = vector_init(VECTOR_MIN_SIZE); + + /* Default host value settings. */ + host.name = NULL; + host.password = NULL; + host.enable = NULL; + host.logfile = NULL; + host.config = NULL; + host.lines = -1; + host.motd = default_motd; + host.motdfile = NULL; + + /* Install top nodes. */ + install_node(&view_node, NULL); + install_node(&enable_node, NULL); + install_node(&auth_node, NULL); + install_node(&auth_enable_node, NULL); + install_node(&config_node, config_write_host); + + /* Each node's basic commands. */ + install_element(VIEW_NODE, &show_version_cmd); + if (terminal) { + install_element(VIEW_NODE, &config_list_cmd); + install_element(VIEW_NODE, &config_exit_cmd); + install_element(VIEW_NODE, &config_help_cmd); + install_element(VIEW_NODE, &config_enable_cmd); + install_element(VIEW_NODE, &config_terminal_length_cmd); + install_element(VIEW_NODE, &config_terminal_no_length_cmd); + install_element(VIEW_NODE, &echo_cmd); + } + + if (terminal) { + install_element(ENABLE_NODE, &config_exit_cmd); + install_default(ENABLE_NODE); + install_element(ENABLE_NODE, &config_disable_cmd); + install_element(ENABLE_NODE, &config_terminal_cmd); + install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + } + install_element (ENABLE_NODE, &show_startup_config_cmd); + install_element(ENABLE_NODE, &show_version_cmd); + + if (terminal) { + install_element(ENABLE_NODE, &config_terminal_length_cmd); + install_element(ENABLE_NODE, &config_terminal_no_length_cmd); + install_element(ENABLE_NODE, &echo_cmd); + + install_default(CONFIG_NODE); + install_element(CONFIG_NODE, &config_exit_cmd); + } + + install_element(CONFIG_NODE, &hostname_cmd); + install_element(CONFIG_NODE, &no_hostname_cmd); + + if (terminal) { + install_element(CONFIG_NODE, &password_cmd); + install_element(CONFIG_NODE, &password_text_cmd); + install_element(CONFIG_NODE, &enable_password_cmd); + install_element(CONFIG_NODE, &enable_password_text_cmd); + install_element(CONFIG_NODE, &no_enable_password_cmd); + +#ifdef VTY_CRYPT_PW + install_element(CONFIG_NODE, &service_password_encrypt_cmd); + install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); +#endif + install_element(CONFIG_NODE, &banner_motd_default_cmd); + install_element(CONFIG_NODE, &banner_motd_file_cmd); + install_element(CONFIG_NODE, &no_banner_motd_cmd); + install_element(CONFIG_NODE, &service_terminal_length_cmd); + install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + + } + srand(time(NULL)); +} diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c new file mode 100644 index 00000000..b037a5bc --- /dev/null +++ b/src/shared/libosmocore/src/vty/logging_vty.c @@ -0,0 +1,585 @@ +/* OpenBSC logging helper for the VTY */ +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdlib.h> +#include <string.h> + +#include "../../config.h" + +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> + +//#include <openbsc/vty.h> + +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/logging.h> + +#define LOG_STR "Configure logging sub-system\n" + +extern const struct log_info *osmo_log_info; + +static void _vty_output(struct log_target *tgt, + unsigned int level, const char *line) +{ + struct vty *vty = tgt->tgt_vty.vty; + vty_out(vty, "%s", line); + /* This is an ugly hack, but there is no easy way... */ + if (strchr(line, '\n')) + vty_out(vty, "\r"); +} + +struct log_target *log_target_create_vty(struct vty *vty) +{ + struct log_target *target; + + target = log_target_create(); + if (!target) + return NULL; + + target->tgt_vty.vty = vty; + target->output = _vty_output; + return target; +} + +DEFUN(enable_logging, + enable_logging_cmd, + "logging enable", + LOGGING_STR + "Enables logging to this vty\n") +{ + struct telnet_connection *conn; + + conn = (struct telnet_connection *) vty->priv; + if (conn->dbg) { + vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + conn->dbg = log_target_create_vty(vty); + if (!conn->dbg) + return CMD_WARNING; + + log_add_target(conn->dbg); + return CMD_SUCCESS; +} + +struct log_target *osmo_log_vty2tgt(struct vty *vty) +{ + struct telnet_connection *conn; + + if (vty->node == CFG_LOG_NODE) + return vty->index; + + + conn = (struct telnet_connection *) vty->priv; + if (!conn->dbg) + vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + + return conn->dbg; +} + +DEFUN(logging_fltr_all, + logging_fltr_all_cmd, + "logging filter all (0|1)", + LOGGING_STR FILTER_STR + "Do you want to log all messages?\n" + "Only print messages matched by other filters\n" + "Bypass filter and print all messages\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_set_all_filter(tgt, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_use_clr, + logging_use_clr_cmd, + "logging color (0|1)", + LOGGING_STR "Configure color-printing for log messages\n" + "Don't use color for printing messages\n" + "Use color for printing messages\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_set_use_color(tgt, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_prnt_timestamp, + logging_prnt_timestamp_cmd, + "logging timestamp (0|1)", + LOGGING_STR "Configure log message timestamping\n" + "Don't prefix each log message\n" + "Prefix each log message with current timestamp\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_set_print_timestamp(tgt, atoi(argv[0])); + return CMD_SUCCESS; +} + +DEFUN(logging_level, + logging_level_cmd, + NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */ + NULL) /* same thing for helpstr. */ +{ + int category = log_parse_category(argv[0]); + int level = log_parse_level(argv[1]); + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + if (level < 0) { + vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check for special case where we want to set global log level */ + if (!strcmp(argv[0], "all")) { + log_set_log_level(tgt, level); + return CMD_SUCCESS; + } + + if (category < 0) { + vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + tgt->categories[category].enabled = 1; + tgt->categories[category].loglevel = level; + + return CMD_SUCCESS; +} + +DEFUN(logging_set_category_mask, + logging_set_category_mask_cmd, + "logging set log mask MASK", + LOGGING_STR + "Decide which categories to output.\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_parse_category_mask(tgt, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(diable_logging, + disable_logging_cmd, + "logging disable", + LOGGING_STR + "Disables logging to this vty\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + struct telnet_connection *conn = (struct telnet_connection *) vty->priv; + + if (!tgt) + return CMD_WARNING; + + log_del_target(tgt); + talloc_free(tgt); + conn->dbg = NULL; + + return CMD_SUCCESS; +} + +static void vty_print_logtarget(struct vty *vty, const struct log_info *info, + const struct log_target *tgt) +{ + unsigned int i; + + vty_out(vty, " Global Loglevel: %s%s", + log_level_str(tgt->loglevel), VTY_NEWLINE); + vty_out(vty, " Use color: %s, Print Timestamp: %s%s", + tgt->use_color ? "On" : "Off", + tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE); + + vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE); + + for (i = 0; i < info->num_cat; i++) { + const struct log_category *cat = &tgt->categories[i]; + vty_out(vty, " %-10s %-10s %-8s %s%s", + info->cat[i].name+1, log_level_str(cat->loglevel), + cat->enabled ? "Enabled" : "Disabled", + info->cat[i].description, + VTY_NEWLINE); + } +} + +#define SHOW_LOG_STR "Show current logging configuration\n" + +DEFUN(show_logging_vty, + show_logging_vty_cmd, + "show logging vty", + SHOW_STR SHOW_LOG_STR + "Show current logging configuration for this vty\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + vty_print_logtarget(vty, osmo_log_info, tgt); + + return CMD_SUCCESS; +} + +gDEFUN(cfg_description, cfg_description_cmd, + "description .TEXT", + "Save human-readable decription of the object\n") +{ + char **dptr = vty->index_sub; + + if (!dptr) { + vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*dptr) + talloc_free(*dptr); + *dptr = argv_concat(argv, argc, 0); + if (!dptr) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +gDEFUN(cfg_no_description, cfg_no_description_cmd, + "no description", + NO_STR + "Remove description of the object\n") +{ + char **dptr = vty->index_sub; + + if (!dptr) { + vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (*dptr) { + talloc_free(*dptr); + *dptr = NULL; + } + + return CMD_SUCCESS; +} + +/* Support for configuration of log targets != the current vty */ + +struct cmd_node cfg_log_node = { + CFG_LOG_NODE, + "%s(config-log)# ", + 1 +}; + +#ifdef HAVE_SYSLOG_H + +#include <syslog.h> + +static const int local_sysl_map[] = { + [0] = LOG_LOCAL0, + [1] = LOG_LOCAL1, + [2] = LOG_LOCAL2, + [3] = LOG_LOCAL3, + [4] = LOG_LOCAL4, + [5] = LOG_LOCAL5, + [6] = LOG_LOCAL6, + [7] = LOG_LOCAL7 +}; + +/* From VTY core code */ +extern struct host host; + +static int _cfg_log_syslog(struct vty *vty, int facility) +{ + struct log_target *tgt; + + /* First delete the old syslog target, if any */ + tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); + if (tgt) + log_target_destroy(tgt); + + tgt = log_target_create_syslog(host.app_info->name, 0, facility); + if (!tgt) { + vty_out(vty, "%% Unable to open syslog%s", VTY_NEWLINE); + return CMD_WARNING; + } + log_add_target(tgt); + + vty->index = tgt; + vty->node = CFG_LOG_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_log_syslog_local, cfg_log_syslog_local_cmd, + "log syslog local <0-7>", + LOG_STR "Logging via syslog\n" "Syslog LOCAL facility\n" + "Local facility number\n") +{ + int local = atoi(argv[0]); + int facility = local_sysl_map[local]; + + return _cfg_log_syslog(vty, facility); +} + +static struct value_string sysl_level_names[] = { + { LOG_AUTHPRIV, "authpriv" }, + { LOG_CRON, "cron" }, + { LOG_DAEMON, "daemon" }, + { LOG_FTP, "ftp" }, + { LOG_LPR, "lpr" }, + { LOG_MAIL, "mail" }, + { LOG_NEWS, "news" }, + { LOG_USER, "user" }, + { LOG_UUCP, "uucp" }, + /* only for value -> string conversion */ + { LOG_LOCAL0, "local 0" }, + { LOG_LOCAL1, "local 1" }, + { LOG_LOCAL2, "local 2" }, + { LOG_LOCAL3, "local 3" }, + { LOG_LOCAL4, "local 4" }, + { LOG_LOCAL5, "local 5" }, + { LOG_LOCAL6, "local 6" }, + { LOG_LOCAL7, "local 7" }, + { 0, NULL } +}; + +DEFUN(cfg_log_syslog, cfg_log_syslog_cmd, + "log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)", + LOG_STR "Logging via syslog\n") +{ + int facility = get_string_value(sysl_level_names, argv[0]); + + return _cfg_log_syslog(vty, facility); +} + +DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd, + "no log syslog", + NO_STR LOG_STR "Logging via syslog\n") +{ + struct log_target *tgt; + + tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); + if (!tgt) { + vty_out(vty, "%% No syslog target found%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + log_target_destroy(tgt); + + return CMD_SUCCESS; +} +#endif /* HAVE_SYSLOG_H */ + +DEFUN(cfg_log_stderr, cfg_log_stderr_cmd, + "log stderr", + LOG_STR "Logging via STDERR of the process\n") +{ + struct log_target *tgt; + + tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); + if (!tgt) { + tgt = log_target_create_stderr(); + if (!tgt) { + vty_out(vty, "%% Unable to create stderr log%s", + VTY_NEWLINE); + return CMD_WARNING; + } + log_add_target(tgt); + } + + vty->index = tgt; + vty->node = CFG_LOG_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd, + "no log stderr", + NO_STR LOG_STR "Logging via STDERR of the process\n") +{ + struct log_target *tgt; + + tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); + if (!tgt) { + vty_out(vty, "%% No stderr logging active%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_target_destroy(tgt); + + return CMD_SUCCESS; +} + +DEFUN(cfg_log_file, cfg_log_file_cmd, + "log file .FILENAME", + LOG_STR "Logging to text file\n" "Filename\n") +{ + const char *fname = argv[0]; + struct log_target *tgt; + + tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); + if (!tgt) { + tgt = log_target_create_file(fname); + if (!tgt) { + vty_out(vty, "%% Unable to create file `%s'%s", + fname, VTY_NEWLINE); + return CMD_WARNING; + } + log_add_target(tgt); + } + + vty->index = tgt; + vty->node = CFG_LOG_NODE; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_no_log_file, cfg_no_log_file_cmd, + "no log file .FILENAME", + NO_STR LOG_STR "Logging to text file\n" "Filename\n") +{ + const char *fname = argv[0]; + struct log_target *tgt; + + tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); + if (!tgt) { + vty_out(vty, "%% No such log file `%s'%s", + fname, VTY_NEWLINE); + return CMD_WARNING; + } + + log_target_destroy(tgt); + + return CMD_SUCCESS; +} + +static int config_write_log_single(struct vty *vty, struct log_target *tgt) +{ + int i; + char level_lower[32]; + + switch (tgt->type) { + case LOG_TGT_TYPE_VTY: + return 1; + break; + case LOG_TGT_TYPE_STDERR: + vty_out(vty, "log stderr%s", VTY_NEWLINE); + break; + case LOG_TGT_TYPE_SYSLOG: +#ifdef HAVE_SYSLOG_H + vty_out(vty, "log syslog %s%s", + get_value_string(sysl_level_names, + tgt->tgt_syslog.facility), + VTY_NEWLINE); +#endif + break; + case LOG_TGT_TYPE_FILE: + vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); + break; + } + + vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0, + VTY_NEWLINE); + vty_out(vty, " logging timestamp %u%s", tgt->print_timestamp ? 1 : 0, + VTY_NEWLINE); + + /* stupid old osmo logging API uses uppercase strings... */ + osmo_str2lower(level_lower, log_level_str(tgt->loglevel)); + vty_out(vty, " logging level all %s%s", level_lower, VTY_NEWLINE); + + for (i = 0; i < osmo_log_info->num_cat; i++) { + const struct log_category *cat = &tgt->categories[i]; + char cat_lower[32]; + + /* stupid old osmo logging API uses uppercase strings... */ + osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1); + osmo_str2lower(level_lower, log_level_str(cat->loglevel)); + + vty_out(vty, " logging level %s %s%s", cat_lower, level_lower, + VTY_NEWLINE); + } + + /* FIXME: levels */ + + return 1; +} + +static int config_write_log(struct vty *vty) +{ + struct log_target *dbg = vty->index; + + llist_for_each_entry(dbg, &osmo_log_target_list, entry) + config_write_log_single(vty, dbg); + + return 1; +} + +void logging_vty_add_cmds(const struct log_info *cat) +{ + install_element_ve(&enable_logging_cmd); + install_element_ve(&disable_logging_cmd); + install_element_ve(&logging_fltr_all_cmd); + install_element_ve(&logging_use_clr_cmd); + install_element_ve(&logging_prnt_timestamp_cmd); + install_element_ve(&logging_set_category_mask_cmd); + + /* Logging level strings are generated dynamically. */ + logging_level_cmd.string = log_vty_command_string(cat); + logging_level_cmd.doc = log_vty_command_description(cat); + install_element_ve(&logging_level_cmd); + install_element_ve(&show_logging_vty_cmd); + + install_node(&cfg_log_node, config_write_log); + install_element(CFG_LOG_NODE, &logging_fltr_all_cmd); + install_element(CFG_LOG_NODE, &logging_use_clr_cmd); + install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd); + install_element(CFG_LOG_NODE, &logging_level_cmd); + + install_element(CONFIG_NODE, &cfg_log_stderr_cmd); + install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); + install_element(CONFIG_NODE, &cfg_log_file_cmd); + install_element(CONFIG_NODE, &cfg_no_log_file_cmd); +#ifdef HAVE_SYSLOG_H + install_element(CONFIG_NODE, &cfg_log_syslog_cmd); + install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd); + install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd); +#endif +} diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c new file mode 100644 index 00000000..c08a256a --- /dev/null +++ b/src/shared/libosmocore/src/vty/telnet_interface.c @@ -0,0 +1,212 @@ +/* minimalistic telnet/network interface it might turn into a wire interface */ +/* (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/command.h> + +/* per connection data */ +LLIST_HEAD(active_connections); + +static void *tall_telnet_ctx; + +/* per network data */ +static int telnet_new_connection(struct osmo_fd *fd, unsigned int what); + +static struct osmo_fd server_socket = { + .when = BSC_FD_READ, + .cb = telnet_new_connection, + .priv_nr = 0, +}; + +int telnet_init(void *tall_ctx, void *priv, int port) +{ + struct sockaddr_in sock_addr; + int fd, rc, on = 1; + + tall_telnet_ctx = talloc_named_const(tall_ctx, 1, + "telnet_connection"); + + fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface socket creation failed\n"); + return fd; + } + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_port = htons(port); + sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + rc = bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (rc < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface failed to bind\n"); + close(fd); + return rc; + } + + rc = listen(fd, 0); + if (rc < 0) { + LOGP(0, LOGL_ERROR, "Telnet interface failed to listen\n"); + close(fd); + return rc; + } + + server_socket.data = priv; + server_socket.fd = fd; + osmo_fd_register(&server_socket); + + return 0; +} + +extern struct host host; + +static void print_welcome(int fd) +{ + int ret; + static const char *msg1 = "Welcome to the "; + static const char *msg2 = " control interface\r\n"; + char *app_name = "<unnamed>"; + + if (host.app_info->name) + app_name = host.app_info->name; + + ret = write(fd, msg1, strlen(msg1)); + ret = write(fd, app_name, strlen(app_name)); + ret = write(fd, msg2, strlen(msg2)); + + if (host.app_info->copyright) + ret = write(fd, host.app_info->copyright, strlen(host.app_info->copyright)); +} + +int telnet_close_client(struct osmo_fd *fd) +{ + struct telnet_connection *conn = (struct telnet_connection*)fd->data; + + close(fd->fd); + osmo_fd_unregister(fd); + + if (conn->dbg) { + log_del_target(conn->dbg); + talloc_free(conn->dbg); + } + + llist_del(&conn->entry); + talloc_free(conn); + return 0; +} + +static int client_data(struct osmo_fd *fd, unsigned int what) +{ + struct telnet_connection *conn = fd->data; + int rc = 0; + + if (what & BSC_FD_READ) { + conn->fd.when &= ~BSC_FD_READ; + rc = vty_read(conn->vty); + } + + /* vty might have been closed from vithin vty_read() */ + if (!conn->vty) + return rc; + + if (what & BSC_FD_WRITE) { + rc = buffer_flush_all(conn->vty->obuf, fd->fd); + if (rc == BUFFER_EMPTY) + conn->fd.when &= ~BSC_FD_WRITE; + } + + return rc; +} + +static int telnet_new_connection(struct osmo_fd *fd, unsigned int what) +{ + struct telnet_connection *connection; + struct sockaddr_in sockaddr; + socklen_t len = sizeof(sockaddr); + int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); + + if (new_connection < 0) { + LOGP(0, LOGL_ERROR, "telnet accept failed\n"); + return new_connection; + } + + connection = talloc_zero(tall_telnet_ctx, struct telnet_connection); + connection->priv = fd->data; + connection->fd.data = connection; + connection->fd.fd = new_connection; + connection->fd.when = BSC_FD_READ; + connection->fd.cb = client_data; + osmo_fd_register(&connection->fd); + llist_add_tail(&connection->entry, &active_connections); + + print_welcome(new_connection); + + connection->vty = vty_create(new_connection, connection); + if (!connection->vty) { + LOGP(0, LOGL_ERROR, "couldn't create VTY\n"); + close(new_connection); + talloc_free(connection); + return -1; + } + + return 0; +} + +/* callback from VTY code */ +void vty_event(enum event event, int sock, struct vty *vty) +{ + struct telnet_connection *connection = vty->priv; + struct osmo_fd *bfd = &connection->fd; + + if (vty->type != VTY_TERM) + return; + + switch (event) { + case VTY_READ: + bfd->when |= BSC_FD_READ; + break; + case VTY_WRITE: + bfd->when |= BSC_FD_WRITE; + break; + case VTY_CLOSED: + /* vty layer is about to free() vty */ + connection->vty = NULL; + telnet_close_client(bfd); + break; + default: + break; + } +} + diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c new file mode 100644 index 00000000..7797e62d --- /dev/null +++ b/src/shared/libosmocore/src/vty/utils.c @@ -0,0 +1,50 @@ +/* utility routines for printing common objects in the Osmocom world */ + +/* (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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <inttypes.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/rate_ctr.h> + +#include <osmocom/vty/vty.h> + +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg) +{ + unsigned int i; + + vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); + for (i = 0; i < ctrg->desc->num_ctr; i++) { + struct rate_ctr *ctr = &ctrg->ctr[i]; + vty_out(vty, " %s%s: %8" PRIu64 " " + "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", + prefix, ctrg->desc->ctr_desc[i].description, ctr->current, + ctr->intv[RATE_CTR_INTV_SEC].rate, + ctr->intv[RATE_CTR_INTV_MIN].rate, + ctr->intv[RATE_CTR_INTV_HOUR].rate, + ctr->intv[RATE_CTR_INTV_DAY].rate, + VTY_NEWLINE); + }; +} diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c new file mode 100644 index 00000000..4012f24b --- /dev/null +++ b/src/shared/libosmocore/src/vty/vector.c @@ -0,0 +1,192 @@ +/* Generic vector interface routine + * Copyright (C) 1997 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <stdlib.h> +#include <unistd.h> + +#include <osmocom/vty/vector.h> +#include <osmocom/vty/vty.h> +#include <osmocom/core/talloc.h> +#include <memory.h> + +void *tall_vty_vec_ctx; + +/* Initialize vector : allocate memory and return vector. */ +vector vector_init(unsigned int size) +{ + vector v = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!v) + return NULL; + + /* allocate at least one slot */ + if (size == 0) + size = 1; + + v->alloced = size; + v->active = 0; + v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size, + "vector_init:index"); + if (!v->index) { + talloc_free(v); + return NULL; + } + return v; +} + +void vector_only_wrapper_free(vector v) +{ + talloc_free(v); +} + +void vector_only_index_free(void *index) +{ + talloc_free(index); +} + +void vector_free(vector v) +{ + talloc_free(v->index); + talloc_free(v); +} + +vector vector_copy(vector v) +{ + unsigned int size; + vector new = talloc_zero(tall_vty_vec_ctx, struct _vector); + if (!new) + return NULL; + + new->active = v->active; + new->alloced = v->alloced; + + size = sizeof(void *) * (v->alloced); + new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index"); + if (!new->index) { + talloc_free(new); + return NULL; + } + memcpy(new->index, v->index, size); + + return new; +} + +/* Check assigned index, and if it runs short double index pointer */ +void vector_ensure(vector v, unsigned int num) +{ + if (v->alloced > num) + return; + + v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index, + sizeof(void *) * (v->alloced * 2)); + memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced); + v->alloced *= 2; + + if (v->alloced <= num) + vector_ensure(v, num); +} + +/* This function only returns next empty slot index. It dose not mean + the slot's index memory is assigned, please call vector_ensure() + after calling this function. */ +int vector_empty_slot(vector v) +{ + unsigned int i; + + if (v->active == 0) + return 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] == 0) + return i; + + return i; +} + +/* Set value to the smallest empty slot. */ +int vector_set(vector v, void *val) +{ + unsigned int i; + + i = vector_empty_slot(v); + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Set value to specified index slot. */ +int vector_set_index(vector v, unsigned int i, void *val) +{ + vector_ensure(v, i); + + v->index[i] = val; + + if (v->active <= i) + v->active = i + 1; + + return i; +} + +/* Look up vector. */ +void *vector_lookup(vector v, unsigned int i) +{ + if (i >= v->active) + return NULL; + return v->index[i]; +} + +/* Lookup vector, ensure it. */ +void *vector_lookup_ensure(vector v, unsigned int i) +{ + vector_ensure(v, i); + return v->index[i]; +} + +/* Unset value at specified index slot. */ +void vector_unset(vector v, unsigned int i) +{ + if (i >= v->alloced) + return; + + v->index[i] = NULL; + + if (i + 1 == v->active) { + v->active--; + while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */ + } +} + +/* Count the number of not emplty slot. */ +unsigned int vector_count(vector v) +{ + unsigned int i; + unsigned count = 0; + + for (i = 0; i < v->active; i++) + if (v->index[i] != NULL) + count++; + + return count; +} diff --git a/src/shared/libosmocore/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c new file mode 100644 index 00000000..a1f0304b --- /dev/null +++ b/src/shared/libosmocore/src/vty/vty.c @@ -0,0 +1,1698 @@ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <termios.h> + +#include <sys/utsname.h> +#include <sys/param.h> + +#include <arpa/telnet.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/core/talloc.h> + +#define SYSCONFDIR "/usr/local/etc" + +/* our callback, located in telnet_interface.c */ +void vty_event(enum event event, int sock, struct vty *vty); + +extern struct host host; + +/* Vector which store each vty structure. */ +static vector vtyvec; + +vector Vvty_serv_thread; + +char *vty_cwd = NULL; + +/* Configure lock. */ +static int vty_config; + +static int no_password_check = 1; + +void *tall_vty_ctx; + +static void vty_clear_buf(struct vty *vty) +{ + memset(vty->buf, 0, vty->max); +} + +/* Allocate new vty struct. */ +struct vty *vty_new() +{ + struct vty *new = talloc_zero(tall_vty_ctx, struct vty); + + if (!new) + goto out; + + new->obuf = buffer_new(new, 0); /* Use default buffer size. */ + if (!new->obuf) + goto out_new; + new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf"); + if (!new->buf) + goto out_obuf; + + new->max = VTY_BUFSIZ; + + return new; + +out_obuf: + buffer_free(new->obuf); +out_new: + talloc_free(new); + new = NULL; +out: + return new; +} + +/* Authentication of vty */ +static void vty_auth(struct vty *vty, char *buf) +{ + char *passwd = NULL; + enum node_type next_node = 0; + int fail; + char *crypt(const char *, const char *); + + switch (vty->node) { + case AUTH_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.password_encrypt; + else +#endif + passwd = host.password; + if (host.advanced) + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + else + next_node = VIEW_NODE; + break; + case AUTH_ENABLE_NODE: +#ifdef VTY_CRYPT_PW + if (host.encrypt) + passwd = host.enable_encrypt; + else +#endif + passwd = host.enable; + next_node = ENABLE_NODE; + break; + } + + if (passwd) { +#ifdef VTY_CRYPT_PW + if (host.encrypt) + fail = strcmp(crypt(buf, passwd), passwd); + else +#endif + fail = strcmp(buf, passwd); + } else + fail = 1; + + if (!fail) { + vty->fail = 0; + vty->node = next_node; /* Success ! */ + } else { + vty->fail++; + if (vty->fail >= 3) { + if (vty->node == AUTH_NODE) { + vty_out(vty, + "%% Bad passwords, too many failures!%s", + VTY_NEWLINE); + vty->status = VTY_CLOSE; + } else { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out(vty, + "%% Bad enable passwords, too many failures!%s", + VTY_NEWLINE); + vty->node = VIEW_NODE; + } + } + } +} + +/* Close vty interface. */ +void vty_close(struct vty *vty) +{ + int i; + + if (vty->obuf) { + /* Flush buffer. */ + buffer_flush_all(vty->obuf, vty->fd); + + /* Free input buffer. */ + buffer_free(vty->obuf); + vty->obuf = NULL; + } + + /* Free command history. */ + for (i = 0; i < VTY_MAXHIST; i++) + if (vty->hist[i]) + talloc_free(vty->hist[i]); + + /* Unset vector. */ + vector_unset(vtyvec, vty->fd); + + /* Close socket. */ + if (vty->fd > 0) + close(vty->fd); + + if (vty->buf) { + talloc_free(vty->buf); + vty->buf = NULL; + } + + /* Check configure. */ + vty_config_unlock(vty); + + /* VTY_CLOSED is handled by the telnet_interface */ + vty_event(VTY_CLOSED, vty->fd, vty); + + /* OK free vty. */ + talloc_free(vty); +} + +int vty_shell(struct vty *vty) +{ + return vty->type == VTY_SHELL ? 1 : 0; +} + + +/* VTY standard output function. */ +int vty_out(struct vty *vty, const char *format, ...) +{ + va_list args; + int len = 0; + int size = 1024; + char buf[1024]; + char *p = NULL; + + if (vty_shell(vty)) { + va_start(args, format); + vprintf(format, args); + va_end(args); + } else { + /* Try to write to initial buffer. */ + va_start(args, format); + len = vsnprintf(buf, sizeof buf, format, args); + va_end(args); + + /* Initial buffer is not enough. */ + if (len < 0 || len >= size) { + while (1) { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = talloc_realloc_size(vty, p, size); + if (!p) + return -1; + + va_start(args, format); + len = vsnprintf(p, size, format, args); + va_end(args); + + if (len > -1 && len < size) + break; + } + } + + /* When initial buffer is enough to store all output. */ + if (!p) + p = buf; + + /* Pointer p must point out buffer. */ + buffer_put(vty->obuf, (u_char *) p, len); + + /* If p is not different with buf, it is allocated buffer. */ + if (p != buf) + talloc_free(p); + } + + vty_event(VTY_WRITE, vty->fd, vty); + + return len; +} + +int vty_out_newline(struct vty *vty) +{ + char *p = vty_newline(vty); + buffer_put(vty->obuf, p, strlen(p)); + return 0; +} + +void *vty_current_index(struct vty *vty) +{ + return vty->index; +} +int vty_current_node(struct vty *vty) +{ + return vty->node; +} + +int vty_config_lock(struct vty *vty) +{ + if (vty_config == 0) { + vty->config = 1; + vty_config = 1; + } + return vty->config; +} + +int vty_config_unlock(struct vty *vty) +{ + if (vty_config == 1 && vty->config == 1) { + vty->config = 0; + vty_config = 0; + } + return vty->config; +} + +/* Say hello to vty interface. */ +void vty_hello(struct vty *vty) +{ + if (host.motdfile) { + FILE *f; + char buf[4096]; + + f = fopen(host.motdfile, "r"); + if (f) { + while (fgets(buf, sizeof(buf), f)) { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen(buf); + (s > buf) && isspace(*(s - 1)); s--) ; + *s = '\0'; + vty_out(vty, "%s%s", buf, VTY_NEWLINE); + } + fclose(f); + } else + vty_out(vty, "MOTD file not found%s", VTY_NEWLINE); + } else if (host.motd) + vty_out(vty, "%s", host.motd); +} + +/* Put out prompt and wait input from user. */ +static void vty_prompt(struct vty *vty) +{ + struct utsname names; + const char *hostname; + + if (vty->type == VTY_TERM) { + hostname = host.app_info->name; + if (!hostname) { + uname(&names); + hostname = names.nodename; + } + vty_out(vty, cmd_prompt(vty->node), hostname); + } +} + +/* Command execution over the vty interface. */ +static int vty_command(struct vty *vty, char *buf) +{ + int ret; + vector vline; + + /* Split readline string up into the vector */ + vline = cmd_make_strvec(buf); + + if (vline == NULL) + return CMD_SUCCESS; + + ret = cmd_execute_command(vline, vty, NULL, 0); + if (ret != CMD_SUCCESS) + switch (ret) { + case CMD_WARNING: + if (vty->type == VTY_FILE) + vty_out(vty, "Warning...%s", VTY_NEWLINE); + break; + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; + case CMD_ERR_NO_MATCH: + vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE); + break; + case CMD_ERR_INCOMPLETE: + vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; + } + cmd_free_strvec(vline); + + return ret; +} + +static const char telnet_backward_char = 0x08; +static const char telnet_space_char = ' '; + +/* Basic function to write buffer to vty. */ +static void vty_write(struct vty *vty, const char *buf, size_t nbytes) +{ + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + /* Should we do buffering here ? And make vty_flush (vty) ? */ + buffer_put(vty->obuf, buf, nbytes); +} + +/* Ensure length of input buffer. Is buffer is short, double it. */ +static void vty_ensure(struct vty *vty, int length) +{ + if (vty->max <= length) { + vty->max *= 2; + vty->buf = talloc_realloc_size(vty, vty->buf, vty->max); + // FIXME: check return + } +} + +/* Basic function to insert character into vty. */ +static void vty_self_insert(struct vty *vty, char c) +{ + int i; + int length; + + vty_ensure(vty, vty->length + 1); + length = vty->length - vty->cp; + memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); + vty->buf[vty->cp] = c; + + vty_write(vty, &vty->buf[vty->cp], length + 1); + for (i = 0; i < length; i++) + vty_write(vty, &telnet_backward_char, 1); + + vty->cp++; + vty->length++; +} + +/* Self insert character 'c' in overwrite mode. */ +static void vty_self_insert_overwrite(struct vty *vty, char c) +{ + vty_ensure(vty, vty->length + 1); + vty->buf[vty->cp++] = c; + + if (vty->cp > vty->length) + vty->length++; + + if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) + return; + + vty_write(vty, &c, 1); +} + +/* Insert a word into vty interface with overwrite mode. */ +static void vty_insert_word_overwrite(struct vty *vty, char *str) +{ + int len = strlen(str); + vty_write(vty, str, len); + strcpy(&vty->buf[vty->cp], str); + vty->cp += len; + vty->length = vty->cp; +} + +/* Forward character. */ +static void vty_forward_char(struct vty *vty) +{ + if (vty->cp < vty->length) { + vty_write(vty, &vty->buf[vty->cp], 1); + vty->cp++; + } +} + +/* Backward character. */ +static void vty_backward_char(struct vty *vty) +{ + if (vty->cp > 0) { + vty->cp--; + vty_write(vty, &telnet_backward_char, 1); + } +} + +/* Move to the beginning of the line. */ +static void vty_beginning_of_line(struct vty *vty) +{ + while (vty->cp) + vty_backward_char(vty); +} + +/* Move to the end of the line. */ +static void vty_end_of_line(struct vty *vty) +{ + while (vty->cp < vty->length) + vty_forward_char(vty); +} + +/* Add current command line to the history buffer. */ +static void vty_hist_add(struct vty *vty) +{ + int index; + + if (vty->length == 0) + return; + + index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; + + /* Ignore the same string as previous one. */ + if (vty->hist[index]) + if (strcmp(vty->buf, vty->hist[index]) == 0) { + vty->hp = vty->hindex; + return; + } + + /* Insert history entry. */ + if (vty->hist[vty->hindex]) + talloc_free(vty->hist[vty->hindex]); + vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf); + + /* History index rotation. */ + vty->hindex++; + if (vty->hindex == VTY_MAXHIST) + vty->hindex = 0; + + vty->hp = vty->hindex; +} + +/* Get telnet window size. */ +static int +vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) +{ +#ifdef TELNET_OPTION_DEBUG + int i; + + for (i = 0; i < nbytes; i++) + { + switch (buf[i]) + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } + } + vty_out (vty, "%s", VTY_NEWLINE); + +#endif /* TELNET_OPTION_DEBUG */ + + switch (buf[0]) + { + case SB: + vty->sb_len = 0; + vty->iac_sb_in_progress = 1; + return 0; + break; + case SE: + { + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + vty_out(vty,"RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); +#ifdef TELNET_OPTION_DEBUG + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); +#endif + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; + } + default: + break; + } + return 1; +} + +/* Execute current command line. */ +static int vty_execute(struct vty *vty) +{ + int ret; + + ret = CMD_SUCCESS; + + switch (vty->node) { + case AUTH_NODE: + case AUTH_ENABLE_NODE: + vty_auth(vty, vty->buf); + break; + default: + ret = vty_command(vty, vty->buf); + if (vty->type == VTY_TERM) + vty_hist_add(vty); + break; + } + + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + + if (vty->status != VTY_CLOSE) + vty_prompt(vty); + + return ret; +} + +/* Send WILL TELOPT_ECHO to remote server. */ +static void +vty_will_echo (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make suppress Go-Ahead telnet option. */ +static void +vty_will_suppress_go_ahead (struct vty *vty) +{ + unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Make don't use linemode over telnet. */ +static void +vty_dont_linemode (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' }; + vty_out (vty, "%s", cmd); +} + +/* Use window size. */ +static void +vty_do_window_size (struct vty *vty) +{ + unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' }; + vty_out (vty, "%s", cmd); +} + +static void vty_kill_line_from_beginning(struct vty *); +static void vty_redraw_line(struct vty *); + +/* Print command line history. This function is called from + vty_next_line and vty_previous_line. */ +static void vty_history_print(struct vty *vty) +{ + int length; + + vty_kill_line_from_beginning(vty); + + /* Get previous line from history buffer */ + length = strlen(vty->hist[vty->hp]); + memcpy(vty->buf, vty->hist[vty->hp], length); + vty->cp = vty->length = length; + + /* Redraw current line */ + vty_redraw_line(vty); +} + +/* Show next command line history. */ +static void vty_next_line(struct vty *vty) +{ + int try_index; + + if (vty->hp == vty->hindex) + return; + + /* Try is there history exist or not. */ + try_index = vty->hp; + if (try_index == (VTY_MAXHIST - 1)) + try_index = 0; + else + try_index++; + + /* If there is not history return. */ + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* Show previous command line history. */ +static void vty_previous_line(struct vty *vty) +{ + int try_index; + + try_index = vty->hp; + if (try_index == 0) + try_index = VTY_MAXHIST - 1; + else + try_index--; + + if (vty->hist[try_index] == NULL) + return; + else + vty->hp = try_index; + + vty_history_print(vty); +} + +/* This function redraw all of the command line character. */ +static void vty_redraw_line(struct vty *vty) +{ + vty_write(vty, vty->buf, vty->length); + vty->cp = vty->length; +} + +/* Forward word. */ +static void vty_forward_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_forward_char(vty); + + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_forward_char(vty); +} + +/* Backward word without skipping training space. */ +static void vty_backward_pure_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* Backward word. */ +static void vty_backward_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_backward_char(vty); + + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_backward_char(vty); +} + +/* When '^D' is typed at the beginning of the line we move to the down + level. */ +static void vty_down_level(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + /* FIXME: we need to call the exit function of the specific node + * in question, not this generic one that doesn't know all nodes */ + (*config_exit_cmd.func) (NULL, vty, 0, NULL); + vty_prompt(vty); + vty->cp = 0; +} + +/* When '^Z' is received from vty, move down to the enable mode. */ +static void vty_end_config(struct vty *vty) +{ + vty_out(vty, "%s", VTY_NEWLINE); + + /* FIXME: we need to call the exit function of the specific node + * in question, not this generic one that doesn't know all nodes */ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + case CFG_LOG_NODE: + vty->node = CONFIG_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + + vty_prompt(vty); + vty->cp = 0; +} + +/* Delete a charcter at the current point. */ +static void vty_delete_char(struct vty *vty) +{ + int i; + int size; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + if (vty->length == 0) { + vty_down_level(vty); + return; + } + + if (vty->cp == vty->length) + return; /* completion need here? */ + + size = vty->length - vty->cp; + + vty->length--; + memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); + vty->buf[vty->length] = '\0'; + + vty_write(vty, &vty->buf[vty->cp], size - 1); + vty_write(vty, &telnet_space_char, 1); + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); +} + +/* Delete a character before the point. */ +static void vty_delete_backward_char(struct vty *vty) +{ + if (vty->cp == 0) + return; + + vty_backward_char(vty); + vty_delete_char(vty); +} + +/* Kill rest of line from current point. */ +static void vty_kill_line(struct vty *vty) +{ + int i; + int size; + + size = vty->length - vty->cp; + + if (size == 0) + return; + + for (i = 0; i < size; i++) + vty_write(vty, &telnet_space_char, 1); + for (i = 0; i < size; i++) + vty_write(vty, &telnet_backward_char, 1); + + memset(&vty->buf[vty->cp], 0, size); + vty->length = vty->cp; +} + +/* Kill line from the beginning. */ +static void vty_kill_line_from_beginning(struct vty *vty) +{ + vty_beginning_of_line(vty); + vty_kill_line(vty); +} + +/* Delete a word before the point. */ +static void vty_forward_kill_word(struct vty *vty) +{ + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') + vty_delete_char(vty); + while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') + vty_delete_char(vty); +} + +/* Delete a word before the point. */ +static void vty_backward_kill_word(struct vty *vty) +{ + while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') + vty_delete_backward_char(vty); + while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') + vty_delete_backward_char(vty); +} + +/* Transpose chars before or at the point. */ +static void vty_transpose_chars(struct vty *vty) +{ + char c1, c2; + + /* If length is short or point is near by the beginning of line then + return. */ + if (vty->length < 2 || vty->cp < 1) + return; + + /* In case of point is located at the end of the line. */ + if (vty->cp == vty->length) { + c1 = vty->buf[vty->cp - 1]; + c2 = vty->buf[vty->cp - 2]; + + vty_backward_char(vty); + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } else { + c1 = vty->buf[vty->cp]; + c2 = vty->buf[vty->cp - 1]; + + vty_backward_char(vty); + vty_self_insert_overwrite(vty, c1); + vty_self_insert_overwrite(vty, c2); + } +} + +/* Do completion at vty interface. */ +static void vty_complete_command(struct vty *vty) +{ + int i; + int ret; + char **matched = NULL; + vector vline; + + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + return; + + vline = cmd_make_strvec(vty->buf); + if (vline == NULL) + return; + + /* In case of 'help \t'. */ + if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + matched = cmd_complete_command(vline, vty, &ret); + + cmd_free_strvec(vline); + + vty_out(vty, "%s", VTY_NEWLINE); + switch (ret) { + case CMD_ERR_AMBIGUOUS: + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NO_MATCH: + /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_COMPLETE_FULL_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + vty_self_insert(vty, ' '); + talloc_free(matched[0]); + break; + case CMD_COMPLETE_MATCH: + vty_prompt(vty); + vty_redraw_line(vty); + vty_backward_pure_word(vty); + vty_insert_word_overwrite(vty, matched[0]); + talloc_free(matched[0]); + break; + case CMD_COMPLETE_LIST_MATCH: + for (i = 0; matched[i] != NULL; i++) { + if (i != 0 && ((i % 6) == 0)) + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "%-10s ", matched[i]); + talloc_free(matched[i]); + } + vty_out(vty, "%s", VTY_NEWLINE); + + vty_prompt(vty); + vty_redraw_line(vty); + break; + case CMD_ERR_NOTHING_TODO: + vty_prompt(vty); + vty_redraw_line(vty); + break; + default: + break; + } + if (matched) + vector_only_index_free(matched); +} + +static void +vty_describe_fold(struct vty *vty, int cmd_width, + unsigned int desc_width, struct desc *desc) +{ + char *buf; + const char *cmd, *p; + int pos; + + cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; + + if (desc_width <= 0) { + vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str, + VTY_NEWLINE); + return; + } + + buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold"); + if (!buf) + return; + + for (p = desc->str; strlen(p) > desc_width; p += pos + 1) { + for (pos = desc_width; pos > 0; pos--) + if (*(p + pos) == ' ') + break; + + if (pos == 0) + break; + + strncpy(buf, p, pos); + buf[pos] = '\0'; + vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); + + cmd = ""; + } + + vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); + + talloc_free(buf); +} + +/* Describe matched command function. */ +static void vty_describe_command(struct vty *vty) +{ + int ret; + vector vline; + vector describe; + unsigned int i, width, desc_width; + struct desc *desc, *desc_cr = NULL; + + vline = cmd_make_strvec(vty->buf); + + /* In case of '> ?'. */ + if (vline == NULL) { + vline = vector_init(1); + vector_set(vline, '\0'); + } else if (isspace((int)vty->buf[vty->length - 1])) + vector_set(vline, '\0'); + + describe = cmd_describe_command(vline, vty, &ret); + + vty_out(vty, "%s", VTY_NEWLINE); + + /* Ambiguous error. */ + switch (ret) { + case CMD_ERR_AMBIGUOUS: + cmd_free_strvec(vline); + vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + case CMD_ERR_NO_MATCH: + cmd_free_strvec(vline); + vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); + vty_prompt(vty); + vty_redraw_line(vty); + return; + break; + } + + /* Get width of command string. */ + width = 0; + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + unsigned int len; + + if (desc->cmd[0] == '\0') + continue; + + len = strlen(desc->cmd); + if (desc->cmd[0] == '.') + len--; + + if (width < len) + width = len; + } + + /* Get width of description string. */ + desc_width = vty->width - (width + 6); + + /* Print out description. */ + for (i = 0; i < vector_active(describe); i++) + if ((desc = vector_slot(describe, i)) != NULL) { + if (desc->cmd[0] == '\0') + continue; + + if (strcmp(desc->cmd, "<cr>") == 0) { + desc_cr = desc; + continue; + } + + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == + '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + +#if 0 + vty_out(vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); +#endif /* 0 */ + } + + if ((desc = desc_cr)) { + if (!desc->str) + vty_out(vty, " %-s%s", + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + VTY_NEWLINE); + else if (desc_width >= strlen(desc->str)) + vty_out(vty, " %-*s %s%s", width, + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str, VTY_NEWLINE); + else + vty_describe_fold(vty, width, desc_width, desc); + } + + cmd_free_strvec(vline); + vector_free(describe); + + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* ^C stop current input and do not add command line to the history. */ +static void vty_stop_input(struct vty *vty) +{ + vty->cp = vty->length = 0; + vty_clear_buf(vty); + vty_out(vty, "%s", VTY_NEWLINE); + + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case VTY_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + break; + case CFG_LOG_NODE: + vty->node = CONFIG_NODE; + break; + default: + /* Unknown node, we have to ignore it. */ + break; + } + vty_prompt(vty); + + /* Set history pointer to the latest one. */ + vty->hp = vty->hindex; +} + +#define CONTROL(X) ((X) - '@') +#define VTY_NORMAL 0 +#define VTY_PRE_ESCAPE 1 +#define VTY_ESCAPE 2 + +/* Escape character command map. */ +static void vty_escape_map(unsigned char c, struct vty *vty) +{ + switch (c) { + case ('A'): + vty_previous_line(vty); + break; + case ('B'): + vty_next_line(vty); + break; + case ('C'): + vty_forward_char(vty); + break; + case ('D'): + vty_backward_char(vty); + break; + default: + break; + } + + /* Go back to normal mode. */ + vty->escape = VTY_NORMAL; +} + +/* Quit print out to the buffer. */ +static void vty_buffer_reset(struct vty *vty) +{ + buffer_reset(vty->obuf); + vty_prompt(vty); + vty_redraw_line(vty); +} + +/* Read data via vty socket. */ +int vty_read(struct vty *vty) +{ + int i; + int nbytes; + unsigned char buf[VTY_READ_BUFSIZ]; + + int vty_sock = vty->fd; + + /* Read raw data from socket */ + if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { + if (nbytes < 0) { + if (ERRNO_IO_RETRY(errno)) { + vty_event(VTY_READ, vty_sock, vty); + return 0; + } + } + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + } + + for (i = 0; i < nbytes; i++) { + if (buf[i] == IAC) { + if (!vty->iac) { + vty->iac = 1; + continue; + } else { + vty->iac = 0; + } + } + + if (vty->iac_sb_in_progress && !vty->iac) { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } + + if (vty->iac) { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option(vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + + if (vty->status == VTY_MORE) { + switch (buf[i]) { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset(vty); + break; +#if 0 /* More line does not work for "show ip bgp". */ + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; +#endif + default: + break; + } + continue; + } + + /* Escape character. */ + if (vty->escape == VTY_ESCAPE) { + vty_escape_map(buf[i], vty); + continue; + } + + /* Pre-escape status. */ + if (vty->escape == VTY_PRE_ESCAPE) { + switch (buf[i]) { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word(vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word(vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } + + switch (buf[i]) { + case CONTROL('A'): + vty_beginning_of_line(vty); + break; + case CONTROL('B'): + vty_backward_char(vty); + break; + case CONTROL('C'): + vty_stop_input(vty); + break; + case CONTROL('D'): + vty_delete_char(vty); + break; + case CONTROL('E'): + vty_end_of_line(vty); + break; + case CONTROL('F'): + vty_forward_char(vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char(vty); + break; + case CONTROL('K'): + vty_kill_line(vty); + break; + case CONTROL('N'): + vty_next_line(vty); + break; + case CONTROL('P'): + vty_previous_line(vty); + break; + case CONTROL('T'): + vty_transpose_chars(vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning(vty); + break; + case CONTROL('W'): + vty_backward_kill_word(vty); + break; + case CONTROL('Z'): + vty_end_config(vty); + break; + case '\n': + case '\r': + vty_out(vty, "%s", VTY_NEWLINE); + vty_execute(vty); + break; + case '\t': + vty_complete_command(vty); + break; + case '?': + if (vty->node == AUTH_NODE + || vty->node == AUTH_ENABLE_NODE) + vty_self_insert(vty, buf[i]); + else + vty_describe_command(vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') { + vty->escape = VTY_ESCAPE; + i++; + } else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert(vty, buf[i]); + break; + } + } + + /* Check status. */ + if (vty->status == VTY_CLOSE) + vty_close(vty); + else { + vty_event(VTY_WRITE, vty_sock, vty); + vty_event(VTY_READ, vty_sock, vty); + } + return 0; +} + +/* Read up configuration file */ +static int +vty_read_file(FILE *confp, void *priv) +{ + int ret; + struct vty *vty; + + vty = vty_new(); + vty->fd = 0; + vty->type = VTY_FILE; + vty->node = CONFIG_NODE; + vty->priv = priv; + + ret = config_from_file(vty, confp); + + if (ret != CMD_SUCCESS) { + switch (ret) { + case CMD_ERR_AMBIGUOUS: + fprintf(stderr, "Ambiguous command.\n"); + break; + case CMD_ERR_NO_MATCH: + fprintf(stderr, "There is no such command.\n"); + break; + } + fprintf(stderr, "Error occurred during reading below " + "line:\n%s\n", vty->buf); + vty_close(vty); + return -EINVAL; + } + + vty_close(vty); + return 0; +} + +/* Create new vty structure. */ +struct vty * +vty_create (int vty_sock, void *priv) +{ + struct vty *vty; + + struct termios t; + + tcgetattr(vty_sock, &t); + cfmakeraw(&t); + tcsetattr(vty_sock, TCSANOW, &t); + + /* Allocate new vty structure and set up default values. */ + vty = vty_new (); + vty->fd = vty_sock; + vty->priv = priv; + vty->type = VTY_TERM; + if (no_password_check) + { + if (host.advanced) + vty->node = ENABLE_NODE; + else + vty->node = VIEW_NODE; + } + else + vty->node = AUTH_NODE; + vty->fail = 0; + vty->cp = 0; + vty_clear_buf (vty); + vty->length = 0; + memset (vty->hist, 0, sizeof (vty->hist)); + vty->hp = 0; + vty->hindex = 0; + vector_set_index (vtyvec, vty_sock, vty); + vty->status = VTY_NORMAL; + if (host.lines >= 0) + vty->lines = host.lines; + else + vty->lines = -1; + + if (! no_password_check) + { + /* Vty is not available if password isn't set. */ + if (host.password == NULL && host.password_encrypt == NULL) + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } + } + + /* Say hello to the world. */ + vty_hello (vty); + if (! no_password_check) + vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + /* Setting up terminal. */ + vty_will_echo (vty); + vty_will_suppress_go_ahead (vty); + + vty_dont_linemode (vty); + vty_do_window_size (vty); + /* vty_dont_lflow_ahead (vty); */ + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_READ, vty_sock, vty); + + return vty; +} + +DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n") +{ + unsigned int i; + struct vty *v; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((v = vector_slot(vtyvec, i)) != NULL) + vty_out(vty, "%svty[%d] %s", + v->config ? "*" : " ", i, VTY_NEWLINE); + return CMD_SUCCESS; +} + +/* Move to vty configuration mode. */ +DEFUN(line_vty, + line_vty_cmd, + "line vty", "Configure a terminal line\n" "Virtual terminal\n") +{ + vty->node = VTY_NODE; + return CMD_SUCCESS; +} + +/* vty login. */ +DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n") +{ + no_password_check = 0; + return CMD_SUCCESS; +} + +DEFUN(no_vty_login, + no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") +{ + no_password_check = 1; + return CMD_SUCCESS; +} + +DEFUN(service_advanced_vty, + service_advanced_vty_cmd, + "service advanced-vty", + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 1; + return CMD_SUCCESS; +} + +DEFUN(no_service_advanced_vty, + no_service_advanced_vty_cmd, + "no service advanced-vty", + NO_STR + "Set up miscellaneous service\n" "Enable advanced mode vty interface\n") +{ + host.advanced = 0; + return CMD_SUCCESS; +} + +DEFUN(terminal_monitor, + terminal_monitor_cmd, + "terminal monitor", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") +{ + vty->monitor = 1; + return CMD_SUCCESS; +} + +DEFUN(terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR "Copy debug output to the current terminal line\n") +{ + vty->monitor = 0; + return CMD_SUCCESS; +} + +DEFUN(show_history, + show_history_cmd, + "show history", SHOW_STR "Display the session command history\n") +{ + int index; + + for (index = vty->hindex + 1; index != vty->hindex;) { + if (index == VTY_MAXHIST) { + index = 0; + continue; + } + + if (vty->hist[index] != NULL) + vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE); + + index++; + } + + return CMD_SUCCESS; +} + +/* Display current configuration. */ +static int vty_config_write(struct vty *vty) +{ + vty_out(vty, "line vty%s", VTY_NEWLINE); + + /* login */ + if (no_password_check) + vty_out(vty, " no login%s", VTY_NEWLINE); + + vty_out(vty, "!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +struct cmd_node vty_node = { + VTY_NODE, + "%s(config-line)# ", + 1, +}; + +/* Reset all VTY status. */ +void vty_reset() +{ + unsigned int i; + struct vty *vty; + struct thread *vty_serv_thread; + + for (i = 0; i < vector_active(vtyvec); i++) + if ((vty = vector_slot(vtyvec, i)) != NULL) { + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); + } + + for (i = 0; i < vector_active(Vvty_serv_thread); i++) + if ((vty_serv_thread = + vector_slot(Vvty_serv_thread, i)) != NULL) { + //thread_cancel (vty_serv_thread); + vector_slot(Vvty_serv_thread, i) = NULL; + close(i); + } +} + +static void vty_save_cwd(void) +{ + char cwd[MAXPATHLEN]; + char *c ; + + c = getcwd(cwd, MAXPATHLEN); + + if (!c) { + if (chdir(SYSCONFDIR) != 0) + perror("chdir failed"); + if (getcwd(cwd, MAXPATHLEN) == NULL) + perror("getcwd failed"); + } + + vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd"); + strcpy(vty_cwd, cwd); +} + +char *vty_get_cwd() +{ + return vty_cwd; +} + +int vty_shell_serv(struct vty *vty) +{ + return vty->type == VTY_SHELL_SERV ? 1 : 0; +} + +void vty_init_vtysh() +{ + vtyvec = vector_init(VECTOR_MIN_SIZE); +} + +extern void *tall_bsc_ctx; +/* Install vty's own commands like `who' command. */ +void vty_init(struct vty_app_info *app_info) +{ + tall_vty_ctx = talloc_named_const(app_info->tall_ctx, 0, "vty"); + tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); + tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); + + cmd_init(1); + + host.app_info = app_info; + + /* For further configuration read, preserve current directory. */ + vty_save_cwd(); + + vtyvec = vector_init(VECTOR_MIN_SIZE); + + /* Install bgp top node. */ + install_node(&vty_node, vty_config_write); + + install_element_ve(&config_who_cmd); + install_element_ve(&show_history_cmd); + install_element(CONFIG_NODE, &line_vty_cmd); + install_element(CONFIG_NODE, &service_advanced_vty_cmd); + install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); + install_element(CONFIG_NODE, &show_history_cmd); + install_element(ENABLE_NODE, &terminal_monitor_cmd); + install_element(ENABLE_NODE, &terminal_no_monitor_cmd); + + install_default(VTY_NODE); + install_element(VTY_NODE, &vty_login_cmd); + install_element(VTY_NODE, &no_vty_login_cmd); +} + +int vty_read_config_file(const char *file_name, void *priv) +{ + FILE *cfile; + int rc; + + cfile = fopen(file_name, "r"); + if (!cfile) + return -ENOENT; + + rc = vty_read_file(cfile, priv); + fclose(cfile); + + host_config_set(file_name); + + return rc; +} diff --git a/src/shared/libosmocore/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c new file mode 100644 index 00000000..a5921042 --- /dev/null +++ b/src/shared/libosmocore/src/write_queue.c @@ -0,0 +1,90 @@ +/* Generic write queue implementation */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/core/write_queue.h> + +int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what) +{ + struct osmo_wqueue *queue; + + queue = container_of(fd, struct osmo_wqueue, bfd); + + if (what & BSC_FD_READ) + queue->read_cb(fd); + + if (what & BSC_FD_EXCEPT) + queue->except_cb(fd); + + if (what & BSC_FD_WRITE) { + struct msgb *msg; + + fd->when &= ~BSC_FD_WRITE; + + /* the queue might have been emptied */ + if (!llist_empty(&queue->msg_queue)) { + --queue->current_length; + + msg = msgb_dequeue(&queue->msg_queue); + queue->write_cb(fd, msg); + msgb_free(msg); + + if (!llist_empty(&queue->msg_queue)) + fd->when |= BSC_FD_WRITE; + } + } + + return 0; +} + +void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length) +{ + queue->max_length = max_length; + queue->current_length = 0; + queue->read_cb = NULL; + queue->write_cb = NULL; + queue->bfd.cb = osmo_wqueue_bfd_cb; + INIT_LLIST_HEAD(&queue->msg_queue); +} + +int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data) +{ +// if (queue->current_length + 1 >= queue->max_length) +// LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n"); + + ++queue->current_length; + msgb_enqueue(&queue->msg_queue, data); + queue->bfd.when |= BSC_FD_WRITE; + + return 0; +} + +void osmo_wqueue_clear(struct osmo_wqueue *queue) +{ + while (!llist_empty(&queue->msg_queue)) { + struct msgb *msg = msgb_dequeue(&queue->msg_queue); + msgb_free(msg); + } + + queue->current_length = 0; + queue->bfd.when &= ~BSC_FD_WRITE; +} diff --git a/src/shared/libosmocore/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am new file mode 100644 index 00000000..2b4ac6e6 --- /dev/null +++ b/src/shared/libosmocore/tests/Makefile.am @@ -0,0 +1,6 @@ +if ENABLE_TESTS +SUBDIRS = timer sms ussd smscb +if ENABLE_MSGFILE +SUBDIRS += msgfile +endif +endif diff --git a/src/shared/libosmocore/tests/msgfile/Makefile.am b/src/shared/libosmocore/tests/msgfile/Makefile.am new file mode 100644 index 00000000..c9f4bec2 --- /dev/null +++ b/src/shared/libosmocore/tests/msgfile/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = msgfile_test + +msgfile_test_SOURCES = msgfile_test.c +msgfile_test_LDADD = $(top_builddir)/src/libosmocore.la diff --git a/src/shared/libosmocore/tests/msgfile/msgconfig.cfg b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg new file mode 100644 index 00000000..28d74326 --- /dev/null +++ b/src/shared/libosmocore/tests/msgfile/msgconfig.cfg @@ -0,0 +1,2 @@ +# This is a comment +*:*::Hello Welt diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c new file mode 100644 index 00000000..ed7aa978 --- /dev/null +++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c @@ -0,0 +1,50 @@ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/core/msgfile.h> + +#include <stdio.h> + +static void dump_entries(struct osmo_config_list *entries) +{ + struct osmo_config_entry *entry; + + if (!entries) { + fprintf(stderr, "Failed to parse the file\n"); + return; + } + + llist_for_each_entry(entry, &entries->entry, list) { + printf("Entry '%s:%s:%s:%s'\n", + entry->mcc, entry->mnc, entry->option, entry->text); + } +} + +int main(int argc, char **argv) +{ + struct osmo_config_list *entries; + + /* todo use msgfile_test.c.in and replace the path */ + entries = osmo_config_list_parse(NULL, "msgconfig.cfg"); + dump_entries(entries); + + return 0; +} diff --git a/src/shared/libosmocore/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am new file mode 100644 index 00000000..fa4e387f --- /dev/null +++ b/src/shared/libosmocore/tests/sms/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = sms_test + +sms_test_SOURCES = sms_test.c +sms_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c new file mode 100644 index 00000000..b4ed631f --- /dev/null +++ b/src/shared/libosmocore/tests/sms/sms_test.c @@ -0,0 +1,121 @@ +/* + * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> + * (C) 2010 by Nico Golde <nico@ngolde.de> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <osmocom/core/msgb.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/core/utils.h> + +struct test_case { + const uint8_t *input; + const uint16_t input_length; + + const uint8_t *expected; + const uint16_t expected_length; +}; + +static const char simple_text[] = "test text"; +static const uint8_t simple_enc[] = { + 0xf4, 0xf2, 0x9c, 0x0e, 0xa2, 0x97, 0xf1, 0x74 +}; + +static const char escape_text[] = "!$ a more#^- complicated test@@?_\%! case"; +static const uint8_t escape_enc[] = { + 0x21, 0x01, 0x28, 0x0c, 0x6a, 0xbf, 0xe5, 0xe5, 0xd1, + 0x86, 0xd2, 0x02, 0x8d, 0xdf, 0x6d, 0x38, 0x3b, 0x3d, + 0x0e, 0xd3, 0xcb, 0x64, 0x10, 0xbd, 0x3c, 0xa7, 0x03, + 0x00, 0xbf, 0x48, 0x29, 0x04, 0x1a, 0x87, 0xe7, 0x65, +}; + +static const struct test_case test_encode[] = +{ + { + .input = simple_text, + .expected = simple_enc, + .expected_length = sizeof(simple_enc), + }, + { + .input = escape_text, + .expected = escape_enc, + .expected_length = sizeof(escape_enc), + }, +}; + +static const struct test_case test_decode[] = +{ + { + .input = simple_enc, + .input_length = sizeof(simple_enc), + .expected = simple_text, + }, + { + .input = escape_enc, + .input_length = sizeof(escape_enc), + .expected = escape_text, + }, +}; + +int main(int argc, char** argv) +{ + printf("SMS testing\n"); + struct msgb *msg; + uint8_t *sms; + uint8_t i; + + uint8_t length; + uint8_t coded[256]; + char result[256]; + + /* test 7-bit encoding */ + for (i = 0; i < ARRAY_SIZE(test_encode); ++i) { + memset(coded, 0x42, sizeof(coded)); + length = gsm_7bit_encode(coded, test_encode[i].input); + + if (length != test_encode[i].expected_length) { + fprintf(stderr, "Failed to encode case %d. Got %d, expected %d\n", + i, length, test_encode[i].expected_length); + return -1; + } + + if (memcmp(coded, test_encode[i].expected, length) != 0) { + fprintf(stderr, "Encoded content does not match for %d\n", + i); + return -1; + } + } + + /* test 7-bit decoding */ + for (i = 0; i < ARRAY_SIZE(test_decode); ++i) { + memset(result, 0x42, sizeof(coded)); + gsm_7bit_decode(result, test_decode[i].input, + test_decode[i].input_length); + + if (strcmp(result, test_decode[i].expected) != 0) { + fprintf(stderr, "Test case %d failed to decode.\n", i); + return -1; + } + } + + printf("OK\n"); + return 0; +} diff --git a/src/shared/libosmocore/tests/smscb/Makefile.am b/src/shared/libosmocore/tests/smscb/Makefile.am new file mode 100644 index 00000000..9a6fb4fd --- /dev/null +++ b/src/shared/libosmocore/tests/smscb/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = smscb_test + +smscb_test_SOURCES = smscb_test.c +smscb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c new file mode 100644 index 00000000..e10e12d8 --- /dev/null +++ b/src/shared/libosmocore/tests/smscb/smscb_test.c @@ -0,0 +1,41 @@ +/* + * (C) 2010 Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/gsm/protocol/gsm_03_41.h> + +#include <stdio.h> + +static uint8_t smscb_msg[] = { 0x40, 0x10, 0x05, 0x0d, 0x01, 0x11 }; + +int main(int argc, char **argv) +{ + struct gsm341_ms_message *msg; + + msg = (struct gsm341_ms_message *) smscb_msg; + printf("(srl) GS: %d MSG_CODE: %d UPDATE: %d\n", + msg->serial.gs, GSM341_MSG_CODE(msg), msg->serial.update); + printf("(msg) msg_id: %d\n", htons(msg->msg_id)); + printf("(dcs) group: %d language: %d\n", + msg->dcs.language, msg->dcs.group); + printf("(pge) page total: %d current: %d\n", + msg->page.total, msg->page.current); + + return 0; +} diff --git a/src/shared/libosmocore/tests/timer/Makefile.am b/src/shared/libosmocore/tests/timer/Makefile.am new file mode 100644 index 00000000..d3decf55 --- /dev/null +++ b/src/shared/libosmocore/tests/timer/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = timer_test + +timer_test_SOURCES = timer_test.c +timer_test_LDADD = $(top_builddir)/src/libosmocore.la + diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c new file mode 100644 index 00000000..240bc480 --- /dev/null +++ b/src/shared/libosmocore/tests/timer/timer_test.c @@ -0,0 +1,77 @@ +/* + * (C) 2008 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 General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> + +#include <osmocom/core/timer.h> +#include <osmocom/core/select.h> + +#include "../../config.h" + +static void timer_fired(void *data); + +static struct osmo_timer_list timer_one = { + .cb = timer_fired, + .data = (void*)1, +}; + +static struct osmo_timer_list timer_two = { + .cb = timer_fired, + .data = (void*)2, +}; + +static struct osmo_timer_list timer_three = { + .cb = timer_fired, + .data = (void*)3, +}; + +static void timer_fired(void *_data) +{ + unsigned long data = (unsigned long) _data; + printf("Fired timer: %lu\n", data); + + if (data == 1) { + osmo_timer_schedule(&timer_one, 3, 0); + osmo_timer_del(&timer_two); + } else if (data == 2) { + printf("Should not be fired... bug in del_timer\n"); + } else if (data == 3) { + printf("Timer fired not registering again\n"); + } else { + printf("wtf... wrong data\n"); + } +} + +int main(int argc, char** argv) +{ + printf("Starting... timer\n"); + + osmo_timer_schedule(&timer_one, 3, 0); + osmo_timer_schedule(&timer_two, 5, 0); + osmo_timer_schedule(&timer_three, 4, 0); + +#ifdef HAVE_SYS_SELECT_H + while (1) { + osmo_select_main(0); + } +#else + printf("Select not supported on this platform!\n"); +#endif +} diff --git a/src/shared/libosmocore/tests/ussd/Makefile.am b/src/shared/libosmocore/tests/ussd/Makefile.am new file mode 100644 index 00000000..ef9aa492 --- /dev/null +++ b/src/shared/libosmocore/tests/ussd/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = ussd_test + +ussd_test_SOURCES = ussd_test.c +ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c new file mode 100644 index 00000000..6d2a8c9b --- /dev/null +++ b/src/shared/libosmocore/tests/ussd/ussd_test.c @@ -0,0 +1,91 @@ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/gsm/gsm0480.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static const uint8_t ussd_request[] = { + 0x0b, 0x7b, 0x1c, 0x15, 0xa1, 0x13, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x3b, 0x30, 0x0b, 0x04, 0x01, + 0x0f, 0x04, 0x06, 0x2a, 0xd5, 0x4c, 0x16, 0x1b, + 0x01, 0x7f, 0x01, 0x00 +}; + +static int parse_ussd(const uint8_t *_data, int len) +{ + uint8_t *data; + int rc; + struct ussd_request req; + struct gsm48_hdr *hdr; + + data = malloc(len); + memcpy(data, _data, len); + hdr = (struct gsm48_hdr *) &data[0]; + rc = gsm0480_decode_ussd_request(hdr, len, &req); + free(data); + + return rc; +} + +static int parse_mangle_ussd(const uint8_t *_data, int len) +{ + uint8_t *data; + int rc; + struct ussd_request req; + struct gsm48_hdr *hdr; + + data = malloc(len); + memcpy(data, _data, len); + hdr = (struct gsm48_hdr *) &data[0]; + hdr->data[1] = len - sizeof(*hdr) - 2; + rc = gsm0480_decode_ussd_request(hdr, len, &req); + free(data); + + return rc; +} + +int main(int argc, char **argv) +{ + struct ussd_request req; + const int size = sizeof(ussd_request); + int i; + + gsm0480_decode_ussd_request((struct gsm48_hdr *) ussd_request, size, &req); + printf("Tested if it still works. Text was: %s\n", req.text); + + + printf("Testing parsing a USSD request and truncated versions\n"); + + for (i = size; i > sizeof(struct gsm48_hdr); --i) { + int rc = parse_ussd(&ussd_request[0], i); + printf("Result for %d is %d\n", rc, i); + } + + printf("Mangling the container now\n"); + for (i = size; i > sizeof(struct gsm48_hdr) + 2; --i) { + int rc = parse_mangle_ussd(&ussd_request[0], i); + printf("Result for %d is %d\n", rc, i); + } + + return 0; +} diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh new file mode 100755 index 00000000..e7940c3d --- /dev/null +++ b/src/shared/update-libosmocore.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +(cd ../.. && git-subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master) |