summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-07-12 09:21:13 +0200
committerHarald Welte <laforge@gnumonks.org>2010-07-12 09:21:13 +0200
commit298b361b133a54f3b7c39d168e5028e69e736113 (patch)
treee09b86a6c6463bc1cbc032afd4bcf2eea02207ab /src/shared
parent231444b548df8aff616f314ebc6135588b0ffec3 (diff)
parented00fe4449ac2309ad80c32f0ba7aa80b2b8895a (diff)
Add 'src/shared/libosmocore/' from commit 'ed00fe4449ac2309ad80c32f0ba7aa80b2b8895a'
git-subtree-dir: src/shared/libosmocore git-subtree-mainline: 231444b548df8aff616f314ebc6135588b0ffec3 git-subtree-split: ed00fe4449ac2309ad80c32f0ba7aa80b2b8895a
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/libosmocore/.gitignore30
-rw-r--r--src/shared/libosmocore/COPYING339
-rw-r--r--src/shared/libosmocore/Makefile.am14
-rw-r--r--src/shared/libosmocore/configure.in72
-rwxr-xr-xsrc/shared/libosmocore/git-version-gen151
-rw-r--r--src/shared/libosmocore/include/Makefile.am1
-rw-r--r--src/shared/libosmocore/include/osmocom/Makefile.am5
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h54
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/Makefile.am4
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/buffer.h102
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/command.h349
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/logging.h7
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/telnet_interface.h40
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vector.h64
-rw-r--r--src/shared/libosmocore/include/osmocom/vty/vty.h159
-rw-r--r--src/shared/libosmocore/include/osmocore/Makefile.am13
-rw-r--r--src/shared/libosmocore/include/osmocore/bitvec.h75
-rw-r--r--src/shared/libosmocore/include/osmocore/comp128.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm0808.h43
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm48.h36
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm48_ie.h107
-rw-r--r--src/shared/libosmocore/include/osmocore/gsm_utils.h117
-rw-r--r--src/shared/libosmocore/include/osmocore/gsmtap.h72
-rw-r--r--src/shared/libosmocore/include/osmocore/gsmtap_util.h21
-rw-r--r--src/shared/libosmocore/include/osmocore/linuxlist.h360
-rw-r--r--src/shared/libosmocore/include/osmocore/logging.h135
-rw-r--r--src/shared/libosmocore/include/osmocore/mncc.h71
-rw-r--r--src/shared/libosmocore/include/osmocore/msgb.h165
-rw-r--r--src/shared/libosmocore/include/osmocore/plugin.h6
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/Makefile.am3
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h1243
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h188
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h126
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h303
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h516
-rw-r--r--src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h713
-rw-r--r--src/shared/libosmocore/include/osmocore/rate_ctr.h81
-rw-r--r--src/shared/libosmocore/include/osmocore/rsl.h32
-rw-r--r--src/shared/libosmocore/include/osmocore/rxlev_stat.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/select.h22
-rw-r--r--src/shared/libosmocore/include/osmocore/signal.h15
-rw-r--r--src/shared/libosmocore/include/osmocore/statistics.h31
-rw-r--r--src/shared/libosmocore/include/osmocore/talloc.h192
-rw-r--r--src/shared/libosmocore/include/osmocore/timer.h72
-rw-r--r--src/shared/libosmocore/include/osmocore/tlv.h245
-rw-r--r--src/shared/libosmocore/include/osmocore/utils.h20
-rw-r--r--src/shared/libosmocore/include/osmocore/write_queue.h46
-rw-r--r--src/shared/libosmocore/libosmocore.pc.in11
-rw-r--r--src/shared/libosmocore/libosmovty.pc.in11
-rw-r--r--src/shared/libosmocore/m4/DUMMY1
-rw-r--r--src/shared/libosmocore/src/Makefile.am25
-rw-r--r--src/shared/libosmocore/src/bitvec.c219
-rw-r--r--src/shared/libosmocore/src/comp128.c230
-rw-r--r--src/shared/libosmocore/src/gprs_cipher_core.c99
-rw-r--r--src/shared/libosmocore/src/gsm0808.c306
-rw-r--r--src/shared/libosmocore/src/gsm48.c415
-rw-r--r--src/shared/libosmocore/src/gsm48_ie.c659
-rw-r--r--src/shared/libosmocore/src/gsm_utils.c393
-rw-r--r--src/shared/libosmocore/src/gsmtap_util.c200
-rw-r--r--src/shared/libosmocore/src/logging.c419
-rw-r--r--src/shared/libosmocore/src/msgb.c90
-rw-r--r--src/shared/libosmocore/src/plugin.c62
-rw-r--r--src/shared/libosmocore/src/rate_ctr.c128
-rw-r--r--src/shared/libosmocore/src/rsl.c329
-rw-r--r--src/shared/libosmocore/src/rxlev_stat.c94
-rw-r--r--src/shared/libosmocore/src/select.c131
-rw-r--r--src/shared/libosmocore/src/signal.c84
-rw-r--r--src/shared/libosmocore/src/statistics.c66
-rw-r--r--src/shared/libosmocore/src/talloc.c1805
-rw-r--r--src/shared/libosmocore/src/timer.c185
-rw-r--r--src/shared/libosmocore/src/tlv_parser.c179
-rw-r--r--src/shared/libosmocore/src/utils.c50
-rw-r--r--src/shared/libosmocore/src/vty/Makefile.am13
-rw-r--r--src/shared/libosmocore/src/vty/buffer.c463
-rw-r--r--src/shared/libosmocore/src/vty/command.c3177
-rw-r--r--src/shared/libosmocore/src/vty/logging_vty.c347
-rw-r--r--src/shared/libosmocore/src/vty/telnet_interface.c203
-rw-r--r--src/shared/libosmocore/src/vty/utils.c50
-rw-r--r--src/shared/libosmocore/src/vty/vector.c192
-rw-r--r--src/shared/libosmocore/src/vty/vty.c1683
-rw-r--r--src/shared/libosmocore/src/write_queue.c88
-rw-r--r--src/shared/libosmocore/tests/Makefile.am3
-rw-r--r--src/shared/libosmocore/tests/sms/Makefile.am5
-rw-r--r--src/shared/libosmocore/tests/sms/sms_test.c47
-rw-r--r--src/shared/libosmocore/tests/timer/Makefile.am6
-rw-r--r--src/shared/libosmocore/tests/timer/timer_test.c77
87 files changed, 19052 insertions, 0 deletions
diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore
new file mode 100644
index 00000000..06904fa0
--- /dev/null
+++ b/src/shared/libosmocore/.gitignore
@@ -0,0 +1,30 @@
+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
+
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..81da6294
--- /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 libosmovty.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.in b/src/shared/libosmocore/configure.in
new file mode 100644
index 00000000..e3e178c0
--- /dev/null
+++ b/src/shared/libosmocore/configure.in
@@ -0,0 +1,72 @@
+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)
+
+# 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([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,
+ [ --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,
+ [ --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,
+ [ --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,
+ [ --disable-vty Disable building VTY telnet interface ],
+ [enable_vty=0], [enable_vty=1])
+AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1")
+
+
+AC_OUTPUT(
+ libosmocore.pc
+ libosmovty.pc
+ include/osmocom/Makefile
+ include/osmocom/vty/Makefile
+ include/osmocom/crypt/Makefile
+ include/osmocore/Makefile
+ include/osmocore/protocol/Makefile
+ include/Makefile
+ src/Makefile
+ src/vty/Makefile
+ tests/Makefile
+ tests/timer/Makefile
+ tests/sms/Makefile
+ Makefile)
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..185c6968
--- /dev/null
+++ b/src/shared/libosmocore/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = osmocom osmocore
diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am
new file mode 100644
index 00000000..fd9074cd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/Makefile.am
@@ -0,0 +1,5 @@
+if ENABLE_VTY
+SUBDIRS = vty crypt
+else
+SUBDIRS = crypt
+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..3e514ec7
--- /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 <osmocore/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/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am
new file mode 100644
index 00000000..d2f0616d
--- /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
+
+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..69e9e772
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/command.h
@@ -0,0 +1,349 @@
+/*
+ * 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. */
+
+ 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..f8ffbc3e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/logging.h
@@ -0,0 +1,7 @@
+#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"
+
+#endif /* _VTY_LOGGING_H */
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..444e6497
--- /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 <osmocore/logging.h>
+#include <osmocore/select.h>
+
+#include <osmocom/vty/vty.h>
+
+struct telnet_connection {
+ struct llist_head entry;
+ void *priv;
+ struct bsc_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..e7399ba1
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocom/vty/vty.h
@@ -0,0 +1,159 @@
+#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);
+};
+
+/* 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 *tall_vty_ctx;
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/Makefile.am b/src/shared/libosmocore/include/osmocore/Makefile.am
new file mode 100644
index 00000000..84859a4b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/Makefile.am
@@ -0,0 +1,13 @@
+osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
+ tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
+ gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
+ gsm48_ie.h logging.h gsm0808.h rate_ctr.h gsmtap_util.h \
+ plugin.h
+
+if ENABLE_TALLOC
+osmocore_HEADERS += talloc.h
+endif
+
+osmocoredir = $(includedir)/osmocore
+
+SUBDIRS = protocol
diff --git a/src/shared/libosmocore/include/osmocore/bitvec.h b/src/shared/libosmocore/include/osmocore/bitvec.h
new file mode 100644
index 00000000..42977fb2
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/bitvec.h
@@ -0,0 +1,75 @@
+#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);
+
+
+/* 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/osmocore/comp128.h b/src/shared/libosmocore/include/osmocore/comp128.h
new file mode 100644
index 00000000..c37808f0
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/gsm0808.h b/src/shared/libosmocore/include/osmocore/gsm0808.h
new file mode 100644
index 00000000..9166e54f
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm0808.h
@@ -0,0 +1,43 @@
+/* (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, int ci);
+struct msgb *gsm0808_create_reset(void);
+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(struct gsm_lchan *lchan, 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);
+
+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/osmocore/gsm48.h b/src/shared/libosmocore/include/osmocore/gsm48.h
new file mode 100644
index 00000000..ffe0399b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm48.h
@@ -0,0 +1,36 @@
+#ifndef _OSMOCORE_GSM48_H
+#define _OSMOCORE_GSM48_H
+
+#include <osmocore/tlv.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/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/osmocore/gsm48_ie.h b/src/shared/libosmocore/include/osmocore/gsm48_ie.h
new file mode 100644
index 00000000..200619a7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm48_ie.h
@@ -0,0 +1,107 @@
+#ifndef _OSMOCORE_GSM48_IE_H
+#define _OSMOCORE_GSM48_IE_H
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/mncc.h>
+#include <osmocore/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);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsm_utils.h b/src/shared/libosmocore/include/osmocore/gsm_utils.h
new file mode 100644
index 00000000..7dc2388b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsm_utils.h
@@ -0,0 +1,117 @@
+/* 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
+
+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);
+
+void generate_backtrace();
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/gsmtap.h b/src/shared/libosmocore/include/osmocore/gsmtap.h
new file mode 100644
index 00000000..dcd64bdf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsmtap.h
@@ -0,0 +1,72 @@
+#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_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
+
+#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
+
+#define GSMTAP_ARFCN_F_PCS 0x8000
+#define GSMTAP_ARFCN_F_UPLINK 0x4000
+#define GSMTAP_ARFCN_MASK 0x3fff
+
+#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/osmocore/gsmtap_util.h b/src/shared/libosmocore/include/osmocore/gsmtap_util.h
new file mode 100644
index 00000000..96449443
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/gsmtap_util.h
@@ -0,0 +1,21 @@
+#ifndef _GSMTAP_UTIL_H
+#define _GSMTAP_UTIL_H
+
+#include <stdint.h>
+
+/* convert RSL channel number to GSMTAP channel type */
+uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id);
+
+/* 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);
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(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);
+
+int gsmtap_init(uint32_t dst_ip);
+
+#endif /* _GSMTAP_UTIL_H */
diff --git a/src/shared/libosmocore/include/osmocore/linuxlist.h b/src/shared/libosmocore/include/osmocore/linuxlist.h
new file mode 100644
index 00000000..fb99c5ec
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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(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/osmocore/logging.h b/src/shared/libosmocore/include/osmocore/logging.h
new file mode 100644
index 00000000..2e82959a
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/logging.h
@@ -0,0 +1,135 @@
+#ifndef _OSMOCORE_LOGGING_H
+#define _OSMOCORE_LOGGING_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocore/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
+
+#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+char *hexdump(const unsigned char *buf, int len);
+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;
+};
+
+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;
+
+ union {
+ struct {
+ FILE *out;
+ } tgt_stdout;
+
+ struct {
+ int priority;
+ } tgt_syslog;
+
+ struct {
+ void *vty;
+ } tgt_vty;
+ };
+
+ void (*output) (struct log_target *target, 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);
+struct log_target *log_target_create_stderr(void);
+void log_add_target(struct log_target *target);
+void log_del_target(struct log_target *target);
+
+/* Gernerate command argument strings for VTY use */
+const char *log_vty_category_string(struct log_info *info);
+const char *log_vty_level_string(struct log_info *info);
+
+#endif /* _OSMOCORE_LOGGING_H */
diff --git a/src/shared/libosmocore/include/osmocore/mncc.h b/src/shared/libosmocore/include/osmocore/mncc.h
new file mode 100644
index 00000000..a094bb9b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/msgb.h b/src/shared/libosmocore/include/osmocore/msgb.h
new file mode 100644
index 00000000..2841dc56
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/msgb.h
@@ -0,0 +1,165 @@
+#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 "linuxlist.h"
+
+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);
+
+#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 unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ 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)
+{
+ 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;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->head + msgb->data_len) - msgb->tail;
+}
+
+/* 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)
+{
+ struct msgb *msg = msgb_alloc(size, name);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/src/shared/libosmocore/include/osmocore/plugin.h b/src/shared/libosmocore/include/osmocore/plugin.h
new file mode 100644
index 00000000..98f9b56d
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/plugin.h
@@ -0,0 +1,6 @@
+#ifndef _OSMO_PLUGIN_H
+#define _OSMO_PLUGIN_H
+
+int plugin_load_all(const char *directory);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/protocol/Makefile.am b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am
new file mode 100644
index 00000000..557950ec
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am
@@ -0,0 +1,3 @@
+osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h
+
+osmocore_protodir = $(includedir)/osmocore/protocol
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
new file mode 100644
index 00000000..80a455dd
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -0,0 +1,1243 @@
+#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 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.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,
+ spare:3,
+ cr:1;
+} __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));
+
+
+
+#endif /* PROTO_GSM_04_08_H */
diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h
new file mode 100644
index 00000000..c6a2b193
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h
new file mode 100644
index 00000000..fa5c9451
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h
new file mode 100644
index 00000000..6b8f9359
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h
new file mode 100644
index 00000000..7dc35693
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h
@@ -0,0 +1,516 @@
+#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 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,
+ /* 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 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/osmocore/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h
new file mode 100644
index 00000000..9cae45da
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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 <osmocore/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/osmocore/rate_ctr.h b/src/shared/libosmocore/include/osmocore/rate_ctr.h
new file mode 100644
index 00000000..f887d9a7
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/rate_ctr.h
@@ -0,0 +1,81 @@
+#ifndef _RATE_CTR_H
+#define _RATE_CTR_H
+
+#include <stdint.h>
+
+#include <osmocore/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 vty;
+void vty_out_rate_ctr_group(struct vty *vty, const char *prefix,
+ struct rate_ctr_group *ctrg);
+#endif /* RATE_CTR_H */
diff --git a/src/shared/libosmocore/include/osmocore/rsl.h b/src/shared/libosmocore/include/osmocore/rsl.h
new file mode 100644
index 00000000..99b90d68
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/rsl.h
@@ -0,0 +1,32 @@
+#ifndef _OSMOCORE_RSL_H
+#define _OSMOCORE_RSL_H
+
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/protocol/gsm_08_58.h>
+
+void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, 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);
+
+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 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/osmocore/rxlev_stat.h b/src/shared/libosmocore/include/osmocore/rxlev_stat.h
new file mode 100644
index 00000000..415509dc
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/select.h b/src/shared/libosmocore/include/osmocore/select.h
new file mode 100644
index 00000000..2d8b3ec0
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/select.h
@@ -0,0 +1,22 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include "linuxlist.h"
+
+#define BSC_FD_READ 0x0001
+#define BSC_FD_WRITE 0x0002
+#define BSC_FD_EXCEPT 0x0004
+
+struct bsc_fd {
+ struct llist_head list;
+ int fd;
+ unsigned int when;
+ int (*cb)(struct bsc_fd *fd, unsigned int what);
+ void *data;
+ unsigned int priv_nr;
+};
+
+int bsc_register_fd(struct bsc_fd *fd);
+void bsc_unregister_fd(struct bsc_fd *fd);
+int bsc_select_main(int polling);
+#endif /* _BSC_SELECT_H */
diff --git a/src/shared/libosmocore/include/osmocore/signal.h b/src/shared/libosmocore/include/osmocore/signal.h
new file mode 100644
index 00000000..02d83d2e
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/signal.h
@@ -0,0 +1,15 @@
+#ifndef OSMOCORE_SIGNAL_H
+#define OSMOCORE_SIGNAL_H
+
+typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+
+
+/* Management */
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
+
+#endif /* OSMOCORE_SIGNAL_H */
diff --git a/src/shared/libosmocore/include/osmocore/statistics.h b/src/shared/libosmocore/include/osmocore/statistics.h
new file mode 100644
index 00000000..1d56054a
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/statistics.h
@@ -0,0 +1,31 @@
+#ifndef _STATISTICS_H
+#define _STATISTICS_H
+
+struct counter {
+ struct llist_head list;
+ const char *name;
+ const char *description;
+ unsigned long value;
+};
+
+static inline void counter_inc(struct counter *ctr)
+{
+ ctr->value++;
+}
+
+static inline unsigned long counter_get(struct counter *ctr)
+{
+ return ctr->value;
+}
+
+static inline void counter_reset(struct counter *ctr)
+{
+ ctr->value = 0;
+}
+
+struct counter *counter_alloc(const char *name);
+void counter_free(struct counter *ctr);
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
+
+#endif /* _STATISTICS_H */
diff --git a/src/shared/libosmocore/include/osmocore/talloc.h b/src/shared/libosmocore/include/osmocore/talloc.h
new file mode 100644
index 00000000..f7f7643b
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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/osmocore/timer.h b/src/shared/libosmocore/include/osmocore/timer.h
new file mode 100644
index 00000000..fee888bf
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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 "linuxlist.h"
+
+/**
+ * Timer management:
+ * - Create a struct 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 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 bsc_add_timer(struct timer_list *timer);
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
+void bsc_del_timer(struct timer_list *timer);
+int bsc_timer_pending(struct timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
+struct timeval *bsc_nearest_timer();
+void bsc_prepare_timers();
+int bsc_update_timers();
+int bsc_timer_check(void);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/tlv.h b/src/shared/libosmocore/include/osmocore/tlv.h
new file mode 100644
index 00000000..4cfce872
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/tlv.h
@@ -0,0 +1,245 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocore/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;
+}
+
+/* '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_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/osmocore/utils.h b/src/shared/libosmocore/include/osmocore/utils.h
new file mode 100644
index 00000000..51c6f035
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/utils.h
@@ -0,0 +1,20 @@
+#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 bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
+uint8_t char2bcd(char c);
+
+#endif
diff --git a/src/shared/libosmocore/include/osmocore/write_queue.h b/src/shared/libosmocore/include/osmocore/write_queue.h
new file mode 100644
index 00000000..ef244c32
--- /dev/null
+++ b/src/shared/libosmocore/include/osmocore/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 write_queue_h
+#define write_queue_h
+
+#include "select.h"
+#include "msgb.h"
+
+struct write_queue {
+ struct bsc_fd bfd;
+ unsigned int max_length;
+ unsigned int current_length;
+
+ struct llist_head msg_queue;
+
+ int (*read_cb)(struct bsc_fd *fd);
+ int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
+ int (*except_cb)(struct bsc_fd *fd);
+};
+
+void write_queue_init(struct write_queue *queue, int max_length);
+void write_queue_clear(struct write_queue *queue);
+int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
+int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
+
+#endif
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/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..a7d450b1
--- /dev/null
+++ b/src/shared/libosmocore/src/Makefile.am
@@ -0,0 +1,25 @@
+SUBDIRS=vty
+
+# 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 = libosmocore.la
+
+libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
+ tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
+ write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
+ logging.c gsm0808.c rate_ctr.c gsmtap_util.c \
+ gprs_cipher_core.c
+
+if ENABLE_PLUGIN
+libosmocore_la_SOURCES += plugin.c
+libosmocore_la_LDFLAGS = -ldl
+endif
+
+if ENABLE_TALLOC
+libosmocore_la_SOURCES += talloc.c
+endif
diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c
new file mode 100644
index 00000000..04c465a8
--- /dev/null
+++ b/src/shared/libosmocore/src/bitvec.c
@@ -0,0 +1,219 @@
+/* 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 <osmocore/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] & 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;
+}
diff --git a/src/shared/libosmocore/src/comp128.c b/src/shared/libosmocore/src/comp128.c
new file mode 100644
index 00000000..5d5680c7
--- /dev/null
+++ b/src/shared/libosmocore/src/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/gprs_cipher_core.c b/src/shared/libosmocore/src/gprs_cipher_core.c
new file mode 100644
index 00000000..6174bd72
--- /dev/null
+++ b/src/shared/libosmocore/src/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 <osmocore/utils.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/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 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/gsm0808.c b/src/shared/libosmocore/src/gsm0808.c
new file mode 100644
index 00000000..1dc035b3
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm0808.c
@@ -0,0 +1,306 @@
+/* (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 <osmocore/gsm0808.h>
+#include <osmocore/protocol/gsm_08_08.h>
+#include <osmocore/gsm48.h>
+
+#include <arpa/inet.h>
+
+#define BSSMAP_MSG_SIZE 512
+#define BSSMAP_MSG_HEADROOM 128
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, int _ci)
+{
+ uint8_t *data;
+ uint16_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 = (uint16_t *) msgb_put(msg, 2);
+ *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_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(struct gsm_lchan *lchan, 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;
+}
+
+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;
+}
+
+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_TV },
+ [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_TV},
+ [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 },
+ },
+};
+
+const struct tlv_definition *gsm0808_att_tlvdef()
+{
+ return &bss_att_tlvdef;
+}
diff --git a/src/shared/libosmocore/src/gsm48.c b/src/shared/libosmocore/src/gsm48.c
new file mode 100644
index 00000000..daec4f39
--- /dev/null
+++ b/src/shared/libosmocore/src/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 <osmocore/utils.h>
+#include <osmocore/tlv.h>
+#include <osmocore/gsm48.h>
+
+#include <osmocore/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] = 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 = char2bcd(imsi[++off]);
+ if (!odd && off + 1 == length)
+ upper = 0x0f;
+ else
+ upper = 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++ = bcd2char(mi[0] >> 4);
+
+ for (i = 1; i < mi_len; i++) {
+ if (str_cur + 2 >= string + str_len)
+ return str_cur - string;
+ *str_cur++ = 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++ = 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/gsm48_ie.c b/src/shared/libosmocore/src/gsm48_ie.c
new file mode 100644
index 00000000..3c2a1f7b
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm48_ie.c
@@ -0,0 +1,659 @@
+/* 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 <osmocore/utils.h>
+#include <osmocore/msgb.h>
+#include <osmocore/tlv.h>
+#include <osmocore/mncc.h>
+#include <osmocore/protocol/gsm_04_08.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] = 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;
+}
+
diff --git a/src/shared/libosmocore/src/gsm_utils.c b/src/shared/libosmocore/src/gsm_utils.c
new file mode 100644
index 00000000..dc97ceff
--- /dev/null
+++ b/src/shared/libosmocore/src/gsm_utils.c
@@ -0,0 +1,393 @@
+/*
+ * (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.
+ *
+ */
+
+//#include <openbsc/gsm_data.h>
+#include <osmocore/utils.h>
+#include <osmocore/gsm_utils.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../config.h"
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t length)
+{
+ int i = 0;
+ int l = 0;
+
+ /* FIXME: We need to account for user data headers here */
+ i += l;
+ for (; i < length; i ++)
+ *(text ++) =
+ ((user_data[(i * 7 + 7) >> 3] <<
+ (7 - ((i * 7 + 7) & 7))) |
+ (user_data[(i * 7) >> 3] >>
+ ((i * 7) & 7))) & 0x7f;
+ *text = '\0';
+
+ return i - l;
+}
+
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_encode(uint8_t *result, const char *data)
+{
+ int i,j = 0;
+ unsigned char ch1, ch2;
+ int shift = 0;
+
+ for ( i=0; i<strlen(data); i++ ) {
+
+ ch1 = data[i] & 0x7F;
+ ch1 = ch1 >> shift;
+ ch2 = data[(i+1)] & 0x7F;
+ ch2 = ch2 << (7-shift);
+
+ ch1 = ch1 | ch2;
+
+ result[j++] = ch1;
+
+ shift++;
+
+ if ((shift == 7) && (i+1<strlen(data))) {
+ shift = 0;
+ i++;
+ }
+ }
+
+ return i;
+}
+
+/* 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;
+ }
+}
+
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+void 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
+
+enum gsm_band gsm_arfcn2band(uint16_t arfcn)
+{
+ if (arfcn & ARFCN_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;
+
+ if (arfcn & ARFCN_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/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c
new file mode 100644
index 00000000..abee4dac
--- /dev/null
+++ b/src/shared/libosmocore/src/gsmtap_util.c
@@ -0,0 +1,200 @@
+/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */
+/*
+ * (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"
+
+#ifdef HAVE_SYS_SELECT_H
+
+#include <osmocore/gsmtap_util.h>
+#include <osmocore/logging.h>
+#include <osmocore/protocol/gsm_04_08.h>
+#include <osmocore/gsmtap.h>
+#include <osmocore/msgb.h>
+#include <osmocore/rsl.h>
+#include <osmocore/select.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+static struct bsc_fd gsmtap_bfd = { .fd = -1 };
+static LLIST_HEAD(gsmtap_txqueue);
+
+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;
+}
+
+/* receive a message from L1/L2 and put it in GSMTAP */
+int gsmtap_sendmsg(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;
+
+ /* gsmtap was never initialized, so don't try to send anything */
+ if (gsmtap_bfd.fd == -1)
+ return 0;
+
+ msg = gsmtap_makemsg(arfcn, ts, chan_type, ss, fn, signal_dbm,
+ snr, data, len);
+ if (!msg)
+ return -ENOMEM;
+
+ msgb_enqueue(&gsmtap_txqueue, msg);
+ gsmtap_bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+/* Callback from select layer if we can write to the socket */
+static int gsmtap_fd_cb(struct bsc_fd *fd, unsigned int flags)
+{
+ struct msgb *msg;
+ int rc;
+
+ if (!(flags & BSC_FD_WRITE))
+ return 0;
+
+ msg = msgb_dequeue(&gsmtap_txqueue);
+ if (!msg) {
+ /* no more messages in the queue, disable READ cb */
+ gsmtap_bfd.when = 0;
+ return 0;
+ }
+ rc = write(gsmtap_bfd.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;
+}
+
+int gsmtap_init(uint32_t dst_ip)
+{
+ int rc;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(GSMTAP_UDP_PORT);
+ sin.sin_addr.s_addr = htonl(dst_ip);
+
+ /* FIXME: create socket */
+ rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc < 0) {
+ perror("creating UDP socket");
+ return rc;
+ }
+ gsmtap_bfd.fd = rc;
+ rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin));
+ if (rc < 0) {
+ perror("connecting UDP socket");
+ close(gsmtap_bfd.fd);
+ gsmtap_bfd.fd = 0;
+ return rc;
+ }
+
+ gsmtap_bfd.when = BSC_FD_WRITE;
+ gsmtap_bfd.cb = gsmtap_fd_cb;
+ gsmtap_bfd.data = NULL;
+
+ return bsc_register_fd(&gsmtap_bfd);
+}
+
+#endif /* HAVE_SYS_SELECT_H */
diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c
new file mode 100644
index 00000000..1dc30db3
--- /dev/null
+++ b/src/shared/libosmocore/src/logging.c
@@ -0,0 +1,419 @@
+/* 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>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <time.h>
+#include <errno.h>
+
+#include <osmocore/talloc.h>
+#include <osmocore/utils.h>
+#include <osmocore/logging.h>
+
+const struct log_info *osmo_log_info;
+
+static struct log_context log_context;
+static void *tall_log_ctx = NULL;
+static LLIST_HEAD(target_list);
+
+static const struct value_string loglevel_strs[] = {
+ { 0, "EVERYTHING" },
+ { LOGL_DEBUG, "DEBUG" },
+ { LOGL_INFO, "INFO" },
+ { LOGL_NOTICE, "NOTICE" },
+ { LOGL_ERROR, "ERROR" },
+ { LOGL_FATAL, "FATAL" },
+ { 0, 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,
+ char *file, int line, int cont, const char *format,
+ va_list ap)
+{
+ char col[30];
+ char sub[30];
+ char tim[30];
+ char buf[4096];
+ char final[4096];
+
+ /* prepare the data */
+ col[0] = '\0';
+ sub[0] = '\0';
+ tim[0] = '\0';
+ buf[0] = '\0';
+
+ /* are we using color */
+ if (target->use_color) {
+ const char *c = color(subsys);
+ if (c) {
+ snprintf(col, sizeof(col), "%s", color(subsys));
+ col[sizeof(col)-1] = '\0';
+ }
+ }
+ vsnprintf(buf, sizeof(buf), format, ap);
+ buf[sizeof(buf)-1] = '\0';
+
+ if (!cont) {
+ if (target->print_timestamp) {
+ char *timestr;
+ time_t tm;
+ tm = time(NULL);
+ timestr = ctime(&tm);
+ timestr[strlen(timestr)-1] = '\0';
+ snprintf(tim, sizeof(tim), "%s ", timestr);
+ tim[sizeof(tim)-1] = '\0';
+ }
+ snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
+ sub[sizeof(sub)-1] = '\0';
+ }
+
+ snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
+ final[sizeof(final)-1] = '\0';
+ target->output(target, final);
+}
+
+
+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, &target_list, entry) {
+ struct log_category *category;
+ int output = 0;
+
+ 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) {
+ /* FIXME: copying the va_list is an ugly
+ * workaround against a bug hidden somewhere in
+ * _output. If we do not copy here, the first
+ * call to _output() will corrupt the va_list
+ * contents, and any further _output() calls
+ * with the same va_list will segfault */
+ va_list bp;
+ va_copy(bp, ap);
+ _output(tar, subsys, file, line, cont, format, bp);
+ 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);
+}
+
+static char hexd_buff[4096];
+
+char *hexdump(const unsigned char *buf, int len)
+{
+ 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 ", buf[i]);
+ if (rc <= 0)
+ break;
+ cur += rc;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
+void log_add_target(struct log_target *target)
+{
+ llist_add_tail(&target->entry, &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;
+}
+
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
+static void _stderr_output(struct log_target *target, const char *log)
+{
+ fprintf(target->tgt_stdout.out, "%s", log);
+ fflush(target->tgt_stdout.out);
+}
+#endif
+
+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->tgt_stdout.out = stderr;
+ target->output = _stderr_output;
+ return target;
+#else
+ return NULL;
+#endif /* stderr */
+}
+
+const char *log_vty_level_string(struct log_info *info)
+{
+ const struct value_string *vs;
+ unsigned int len = 3; /* ()\0 */
+ char *str;
+
+ for (vs = loglevel_strs; vs->value || vs->str; vs++)
+ len += strlen(vs->str) + 1;
+
+ str = talloc_zero_size(NULL, len);
+ if (!str)
+ return NULL;
+
+ str[0] = '(';
+ for (vs = loglevel_strs; vs->value || vs->str; vs++) {
+ strcat(str, vs->str);
+ strcat(str, "|");
+ }
+ str[strlen(str)-1] = ')';
+
+ return str;
+}
+
+const char *log_vty_category_string(struct log_info *info)
+{
+ unsigned int len = 3; /* "()\0" */
+ unsigned int i;
+ char *str;
+
+ for (i = 0; i < info->num_cat; i++)
+ len += strlen(info->cat[i].name) + 1;
+
+ str = talloc_zero_size(NULL, len);
+ if (!str)
+ return NULL;
+
+ str[0] = '(';
+ for (i = 0; i < info->num_cat; i++) {
+ strcat(str, info->cat[i].name+1);
+ strcat(str, "|");
+ }
+ str[strlen(str)-1] = ')';
+
+ 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/msgb.c b/src/shared/libosmocore/src/msgb.c
new file mode 100644
index 00000000..a60e2ffa
--- /dev/null
+++ b/src/shared/libosmocore/src/msgb.c
@@ -0,0 +1,90 @@
+/* (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 <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <osmocore/msgb.h>
+//#include <openbsc/gsm_data.h>
+#include <osmocore/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));
+}
diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c
new file mode 100644
index 00000000..e953508a
--- /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 <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <osmocore/plugin.h>
+
+int 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 plugin_load_all(const char *directory)
+{
+ return 0;
+}
+#endif /* HAVE_DLFCN_H */
diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c
new file mode 100644
index 00000000..f58b5c4a
--- /dev/null
+++ b/src/shared/libosmocore/src/rate_ctr.c
@@ -0,0 +1,128 @@
+/* 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 <inttypes.h>
+#include <string.h>
+
+#include <osmocore/utils.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/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 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);
+
+ bsc_schedule_timer(&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;
+ bsc_schedule_timer(&rate_ctr_timer, 1, 0);
+
+ return 0;
+}
diff --git a/src/shared/libosmocore/src/rsl.c b/src/shared/libosmocore/src/rsl.c
new file mode 100644
index 00000000..c002d33e
--- /dev/null
+++ b/src/shared/libosmocore/src/rsl.c
@@ -0,0 +1,329 @@
+/* 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 <errno.h>
+
+#include <osmocore/tlv.h>
+#include <osmocore/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;
+}
+
+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;
+}
+
+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 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);
+ struct abis_rsl_rll_hdr *rh;
+
+ /* 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 */
+ 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;
+}
+
+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/rxlev_stat.c b/src/shared/libosmocore/src/rxlev_stat.c
new file mode 100644
index 00000000..1bfd6795
--- /dev/null
+++ b/src/shared/libosmocore/src/rxlev_stat.c
@@ -0,0 +1,94 @@
+/* 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 <osmocore/bitvec.h>
+#include <osmocore/rxlev_stat.h>
+
+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;
+}
+
+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 = 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/select.c b/src/shared/libosmocore/src/select.c
new file mode 100644
index 00000000..2f6afa7f
--- /dev/null
+++ b/src/shared/libosmocore/src/select.c
@@ -0,0 +1,131 @@
+/* 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 version 2
+ * as published by the Free Software Foundation
+ *
+ * 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 <osmocore/select.h>
+#include <osmocore/linuxlist.h>
+#include <osmocore/timer.h>
+
+#include "../config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+
+static int maxfd = 0;
+static LLIST_HEAD(bsc_fds);
+static int unregistered_count;
+
+int bsc_register_fd(struct bsc_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;
+
+ llist_add_tail(&fd->list, &bsc_fds);
+
+ return 0;
+}
+
+void bsc_unregister_fd(struct bsc_fd *fd)
+{
+ unregistered_count++;
+ llist_del(&fd->list);
+}
+
+int bsc_select_main(int polling)
+{
+ struct bsc_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, &bsc_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);
+ }
+
+ bsc_timer_check();
+
+ if (!polling)
+ bsc_prepare_timers();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ bsc_update_timers();
+
+ /* call registered callback functions */
+restart:
+ unregistered_count = 0;
+ llist_for_each_entry_safe(ufd, tmp, &bsc_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..c7ca86c4
--- /dev/null
+++ b/src/shared/libosmocore/src/signal.c
@@ -0,0 +1,84 @@
+/* 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 <osmocore/signal.h>
+#include <osmocore/talloc.h>
+#include <osmocore/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;
+ signal_cbfn *cbfn;
+ void *data;
+};
+
+
+int register_signal_handler(unsigned int subsys, 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 unregister_signal_handler(unsigned int subsys, 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 dispatch_signal(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/statistics.c b/src/shared/libosmocore/src/statistics.c
new file mode 100644
index 00000000..34e6a408
--- /dev/null
+++ b/src/shared/libosmocore/src/statistics.c
@@ -0,0 +1,66 @@
+/* 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 <sys/types.h>
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/statistics.h>
+
+static LLIST_HEAD(counters);
+
+void *tall_ctr_ctx;
+
+struct counter *counter_alloc(const char *name)
+{
+ struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
+
+ if (!ctr)
+ return NULL;
+
+ ctr->name = name;
+ llist_add_tail(&ctr->list, &counters);
+
+ return ctr;
+}
+
+void counter_free(struct counter *ctr)
+{
+ llist_del(&ctr->list);
+ talloc_free(ctr);
+}
+
+int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
+{
+ struct counter *ctr;
+ int rc = 0;
+
+ llist_for_each_entry(ctr, &counters, list) {
+ rc = handle_counter(ctr, data);
+ if (rc < 0)
+ return rc;
+ }
+
+ return rc;
+}
+
diff --git a/src/shared/libosmocore/src/talloc.c b/src/shared/libosmocore/src/talloc.c
new file mode 100644
index 00000000..98c2ee09
--- /dev/null
+++ b/src/shared/libosmocore/src/talloc.c
@@ -0,0 +1,1805 @@
+/*
+ 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 <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#define __USE_GNU
+#include <string.h>
+#undef __USE_GNU
+#include <osmocore/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..37d7d166
--- /dev/null
+++ b/src/shared/libosmocore/src/timer.c
@@ -0,0 +1,185 @@
+/*
+ * (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 <osmocore/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 bsc_add_timer(struct timer_list *timer)
+{
+ struct 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 bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds)
+{
+ struct timeval current_time;
+
+ gettimeofday(&current_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;
+ bsc_add_timer(timer);
+}
+
+void bsc_del_timer(struct timer_list *timer)
+{
+ if (timer->in_list) {
+ timer->active = 0;
+ timer->in_list = 0;
+ llist_del(&timer->entry);
+ }
+}
+
+int bsc_timer_pending(struct 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 *bsc_nearest_timer()
+{
+ struct timeval current_time;
+
+ if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
+ return NULL;
+
+ if (gettimeofday(&current_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 bsc_prepare_timers()
+{
+ struct 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 bsc_update_timers()
+{
+ struct timeval current_time;
+ struct timer_list *timer, *tmp;
+ int work = 0;
+
+ gettimeofday(&current_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) {
+ bsc_del_timer(timer);
+ }
+ }
+
+ return work;
+}
+
+int bsc_timer_check(void)
+{
+ struct timer_list *timer;
+ int i = 0;
+
+ llist_for_each_entry(timer, &timer_list, entry) {
+ i++;
+ }
+ return i;
+}
diff --git a/src/shared/libosmocore/src/tlv_parser.c b/src/shared/libosmocore/src/tlv_parser.c
new file mode 100644
index 00000000..bbef7a9a
--- /dev/null
+++ b/src/shared/libosmocore/src/tlv_parser.c
@@ -0,0 +1,179 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <osmocore/utils.h>
+#include <osmocore/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/utils.c b/src/shared/libosmocore/src/utils.c
new file mode 100644
index 00000000..4dab0645
--- /dev/null
+++ b/src/shared/libosmocore/src/utils.c
@@ -0,0 +1,50 @@
+
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <osmocore/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 bcd2char(uint8_t bcd)
+{
+ if (bcd < 0xa)
+ return '0' + bcd;
+ else
+ return 'A' + (bcd - 0xa);
+}
+
+/* only works for numbers in ascci */
+uint8_t char2bcd(char c)
+{
+ return c - 0x30;
+}
diff --git a/src/shared/libosmocore/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am
new file mode 100644
index 00000000..f2859cff
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/Makefile.am
@@ -0,0 +1,13 @@
+# 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
+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..a5655b93
--- /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 <osmocore/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..21afa5c0
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/command.c
@@ -0,0 +1,3177 @@
+/*
+ $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 <osmocore/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);
+}
+
+/* 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;
+ unsigned long min, max, val;
+
+ if (str == NULL)
+ return 1;
+
+ 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;
+
+ /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && vty->node > CONFIG_NODE) {
+ 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) {
+ 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;
+ 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 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, &copy_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..896d79a9
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/logging_vty.c
@@ -0,0 +1,347 @@
+/* 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 <osmocore/talloc.h>
+#include <osmocore/logging.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>
+
+extern const struct log_info *osmo_log_info;
+
+static void _vty_output(struct log_target *tgt, 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;
+}
+
+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 telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_set_all_filter(conn->dbg, 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 telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_set_use_color(conn->dbg, 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 telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_set_print_timestamp(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+/* FIXME: those have to be kept in sync with the log levels and categories */
+#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|llc|sndcp|all)"
+#define CATEGORIES_HELP \
+ "A-bis Radio Link Layer (RLL)\n" \
+ "Layer3 Call Control (CC)\n" \
+ "Layer3 Mobility Management (MM)\n" \
+ "Layer3 Radio Resource (RR)\n" \
+ "A-bis Radio Signalling Link (RSL)\n" \
+ "A-bis Network Management / O&M (NM/OML)\n" \
+ "Layer3 Short Messagaging Service (SMS)\n" \
+ "Paging Subsystem\n" \
+ "MNCC API for Call Control application\n" \
+ "A-bis Input Subsystem\n" \
+ "A-bis Input Driver for Signalling\n" \
+ "A-bis Input Driver for B-Channel (voice data)\n" \
+ "A-bis B-Channel / Sub-channel Multiplexer\n" \
+ "Radio Measurements\n" \
+ "SCCP\n" \
+ "Mobile Switching Center\n" \
+ "Media Gateway Control Protocol\n" \
+ "Hand-over\n" \
+ "Database Layer\n" \
+ "Reference Counting\n" \
+ "GPRS Core\n" \
+ "GPRS Network Service (NS)\n" \
+ "GPRS BSS Gateway Protocol (BSSGP)\n" \
+ "GPRS Logical Link Control Protocol (LLC)\n" \
+ "GPRS Sub-Network Dependent Control Protocol (SNDCP)\n" \
+ "Global setting for all subsytems\n"
+
+#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
+#define LEVELS_HELP \
+ "Log simply everything\n" \
+ "Log debug messages and higher levels\n" \
+ "Log informational messages and higher levels\n" \
+ "Log noticable messages and higher levels\n" \
+ "Log error messages and higher levels\n" \
+ "Log only fatal messages\n"
+DEFUN(logging_level,
+ logging_level_cmd,
+ "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
+ LOGGING_STR
+ "Set the log level for a specified category\n"
+ CATEGORIES_HELP
+ LEVELS_HELP)
+{
+ struct telnet_connection *conn;
+ int category = log_parse_category(argv[0]);
+ int level = log_parse_level(argv[1]);
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ 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(conn->dbg, level);
+ return CMD_SUCCESS;
+ }
+
+ if (category < 0) {
+ vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ conn->dbg->categories[category].enabled = 1;
+ conn->dbg->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 telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_parse_category_mask(conn->dbg, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+ disable_logging_cmd,
+ "logging disable",
+ LOGGING_STR
+ "Disables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ log_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ 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 telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vty_print_logtarget(vty, osmo_log_info, conn->dbg);
+
+ 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;
+ }
+
+ *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;
+}
+
+void logging_vty_add_cmds()
+{
+ 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);
+ install_element_ve(&logging_level_cmd);
+ install_element_ve(&show_logging_vty_cmd);
+}
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..90690960
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/telnet_interface.c
@@ -0,0 +1,203 @@
+/* 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 <osmocore/msgb.h>
+#include <osmocore/talloc.h>
+#include <osmocore/logging.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/buffer.h>
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+static void *tall_telnet_ctx;
+
+/* per network data */
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+
+static struct bsc_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 (bind < 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;
+ bsc_register_fd(&server_socket);
+
+ return 0;
+}
+
+extern const char *openbsc_copyright;
+
+static void print_welcome(int fd)
+{
+ int ret;
+ static char *msg =
+ "Welcome to the OpenBSC Control interface\n";
+
+ ret = write(fd, msg, strlen(msg));
+ ret = write(fd, openbsc_copyright, strlen(openbsc_copyright));
+}
+
+int telnet_close_client(struct bsc_fd *fd)
+{
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
+ bsc_unregister_fd(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 bsc_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 bsc_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;
+ bsc_register_fd(&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 bsc_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..e163526e
--- /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 <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <osmocore/timer.h>
+#include <osmocore/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..0343163f
--- /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 <osmocore/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..ff17abf6
--- /dev/null
+++ b/src/shared/libosmocore/src/vty/vty.c
@@ -0,0 +1,1683 @@
+
+#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 <osmocore/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;
+}
+
+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.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;
+ 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;
+ 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..618a8c0b
--- /dev/null
+++ b/src/shared/libosmocore/src/write_queue.c
@@ -0,0 +1,88 @@
+/* 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 <osmocore/write_queue.h>
+
+int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
+{
+ struct write_queue *queue;
+
+ queue = container_of(fd, struct write_queue, 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;
+ msg = msgb_dequeue(&queue->msg_queue);
+ if (!msg)
+ return -1;
+
+ --queue->current_length;
+ queue->write_cb(fd, msg);
+ msgb_free(msg);
+
+ if (!llist_empty(&queue->msg_queue))
+ fd->when |= BSC_FD_WRITE;
+ }
+
+ return 0;
+}
+
+void write_queue_init(struct write_queue *queue, int max_length)
+{
+ queue->max_length = max_length;
+ queue->current_length = 0;
+ queue->read_cb = NULL;
+ queue->write_cb = NULL;
+ queue->bfd.cb = write_queue_bfd_cb;
+ INIT_LLIST_HEAD(&queue->msg_queue);
+}
+
+int write_queue_enqueue(struct write_queue *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 write_queue_clear(struct write_queue *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..0119a02c
--- /dev/null
+++ b/src/shared/libosmocore/tests/Makefile.am
@@ -0,0 +1,3 @@
+if ENABLE_TESTS
+SUBDIRS = timer sms
+endif
diff --git a/src/shared/libosmocore/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am
new file mode 100644
index 00000000..a8f1ff6a
--- /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
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..f5183d54
--- /dev/null
+++ b/src/shared/libosmocore/tests/sms/sms_test.c
@@ -0,0 +1,47 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.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 <sys/types.h>
+#include <osmocore/msgb.h>
+#include <osmocore/gsm_utils.h>
+
+int main(int argc, char** argv)
+{
+ printf("SMS testing\n");
+ struct msgb *msg;
+ uint8_t *sms;
+ uint8_t i;
+
+ /* test 7-bit coding/decoding */
+ const char *input = "test text";
+ uint8_t length;
+ uint8_t coded[256];
+ char result[256];
+
+ length = gsm_7bit_encode(coded, input);
+ gsm_7bit_decode(result, coded, length);
+ if (strcmp(result, input) != 0) {
+ printf("7 Bit coding failed... life sucks\n");
+ printf("Wanted: '%s' got '%s'\n", input, result);
+ }
+}
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..1b458d81
--- /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 <osmocore/timer.h>
+#include <osmocore/select.h>
+
+#include "../../config.h"
+
+static void timer_fired(void *data);
+
+static struct timer_list timer_one = {
+ .cb = timer_fired,
+ .data = (void*)1,
+};
+
+static struct timer_list timer_two = {
+ .cb = timer_fired,
+ .data = (void*)2,
+};
+
+static struct 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) {
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_del_timer(&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");
+
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_schedule_timer(&timer_two, 5, 0);
+ bsc_schedule_timer(&timer_three, 4, 0);
+
+#ifdef HAVE_SYS_SELECT_H
+ while (1) {
+ bsc_select_main(0);
+ }
+#else
+ printf("Select not supported on this platform!\n");
+#endif
+}