pax_global_header00006660000000000000000000000064126160704400014512gustar00rootroot0000000000000052 comment=8649d57f507d359c99a89654aac7e19ce22db282 libosmocore-0.9.0/000077500000000000000000000000001261607044000140355ustar00rootroot00000000000000libosmocore-0.9.0/.gitignore000066400000000000000000000030461261607044000160300ustar00rootroot00000000000000Makefile Makefile.in .deps .dirstamp .libs *.o *.lo *.la *.pc aclocal.m4 acinclude.m4 aminclude.am m4/*.m4 autom4te.cache config.h* config.sub config.log config.status config.guess configure compile depcomp missing ltmain.sh install-sh stamp-h1 libtool libosmocore-*.tar.* Doxyfile.core Doxyfile.gsm Doxyfile.vty Doxyfile.codec debian/autoreconf.after debian/autoreconf.before debian/files debian/libosmocore-dev/ debian/libosmocore*.log debian/tmp/ debian/libosmocore*.substvars debian/libosmocore/ debian/libosmocore.post*.debhelper .tarball-version .version #gnu autotest tests/package.m4 tests/atconfig tests/atlocal tests/osmo-test tests/package.m4 tests/testsuite tests/testsuite.dir/ tests/testsuite.log tests/utils/utils_test tests/stats/stats_test tests/kasumi/kasumi_test tests/sms/sms_test tests/timer/timer_test tests/msgfile/msgfile_test tests/ussd/ussd_test tests/smscb/smscb_test tests/bits/bitrev_test tests/a5/a5_test tests/comp128/comp128_test tests/auth/milenage_test tests/conv/conv_test tests/lapd/lapd_test tests/gsm0808/gsm0808_test tests/gb/bssgp_fc_test tests/gb/gprs_ns_test tests/gsm0408/gsm0408_test tests/logging/logging_test tests/fr/fr_test tests/loggingrb/loggingrb_test tests/ringbuf/ringbuf_test tests/strrb/strrb_test tests/vty/vty_test tests/gb/gprs_bssgp_test tests/smscb/gsm0341_test utils/osmo-arfcn utils/osmo-auc-gen utils/osmo-sim-test doc/codec doc/core doc/vty/latex doc/vty/html doc/vty/doxygen_sqlite3.db doc/gsm doc/html.tar src/crc*gen.c include/osmocom/core/crc*gen.h include/osmocom/core/bit*gen.h # vi files *.sw? libosmocore-0.9.0/COPYING000066400000000000000000000431031261607044000150710ustar00rootroot00000000000000 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. Copyright (C) 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. , 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. libosmocore-0.9.0/Doxyfile.codec.in000066400000000000000000002153331261607044000172330ustar00rootroot00000000000000# Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libosmocodec # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Osmocom codec library" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/codec # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @srcdir@/include/osmocom/codec @srcdir@/src/codec # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). # IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is adviced to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin/dot # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libosmocore-0.9.0/Doxyfile.core.in000066400000000000000000002153251261607044000171070ustar00rootroot00000000000000# Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libosmocore # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Osmocom core library" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/core # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @srcdir@/include/osmocom/core @srcdir@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). # IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is adviced to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc/core # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin/dot # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libosmocore-0.9.0/Doxyfile.gsm.in000066400000000000000000002153671261607044000167530ustar00rootroot00000000000000# Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libosmogsm # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Osmocom GSM library" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/gsm # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @srcdir@/include/osmocom/gsm @srcdir@/include/osmocom/gsm/protocol @srcdir@/src/gsm # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). # IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is adviced to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin/dot # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libosmocore-0.9.0/Doxyfile.vty.in000066400000000000000000002153211261607044000167750ustar00rootroot00000000000000# Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = libosmovty # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Osmocom VTY library" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc/vty # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @srcdir@/include/osmocom/vty @srcdir@/src/vty # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). # IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is adviced to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin/dot # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES libosmocore-0.9.0/Makefile.am000066400000000000000000000031341261607044000160720ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include SUBDIRS = include src src/vty src/codec src/gsm src/gb src/ctrl src/sim tests utils pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc \ libosmogb.pc libosmoctrl.pc BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version EXTRA_DIST = git-version-gen .version if HAVE_DOXYGEN html_DATA = $(top_builddir)/doc/html.tar $(html_DATA): $(top_builddir)/doc/core/html/index.html \ $(top_builddir)/doc/gsm/html/index.html \ $(top_builddir)/doc/vty/html/index.html \ $(top_builddir)/doc/codec/html/index.html cd $(top_builddir)/doc && tar cf html.tar */html $(top_builddir)/doc/core/html/index.html: $(SOURCES) Doxyfile.core @rm -rf doc/core mkdir -p doc/core $(DOXYGEN) Doxyfile.core $(top_builddir)/doc/gsm/html/index.html: $(SOURCES) Doxyfile.gsm @rm -rf doc/gsm mkdir -p doc/gsm $(DOXYGEN) Doxyfile.gsm $(top_builddir)/doc/vty/html/index.html: $(SOURCES) Doxyfile.vty @rm -rf doc/vty/{html,latex} $(DOXYGEN) Doxyfile.vty $(top_builddir)/doc/codec/html/index.html: $(SOURCES) Doxyfile.codec @rm -rf doc/codec mkdir -p doc/codec $(DOXYGEN) Doxyfile.codec install-data-hook: cd $(DESTDIR)$(htmldir) && tar xf html.tar && rm -f html.tar uninstall-hook: cd $(DESTDIR)$(htmldir) && rm -rf {core,gsm,vty,codec} DX_CLEAN = doc/{core,gsm,vty,codec}/{html,latex}/* doc/html.tar doc/{core,gsm,vty,codec}/doxygen_sqlite3.db endif MOSTLYCLEANFILES = $(DX_CLEAN) libosmocore-0.9.0/TODO-RELEASE000066400000000000000000000000611261607044000156000ustar00rootroot00000000000000#library what description / commit summary line libosmocore-0.9.0/configure.ac000066400000000000000000000132461261607044000163310ustar00rootroot00000000000000AC_INIT([libosmocore], m4_esyscmd([./git-version-gen .tarball-version]), [openbsc@lists.osmocom.org]) AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects]) AC_CONFIG_TESTDIR(tests) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl checks for programs AC_PROG_MAKE_SET AC_PROG_MKDIR_P AC_PROG_CC AC_PROG_INSTALL LT_INIT([pic-only disable-static]) AC_CONFIG_MACRO_DIR([m4]) dnl check os: some linker flags not available on osx case $host in *-darwin*) ;; *) LTLDFLAGS_OSMOGB='-Wl,--version-script=$(srcdir)/libosmogb.map' LTLDFLAGS_OSMOGSM='-Wl,--version-script=$(srcdir)/libosmogsm.map' ;; esac AC_SUBST(LTLDFLAGS_OSMOGB) AC_SUBST(LTLDFLAGS_OSMOGSM) dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS(execinfo.h sys/select.h sys/socket.h syslog.h ctype.h netinet/tcp.h) # for src/conv.c AC_FUNC_ALLOCA AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) AC_SUBST(LIBRARY_DL) # for src/backtrace.c AC_CHECK_LIB(execinfo, backtrace, BACKTRACE_LIB=-lexecinfo, BACKTRACE_LIB=) AC_SUBST(BACKTRACE_LIB) AC_PATH_PROG(DOXYGEN,doxygen,false) AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) # The following test is taken from WebKit's webkit.m4 saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden " AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], [ AC_MSG_RESULT([yes]) SYMBOL_VISIBILITY="-fvisibility=hidden"], AC_MSG_RESULT([no])) CFLAGS="$saved_CFLAGS" AC_SUBST(SYMBOL_VISIBILITY) AC_DEFUN([CHECK_TM_INCLUDES_TM_GMTOFF], [ AC_CACHE_CHECK( [whether struct tm has tm_gmtoff member], osmo_cv_tm_includes_tm_gmtoff, [AC_LINK_IFELSE([ AC_LANG_PROGRAM([ #include ], [ time_t t = time(NULL); struct tm* lt = localtime(&t); int off = lt->tm_gmtoff; ]) ], osmo_cv_tm_includes_tm_gmtoff=yes, osmo_cv_tm_includes_tm_gmtoff=no )] ) if test "x$osmo_cv_tm_includes_tm_gmtoff" = xyes; then AC_DEFINE(HAVE_TM_GMTOFF_IN_TM, 1, [Define if struct tm has tm_gmtoff member.]) fi ]) CHECK_TM_INCLUDES_TM_GMTOFF dnl Generate the output AC_CONFIG_HEADER(config.h) AC_ARG_ENABLE([pcsc], [AS_HELP_STRING([--disable-pcsc], [Build without PC/SC support])], [ enable_pcsc=$enableval ], [ enable_pcsc="yes" ]) if test "x$enable_pcsc" = "xyes" ; then PKG_CHECK_MODULES(PCSC, libpcsclite) fi AM_CONDITIONAL(ENABLE_PCSC, test "x$enable_pcsc" = "xyes") AC_ARG_ENABLE(talloc, [AS_HELP_STRING( [--disable-talloc], [Disable building talloc memory allocator] )], [enable_talloc=$enableval], [enable_talloc="yes"]) AM_CONDITIONAL(ENABLE_TALLOC, [test x"$enable_talloc" = x"yes"]) AC_ARG_ENABLE(plugin, [AS_HELP_STRING( [--disable-plugin], [Disable support for dlopen plugins], )], [enable_plugin=$enableval], [enable_plugin="yes"]) AM_CONDITIONAL(ENABLE_PLUGIN, test x"$enable_plugin" = x"yes") AC_ARG_ENABLE(vty, [AS_HELP_STRING( [--disable-vty], [Disable building VTY telnet interface] )], [enable_vty=$enableval], [enable_vty="yes"]) AM_CONDITIONAL(ENABLE_VTY, test x"$enable_vty" = x"yes") AC_ARG_ENABLE(panic_infloop, [AS_HELP_STRING( [--enable-panic-infloop], [Trigger infinite loop on panic rather than fprintf/abort] )], [panic_infloop=$enableval], [panic_infloop="no"]) if test x"$panic_infloop" = x"yes" then AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort]) fi AC_ARG_ENABLE(bsc_fd_check, [AS_HELP_STRING( [--enable-bsc-fd-check], [Instrument bsc_register_fd to check that the fd is registered] )], [fd_check=$enableval], [fd_check="no"]) if test x"$fd_check" = x"no" then AC_DEFINE([BSC_FD_CHECK],[1],[Instrument the bsc_register_fd]) fi AC_ARG_ENABLE(msgfile, [AS_HELP_STRING( [--disable-msgfile], [Disable support for the msgfile], )], [enable_msgfile=$enableval], [enable_msgfile="yes"]) AM_CONDITIONAL(ENABLE_MSGFILE, test x"$enable_msgfile" = x"yes") AC_ARG_ENABLE(serial, [AS_HELP_STRING( [--disable-serial], [Disable support for the serial helpers], )], [enable_serial=$enableval], [enable_serial="yes"]) AM_CONDITIONAL(ENABLE_SERIAL, test x"$enable_serial" = x"yes") AC_ARG_ENABLE(utilities, [AS_HELP_STRING( [--disable-utilities], [Disable building utility programs], )], [enable_utilities=$enableval], [enable_utilities="yes"]) AM_CONDITIONAL(ENABLE_UTILITIES, test x"$enable_utilities" = x"yes") AC_ARG_ENABLE(gb, [AS_HELP_STRING( [--disable-gb], [Disable building Gb library], )], [enable_gb=$enableval], [enable_gb="yes"]) AM_CONDITIONAL(ENABLE_GB, test x"$enable_gb" = x"yes") AC_ARG_ENABLE(embedded, [AS_HELP_STRING( [--enable-embedded], [Enable building for embedded use and disable unsupported features] )], [embedded=$enableval], [embedded="no"]) if test x"$embedded" = x"yes" then AC_DEFINE([EMBEDDED],[1],[Select building for embedded use]) AM_CONDITIONAL(ENABLE_PLUGIN, false) AM_CONDITIONAL(ENABLE_MSGFILE, false) AM_CONDITIONAL(ENABLE_SERIAL, false) AM_CONDITIONAL(ENABLE_VTY, false) AM_CONDITIONAL(ENABLE_TALLOC, false) AM_CONDITIONAL(ENABLE_UTILITIES, false) AM_CONDITIONAL(ENABLE_GB, false) AC_DEFINE([PANIC_INFLOOP],[1],[Use infinite loop on panic rather than fprintf/abort]) fi AC_OUTPUT( libosmocore.pc libosmocodec.pc libosmovty.pc libosmogsm.pc libosmogb.pc libosmoctrl.pc libosmosim.pc include/Makefile src/Makefile src/vty/Makefile src/codec/Makefile src/sim/Makefile src/gsm/Makefile src/gb/Makefile src/ctrl/Makefile tests/Makefile utils/Makefile Doxyfile.core Doxyfile.gsm Doxyfile.vty Doxyfile.codec Makefile) libosmocore-0.9.0/debian/000077500000000000000000000000001261607044000152575ustar00rootroot00000000000000libosmocore-0.9.0/debian/changelog000066400000000000000000000066371261607044000171450ustar00rootroot00000000000000libosmocore (0.9.0) unstable; urgency=medium * StatsD support * Disable building of static libraries * GTP Hub and OAP ports/identifiers assigned -- Holger Hans Peter Freyther Tue, 03 Nov 2015 09:31:41 +0100 libosmocore (0.8.3) unstable; urgency=medium * Prepare new release -- Holger Hans Peter Freyther Sun, 23 Aug 2015 17:38:55 +0200 libosmocore (0.8.2) unstable; urgency=medium * Add endian header * GPRS flow control changes * Add APN format routines to libosmocore. -- Holger Hans Peter Freyther Sat, 01 Aug 2015 20:18:45 +0200 libosmocore (0.8.0) unstable; urgency=medium * New upstream release of libosmocore. -- Holger Hans Peter Freyther Sun, 18 Jan 2015 19:04:10 +0100 libosmocore (0.7.0) unstable; urgency=medium * New upstream release of libosmocore. -- Harald Welte Thu, 21 Aug 2014 15:52:00 +0200 libosmocore (0.6.6) UNRELEASED; urgency=medium * New upstream release of libosmocore. -- Holger Hans Peter Freyther Mon, 31 Mar 2014 15:37:33 +0200 libosmocore (0.6.5) unstable; urgency=medium * New upstream release of libosmocore -- Holger Hans Peter Freyther Mon, 20 Jan 2014 10:37:23 +0100 libosmocore (0.6.4+git3) unstable; urgency=low * GPRS fix NS connections to a SGSN when configured via VTY -- Jacob Erlbeck Thu, 07 Nov 2013 16:07:20 +0100 libosmocore (0.6.4+git2) unstable; urgency=low * GPRS related changes, some GSM encoding/decoding changes * GPRS correctly determine routable NSVC. * Update with GPRS changes -- Holger Hans Peter Freyther Thu, 10 Oct 2013 14:16:37 +0200 libosmocore (0.6.3+git1-1) unstable; urgency=low * New upstream release with new primitives, USSD fixes. * Split libosmocore into several package. One lib per package. -- Holger Hans Peter Freyther Fri, 09 Aug 2013 17:47:30 +0200 libosmocore (0.6.2) unstable; urgency=low * New upstream release with NS UDP DSCP ABI changes -- Holger Hans Peter Freyther Wed, 26 Jun 2013 08:01:55 +0200 libosmocore (0.5.3+git1-6) unstable; urgency=low * Build new package with the new API required for osmo-bts/openbsc * Package the arfcn utility. * Add the SOCK_RAW work-around for glibc. * Re-enable the debug packages thanks to Jan. -- Holger Hans Peter Freyther Fri, 04 Jan 2013 09:54:55 +0100 libosmocore (0.5.3+git1-2) unstable; urgency=low * New upstream version -- Holger Hans Peter Freyther Mon, 05 Nov 2012 21:35:57 +0100 libosmocore (0.5.3+git1-1) precise; urgency=low * Fix issue with package version. -- Eric Butler Tue, 14 Aug 2012 20:43:17 -0700 libosmocore (0.5.3+git1) precise; urgency=low * Updated debian package. -- Eric Butler Tue, 14 Aug 2012 16:53:56 -0700 libosmocore (0.3.0) natty; urgency=low * New upstream version of libosmocore -- Harald Welte Tue, 10 May 2011 17:28:24 +0200 libosmocore (0.1.27) natty; urgency=low * New upstream version of libosmocore. -- Holger Hans Peter Freyther Thu, 13 Jan 2011 18:07:36 +0800 libosmocore (0.1.17-1) unstable; urgency=low * Initial release -- Harald Welte Tue, 24 Aug 2010 10:55:04 +0200 libosmocore-0.9.0/debian/compat000066400000000000000000000000021261607044000164550ustar00rootroot000000000000009 libosmocore-0.9.0/debian/control000066400000000000000000000043631261607044000166700ustar00rootroot00000000000000Source: libosmocore Section: libs Priority: optional Maintainer: Harald Welte Build-Depends: debhelper (>= 9), autotools-dev, autoconf, automake, libtool, dh-autoreconf, libdpkg-perl, git, doxygen, libpcsclite-dev, pkg-config Standards-Version: 3.9.6 Homepage: http://bb.osmocom.org/trac/wiki/libosmocore Vcs-Git: git://git.osmocom.org/libosmocore.git Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmocore.git;a=summary Package: libosmocore Section: libs Architecture: any Depends: libosmocodec0, libosmocore6, libosmogb4, libosmogsm5, libosmovty3, ${misc:Depends} Multi-Arch: foreign Description: Open Source MObile COMmunications CORE library Package: libosmocodec0 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo codec library Package: libosmocore6 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo Core library Package: libosmogb4 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo GPRS GB library Package: libosmogsm5 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo GSM utility library Package: libosmovty3 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo VTY library Package: libosmoctrl0 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo control library Package: libosmosim0 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: Osmo SIM library Package: libosmocore-dev Section: libdevel Architecture: any Depends: ${misc:Depends}, libosmocore Multi-Arch: same Description: Development headers for Open Source MObile COMmunications CORE library Package: libosmocore-utils Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libosmocore Multi-Arch: same Description: Utilities for gsm Package: libosmocore-dbg Architecture: any Section: debug Priority: extra Depends: libosmocore (= ${binary:Version}), ${misc:Depends} Multi-Arch: same Description: Debug symbols for Open Source MObile COMmunications CORE library libosmocore-0.9.0/debian/copyright000066400000000000000000000035211261607044000172130ustar00rootroot00000000000000This work was packaged for Debian by: Harald Welte on Tue, 24 Aug 2010 10:55:04 +0200 It was downloaded from: git://git.osmocom.org/libosmocore.git Upstream Author(s): Harald Welte Holger Hans Peter Freyther Sylvain Munaut Daniel Willmann Golde For src/talloc.c and include/osmocore/talloc.h: Andrew Tridgell Stefan Metzmacher For src/vty/* and include/osmocom/vty/* Kunihiro Ishiguro Copyright: Copyright (C) 2008-2010 Harald Welte Copyright (C) 2008-2010 Holger Hans Peter Freyther Copyright (C) 2009-2010 Sylvain Munaut Copyright (C) 2009-2010 On-Waves Copyright (C) 2008 Daniel Willmann Copyright (C) 2010 Nico Golde For src/talloc.c and include/osmocore/talloc.h: Copyright (C) 2004 Andrew Tridgell Copyright (C) 2006 Stefan Metzmacher For src/vty/* and include/osmocom/vty/* Copyright (C) 1998 Kunihiro Ishiguro License: GNU General Public License, Version 2 or later The Debian packaging is: Copyright (C) 2010 Harald Welte # Please chose a license for your packaging work. If the program you package # uses a mainstream license, using the same license is the safest choice. # Please avoid to pick license terms that are more restrictive than the # packaged work, as it may make Debian's contributions unacceptable upstream. # If you just want it to be GPL version 3, leave the following lines in. and is licensed under the GPL version 3, see "/usr/share/common-licenses/GPL-3". # Please also look if there are files or directories which have a # different copyright/license attached and list them here. libosmocore-0.9.0/debian/docs000066400000000000000000000000001261607044000161200ustar00rootroot00000000000000libosmocore-0.9.0/debian/libosmocodec0.install000066400000000000000000000000351261607044000213670ustar00rootroot00000000000000usr/lib/*/libosmocodec*.so.* libosmocore-0.9.0/debian/libosmocore-dev.dirs000066400000000000000000000002451261607044000212340ustar00rootroot00000000000000usr/lib usr/include usr/include/osmocom usr/include/osmocom/codec usr/include/osmocom/core usr/include/osmocom/crypt usr/include/osmocom/gsm usr/include/osmocom/vty libosmocore-0.9.0/debian/libosmocore-dev.install000066400000000000000000000001441261607044000217370ustar00rootroot00000000000000usr/include/* usr/lib/*/lib*.so usr/lib/*/lib*.la usr/lib/*/pkgconfig/* usr/share/doc/libosmocore/* libosmocore-0.9.0/debian/libosmocore-utils.dirs000066400000000000000000000000101261607044000216040ustar00rootroot00000000000000usr/bin libosmocore-0.9.0/debian/libosmocore-utils.install000066400000000000000000000000501261607044000223150ustar00rootroot00000000000000usr/bin/osmo-arfcn usr/bin/osmo-auc-gen libosmocore-0.9.0/debian/libosmocore.dirs000066400000000000000000000002451261607044000204600ustar00rootroot00000000000000usr/lib usr/include usr/include/osmocom usr/include/osmocom/codec usr/include/osmocom/core usr/include/osmocom/crypt usr/include/osmocom/gsm usr/include/osmocom/vty libosmocore-0.9.0/debian/libosmocore.install000066400000000000000000000000001261607044000211520ustar00rootroot00000000000000libosmocore-0.9.0/debian/libosmocore6.install000066400000000000000000000000341261607044000212470ustar00rootroot00000000000000usr/lib/*/libosmocore*.so.* libosmocore-0.9.0/debian/libosmoctrl0.install000066400000000000000000000000341261607044000212550ustar00rootroot00000000000000usr/lib/*/libosmoctrl*.so.* libosmocore-0.9.0/debian/libosmogb4.install000066400000000000000000000000321261607044000207030ustar00rootroot00000000000000usr/lib/*/libosmogb*.so.* libosmocore-0.9.0/debian/libosmogsm5.install000066400000000000000000000000331261607044000211030ustar00rootroot00000000000000usr/lib/*/libosmogsm*.so.* libosmocore-0.9.0/debian/libosmosim0.install000066400000000000000000000000331261607044000211000ustar00rootroot00000000000000usr/lib/*/libosmosim*.so.* libosmocore-0.9.0/debian/libosmovty3.install000066400000000000000000000000331261607044000211350ustar00rootroot00000000000000usr/lib/*/libosmovty*.so.* libosmocore-0.9.0/debian/patches/000077500000000000000000000000001261607044000167065ustar00rootroot00000000000000libosmocore-0.9.0/debian/patches/debian-changes-0.1.17-1000066400000000000000000000031001261607044000223330ustar00rootroot00000000000000Description: Upstream changes introduced in version 0.1.17-1 This patch has been created by dpkg-source during the package build. Here's the last changelog entry, hopefully it gives details on why those changes were made: . libosmocore (0.1.17-1) unstable; urgency=low . * Initial release (Closes: #nnnn) . The person named in the Author field signed this changelog entry. Author: Harald Welte --- The information above should follow the Patch Tagging Guidelines, please checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here are templates for supplementary fields that you might want to add: Origin: , Bug: Bug-Debian: http://bugs.debian.org/ Bug-Ubuntu: https://launchpad.net/bugs/ Forwarded: Reviewed-By: Last-Update: --- /dev/null +++ libosmocore-0.1.17/.version @@ -0,0 +1 @@ +0.1.17 --- /dev/null +++ libosmocore-0.1.17/copyright @@ -0,0 +1,14 @@ +Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 +Name: libosmocore +Maintainer: Harald Welte +Source: git://git.osmocom.org/libosmocore.git + +Copyright: 2008-2010 Harald Welte +License: GPL-2+ + +Files: src/talloc.c include/osmocore/talloc.h +Copyright: 2004 Andrew Tridgell +License: LGPL-3+ + +Files: include/osmocore/linuxlist.h +License: GPL-2 libosmocore-0.9.0/debian/patches/series000066400000000000000000000000301261607044000201140ustar00rootroot00000000000000debian-changes-0.1.17-1 libosmocore-0.9.0/debian/rules000077500000000000000000000020541261607044000163400ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 DEBIAN := $(shell dpkg-parsechangelog | grep ^Version: | cut -d' ' -f2) DEBVERS := $(shell echo '$(DEBIAN)' | cut -d- -f1) VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/[+-].*//' -e 's/~//g') export DEB_BUILD_HARDENING=1 %: dh $@ --with autoreconf --fail-missing override_dh_strip: dh_strip --dbg-package=libosmocore-dbg override_dh_install: sed -i "/dependency_libs/ s/'.*'/''/" `find . -name '*.la'` dh_install # Print test results in case of a failure override_dh_auto_test: dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) override_dh_autoreconf: echo $(VERSION) > .tarball-version dh_autoreconf libosmocore-0.9.0/debian/source/000077500000000000000000000000001261607044000165575ustar00rootroot00000000000000libosmocore-0.9.0/debian/source/format000066400000000000000000000000151261607044000177660ustar00rootroot000000000000003.0 (native) libosmocore-0.9.0/doc/000077500000000000000000000000001261607044000146025ustar00rootroot00000000000000libosmocore-0.9.0/doc/.empty000066400000000000000000000000001261607044000157270ustar00rootroot00000000000000libosmocore-0.9.0/doc/vty/000077500000000000000000000000001261607044000154245ustar00rootroot00000000000000libosmocore-0.9.0/doc/vty/example.xml000066400000000000000000000010501261607044000175750ustar00rootroot00000000000000 General docs General docs libosmocore-0.9.0/doc/vty/merge_doc.xsl000066400000000000000000000030741261607044000201040ustar00rootroot00000000000000 libosmocore-0.9.0/doc/vty/vtydoc.xsd000066400000000000000000000032171261607044000174570ustar00rootroot00000000000000 libosmocore-0.9.0/git-version-gen000077500000000000000000000125001261607044000167760ustar00rootroot00000000000000#!/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 . # 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: libosmocore-0.9.0/include/000077500000000000000000000000001261607044000154605ustar00rootroot00000000000000libosmocore-0.9.0/include/Makefile.am000066400000000000000000000127751261607044000175300ustar00rootroot00000000000000nobase_include_HEADERS = \ osmocom/codec/codec.h \ osmocom/core/application.h \ osmocom/core/backtrace.h \ osmocom/core/bit16gen.h \ osmocom/core/bit32gen.h \ osmocom/core/bit64gen.h \ osmocom/core/bits.h \ osmocom/core/bitvec.h \ osmocom/core/conv.h \ osmocom/core/crc16.h \ osmocom/core/crc16gen.h \ osmocom/core/crc32gen.h \ osmocom/core/crc64gen.h \ osmocom/core/crc8gen.h \ osmocom/core/crcgen.h \ osmocom/core/endian.h \ osmocom/core/defs.h \ osmocom/core/gsmtap.h \ osmocom/core/gsmtap_util.h \ osmocom/core/linuxlist.h \ osmocom/core/linuxrbtree.h \ osmocom/core/logging.h \ osmocom/core/loggingrb.h \ osmocom/core/stats.h \ osmocom/core/macaddr.h \ osmocom/core/msgb.h \ osmocom/core/panic.h \ osmocom/core/prim.h \ osmocom/core/process.h \ osmocom/core/rate_ctr.h \ osmocom/core/stat_item.h \ osmocom/core/select.h \ osmocom/core/signal.h \ osmocom/core/socket.h \ osmocom/core/statistics.h \ osmocom/core/strrb.h \ osmocom/core/timer.h \ osmocom/core/utils.h \ osmocom/core/write_queue.h \ osmocom/crypt/auth.h \ osmocom/crypt/gprs_cipher.h \ osmocom/ctrl/control_cmd.h \ osmocom/ctrl/control_if.h \ osmocom/ctrl/ports.h \ osmocom/gprs/gprs_bssgp.h \ osmocom/gprs/gprs_bssgp_bss.h \ osmocom/gprs/gprs_msgb.h \ osmocom/gprs/gprs_ns.h \ osmocom/gprs/gprs_ns_frgre.h \ osmocom/gprs/protocol/gsm_08_16.h \ osmocom/gprs/protocol/gsm_08_18.h \ osmocom/gsm/a5.h \ osmocom/gsm/abis_nm.h \ osmocom/gsm/apn.h \ osmocom/gsm/comp128.h \ osmocom/gsm/comp128v23.h \ osmocom/gsm/gan.h \ osmocom/gsm/gsm0341.h \ osmocom/gsm/gsm0411_smc.h \ osmocom/gsm/gsm0411_smr.h \ osmocom/gsm/gsm0411_utils.h \ osmocom/gsm/gsm0480.h \ osmocom/gsm/gsm0502.h \ osmocom/gsm/gsm0808.h \ osmocom/gsm/gsm48.h \ osmocom/gsm/gsm48_ie.h \ osmocom/gsm/gsm_utils.h \ osmocom/gsm/ipa.h \ osmocom/gsm/lapd_core.h \ osmocom/gsm/lapdm.h \ osmocom/gsm/meas_rep.h \ osmocom/gsm/mncc.h \ osmocom/gsm/prim.h \ osmocom/gsm/l1sap.h \ osmocom/gsm/protocol/gsm_03_40.h \ osmocom/gsm/protocol/gsm_03_41.h \ osmocom/gsm/protocol/gsm_04_08.h \ osmocom/gsm/protocol/gsm_04_11.h \ osmocom/gsm/protocol/gsm_04_12.h \ osmocom/gsm/protocol/gsm_04_80.h \ osmocom/gsm/protocol/gsm_08_08.h \ osmocom/gsm/protocol/gsm_08_58.h \ osmocom/gsm/protocol/gsm_09_02.h \ osmocom/gsm/protocol/gsm_12_21.h \ osmocom/gsm/protocol/gsm_44_318.h \ osmocom/gsm/protocol/ipaccess.h \ osmocom/gsm/protocol/smpp34_osmocom.h \ osmocom/gsm/rsl.h \ osmocom/gsm/rxlev_stat.h \ osmocom/gsm/sysinfo.h \ osmocom/gsm/tlv.h \ osmocom/sim/sim.h if ENABLE_PLUGIN nobase_include_HEADERS += osmocom/core/plugin.h endif if ENABLE_TALLOC nobase_include_HEADERS += osmocom/core/talloc.h endif if ENABLE_MSGFILE nobase_include_HEADERS += osmocom/core/msgfile.h endif if ENABLE_SERIAL nobase_include_HEADERS += osmocom/core/serial.h endif if ENABLE_VTY nobase_include_HEADERS += \ osmocom/vty/buffer.h \ osmocom/vty/command.h \ osmocom/vty/logging.h \ osmocom/vty/stats.h \ osmocom/vty/misc.h \ osmocom/vty/telnet_interface.h \ osmocom/vty/vector.h \ osmocom/vty/vty.h \ osmocom/vty/ports.h endif noinst_HEADERS = \ osmocom/core/timer_compat.h \ osmocom/gsm/kasumi.h osmocom/core/bit%gen.h: osmocom/core/bitXXgen.h.tpl $(AM_V_GEN)$(MKDIR_P) $(dir $@) $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ osmocom/core/crc%gen.h: osmocom/core/crcXXgen.h.tpl $(AM_V_GEN)$(MKDIR_P) $(dir $@) $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ libosmocore-0.9.0/include/osmocom/000077500000000000000000000000001261607044000171345ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/codec/000077500000000000000000000000001261607044000202115ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/codec/codec.h000066400000000000000000000014541261607044000214430ustar00rootroot00000000000000#pragma once #include extern const uint16_t gsm610_bitorder[]; /* FR */ extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */ extern const uint16_t gsm620_voiced_bitorder[]; /* HR voiced */ extern const uint16_t gsm660_bitorder[]; /* EFR */ extern const uint16_t gsm690_12_2_bitorder[]; /* AMR 12.2 kbits */ extern const uint16_t gsm690_10_2_bitorder[]; /* AMR 10.2 kbits */ extern const uint16_t gsm690_7_95_bitorder[]; /* AMR 7.95 kbits */ extern const uint16_t gsm690_7_4_bitorder[]; /* AMR 7.4 kbits */ extern const uint16_t gsm690_6_7_bitorder[]; /* AMR 6.7 kbits */ extern const uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */ extern const uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */ extern const uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */ libosmocore-0.9.0/include/osmocom/core/000077500000000000000000000000001261607044000200645ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/core/application.h000066400000000000000000000007731261607044000225470ustar00rootroot00000000000000#pragma once /*! * \file application.h * \brief Routines for helping with the osmocom application setup. */ /*! \brief information containing the available logging subsystems */ struct log_info; /*! \brief one instance of a logging target (file, stderr, ...) */ struct log_target; /*! \brief the default logging target, logging to stderr */ extern struct log_target *osmo_stderr_target; void osmo_init_ignore_signals(void); int osmo_init_logging(const struct log_info *); int osmo_daemonize(void); libosmocore-0.9.0/include/osmocom/core/backtrace.h000066400000000000000000000001421261607044000221510ustar00rootroot00000000000000#pragma once void osmo_generate_backtrace(void); void osmo_log_backtrace(int subsys, int level); libosmocore-0.9.0/include/osmocom/core/bitXXgen.h.tpl000066400000000000000000000061171261607044000225700ustar00rootroot00000000000000/* * bitXXgen.h * * Copyright (C) 2014 Max * * 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. */ #pragma once /*! \brief load unaligned n-byte integer (little-endian encoding) into uintXX_t * \param[in] p Buffer where integer is stored * \param[in] n Number of bytes stored in p * \returns XX bit unsigned integer */ static inline uintXX_t osmo_loadXXle_ext(const void *p, uint8_t n) { uint8_t i; uintXX_t r = 0; const uint8_t *q = (uint8_t *)p; for(i = 0; i < n; r |= ((uintXX_t)q[i] << (8 * i)), i++); return r; } /*! \brief load unaligned n-byte integer (big-endian encoding) into uintXX_t * \param[in] p Buffer where integer is stored * \param[in] n Number of bytes stored in p * \returns XX bit unsigned integer */ static inline uintXX_t osmo_loadXXbe_ext(const void *p, uint8_t n) { uint8_t i; uintXX_t r = 0; const uint8_t *q = (uint8_t *)p; for(i = 0; i < n; r |= ((uintXX_t)q[i] << (XX - 8* (1 + i))), i++); return r; } /*! \brief store unaligned n-byte integer (little-endian encoding) from uintXX_t * \param[in] x unsigned XX bit integer * \param[out] p Buffer to store integer * \param[in] n Number of bytes to store */ static inline void osmo_storeXXle_ext(uintXX_t x, void *p, uint8_t n) { uint8_t i; uint8_t *q = (uint8_t *)p; for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++); } /*! \brief store unaligned n-byte integer (big-endian encoding) from uintXX_t * \param[in] x unsigned XX bit integer * \param[out] p Buffer to store integer * \param[in] n Number of bytes to store */ static inline void osmo_storeXXbe_ext(uintXX_t x, void *p, uint8_t n) { uint8_t i; uint8_t *q = (uint8_t *)p; for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++); } /* Convenience function for most-used cases */ /*! \brief load unaligned XX-bit integer (little-endian encoding) */ static inline uintXX_t osmo_loadXXle(const void *p) { return osmo_loadXXle_ext(p, XX / 8); } /*! \brief load unaligned XX-bit integer (big-endian encoding) */ static inline uintXX_t osmo_loadXXbe(const void *p) { return osmo_loadXXbe_ext(p, XX / 8); } /*! \brief store unaligned XX-bit integer (little-endian encoding) */ static inline void osmo_storeXXle(uintXX_t x, void *p) { osmo_storeXXle_ext(x, p, XX / 8); } /*! \brief store unaligned XX-bit integer (big-endian encoding) */ static inline void osmo_storeXXbe(uintXX_t x, void *p) { osmo_storeXXbe_ext(x, p, XX / 8); } libosmocore-0.9.0/include/osmocom/core/bits.h000066400000000000000000000051001261607044000211720ustar00rootroot00000000000000#pragma once #include #include #include #include #include /*! \defgroup bits soft, unpacked and packed bits * @{ */ /*! \file bits.h * \brief Osmocom bit level support code */ typedef int8_t sbit_t; /*!< \brief soft bit (-127...127) */ typedef uint8_t ubit_t; /*!< \brief unpacked bit (0 or 1) */ typedef uint8_t pbit_t; /*!< \brief packed bis (8 bits in a byte) */ /* NOTE on the endianess of pbit_t: Bits in a pbit_t are ordered MSB first, i.e. 0x80 is the first bit. Bit i in a pbit_t array is array[i/8] & (1<<(7-i%8)) */ /*! \brief determine how many bytes we would need for \a num_bits packed bits * \param[in] num_bits Number of packed bits */ static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits) { unsigned int pbit_bytesize = num_bits / 8; if (num_bits % 8) pbit_bytesize++; return pbit_bytesize; } int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits); int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits); int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, const ubit_t *in, unsigned int in_ofs, unsigned int num_bits, int lsb_mode); int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, const pbit_t *in, unsigned int in_ofs, unsigned int num_bits, int lsb_mode); /* BIT REVERSAL */ /*! \brief bit-reversal mode for osmo_bit_reversal() */ enum osmo_br_mode { /*! \brief reverse all bits in a 32bit dword */ OSMO_BR_BITS_IN_DWORD = 31, /*! \brief reverse byte order in a 32bit dword */ OSMO_BR_BYTES_IN_DWORD = 24, /*! \brief reverse bits of each byte in a 32bit dword */ OSMO_BR_BITS_IN_BYTE = 7, /*! \brief swap the two 16bit words in a 32bit dword */ OSMO_BR_WORD_SWAP = 16, }; /*! \brief generic bit reversal function */ uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k); /* \brief reverse the bits within each byte of a 32bit word */ uint32_t osmo_revbytebits_32(uint32_t x); /* \brief reverse the bits within a byte */ uint32_t osmo_revbytebits_8(uint8_t x); /* \brief reverse the bits of each byte in a given buffer */ void osmo_revbytebits_buf(uint8_t *buf, int len); /*! \brief left circular shift * \param[in] in The 16 bit unsigned integer to be rotated * \param[in] shift Number of bits to shift \a in to, [0;16] bits * \returns shifted value */ static inline uint16_t osmo_rol16(uint16_t in, unsigned shift) { return (in << shift) | (in >> (16 - shift)); } /*! @} */ libosmocore-0.9.0/include/osmocom/core/bitvec.h000066400000000000000000000045051261607044000215150ustar00rootroot00000000000000#pragma once /* bit vector utility routines */ /* (C) 2009 by Harald Welte * * 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. * */ /*! \defgroup bitvec Bit vectors * @{ */ /*! \file bitvec.h * \brief Osmocom bit vector abstraction */ #include /*! \brief A single GSM bit * * 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, /*!< \brief A zero (0) bit */ ONE = 1, /*!< \brief A one (1) bit */ L = 2, /*!< \brief A CSN.1 "L" bit */ H = 3, /*!< \brief A CSN.1 "H" bit */ }; /*! \brief structure describing a bit vector */ struct bitvec { unsigned int cur_bit; /*!< \brief cursor to the next unused bit */ unsigned int data_len; /*!< \brief length of data array in bytes */ uint8_t *data; /*!< \brief pointer to data array */ }; enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr); enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, unsigned int bitnr); unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n); int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum, enum bit_value bit); int bitvec_set_bit(struct bitvec *bv, enum bit_value bit); int bitvec_get_bit_high(struct bitvec *bv); int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count); int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count); int bitvec_get_uint(struct bitvec *bv, int num_bits); int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val); int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); /*! @} */ libosmocore-0.9.0/include/osmocom/core/conv.h000066400000000000000000000115121261607044000212020ustar00rootroot00000000000000/* * conv.h * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \defgroup conv Convolutional encoding and decoding routines * @{ */ /*! \file conv.h * Osmocom convolutional encoder and decoder */ #pragma once #include #include /*! \brief possibe termination types * * The termination type will determine which state the encoder/decoder * can start/end with. This is mostly taken care of in the high level API * call. So if you use the low level API, you must take care of making the * proper calls yourself. */ enum osmo_conv_term { CONV_TERM_FLUSH = 0, /*!< \brief Flush encoder state */ CONV_TERM_TRUNCATION, /*!< \brief Direct truncation */ CONV_TERM_TAIL_BITING, /*!< \brief Tail biting */ }; /*! \brief structure describing a given convolutional code * * The only required fields are N,K and the next_output/next_state arrays. The * other can be left to default value of zero depending on what the code does. * If 'len' is left at 0 then only the low level API can be used. */ struct osmo_conv_code { int N; /*!< \brief Inverse of code rate */ int K; /*!< \brief Constraint length */ int len; /*!< \brief # of data bits */ enum osmo_conv_term term; /*!< \brief Termination type */ const uint8_t (*next_output)[2];/*!< \brief Next output array */ const uint8_t (*next_state)[2]; /*!< \brief Next state array */ const uint8_t *next_term_output;/*!< \brief Flush termination output */ const uint8_t *next_term_state; /*!< \brief Flush termination state */ const int *puncture; /*!< \brief Punctured bits indexes */ }; /* Common */ int osmo_conv_get_input_length(const struct osmo_conv_code *code, int len); int osmo_conv_get_output_length(const struct osmo_conv_code *code, int len); /* Encoding */ /* Low level API */ /*! \brief convolutional encoder state */ struct osmo_conv_encoder { const struct osmo_conv_code *code; /*!< \brief for which code? */ int i_idx; /*!< \brief Next input bit index */ int p_idx; /*!< \brief Current puncture index */ uint8_t state; /*!< \brief Current state */ }; void osmo_conv_encode_init(struct osmo_conv_encoder *encoder, const struct osmo_conv_code *code); void osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder, const ubit_t *input); int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, const ubit_t *input, ubit_t *output, int n); int osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output); /* All-in-one */ int osmo_conv_encode(const struct osmo_conv_code *code, const ubit_t *input, ubit_t *output); /* Decoding */ /* Low level API */ /*! \brief convolutional decoder state */ struct osmo_conv_decoder { const struct osmo_conv_code *code; /*!< \brief for which code? */ int n_states; /*!< \brief number of states */ int len; /*!< \brief Max o_idx (excl. termination) */ int o_idx; /*!< \brief output index */ int p_idx; /*!< \brief puncture index */ unsigned int *ae; /*!< \brief accumulated error */ unsigned int *ae_next; /*!< \brief next accumulated error (tmp in scan) */ uint8_t *state_history; /*!< \brief state history [len][n_states] */ }; void osmo_conv_decode_init(struct osmo_conv_decoder *decoder, const struct osmo_conv_code *code, int len, int start_state); void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state); void osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder); void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder); int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, const sbit_t *input, int n); int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, const sbit_t *input); int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, ubit_t *output, int has_flush, int end_state); /* All-in-one */ int osmo_conv_decode(const struct osmo_conv_code *code, const sbit_t *input, ubit_t *output); /*! @} */ libosmocore-0.9.0/include/osmocom/core/crc16.h000066400000000000000000000013241261607044000211530ustar00rootroot00000000000000/* * This was copied from the linux kernel and adjusted for our types. */ /* * crc16.h - CRC-16 routine * * Implements the standard CRC-16: * Width 16 * Poly 0x8005 (x^16 + x^15 + x^2 + 1) * Init 0 * * Copyright (c) 2005 Ben Gardner * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #pragma once #include #include extern uint16_t const osmo_crc16_table[256]; extern uint16_t osmo_crc16(uint16_t crc, const uint8_t *buffer, size_t len); static inline uint16_t osmo_crc16_byte(uint16_t crc, const uint8_t data) { return (crc >> 8) ^ osmo_crc16_table[(crc ^ data) & 0xff]; } libosmocore-0.9.0/include/osmocom/core/crcXXgen.h.tpl000066400000000000000000000035231261607044000225570ustar00rootroot00000000000000/* * crcXXgen.h * * Copyright (C) 2011 Sylvain Munaut * * 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. */ #pragma once /*! \addtogroup crcgen * @{ */ /*! \file crcXXgen.h * Osmocom generic CRC routines (for max XX bits poly) header */ #include #include /*! \brief structure describing a given CRC code of max XX bits */ struct osmo_crcXXgen_code { int bits; /*!< \brief Actual number of bits of the CRC */ uintXX_t poly; /*!< \brief Polynom (normal representation, MSB omitted */ uintXX_t init; /*!< \brief Initialization value of the CRC state */ uintXX_t remainder; /*!< \brief Remainder of the CRC (final XOR) */ }; uintXX_t osmo_crcXXgen_compute_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len); int osmo_crcXXgen_check_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len, const ubit_t *crc_bits); void osmo_crcXXgen_set_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len, ubit_t *crc_bits); /*! @} */ /* vim: set syntax=c: */ libosmocore-0.9.0/include/osmocom/core/crcgen.h000066400000000000000000000021401261607044000214730ustar00rootroot00000000000000/* * crcgen.h * * Copyright (C) 2011 Sylvain Munaut * * 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. */ #pragma once /*! \defgroup crcgen Osmocom generic CRC routines * @{ */ /*! \file crcgen.h * Osmocom generic CRC routines global header */ #include #include #include #include /*! @} */ libosmocore-0.9.0/include/osmocom/core/defs.h000066400000000000000000000027051261607044000211620ustar00rootroot00000000000000#pragma once /*! \defgroup utils General-purpose utility functions * @{ */ /*! \file defs.h * \brief General definitions that are meant to be included from header files. */ /*! \brief Check for gcc and version. * * \note Albeit glibc provides a features.h file that contains a similar * definition (__GNUC_PREREQ), this definition has been copied from there * to have it available with other libraries, too. * * \return != 0 iff gcc is used and it's version is at least maj.min. */ #if defined __GNUC__ && defined __GNUC_MINOR__ # define OSMO_GNUC_PREREQ(maj, min) \ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) #else # define OSMO_GNUC_PREREQ(maj, min) 0 #endif /*! \brief Set the deprecated attribute with a message. */ #if defined(__clang__) # define _OSMO_HAS_ATTRIBUTE_DEPRECATED __has_attribute(deprecated) # define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE __has_extension(attribute_deprecated_with_message) #elif defined(__GNUC__) # define _OSMO_HAS_ATTRIBUTE_DEPRECATED 1 # define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE OSMO_GNUC_PREREQ(4,5) #endif #if _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE # define OSMO_DEPRECATED(text) __attribute__((__deprecated__(text))) #elif _OSMO_HAS_ATTRIBUTE_DEPRECATED # define OSMO_DEPRECATED(text) __attribute__((__deprecated__)) #else # define OSMO_DEPRECATED(text) #endif #undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE #undef _OSMO_HAS_ATTRIBUTE_DEPRECATED /*! @} */ libosmocore-0.9.0/include/osmocom/core/endian.h000066400000000000000000000022411261607044000214720ustar00rootroot00000000000000#pragma once /** * GNU and FreeBSD have various ways to express the * endianess but none of them is similiar enough. This * will create two defines that allows to decide on the * endian. The following will be defined to either 0 or * 1 at the end of the file. * * OSMO_IS_LITTLE_ENDIAN * OSMO_IS_BIG_ENDIAN * */ #if defined(__FreeBSD__) #include #if BYTE_ORDER == LITTLE_ENDIAN #define OSMO_IS_LITTLE_ENDIAN 1 #define OSMO_IS_BIG_ENDIAN 0 #elif BYTE_ORDER == BIG_ENDIAN #define OSMO_IS_LITTLE_ENDIAN 0 #define OSMO_IS_BIG_ENDIAN 1 #else #error "Unknown endian" #endif #else #include #if __BYTE_ORDER == __LITTLE_ENDIAN #define OSMO_IS_LITTLE_ENDIAN 1 #define OSMO_IS_BIG_ENDIAN 0 #elif __BYTE_ORDER == __BIG_ENDIAN #define OSMO_IS_LITTLE_ENDIAN 0 #define OSMO_IS_BIG_ENDIAN 1 #else #error "Unknown endian" #endif #endif libosmocore-0.9.0/include/osmocom/core/gsmtap.h000066400000000000000000000205311261607044000215310ustar00rootroot00000000000000#pragma once /* 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 /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ /* The GSMTAP format definition is maintained in libosmocore, * specifically the latest version can always be obtained from * http://cgit.osmocom.org/cgit/libosmocore/tree/include/osmocom/core/gsmtap.h * * If you want to introduce new protocol/burst/channel types or extend * GSMTAP in any way, please contact the GSMTAP maintainer at either the * public openbsc@lists.osmocom.org mailing list, or privately at * Harald Welte . * * Your cooperation ensures that all projects will use the same GSMTAP * definitions and remain compatible with each other. */ #define GSMTAP_VERSION 0x02 #define GSMTAP_TYPE_UM 0x01 #define GSMTAP_TYPE_ABIS 0x02 #define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ #define GSMTAP_TYPE_SIM 0x04 #define GSMTAP_TYPE_TETRA_I1 0x05 /* tetra air interface */ #define GSMTAP_TYPE_TETRA_I1_BURST 0x06 /* tetra air interface */ #define GSMTAP_TYPE_WMX_BURST 0x07 /* WiMAX burst */ #define GSMTAP_TYPE_GB_LLC 0x08 /* GPRS Gb interface: LLC */ #define GSMTAP_TYPE_GB_SNDCP 0x09 /* GPRS Gb interface: SNDCP */ #define GSMTAP_TYPE_GMR1_UM 0x0a /* GMR-1 L2 packets */ #define GSMTAP_TYPE_UMTS_RLC_MAC 0x0b #define GSMTAP_TYPE_UMTS_RRC 0x0c #define GSMTAP_TYPE_LTE_RRC 0x0d /* LTE interface */ #define GSMTAP_TYPE_LTE_MAC 0x0e /* LTE MAC interface */ /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ /* sub-types for TYPE_UM_BURST */ #define GSMTAP_BURST_UNKNOWN 0x00 #define GSMTAP_BURST_FCCH 0x01 #define GSMTAP_BURST_PARTIAL_SCH 0x02 #define GSMTAP_BURST_SCH 0x03 #define GSMTAP_BURST_CTS_SCH 0x04 #define GSMTAP_BURST_COMPACT_SCH 0x05 #define GSMTAP_BURST_NORMAL 0x06 #define GSMTAP_BURST_DUMMY 0x07 #define GSMTAP_BURST_ACCESS 0x08 #define GSMTAP_BURST_NONE 0x09 /* WiMAX bursts */ #define GSMTAP_BURST_CDMA_CODE 0x10 /* WiMAX CDMA Code Attribute burst */ #define GSMTAP_BURST_FCH 0x11 /* WiMAX FCH burst */ #define GSMTAP_BURST_FFB 0x12 /* WiMAX Fast Feedback burst */ #define GSMTAP_BURST_PDU 0x13 /* WiMAX PDU burst */ #define GSMTAP_BURST_HACK 0x14 /* WiMAX HARQ ACK burst */ #define GSMTAP_BURST_PHY_ATTRIBUTES 0x15 /* WiMAX PHY Attributes burst */ /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ /* sub-types for TYPE_UM */ #define GSMTAP_CHANNEL_UNKNOWN 0x00 #define GSMTAP_CHANNEL_BCCH 0x01 #define GSMTAP_CHANNEL_CCCH 0x02 #define GSMTAP_CHANNEL_RACH 0x03 #define GSMTAP_CHANNEL_AGCH 0x04 #define GSMTAP_CHANNEL_PCH 0x05 #define GSMTAP_CHANNEL_SDCCH 0x06 #define GSMTAP_CHANNEL_SDCCH4 0x07 #define GSMTAP_CHANNEL_SDCCH8 0x08 #define GSMTAP_CHANNEL_TCH_F 0x09 #define GSMTAP_CHANNEL_TCH_H 0x0a #define GSMTAP_CHANNEL_PACCH 0x0b #define GSMTAP_CHANNEL_CBCH52 0x0c #define GSMTAP_CHANNEL_PDCH 0x0d #define GSMTAP_CHANNEL_PTCCH 0x0e #define GSMTAP_CHANNEL_CBCH51 0x0f /* GPRS Coding Scheme CS1..4 */ #define GSMTAP_GPRS_CS_BASE 0x20 #define GSMTAP_GPRS_CS(N) (GSMTAP_GPRS_CS_BASE + N) /* (E) GPRS Coding Scheme MCS0..9 */ #define GSMTAP_GPRS_MCS_BASE 0x30 #define GSMTAP_GPRS_MCS(N) (GSMTAP_GPRS_MCS_BASE + N) #define GSMTAP_CHANNEL_ACCH 0x80 /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ /* sub-types for TYPE_TETRA_AIR */ #define GSMTAP_TETRA_BSCH 0x01 #define GSMTAP_TETRA_AACH 0x02 #define GSMTAP_TETRA_SCH_HU 0x03 #define GSMTAP_TETRA_SCH_HD 0x04 #define GSMTAP_TETRA_SCH_F 0x05 #define GSMTAP_TETRA_BNCH 0x06 #define GSMTAP_TETRA_STCH 0x07 #define GSMTAP_TETRA_TCH_F 0x08 /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ /* sub-types for TYPE_GMR1_UM */ #define GSMTAP_GMR1_UNKNOWN 0x00 #define GSMTAP_GMR1_BCCH 0x01 #define GSMTAP_GMR1_CCCH 0x02 /* either AGCH or PCH */ #define GSMTAP_GMR1_PCH 0x03 #define GSMTAP_GMR1_AGCH 0x04 #define GSMTAP_GMR1_BACH 0x05 #define GSMTAP_GMR1_RACH 0x06 #define GSMTAP_GMR1_CBCH 0x07 #define GSMTAP_GMR1_SDCCH 0x08 #define GSMTAP_GMR1_TACCH 0x09 #define GSMTAP_GMR1_GBCH 0x0a #define GSMTAP_GMR1_SACCH 0x01 /* to be combined with _TCH{6,9} */ #define GSMTAP_GMR1_FACCH 0x02 /* to be combines with _TCH{3,6,9} */ #define GSMTAP_GMR1_DKAB 0x03 /* to be combined with _TCH3 */ #define GSMTAP_GMR1_TCH3 0x10 #define GSMTAP_GMR1_TCH6 0x14 #define GSMTAP_GMR1_TCH9 0x18 /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ #define GSMTAP_UMTS_CH_PCCH 0x01 #define GSMTAP_UMTS_CH_CCCH 0x02 #define GSMTAP_UMTS_CH_DCCH 0x03 /* sub-types for TYPE_LTE_RRC */ #define GSMTAP_LTE_CH_BCCH 0x01 #define GSMTAP_LTE_CH_CCCH 0x02 #define GSMTAP_LTE_CH_DCCH 0x03 #define GSMTAP_LTE_CH_MCCH 0x04 #define GSMTAP_LTE_CH_PCCH 0x05 #define GSMTAP_LTE_CH_DTCH 0x06 #define GSMTAP_LTE_CH_MTCH 0x07 /* flags for the ARFCN */ #define GSMTAP_ARFCN_F_PCS 0x8000 #define GSMTAP_ARFCN_F_UPLINK 0x4000 #define GSMTAP_ARFCN_MASK 0x3fff /* IANA-assigned well-known UDP port for GSMTAP messages */ #define GSMTAP_UDP_PORT 4729 /* UMTS RRC message types */ enum { GSMTAP_RRC_SUB_DL_DCCH_Message = 0, GSMTAP_RRC_SUB_UL_DCCH_Message, GSMTAP_RRC_SUB_DL_CCCH_Message, GSMTAP_RRC_SUB_UL_CCCH_Message, GSMTAP_RRC_SUB_PCCH_Message, GSMTAP_RRC_SUB_DL_SHCCH_Message, GSMTAP_RRC_SUB_UL_SHCCH_Message, GSMTAP_RRC_SUB_BCCH_FACH_Message, GSMTAP_RRC_SUB_BCCH_BCH_Message, GSMTAP_RRC_SUB_MCCH_Message, GSMTAP_RRC_SUB_MSCH_Message, GSMTAP_RRC_SUB_HandoverToUTRANCommand, GSMTAP_RRC_SUB_InterRATHandoverInfo, GSMTAP_RRC_SUB_SystemInformation_BCH, GSMTAP_RRC_SUB_System_Information_Container, GSMTAP_RRC_SUB_UE_RadioAccessCapabilityInfo, GSMTAP_RRC_SUB_MasterInformationBlock, GSMTAP_RRC_SUB_SysInfoType1, GSMTAP_RRC_SUB_SysInfoType2, GSMTAP_RRC_SUB_SysInfoType3, GSMTAP_RRC_SUB_SysInfoType4, GSMTAP_RRC_SUB_SysInfoType5, GSMTAP_RRC_SUB_SysInfoType5bis, GSMTAP_RRC_SUB_SysInfoType6, GSMTAP_RRC_SUB_SysInfoType7, GSMTAP_RRC_SUB_SysInfoType8, GSMTAP_RRC_SUB_SysInfoType9, GSMTAP_RRC_SUB_SysInfoType10, GSMTAP_RRC_SUB_SysInfoType11, GSMTAP_RRC_SUB_SysInfoType11bis, GSMTAP_RRC_SUB_SysInfoType12, GSMTAP_RRC_SUB_SysInfoType13, GSMTAP_RRC_SUB_SysInfoType13_1, GSMTAP_RRC_SUB_SysInfoType13_2, GSMTAP_RRC_SUB_SysInfoType13_3, GSMTAP_RRC_SUB_SysInfoType13_4, GSMTAP_RRC_SUB_SysInfoType14, GSMTAP_RRC_SUB_SysInfoType15, GSMTAP_RRC_SUB_SysInfoType15bis, GSMTAP_RRC_SUB_SysInfoType15_1, GSMTAP_RRC_SUB_SysInfoType15_1bis, GSMTAP_RRC_SUB_SysInfoType15_2, GSMTAP_RRC_SUB_SysInfoType15_2bis, GSMTAP_RRC_SUB_SysInfoType15_2ter, GSMTAP_RRC_SUB_SysInfoType15_3, GSMTAP_RRC_SUB_SysInfoType15_3bis, GSMTAP_RRC_SUB_SysInfoType15_4, GSMTAP_RRC_SUB_SysInfoType15_5, GSMTAP_RRC_SUB_SysInfoType15_6, GSMTAP_RRC_SUB_SysInfoType15_7, GSMTAP_RRC_SUB_SysInfoType15_8, GSMTAP_RRC_SUB_SysInfoType16, GSMTAP_RRC_SUB_SysInfoType17, GSMTAP_RRC_SUB_SysInfoType18, GSMTAP_RRC_SUB_SysInfoType19, GSMTAP_RRC_SUB_SysInfoType20, GSMTAP_RRC_SUB_SysInfoType21, GSMTAP_RRC_SUB_SysInfoType22, GSMTAP_RRC_SUB_SysInfoTypeSB1, GSMTAP_RRC_SUB_SysInfoTypeSB2, GSMTAP_RRC_SUB_ToTargetRNC_Container, GSMTAP_RRC_SUB_TargetRNC_ToSourceRNC_Container, GSMTAP_RRC_SUB_MAX }; /* LTE RRC message types */ enum { GSMTAP_LTE_RRC_SUB_DL_CCCH_Message = 0, GSMTAP_LTE_RRC_SUB_DL_DCCH_Message, GSMTAP_LTE_RRC_SUB_UL_CCCH_Message, GSMTAP_LTE_RRC_SUB_UL_DCCH_Message, GSMTAP_LTE_RRC_SUB_BCCH_BCH_Message, GSMTAP_LTE_RRC_SUB_BCCH_DL_SCH_Message, GSMTAP_LTE_RRC_SUB_PCCH_Message, GSMTAP_LTE_RRC_SUB_MCCH_Message, GSMTAP_LTE_RRC_SUB_MAX }; /* ====== DO NOT MAKE UNAPPROVED MODIFICATIONS HERE ===== */ 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)); libosmocore-0.9.0/include/osmocom/core/gsmtap_util.h000066400000000000000000000033271261607044000225720ustar00rootroot00000000000000#pragma once #include #include #include /*! \defgroup gsmtap GSMTAP * @{ */ /*! \file gsmtap_util.h */ uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id); struct msgb *gsmtap_makemsg_ex(uint8_t type, 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 *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); /*! \brief one gsmtap instance */ struct gsmtap_inst { int ofd_wq_mode; /*!< \brief wait queue mode? */ struct osmo_wqueue wq; /*!< \brief the wait queue */ struct osmo_fd sink_ofd;/*!< \brief file descriptor */ }; /*! \brief obtain the file descriptor associated with a gsmtap instance */ static inline int gsmtap_inst_fd(struct gsmtap_inst *gti) { return gti->wq.bfd.fd; } int gsmtap_source_init_fd(const char *host, uint16_t port); int gsmtap_source_add_sink_fd(int gsmtap_fd); struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, int ofd_wq_mode); int gsmtap_source_add_sink(struct gsmtap_inst *gti); int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg); int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, 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_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, uint8_t snr, const uint8_t *data, unsigned int len); /*! @} */ libosmocore-0.9.0/include/osmocom/core/linuxlist.h000066400000000000000000000256011261607044000222740ustar00rootroot00000000000000#pragma once #include #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 = (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 non-consecutive(!) llist entries * @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)) libosmocore-0.9.0/include/osmocom/core/linuxrbtree.h000066400000000000000000000113301261607044000225760ustar00rootroot00000000000000/* Red Black Trees (C) 1999 Andrea Arcangeli This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. This will avoid us to use callbacks and to drop drammatically performances. I know it's not the cleaner way, but in C (not in C++) to get performances and genericity... Some example of insert and search follows here. The search is a plain normal search over an ordered tree. The insert instead must be implemented int two steps: as first thing the code must insert the element in order as a red leaf in the tree, then the support library function rb_insert_color() must be called. Such function will do the not trivial work to rebalance the rbtree if necessary. ----------------------------------------------------------------------- static inline struct page * rb_search_page_cache(struct inode * inode, unsigned long offset) { struct rb_node * n = inode->i_rb_page_cache.rb_node; struct page * page; while (n) { page = rb_entry(n, struct page, rb_page_cache); if (offset < page->offset) n = n->rb_left; else if (offset > page->offset) n = n->rb_right; else return page; } return NULL; } static inline struct page * __rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct rb_node ** p = &inode->i_rb_page_cache.rb_node; struct rb_node * parent = NULL; struct page * page; while (*p) { parent = *p; page = rb_entry(parent, struct page, rb_page_cache); if (offset < page->offset) p = &(*p)->rb_left; else if (offset > page->offset) p = &(*p)->rb_right; else return page; } rb_link_node(node, parent, p); return NULL; } static inline struct page * rb_insert_page_cache(struct inode * inode, unsigned long offset, struct rb_node * node) { struct page * ret; if ((ret = __rb_insert_page_cache(inode, offset, node))) goto out; rb_insert_color(node, &inode->i_rb_page_cache); out: return ret; } ----------------------------------------------------------------------- */ #pragma once #include struct rb_node { unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long)))); /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) #define rb_color(r) ((r)->rb_parent_color & 1) #define rb_is_red(r) (!rb_color(r)) #define rb_is_black(r) rb_color(r) #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; } static inline void rb_set_color(struct rb_node *rb, int color) { rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; } #define RB_ROOT { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) #define RB_EMPTY_NODE(node) (rb_parent(node) == node) #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); /* Find logical next and previous nodes in a tree */ extern struct rb_node *rb_next(const struct rb_node *); extern struct rb_node *rb_prev(const struct rb_node *); extern struct rb_node *rb_first(const struct rb_root *); extern struct rb_node *rb_last(const struct rb_root *); /* Fast replacement of a single node without remove/rebalance/add/rebalance */ extern void rb_replace_node(struct rb_node *victim, struct rb_node *_new, struct rb_root *root); static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; *rb_link = node; } libosmocore-0.9.0/include/osmocom/core/logging.h000066400000000000000000000164421261607044000216720ustar00rootroot00000000000000#pragma once /*! \defgroup logging Osmocom logging framework * @{ */ /*! \file logging.h */ #include #include #include #include /*! \brief Maximum number of logging contexts */ #define LOG_MAX_CTX 8 /*! \brief Maximum number of logging filters */ #define LOG_MAX_FILTERS 8 #define DEBUG #ifdef DEBUG #define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args) #define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args) #else #define DEBUGP(xss, fmt, args...) #define DEBUGPC(ss, fmt, args...) #endif void osmo_vlogp(int subsys, int level, const char *file, int line, int cont, const char *format, va_list ap); void logp(int subsys, const char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); /*! \brief Log a new message through the Osmocom logging framework * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL) * \param[in] level logging level (e.g. \ref LOGL_NOTICE) * \param[in] fmt format string * \param[in] args variable argument list */ #define LOGP(ss, level, fmt, args...) \ logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) /*! \brief Continue a log message through the Osmocom logging framework * \param[in] ss logging subsystem (e.g. \ref DLGLOBAL) * \param[in] level logging level (e.g. \ref LOGL_NOTICE) * \param[in] fmt format string * \param[in] args variable argument list */ #define LOGPC(ss, level, fmt, args...) \ logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) /*! \brief different log levels */ #define LOGL_DEBUG 1 /*!< \brief debugging information */ #define LOGL_INFO 3 #define LOGL_NOTICE 5 /*!< \brief abnormal/unexpected condition */ #define LOGL_ERROR 7 /*!< \brief error condition, requires user action */ #define LOGL_FATAL 8 /*!< \brief fatal, program aborted */ #define LOG_FILTER_ALL 0x0001 /* logging levels defined by the library itself */ #define DLGLOBAL -1 #define DLLAPD -2 #define DLINP -3 #define DLMUX -4 #define DLMI -5 #define DLMIB -6 #define DLSMS -7 #define DLCTRL -8 #define DLGTP -9 #define DLSTATS -10 #define OSMO_NUM_DLIB 10 struct log_category { uint8_t loglevel; uint8_t enabled; }; /*! \brief Information regarding one logging category */ struct log_info_cat { const char *name; /*!< name of category */ const char *color; /*!< color string for cateyory */ const char *description; /*!< description text */ uint8_t loglevel; /*!< currently selected log-level */ uint8_t enabled; /*!< is this category enabled or not */ }; /*! \brief Log context information, passed to filter */ struct log_context { void *ctx[LOG_MAX_CTX+1]; }; struct log_target; /*! \brief Log filter function */ typedef int log_filter(const struct log_context *ctx, struct log_target *target); struct log_info; struct vty; typedef void log_print_filters(struct vty *vty, const struct log_info *info, const struct log_target *tgt); typedef void log_save_filters(struct vty *vty, const struct log_info *info, const struct log_target *tgt); /*! \brief Logging configuration, passed to \ref log_init */ struct log_info { /* \brief filter callback function */ log_filter *filter_fn; /*! \brief per-category information */ const struct log_info_cat *cat; /*! \brief total number of categories */ unsigned int num_cat; /*! \brief total number of user categories (not library) */ unsigned int num_cat_user; /* \brief filter saving function */ log_save_filters *save_fn; /* \brief filter saving function */ log_print_filters *print_fn; }; /*! \brief Type of logging target */ enum log_target_type { LOG_TGT_TYPE_VTY, /*!< \brief VTY logging */ LOG_TGT_TYPE_SYSLOG, /*!< \brief syslog based logging */ LOG_TGT_TYPE_FILE, /*!< \brief text file logging */ LOG_TGT_TYPE_STDERR, /*!< \brief stderr logging */ LOG_TGT_TYPE_STRRB, /*!< \brief osmo_strrb-backed logging */ }; /*! \brief structure representing a logging target */ struct log_target { struct llist_head entry; /*!< \brief linked list */ /*! \brief Internal data for filtering */ int filter_map; /*! \brief Internal data for filtering */ void *filter_data[LOG_MAX_FILTERS+1]; /*! \brief logging categories */ struct log_category *categories; /*! \brief global log level */ uint8_t loglevel; /*! \brief should color be used when printing log messages? */ unsigned int use_color:1; /*! \brief should log messages be prefixed with a timestamp? */ unsigned int print_timestamp:1; /*! \brief should log messages be prefixed with a filename? */ unsigned int print_filename:1; /*! \brief should log messages be prefixed with a category name? */ unsigned int print_category:1; /*! \brief should log messages be prefixed with an extended timestamp? */ unsigned int print_ext_timestamp:1; /*! \brief the type of this log taget */ enum log_target_type type; union { struct { FILE *out; const char *fname; } tgt_file; struct { int priority; int facility; } tgt_syslog; struct { void *vty; } tgt_vty; struct { void *rb; } tgt_rb; }; /*! \brief call-back function to be called when the logging framework * wants to log somethnig. * \param[in] target logging target * \param[in] level log level of currnet message * \param[in] string the string that is to be written to the log */ void (*output) (struct log_target *target, unsigned int level, const char *string); }; /* use the above macros */ void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7))); int log_init(const struct log_info *inf, void *talloc_ctx); /* 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_extended_timestamp(struct log_target *target, int); void log_set_print_timestamp(struct log_target *target, int); void log_set_print_filename(struct log_target *target, int); void log_set_print_category(struct log_target *target, int); void log_set_log_level(struct log_target *target, int log_level); void log_parse_category_mask(struct log_target *target, const char* mask); int log_parse_level(const char *lvl); const char *log_level_str(unsigned int lvl); int log_parse_category(const char *category); void log_set_category_filter(struct log_target *target, int category, int enable, int level); /* management of the targets */ struct log_target *log_target_create(void); void log_target_destroy(struct log_target *target); struct log_target *log_target_create_stderr(void); struct log_target *log_target_create_file(const char *fname); struct log_target *log_target_create_syslog(const char *ident, int option, int facility); int log_target_file_reopen(struct log_target *tgt); int log_targets_reopen(void); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); /* Generate command string for VTY use */ const char *log_vty_command_string(const struct log_info *info); const char *log_vty_command_description(const struct log_info *info); struct log_target *log_target_find(int type, const char *fname); extern struct llist_head osmo_log_target_list; /*! @} */ libosmocore-0.9.0/include/osmocom/core/loggingrb.h000066400000000000000000000023051261607044000222070ustar00rootroot00000000000000#pragma once /* (C) 2012-2013 by Katerina Barone-Adesi * 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. * */ /*! \defgroup loggingrb Osmocom ringbuffer-backed logging * @{ */ /*! \file loggingrb.h */ struct log_info; size_t log_target_rb_used_size(struct log_target const *target); size_t log_target_rb_avail_size(struct log_target const *target); const char *log_target_rb_get(struct log_target const *target, size_t logindex); struct log_target *log_target_create_rb(size_t size); /*! @} */ libosmocore-0.9.0/include/osmocom/core/macaddr.h000066400000000000000000000002021261607044000216220ustar00rootroot00000000000000#pragma once int osmo_macaddr_parse(uint8_t *out, const char *in); int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name); libosmocore-0.9.0/include/osmocom/core/msgb.h000066400000000000000000000314201261607044000211650ustar00rootroot00000000000000#pragma once /* (C) 2008 by Harald Welte * 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 #include #include #include /*! \defgroup msgb Message buffers * @{ */ /*! \file msgb.h * \brief Osmocom message buffers * The Osmocom message buffers are modelled after the 'struct skb' * inside the Linux kernel network stack. As they exist in userspace, * they are much simplified. However, terminology such as headroom, * tailroom, push/pull/put etc. remains the same. */ #define MSGB_DEBUG /*! \brief Osmocom message buffer */ struct msgb { struct llist_head list; /*!< \brief linked list header */ /* Part of which TRX logical channel we were received / transmitted */ /* FIXME: move them into the control buffer */ union { void *dst; /*!< \brief reference of origin/destination */ struct gsm_bts_trx *trx; }; struct gsm_lchan *lchan; /*!< \brief logical channel */ unsigned char *l1h; /*!< \brief pointer to Layer1 header (if any) */ unsigned char *l2h; /*!< \brief pointer to A-bis layer 2 header: OML, RSL(RLL), NS */ unsigned char *l3h; /*!< \brief pointer to Layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ unsigned char *l4h; /*!< \brief pointer to layer 4 header */ unsigned long cb[5]; /*!< \brief control buffer */ uint16_t data_len; /*!< \brief length of underlying data array */ uint16_t len; /*!< \brief length of bytes used in msgb */ unsigned char *head; /*!< \brief start of underlying memory buffer */ unsigned char *tail; /*!< \brief end of message in buffer */ unsigned char *data; /*!< \brief start of message in buffer */ unsigned char _data[0]; /*!< \brief optional immediate data array */ }; 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); uint16_t msgb_length(const struct msgb *msg); extern const char *msgb_hexdump(const struct msgb *msg); #ifdef MSGB_DEBUG #include #define MSGB_ABORT(msg, fmt, args ...) do { \ osmo_panic("msgb(%p): " fmt, msg, ## args); \ } while(0) #else #define MSGB_ABORT(msg, fmt, args ...) #endif /*! \brief obtain L1 header of msgb */ #define msgb_l1(m) ((void *)(m->l1h)) /*! \brief obtain L2 header of msgb */ #define msgb_l2(m) ((void *)(m->l2h)) /*! \brief obtain L3 header of msgb */ #define msgb_l3(m) ((void *)(m->l3h)) /*! \brief obtain SMS header of msgb */ #define msgb_sms(m) ((void *)(m->l4h)) /*! \brief determine length of L1 message * \param[in] msgb message buffer * \returns size of L1 message in bytes * * This function computes the number of bytes between the tail of the * message and the layer 1 header. */ static inline unsigned int msgb_l1len(const struct msgb *msgb) { return msgb->tail - (uint8_t *)msgb_l1(msgb); } /*! \brief determine length of L2 message * \param[in] msgb message buffer * \returns size of L2 message in bytes * * This function computes the number of bytes between the tail of the * message and the layer 2 header. */ static inline unsigned int msgb_l2len(const struct msgb *msgb) { return msgb->tail - (uint8_t *)msgb_l2(msgb); } /*! \brief determine length of L3 message * \param[in] msgb message buffer * \returns size of L3 message in bytes * * This function computes the number of bytes between the tail of the * message and the layer 3 header. */ static inline unsigned int msgb_l3len(const struct msgb *msgb) { return msgb->tail - (uint8_t *)msgb_l3(msgb); } /*! \brief determine the length of the header * \param[in] msgb message buffer * \returns number of bytes between start of buffer and start of msg * * This function computes the length difference between the underlying * data buffer and the used section of the \a msgb. */ static inline unsigned int msgb_headlen(const struct msgb *msgb) { return msgb->len - msgb->data_len; } /*! \brief determine how much tail room is left in msgb * \param[in] msgb message buffer * \returns number of bytes remaining at end of msgb * * This function computes the amount of octets left in the underlying * data buffer after the end of the message. */ static inline int msgb_tailroom(const struct msgb *msgb) { return (msgb->head + msgb->data_len) - msgb->tail; } /*! \brief determine the amount of headroom in msgb * \param[in] msgb message buffer * \returns number of bytes left ahead of message start in msgb * * This function computes the amount of bytes left in the underlying * data buffer before the start of the actual message. */ static inline int msgb_headroom(const struct msgb *msgb) { return (msgb->data - msgb->head); } /*! \brief append data to end of message buffer * \param[in] msgb message buffer * \param[in] len number of bytes to append to message * \returns pointer to start of newly-appended data * * This function will move the \a tail pointer of the message buffer \a * len bytes further, thus enlarging the message by \a len bytes. * * The return value is a pointer to start of the newly added section at * the end of the message and can be used for actually filling/copying * data into it. */ static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) { unsigned char *tmp = msgb->tail; if (msgb_tailroom(msgb) < (int) len) MSGB_ABORT(msgb, "Not enough tailroom msgb_push (%u < %u)\n", msgb_tailroom(msgb), len); msgb->tail += len; msgb->len += len; return tmp; } /*! \brief append a uint8 value to the end of the message * \param[in] msgb message buffer * \param[in] word unsigned 8bit byte to be appended */ static inline void msgb_put_u8(struct msgb *msgb, uint8_t word) { uint8_t *space = msgb_put(msgb, 1); space[0] = word & 0xFF; } /*! \brief append a uint16 value to the end of the message * \param[in] msgb message buffer * \param[in] word unsigned 16bit byte to be appended */ static inline void msgb_put_u16(struct msgb *msgb, uint16_t word) { uint8_t *space = msgb_put(msgb, 2); osmo_store16be(word, space); } /*! \brief append a uint32 value to the end of the message * \param[in] msgb message buffer * \param[in] word unsigned 32bit byte to be appended */ static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) { uint8_t *space = msgb_put(msgb, 4); osmo_store32be(word, space); } /*! \brief remove data from end of message * \param[in] msgb message buffer * \param[in] len number of bytes to remove from end */ static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) { unsigned char *tmp = msgb->tail - len; if (msgb_length(msgb) < len) MSGB_ABORT(msgb, "msgb too small to get %u (len %u)\n", len, msgb_length(msgb)); msgb->tail -= len; msgb->len -= len; return tmp; } /*! \brief remove uint8 from end of message * \param[in] msgb message buffer * \returns 8bit value taken from end of msgb */ static inline uint8_t msgb_get_u8(struct msgb *msgb) { uint8_t *space = msgb_get(msgb, 1); return space[0]; } /*! \brief remove uint16 from end of message * \param[in] msgb message buffer * \returns 16bit value taken from end of msgb */ static inline uint16_t msgb_get_u16(struct msgb *msgb) { uint8_t *space = msgb_get(msgb, 2); return osmo_load16be(space); } /*! \brief remove uint32 from end of message * \param[in] msgb message buffer * \returns 32bit value taken from end of msgb */ static inline uint32_t msgb_get_u32(struct msgb *msgb) { uint8_t *space = msgb_get(msgb, 4); return osmo_load32be(space); } /*! \brief prepend (push) some data to start of message * \param[in] msgb message buffer * \param[in] len number of bytes to pre-pend * \returns pointer to newly added portion at start of \a msgb * * This function moves the \a data pointer of the \ref msgb further * to the front (by \a len bytes), thereby enlarging the message by \a * len bytes. * * The return value is a pointer to the newly added section in the * beginning of the message. It can be used to fill/copy data into it. */ static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len) { if (msgb_headroom(msgb) < (int) len) MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n", msgb_headroom(msgb), len); msgb->data -= len; msgb->len += len; return msgb->data; } /*! \brief remove (pull) a header from the front of the message buffer * \param[in] msgb message buffer * \param[in] len number of octets to be pulled * \returns pointer to new start of msgb * * This function moves the \a data pointer of the \ref msgb further back * in the message, thereby shrinking the size of the message by \a len * bytes. */ static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len) { msgb->len -= len; return msgb->data += len; } /*! \brief remove (pull) all headers in front of l3h from the message buffer. * \param[in] msgb message buffer with a valid l3h * \returns pointer to new start of msgb (l3h) * * This function moves the \a data pointer of the \ref msgb further back * in the message, thereby shrinking the size of the message. * l1h and l2h will be cleared. */ static inline unsigned char *msgb_pull_to_l3(struct msgb *msg) { unsigned char *ret = msgb_pull(msg, msg->l3h - msg->data); msg->l1h = msg->l2h = NULL; return ret; } /*! \brief remove uint8 from front of message * \param[in] msgb message buffer * \returns 8bit value taken from end of msgb */ static inline uint8_t msgb_pull_u8(struct msgb *msgb) { uint8_t *space = msgb_pull(msgb, 1) - 1; return space[0]; } /*! \brief remove uint16 from front of message * \param[in] msgb message buffer * \returns 16bit value taken from end of msgb */ static inline uint16_t msgb_pull_u16(struct msgb *msgb) { uint8_t *space = msgb_pull(msgb, 2) - 2; return osmo_load16be(space); } /*! \brief remove uint32 from front of message * \param[in] msgb message buffer * \returns 32bit value taken from end of msgb */ static inline uint32_t msgb_pull_u32(struct msgb *msgb) { uint8_t *space = msgb_pull(msgb, 4) - 4; return osmo_load32be(space); } /*! \brief Increase headroom of empty msgb, reducing the tailroom * \param[in] msg message buffer * \param[in] len amount of extra octets to be reserved as headroom * * This function reserves some memory at the beginning of the underlying * data buffer. The idea is to reserve space in case further headers * have to be pushed to the \ref msgb during further processing. * * Calling this function leads to undefined reusults if it is called on * a non-empty \ref msgb. */ static inline void msgb_reserve(struct msgb *msg, int len) { msg->data += len; msg->tail += len; } /*! \brief Trim the msgb to a given absolute length * \param[in] msg message buffer * \param[in] len new total length of buffer * \returns 0 in case of success, negative in case of error */ static inline int msgb_trim(struct msgb *msg, int len) { if (len > msg->data_len) return -1; msg->len = len; msg->tail = msg->data + len; return 0; } /*! \brief Trim the msgb to a given layer3 length * \param[in] msg message buffer * \param[in] l3len new layer3 length * \returns 0 in case of success, negative in case of error */ static inline int msgb_l3trim(struct msgb *msg, int l3len) { return msgb_trim(msg, (msg->l3h - msg->data) + l3len); } /*! \brief Allocate message buffer with specified headroom * \param[in] size size in bytes, including headroom * \param[in] headroom headroom in bytes * \param[in] name human-readable name * \returns allocated message buffer with specified headroom * * This function is a convenience wrapper around \ref msgb_alloc * followed by \ref msgb_reserve in order to create a new \ref msgb with * user-specified amount of headroom. */ static inline struct msgb *msgb_alloc_headroom(int size, int headroom, const char *name) { osmo_static_assert(size > headroom, headroom_bigger); struct msgb *msg = msgb_alloc(size, name); if (msg) msgb_reserve(msg, headroom); return msg; } /* non inline functions to ease binding */ uint8_t *msgb_data(const struct msgb *msg); void msgb_set_talloc_ctx(void *ctx); /*! @} */ libosmocore-0.9.0/include/osmocom/core/msgfile.h000066400000000000000000000023101261607044000216570ustar00rootroot00000000000000/* * (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. * */ #pragma once #include /** * One message in the list. */ struct osmo_config_entry { struct llist_head list; /* number for everyone to use */ int nr; /* data from the file */ char *mcc; char *mnc; char *option; char *text; }; struct osmo_config_list { struct llist_head entry; }; struct osmo_config_list* osmo_config_list_parse(void *ctx, const char *filename); libosmocore-0.9.0/include/osmocom/core/panic.h000066400000000000000000000005111261607044000213240ustar00rootroot00000000000000#pragma once /*! \addtogroup utils * @{ */ /*! \file panic.h */ #include /*! \brief panic handler callback function type */ typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args); extern void osmo_panic(const char *fmt, ...); extern void osmo_set_panic_handler(osmo_panic_handler_t h); /*! @} */ libosmocore-0.9.0/include/osmocom/core/plugin.h000066400000000000000000000000771261607044000215370ustar00rootroot00000000000000#pragma once int osmo_plugin_load_all(const char *directory); libosmocore-0.9.0/include/osmocom/core/prim.h000066400000000000000000000030541261607044000212060ustar00rootroot00000000000000#pragma once /*! \defgroup prim Osmocom primitives * @{ */ /*! \file prim.h */ #include #include #define OSMO_PRIM(prim, op) ((prim << 8) | (op & 0xFF)) #define OSMO_PRIM_HDR(oph) OSMO_PRIM((oph)->primitive, (oph)->operation) /*! \brief primitive operation */ enum osmo_prim_operation { PRIM_OP_REQUEST, /*!< \brief request */ PRIM_OP_RESPONSE, /*!< \brief response */ PRIM_OP_INDICATION, /*!< \brief indication */ PRIM_OP_CONFIRM, /*!< \brief cofirm */ }; #define _SAP_GSM_SHIFT 24 #define _SAP_GSM_BASE (0x01 << _SAP_GSM_SHIFT) #define _SAP_TETRA_BASE (0x02 << _SAP_GSM_SHIFT) /*! \brief primitive header */ struct osmo_prim_hdr { unsigned int sap; /*!< \brief Service Access Point */ unsigned int primitive; /*!< \brief Primitive number */ enum osmo_prim_operation operation; /*! \brief Primitive Operation */ struct msgb *msg; /*!< \brief \ref msgb containing associated data */ }; /*! \brief initialize a primitive header * \param[in,out] oph primitive header * \param[in] sap Service Access Point * \param[in] primitive Primitive Number * \param[in] operation Primitive Operation (REQ/RESP/IND/CONF) * \param[in] msg Message */ static inline void osmo_prim_init(struct osmo_prim_hdr *oph, unsigned int sap, unsigned int primitive, enum osmo_prim_operation operation, struct msgb *msg) { oph->sap = sap; oph->primitive = primitive; oph->operation = operation; oph->msg = msg; } /*! \brief primitive handler callback type */ typedef int (*osmo_prim_cb)(struct osmo_prim_hdr *oph, void *ctx); /*! @} */ libosmocore-0.9.0/include/osmocom/core/process.h000066400000000000000000000001621261607044000217120ustar00rootroot00000000000000#warning "Update from osmocom/core/process.h to osmocom/core/application.h" #include libosmocore-0.9.0/include/osmocom/core/rate_ctr.h000066400000000000000000000064051261607044000220450ustar00rootroot00000000000000#pragma once /*! \defgroup rate_ctr Rate counters * @{ */ /*! \file rate_ctr.h */ #include #include /*! \brief Number of rate counter intervals */ #define RATE_CTR_INTV_NUM 4 /*! \brief Rate counter interval */ enum rate_ctr_intv { RATE_CTR_INTV_SEC, /*!< \brief last second */ RATE_CTR_INTV_MIN, /*!< \brief last minute */ RATE_CTR_INTV_HOUR, /*!< \brief last hour */ RATE_CTR_INTV_DAY, /*!< \brief last day */ }; /*! \brief data we keep for each of the intervals */ struct rate_ctr_per_intv { uint64_t last; /*!< \brief counter value in last interval */ uint64_t rate; /*!< \brief counter rate */ }; /*! \brief data we keep for each actual value */ struct rate_ctr { uint64_t current; /*!< \brief current value */ uint64_t previous; /*!< \brief previous value, used for delta */ /*! \brief per-interval data */ struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM]; }; /*! \brief rate counter description */ struct rate_ctr_desc { const char *name; /*!< \brief name of the counter */ const char *description;/*!< \brief description of the counter */ }; /*! \brief description of a rate counter group */ struct rate_ctr_group_desc { /*! \brief The prefix to the name of all counters in this group */ const char *group_name_prefix; /*! \brief The human-readable description of the group */ const char *group_description; /*! \brief The class to which this group belongs */ int class_id; /*! \brief The number of counters in this group */ const unsigned int num_ctr; /*! \brief Pointer to array of counter names */ const struct rate_ctr_desc *ctr_desc; }; /*! \brief One instance of a counter group class */ struct rate_ctr_group { /*! \brief Linked list of all counter groups in the system */ struct llist_head list; /*! \brief Pointer to the counter group class */ const struct rate_ctr_group_desc *desc; /*! \brief The index of this ctr_group within its class */ unsigned int idx; /*! \brief Actual counter structures below */ struct rate_ctr ctr[0]; }; struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, const struct rate_ctr_group_desc *desc, unsigned int idx); void rate_ctr_group_free(struct rate_ctr_group *grp); void rate_ctr_add(struct rate_ctr *ctr, int inc); /*! \brief Increment the counter by 1 */ static inline void rate_ctr_inc(struct rate_ctr *ctr) { rate_ctr_add(ctr, 1); } /*! \brief Return the counter difference since the last call to this function */ int64_t rate_ctr_difference(struct rate_ctr *ctr); int rate_ctr_init(void *tall_ctx); struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx); const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name); typedef int (*rate_ctr_handler_t)( struct rate_ctr_group *, struct rate_ctr *, const struct rate_ctr_desc *, void *); typedef int (*rate_ctr_group_handler_t)(struct rate_ctr_group *, void *); /*! \brief Iterate over all counters * \param[in] handle_item Call-back function, aborts if rc < 0 * \param[in] data Private data handed through to \a handle_counter */ int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg, rate_ctr_handler_t handle_counter, void *data); int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data); /*! @} */ libosmocore-0.9.0/include/osmocom/core/select.h000066400000000000000000000022311261607044000215120ustar00rootroot00000000000000#pragma once #include /*! \defgroup select Select loop abstraction * @{ */ /*! \file select.h * \brief select loop abstraction */ /*! \brief Indicate interest in reading from the file descriptor */ #define BSC_FD_READ 0x0001 /*! \brief Indicate interest in writing to the file descriptor */ #define BSC_FD_WRITE 0x0002 /*! \brief Indicate interest in exceptions from the file descriptor */ #define BSC_FD_EXCEPT 0x0004 /*! \brief Structure representing a file dsecriptor */ struct osmo_fd { /*! linked list for internal management */ struct llist_head list; /*! actual operating-system level file decriptor */ int fd; /*! bit-mask or of \ref BSC_FD_READ, \ref BSC_FD_WRITE and/or * \ref BSC_FD_EXCEPT */ unsigned int when; /*! call-back function to be called once file descriptor becomes * available */ int (*cb)(struct osmo_fd *fd, unsigned int what); /*! data pointer passed through to call-back function */ void *data; /*! private number, extending \a data */ unsigned int priv_nr; }; int osmo_fd_register(struct osmo_fd *fd); void osmo_fd_unregister(struct osmo_fd *fd); int osmo_select_main(int polling); /*! @} */ libosmocore-0.9.0/include/osmocom/core/serial.h000066400000000000000000000023061261607044000215150ustar00rootroot00000000000000/* * serial.h * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \defgroup serial Utility functions to deal with serial ports * @{ */ /*! \file serial.h * Osmocom serial port helpers */ #pragma once #include int osmo_serial_init(const char *dev, speed_t baudrate); int osmo_serial_set_baudrate(int fd, speed_t baudrate); int osmo_serial_set_custom_baudrate(int fd, int baudrate); int osmo_serial_clear_custom_baudrate(int fd); /*! @} */ libosmocore-0.9.0/include/osmocom/core/signal.h000066400000000000000000000022071261607044000215130ustar00rootroot00000000000000#pragma once #include /*! \defgroup signal Intra-application signals * @{ */ /*! \file signal.h */ /* subsystem signaling numbers: we split the numberspace for applications and * libraries: from 0 to UINT_MAX/2 for applications, from UINT_MAX/2 to * UINT_MAX for libraries. */ #define OSMO_SIGNAL_SS_APPS 0 #define OSMO_SIGNAL_SS_RESERVED 2147483648u /*! \brief signal subsystems */ enum { SS_L_GLOBAL = OSMO_SIGNAL_SS_RESERVED, SS_L_INPUT, SS_L_NS, SS_L_VTY, }; /* application-defined signal types. */ #define OSMO_SIGNAL_T_APPS 0 #define OSMO_SIGNAL_T_RESERVED 2147483648u /*! \brief signal types. */ enum { S_L_GLOBAL_SHUTDOWN = OSMO_SIGNAL_T_RESERVED, }; /*! signal callback function type */ typedef int osmo_signal_cbfn(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data); /* Management */ int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data); /* Dispatch */ void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data); /*! @} */ libosmocore-0.9.0/include/osmocom/core/socket.h000066400000000000000000000020061261607044000215230ustar00rootroot00000000000000#pragma once /*! \defgroup socket Socket convenience functions * @{ */ /*! \file socket.h * \brief Osmocom socket convenience functions */ #include struct sockaddr; struct osmo_fd; /* flags for osmo_sock_init. */ #define OSMO_SOCK_F_CONNECT (1 << 0) #define OSMO_SOCK_F_BIND (1 << 1) #define OSMO_SOCK_F_NONBLOCK (1 << 2) int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port, unsigned int flags); int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, const char *host, uint16_t port, unsigned int flags); int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, uint8_t proto, unsigned int flags); int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen); int osmo_sock_unix_init(uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags); /*! @} */ libosmocore-0.9.0/include/osmocom/core/stat_item.h000066400000000000000000000105511261607044000222300ustar00rootroot00000000000000#pragma once /*! \defgroup osmo_stat_item Statistics value item * @{ */ /*! \file stat_item.h */ #include #include struct osmo_stat_item_desc; #define STAT_ITEM_NOVALUE_ID 0 struct osmo_stat_item_value { int32_t id; int32_t value; }; /*! \brief data we keep for each actual value */ struct osmo_stat_item { const struct osmo_stat_item_desc *desc; /*! \brief the index of the freshest value */ int32_t last_value_index; /*! \brief offset to the freshest value in the value fifo */ int16_t last_offs; /*! \brief value fifo */ struct osmo_stat_item_value values[0]; }; /*! \brief statistics value description */ struct osmo_stat_item_desc { const char *name; /*!< \brief name of the item */ const char *description;/*!< \brief description of the item */ const char *unit; /*!< \brief unit of a value */ unsigned int num_values;/*!< \brief number of values to store */ int32_t default_value; }; /*! \brief description of a statistics value group */ struct osmo_stat_item_group_desc { /*! \brief The prefix to the name of all values in this group */ const char *group_name_prefix; /*! \brief The human-readable description of the group */ const char *group_description; /*! \brief The class to which this group belongs */ int class_id; /*! \brief The number of values in this group */ const unsigned int num_items; /*! \brief Pointer to array of value names */ const struct osmo_stat_item_desc *item_desc; }; /*! \brief One instance of a counter group class */ struct osmo_stat_item_group { /*! \brief Linked list of all value groups in the system */ struct llist_head list; /*! \brief Pointer to the counter group class */ const struct osmo_stat_item_group_desc *desc; /*! \brief The index of this value group within its class */ unsigned int idx; /*! \brief Actual counter structures below */ struct osmo_stat_item *items[0]; }; struct osmo_stat_item_group *osmo_stat_item_group_alloc( void *ctx, const struct osmo_stat_item_group_desc *desc, unsigned int idx); void osmo_stat_item_group_free(struct osmo_stat_item_group *statg); void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value); int osmo_stat_item_init(void *tall_ctx); struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx( const char *name, const unsigned int idx); const struct osmo_stat_item *osmo_stat_item_get_by_name( const struct osmo_stat_item_group *statg, const char *name); /*! \brief Retrieve the next value from the osmo_stat_item object. * If a new value has been set, it is returned. The idx is used to decide * which value to return. * On success, *idx is updated to refer to the next unread value. If * values have been missed due to FIFO overflow, *idx is incremented by * (1 + num_lost). * This way, the osmo_stat_item object can be kept stateless from the reader's * perspective and therefore be used by several backends simultaneously. * * \param val the osmo_stat_item object * \param idx identifies the next value to be read * \param value a pointer to store the value * \returns the increment of the index (0: no value has been read, * 1: one value has been taken, * (1+n): n values have been skipped, one has been taken) */ int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *idx, int32_t *value); /*! \brief Get the last (freshest) value */ static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item); /*! \brief Skip all values of the item and update idx accordingly */ int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx); /*! \brief Skip all values of all items and update idx accordingly */ int osmo_stat_item_discard_all(int32_t *idx); typedef int (*osmo_stat_item_handler_t)( struct osmo_stat_item_group *, struct osmo_stat_item *, void *); typedef int (*osmo_stat_item_group_handler_t)(struct osmo_stat_item_group *, void *); /*! \brief Iteate over all items * \param[in] handle_item Call-back function, aborts if rc < 0 * \param[in] data Private data handed through to \a handle_item */ int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg, osmo_stat_item_handler_t handle_item, void *data); int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data); static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item) { return item->values[item->last_offs].value; } /*! @} */ libosmocore-0.9.0/include/osmocom/core/statistics.h000066400000000000000000000032541261607044000224330ustar00rootroot00000000000000#pragma once /*! \file statistics.h * \brief Common routines regarding statistics */ /*! structure representing a single counter */ struct osmo_counter { struct llist_head list; /*!< \brief internal list head */ const char *name; /*!< \brief human-readable name */ const char *description; /*!< \brief humn-readable description */ unsigned long value; /*!< \brief current value */ unsigned long previous; /*!< \brief previous value */ }; /*! \brief Increment counter */ static inline void osmo_counter_inc(struct osmo_counter *ctr) { ctr->value++; } /*! \brief Get current value of counter */ static inline unsigned long osmo_counter_get(struct osmo_counter *ctr) { return ctr->value; } /*! \brief Reset current value of counter to 0 */ static inline void osmo_counter_reset(struct osmo_counter *ctr) { ctr->value = 0; } /*! \brief Allocate a new counter */ struct osmo_counter *osmo_counter_alloc(const char *name); /*! \brief Free the specified counter * \param[in] ctr Counter */ void osmo_counter_free(struct osmo_counter *ctr); /*! \brief Iterate over all counters * \param[in] handle_counter Call-back function, aborts if rc < 0 * \param[in] data Private dtata handed through to \a handle_counter */ int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data); /*! \brief Resolve counter by human-readable name * \param[in] name human-readable name of counter * \returns pointer to counter (\ref osmo_counter) or NULL otherwise */ struct osmo_counter *osmo_counter_get_by_name(const char *name); /*! \brief Return the counter difference since the last call to this function */ int osmo_counter_difference(struct osmo_counter *ctr); libosmocore-0.9.0/include/osmocom/core/stats.h000066400000000000000000000065621261607044000214040ustar00rootroot00000000000000/* (C) 2015 by Sysmocom s.f.m.c. GmbH * * 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. * */ #pragma once #include #include #include struct msgb; struct osmo_stat_item_group; struct osmo_stat_item_desc; struct rate_ctr_group; struct rate_ctr_desc; enum osmo_stats_class { OSMO_STATS_CLASS_UNKNOWN, OSMO_STATS_CLASS_GLOBAL, OSMO_STATS_CLASS_PEER, OSMO_STATS_CLASS_SUBSCRIBER, }; enum osmo_stats_reporter_type { OSMO_STATS_REPORTER_STATSD, OSMO_STATS_REPORTER_LOG, }; struct osmo_stats_reporter { enum osmo_stats_reporter_type type; char *name; unsigned int have_net_config : 1; /* config */ int enabled; char *name_prefix; char *dest_addr_str; char *bind_addr_str; int dest_port; int mtu; enum osmo_stats_class max_class; /* state */ int running; struct sockaddr dest_addr; int dest_addr_len; struct sockaddr bind_addr; int bind_addr_len; int fd; struct msgb *buffer; int agg_enabled; struct llist_head list; int (*open)(struct osmo_stats_reporter *srep); int (*close)(struct osmo_stats_reporter *srep); int (*send_counter)(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta); int (*send_item)(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int32_t value); }; struct osmo_stats_config { int interval; }; extern struct osmo_stats_config *osmo_stats_config; void osmo_stats_init(void *ctx); int osmo_stats_report(); int osmo_stats_set_interval(int interval); struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type, const char *name); void osmo_stats_reporter_free(struct osmo_stats_reporter *srep); struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name); struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name); struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type, const char *name); int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr); int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port); int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr); int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu); int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep, enum osmo_stats_class class_id); int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix); int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep); int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep); libosmocore-0.9.0/include/osmocom/core/strrb.h000066400000000000000000000035231261607044000213740ustar00rootroot00000000000000#pragma once /* (C) 2012-2013 by Katerina Barone-Adesi * 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. * */ /*! \defgroup osmo_strrb Osmocom ringbuffers for log strings * @{ */ /*! \file strrb.h * \brief Osmocom string ringbuffer handling routines */ #include #include #include #include /*! \brief A structure representing an osmocom string ringbuffer */ #define RB_MAX_MESSAGE_SIZE 240 struct osmo_strrb { uint16_t start; /*!< \brief index of the first slot */ uint16_t end; /*!< \brief index of the last slot */ uint16_t size; /*!< \brief max number of messages to store */ char **buffer; /*!< \brief storage for messages */ }; struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size); bool osmo_strrb_is_empty(const struct osmo_strrb *rb); const char *osmo_strrb_get_nth(const struct osmo_strrb *rb, unsigned int string_index); bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb, unsigned int offset); size_t osmo_strrb_elements(const struct osmo_strrb *rb); int osmo_strrb_add(struct osmo_strrb *rb, const char *data); /*! @} */ libosmocore-0.9.0/include/osmocom/core/talloc.h000066400000000000000000000213141261607044000215140ustar00rootroot00000000000000#pragma once /* 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 . */ #include #include #include #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)); libosmocore-0.9.0/include/osmocom/core/timer.h000066400000000000000000000051221261607044000213550ustar00rootroot00000000000000/* * (C) 2008, 2009 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. * */ /*! \defgroup timer Osmocom timers * @{ */ /*! \file timer.h * \brief Osmocom timer handling routines */ #pragma once #include #include #include /** * Timer management: * - Create a struct osmo_timer_list * - Fill out timeout and use add_timer or * use osmo_timer_schedule to schedule a timer in * x seconds and microseconds from now... * - Use osmo_timer_del 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 * - osmo_timers_update will call the callbacks and * remove the timers. * */ /*! \brief A structure representing a single instance of a timer */ struct osmo_timer_list { struct rb_node node; /*!< \brief rb-tree node header */ struct llist_head list; /*!< \brief internal list header */ struct timeval timeout; /*!< \brief expiration time */ unsigned int active : 1; /*!< \brief is it active? */ void (*cb)(void*); /*!< \brief call-back called at timeout */ void *data; /*!< \brief user data for callback */ }; /** * timer management */ void osmo_timer_add(struct osmo_timer_list *timer); void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds); void osmo_timer_del(struct osmo_timer_list *timer); int osmo_timer_pending(struct osmo_timer_list *timer); int osmo_timer_remaining(const struct osmo_timer_list *timer, const struct timeval *now, struct timeval *remaining); /* * internal timer list management */ struct timeval *osmo_timers_nearest(void); void osmo_timers_prepare(void); int osmo_timers_update(void); int osmo_timers_check(void); /*! @} */ libosmocore-0.9.0/include/osmocom/core/timer_compat.h000066400000000000000000000042511261607044000227220ustar00rootroot00000000000000/* * (C) 2011 Sylvain Munaut * 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. * */ /*! \defgroup timer Osmocom timers * @{ */ /*! \file timer_compat.h * \brief Compatibility header with some helpers */ #pragma once /* Convenience macros for operations on timevals. NOTE: `timercmp' does not work for >= or <=. */ #ifndef timerisset # define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) #endif #ifndef timerclear # define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) #endif #ifndef timercmp # define timercmp(a, b, CMP) \ (((a)->tv_sec == (b)->tv_sec) ? \ ((a)->tv_usec CMP (b)->tv_usec) : \ ((a)->tv_sec CMP (b)->tv_sec)) #endif #ifndef timeradd # define timeradd(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ if ((result)->tv_usec >= 1000000) \ { \ ++(result)->tv_sec; \ (result)->tv_usec -= 1000000; \ } \ } while (0) #endif #ifndef timersub # define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0) #endif /*! @} */ libosmocore-0.9.0/include/osmocom/core/utils.h000066400000000000000000000037721261607044000214060ustar00rootroot00000000000000#pragma once #include #include /*! \defgroup utils General-purpose utility functions * @{ */ /*! \file utils.h */ /*! \brief Determine number of elements in an array of static size */ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /*! \brief Return the maximum of two specified values */ #define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b)) /*! \brief Return the minimum of two specified values */ #define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a)) #include /*! \brief A mapping between human-readable string and numeric value */ struct value_string { unsigned int value; /*!< \brief numeric value */ const char *str; /*!< \brief human-readable string */ }; const char *get_value_string(const struct value_string *vs, uint32_t val); int get_string_value(const struct value_string *vs, const char *str); char osmo_bcd2char(uint8_t bcd); /* only works for numbers in ascci */ uint8_t osmo_char2bcd(char c); int osmo_hexparse(const char *str, uint8_t *b, int max_len); char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); char *osmo_hexdump(const unsigned char *buf, int len); char *osmo_hexdump_nospc(const unsigned char *buf, int len); char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__)); #define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__)); void osmo_str2lower(char *out, const char *in); void osmo_str2upper(char *out, const char *in); #define OSMO_SNPRINTF_RET(ret, rem, offset, len) \ do { \ len += ret; \ if (ret > rem) \ ret = rem; \ offset += ret; \ rem -= ret; \ } while (0) #define OSMO_ASSERT(exp) \ if (!(exp)) { \ fprintf(stderr, "Assert failed %s %s:%d\n", #exp, __FILE__, __LINE__); \ osmo_generate_backtrace(); \ abort(); \ } static inline void osmo_talloc_replace_string(void *ctx, char **dst, char *newstr) { if (*dst) talloc_free(*dst); *dst = talloc_strdup(ctx, newstr); } /*! @} */ libosmocore-0.9.0/include/osmocom/core/write_queue.h000066400000000000000000000036201261607044000225740ustar00rootroot00000000000000/* 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. * */ #pragma once /*! \defgroup write_queue Osmocom msgb write queues * @{ */ /*! \file write_queue.h */ #include #include /*! write queue instance */ struct osmo_wqueue { /*! \brief osmocom file descriptor */ struct osmo_fd bfd; /*! \brief maximum length of write queue */ unsigned int max_length; /*! \brief current length of write queue */ unsigned int current_length; /*! \brief actual linked list implementing the queue */ struct llist_head msg_queue; /*! \brief call-back in case qeueue is readable */ int (*read_cb)(struct osmo_fd *fd); /*! \brief call-back in case qeueue is writable */ int (*write_cb)(struct osmo_fd *fd, struct msgb *msg); /*! \brief call-back in case qeueue has exceptions */ int (*except_cb)(struct osmo_fd *fd); }; void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length); void osmo_wqueue_clear(struct osmo_wqueue *queue); int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data); int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what); /*! @} */ libosmocore-0.9.0/include/osmocom/crypt/000077500000000000000000000000001261607044000202755ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/crypt/auth.h000066400000000000000000000053561261607044000214200ustar00rootroot00000000000000#pragma once /*! \addtogroup auth * @{ */ /*! \file auth.h */ #include #include /*! \brief Authentication Type (GSM/UMTS) */ enum osmo_sub_auth_type { OSMO_AUTH_TYPE_NONE = 0x00, OSMO_AUTH_TYPE_GSM = 0x01, OSMO_AUTH_TYPE_UMTS = 0x02, }; /*! \brief Authentication Algorithm */ enum osmo_auth_algo { OSMO_AUTH_ALG_NONE, OSMO_AUTH_ALG_COMP128v1, OSMO_AUTH_ALG_COMP128v2, OSMO_AUTH_ALG_COMP128v3, OSMO_AUTH_ALG_XOR, OSMO_AUTH_ALG_MILENAGE, _OSMO_AUTH_ALG_NUM, }; /*! \brief permanent (secret) subscriber auth data */ struct osmo_sub_auth_data { enum osmo_sub_auth_type type; enum osmo_auth_algo algo; union { struct { uint8_t opc[16]; /*!< operator invariant value */ uint8_t k[16]; /*!< secret key of the subscriber */ uint8_t amf[2]; uint64_t sqn; /*!< sequence number */ int opc_is_op; /*!< is the OPC field OPC (0) or OP (1) ? */ } umts; struct { uint8_t ki[16]; /*!< secret key */ } gsm; } u; }; /* data structure describing a computed auth vector, generated by AuC */ struct osmo_auth_vector { uint8_t rand[16]; /*!< random challenge */ uint8_t autn[16]; /*!< authentication nonce */ uint8_t ck[16]; /*!< ciphering key */ uint8_t ik[16]; /*!< integrity key */ uint8_t res[16]; /*!< authentication result */ uint8_t res_len; /*!< length (in bytes) of res */ uint8_t kc[8]; /*!< Kc for GSM encryption (A5) */ uint8_t sres[4]; /*!< authentication result for GSM */ uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */ }; /* \brief An implementation of an authentication algorithm */ struct osmo_auth_impl { struct llist_head list; enum osmo_auth_algo algo; /*!< algorithm we implement */ const char *name; /*!< name of the implementation */ unsigned int priority; /*!< priority value (resp. othe implementations */ /*! \brief callback for generate authentication vectors */ int (*gen_vec)(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand); /* \brief callback for generationg auth vectors + re-sync */ int (*gen_vec_auts)(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *rand_auts, const uint8_t *auts, const uint8_t *_rand); }; int osmo_auth_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand); int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *rand_auts, const uint8_t *auts, const uint8_t *_rand); int osmo_auth_register(struct osmo_auth_impl *impl); int osmo_auth_load(const char *path); int osmo_auth_supported(enum osmo_auth_algo algo); const char *osmo_auth_alg_name(enum osmo_auth_algo alg); enum osmo_auth_algo osmo_auth_alg_parse(const char *name); /* @} */ libosmocore-0.9.0/include/osmocom/crypt/gprs_cipher.h000066400000000000000000000027511261607044000227600ustar00rootroot00000000000000#pragma once #include #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); libosmocore-0.9.0/include/osmocom/ctrl/000077500000000000000000000000001261607044000201005ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/ctrl/control_cmd.h000066400000000000000000000127341261607044000225630ustar00rootroot00000000000000#pragma once #include #include #include #include #include #define CTRL_CMD_ERROR -1 #define CTRL_CMD_HANDLED 0 #define CTRL_CMD_REPLY 1 struct ctrl_handle; enum ctrl_node_type { CTRL_NODE_ROOT, /* Root elements */ CTRL_NODE_BTS, /* BTS specific (net.btsN.) */ CTRL_NODE_TRX, /* TRX specific (net.btsN.trxM.) */ CTRL_NODE_TS, /* TS specific (net.btsN.trxM.tsI.) */ _LAST_CTRL_NODE }; enum ctrl_type { CTRL_TYPE_UNKNOWN, CTRL_TYPE_GET, CTRL_TYPE_SET, CTRL_TYPE_GET_REPLY, CTRL_TYPE_SET_REPLY, CTRL_TYPE_TRAP, CTRL_TYPE_ERROR }; struct ctrl_connection { struct llist_head list_entry; /* The queue for sending data back */ struct osmo_wqueue write_queue; /* Buffer for partial input data */ struct msgb *pending_msg; /* Callback if the connection was closed */ void (*closed_cb)(struct ctrl_connection *conn); /* Pending commands for this connection */ struct llist_head cmds; /* Pending deferred commands for this connection */ struct llist_head def_cmds; }; struct ctrl_cmd { struct ctrl_connection *ccon; enum ctrl_type type; char *id; void *node; char *variable; char *value; char *reply; }; struct ctrl_cmd_struct { int nr_commands; char **command; }; struct ctrl_cmd_element { const char *name; struct ctrl_cmd_struct strcmd; int (*set)(struct ctrl_cmd *cmd, void *data); int (*get)(struct ctrl_cmd *cmd, void *data); int (*verify)(struct ctrl_cmd *cmd, const char *value, void *data); }; struct ctrl_cmd_map { char *cmd; enum ctrl_type type; }; /* deferred control command, i.e. responded asynchronously */ struct ctrl_cmd_def { struct llist_head list; /* ctrl_connection.def_cmds */ struct ctrl_cmd *cmd; void *data; /* opaque user data */ }; struct ctrl_cmd_def * ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs); int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd); int ctrl_cmd_def_send(struct ctrl_cmd_def *cd); int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data); int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd); int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd); struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg); struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd); struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd); struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type); struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd); #define CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_name) \ static struct ctrl_cmd_element cmd_##cmdname = { \ .name = cmdstr, \ .get = &get_##cmdname, \ .set = &set_##cmdname, \ .verify = verify_name, \ } #define CTRL_HELPER_GET_INT(cmdname, dtype, element) \ static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ { \ dtype *node = cmd->node; \ cmd->reply = talloc_asprintf(cmd, "%i", node->element); \ if (!cmd->reply) { \ cmd->reply = "OOM"; \ return CTRL_CMD_ERROR; \ } \ return CTRL_CMD_REPLY; \ } #define CTRL_HELPER_SET_INT(cmdname, dtype, element) \ static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ { \ dtype *node = cmd->node; \ int tmp = atoi(cmd->value); \ node->element = tmp; \ return get_##cmdname(cmd, _data); \ } #define CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *_data) \ { \ int tmp = atoi(value); \ if ((tmp >= min)&&(tmp <= max)) { \ return 0; \ } \ cmd->reply = "Input not within the range"; \ return -1; \ } #define CTRL_CMD_DEFINE_RANGE(cmdname, cmdstr, dtype, element, min, max) \ CTRL_HELPER_GET_INT(cmdname, dtype, element) \ CTRL_HELPER_SET_INT(cmdname, dtype, element) \ CTRL_HELPER_VERIFY_RANGE(cmdname, min, max) \ CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) #define CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ static int get_##cmdname(struct ctrl_cmd *cmd, void *_data) \ { \ dtype *data = cmd->node; \ cmd->reply = talloc_asprintf(cmd, "%s", data->element); \ if (!cmd->reply) { \ cmd->reply = "OOM"; \ return CTRL_CMD_ERROR; \ } \ return CTRL_CMD_REPLY; \ } #define CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ static int set_##cmdname(struct ctrl_cmd *cmd, void *_data) \ { \ dtype *data = cmd->node; \ osmo_talloc_replace_string(cmd->node, &data->element, cmd->value); \ return get_##cmdname(cmd, _data); \ } #define CTRL_CMD_DEFINE_STRING(cmdname, cmdstr, dtype, element) \ CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, NULL) #define CTRL_CMD_DEFINE(cmdname, cmdstr) \ static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ static int set_##cmdname(struct ctrl_cmd *cmd, void *data); \ static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data); \ CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) #define CTRL_CMD_DEFINE_RO(cmdname, cmdstr) \ static int get_##cmdname(struct ctrl_cmd *cmd, void *data); \ static int set_##cmdname(struct ctrl_cmd *cmd, void *data) \ { \ cmd->reply = "Read Only attribute"; \ return CTRL_CMD_ERROR; \ } \ static int verify_##cmdname(struct ctrl_cmd *cmd, const char *value, void *data) \ { \ cmd->reply = "Read Only attribute"; \ return 1; \ } \ CTRL_CMD_DEFINE_STRUCT(cmdname, cmdstr, verify_##cmdname) struct gsm_network; libosmocore-0.9.0/include/osmocom/ctrl/control_if.h000066400000000000000000000012241261607044000224060ustar00rootroot00000000000000#pragma once #include #include int ctrl_parse_get_num(vector vline, int i, long *num); typedef int (*ctrl_cmd_lookup)(void *data, vector vline, int *node_type, void **node_data, int *i); struct ctrl_handle { struct osmo_fd listen_fd; void *data; ctrl_cmd_lookup lookup; /* List of control connections */ struct llist_head ccon_list; }; int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd); struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port, ctrl_cmd_lookup lookup); int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data); libosmocore-0.9.0/include/osmocom/ctrl/ports.h000066400000000000000000000003351261607044000214210ustar00rootroot00000000000000#pragma once /* TCP port numbers used for CTRL interfaces in osmocom projects */ #define OSMO_CTRL_PORT_BTS 4238 #define OSMO_CTRL_PORT_NITB_BSC 4249 #define OSMO_CTRL_PORT_BSC_NAT 4250 #define OSMO_CTRL_PORT_SGSN 4251 libosmocore-0.9.0/include/osmocom/gprs/000077500000000000000000000000001261607044000201075ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/gprs/gprs_bssgp.h000066400000000000000000000137321261607044000224370ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include /* gprs_bssgp_util.c */ extern struct gprs_ns_inst *bssgp_nsi; struct msgb *bssgp_msgb_alloc(void); const char *bssgp_cause_str(enum gprs_bssgp_cause cause); /* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, uint16_t bvci, uint16_t ns_bvci); /* Chapter 10.4.14: Status */ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg); enum bssgp_prim { PRIM_BSSGP_DL_UD, PRIM_BSSGP_UL_UD, PRIM_BSSGP_PTM_UD, PRIM_BSSGP_GMM_SUSPEND, PRIM_BSSGP_GMM_RESUME, PRIM_BSSGP_GMM_PAGING, PRIM_NM_FLUSH_LL, PRIM_NM_LLC_DISCARDED, PRIM_NM_BVC_RESET, PRIM_NM_BVC_BLOCK, PRIM_NM_BVC_UNBLOCK, PRIM_NM_STATUS, }; struct osmo_bssgp_prim { struct osmo_prim_hdr oph; /* common fields */ uint16_t nsei; uint16_t bvci; uint32_t tlli; struct tlv_parsed *tp; struct gprs_ra_id *ra_id; /* specific fields */ union { struct { uint8_t suspend_ref; } resume; } u; }; /* gprs_bssgp.c */ /*! \brief BSSGP flow control (SGSN side) According to Section 8.2 */ struct bssgp_flow_control { uint32_t bucket_size_max; /*!< maximum size of the bucket (octets) */ uint32_t bucket_leak_rate; /*!< leak rate of the bucket (octets/sec) */ uint32_t bucket_counter; /*!< number of tokens in the bucket */ struct timeval time_last_pdu; /*!< timestamp of last PDU sent */ /* the built-in queue */ uint32_t max_queue_depth; /*!< how many packets to queue (mgs) */ uint32_t queue_depth; /*!< current length of queue (msgs) */ struct llist_head queue; /*!< linked list of msgb's */ struct osmo_timer_list timer; /*!< timer-based dequeueing */ /*! callback to be called at output of flow control */ int (*out_cb)(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv); }; #define BVC_S_BLOCKED 0x0001 /* The per-BTS context that we keep on the SGSN side of the BSSGP link */ struct bssgp_bvc_ctx { struct llist_head list; struct gprs_ra_id ra_id; /*!< parsed RA ID of the remote BTS */ uint16_t cell_id; /*!< Cell ID of the remote BTS */ /* NSEI and BVCI of underlying Gb link. Together they * uniquely identify a link to a BTS (5.4.4) */ uint16_t bvci; uint16_t nsei; uint32_t state; struct rate_ctr_group *ctrg; struct bssgp_flow_control *fc; /*! default maximum size of per-MS bucket in octets */ uint32_t bmax_default_ms; /*! default bucket leak rate of per-MS bucket in octests/s */ uint32_t r_default_ms; /* we might want to add this as a shortcut later, avoiding the NSVC * lookup for every packet, similar to a routing cache */ //struct gprs_nsvc *nsvc; }; extern struct llist_head bssgp_bvc_ctxts; /* Find a BTS Context based on parsed RA ID and Cell ID */ struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid); /* Find a BTS context based on BVCI+NSEI tuple */ struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei); #define BVC_F_BLOCKED 0x0001 enum bssgp_ctr { BSSGP_CTR_PKTS_IN, BSSGP_CTR_PKTS_OUT, BSSGP_CTR_BYTES_IN, BSSGP_CTR_BYTES_OUT, BSSGP_CTR_BLOCKED, BSSGP_CTR_DISCARDED, BSSGP_CTR_STATUS, }; #include #include /* BSSGP-UL-UNITDATA.ind */ int bssgp_rcvmsg(struct msgb *msg); /* BSSGP-DL-UNITDATA.req */ struct bssgp_lv { uint16_t len; uint8_t *v; }; /* parameters for BSSGP downlink userdata transmission */ struct bssgp_dl_ud_par { uint32_t *tlli; char *imsi; struct bssgp_flow_control *fc; uint16_t drx_parms; /* FIXME: priority */ struct bssgp_lv ms_ra_cap; uint8_t qos_profile[3]; }; int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime, struct bssgp_dl_ud_par *dup); uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf); int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid, uint16_t cid); /* Wrapper around TLV parser to parse BSSGP IEs */ static inline int bssgp_tlv_parse(struct tlv_parsed *tp, uint8_t *buf, int len) { return tlv_parse(tp, &tvlv_att_def, buf, len, 0, 0); } /*! \brief BSSGP Paging mode */ enum bssgp_paging_mode { BSSGP_PAGING_PS, BSSGP_PAGING_CS, }; /*! \brief BSSGP Paging scope */ enum bssgp_paging_scope { BSSGP_PAGING_BSS_AREA, /*!< all cells in BSS */ BSSGP_PAGING_LOCATION_AREA, /*!< all cells in LA */ BSSGP_PAGING_ROUTEING_AREA, /*!< all cells in RA */ BSSGP_PAGING_BVCI, /*!< one cell */ }; /*! \brief BSSGP paging information */ struct bssgp_paging_info { enum bssgp_paging_mode mode; /*!< CS or PS paging */ enum bssgp_paging_scope scope; /*!< bssgp_paging_scope */ struct gprs_ra_id raid; /*!< RA Identifier */ uint16_t bvci; /*!< BVCI */ char *imsi; /*!< IMSI, if any */ uint32_t *ptmsi; /*!< P-TMSI, if any */ uint16_t drx_params; /*!< DRX parameters */ uint8_t qos[3]; /*!< QoS parameters */ }; /* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */ int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci, struct bssgp_paging_info *pinfo); void bssgp_fc_init(struct bssgp_flow_control *fc, uint32_t bucket_size_max, uint32_t bucket_leak_rate, uint32_t max_queue_depth, int (*out_cb)(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv)); /* input function of the flow control implementation, called first * for the MM flow control, and then as the MM flow control output * callback in order to perform BVC flow control */ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv); /* Initialize the Flow Control parameters for a new MS according to * default values for the BVC specified by BVCI and NSEI */ int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci, uint16_t nsei, uint32_t max_queue_depth); /* gprs_bssgp_vty.c */ int bssgp_vty_init(void); void bssgp_set_log_ss(int ss); int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx); libosmocore-0.9.0/include/osmocom/gprs/gprs_bssgp_bss.h000066400000000000000000000050311261607044000232770ustar00rootroot00000000000000#pragma once #include #include /* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ /* (C) 2009-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ uint8_t *bssgp_msgb_tlli_put(struct msgb *msg, uint32_t tlli); int bssgp_tx_suspend(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id); int bssgp_tx_resume(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id, uint8_t suspend_ref); int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag); int bssgp_tx_radio_status_tlli(struct bssgp_bvc_ctx *bctx, uint8_t cause, uint32_t tlli); int bssgp_tx_radio_status_tmsi(struct bssgp_bvc_ctx *bctx, uint8_t cause, uint32_t tmsi); int bssgp_tx_radio_status_imsi(struct bssgp_bvc_ctx *bctx, uint8_t cause, const char *imsi); int bssgp_tx_flush_ll_ack(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t action, uint16_t bvci_new, uint32_t num_octets); int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t num_frames, uint32_t num_octets); int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause); int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx); int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause); int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli, const uint8_t *qos_profile, struct msgb *llc_pdu); int bssgp_rx_paging(struct bssgp_paging_info *pinfo, struct msgb *msg); int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag, uint32_t bucket_size, uint32_t bucket_leak_rate, uint32_t bmax_default_ms, uint32_t r_default_ms, uint8_t *bucket_full_ratio, uint32_t *queue_delay_ms); int bssgp_tx_fc_ms(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag, uint32_t ms_bucket_size, uint32_t bucket_leak_rate, uint8_t *bucket_full_ratio); libosmocore-0.9.0/include/osmocom/gprs/gprs_msgb.h000066400000000000000000000021301261607044000222370ustar00rootroot00000000000000#pragma once #include /* the data structure stored in msgb->cb for libgb apps */ struct libgb_msgb_cb { unsigned char *bssgph; unsigned char *llch; /* Cell Identifier */ unsigned char *bssgp_cell_id; /* Identifiers of a BTS, equal to 'struct bssgp_bts_ctx' */ uint16_t nsei; uint16_t bvci; /* Identifier of a MS (inside BTS), equal to 'struct sgsn_mm_ctx' */ uint32_t tlli; } __attribute__((packed, may_alias)); #define LIBGB_MSGB_CB(__msgb) ((struct libgb_msgb_cb *)&((__msgb)->cb[0])) #define msgb_tlli(__x) LIBGB_MSGB_CB(__x)->tlli #define msgb_nsei(__x) LIBGB_MSGB_CB(__x)->nsei #define msgb_bvci(__x) LIBGB_MSGB_CB(__x)->bvci #define msgb_gmmh(__x) (__x)->l3h #define msgb_bssgph(__x) LIBGB_MSGB_CB(__x)->bssgph #define msgb_bssgp_len(__x) ((__x)->tail - (uint8_t *)msgb_bssgph(__x)) #define msgb_bcid(__x) LIBGB_MSGB_CB(__x)->bssgp_cell_id #define msgb_llch(__x) LIBGB_MSGB_CB(__x)->llch /* logging contexts */ #define GPRS_CTX_NSVC 0 #define GPRS_CTX_BVC 1 #include int gprs_log_filter_fn(const struct log_context *ctx, struct log_target *tar); libosmocore-0.9.0/include/osmocom/gprs/gprs_ns.h000066400000000000000000000134651261607044000217440ustar00rootroot00000000000000#pragma once #include /* Our Implementation */ #include #include #include #include #include #include #include #define NS_TIMERS_COUNT 7 #define NS_TIMERS "(tns-block|tns-block-retries|tns-reset|tns-reset-retries|tns-test|tns-alive|tns-alive-retries)" #define NS_TIMERS_HELP \ "(un)blocking Timer (Tns-block) timeout\n" \ "(un)blocking Timer (Tns-block) number of retries\n" \ "Reset Timer (Tns-reset) timeout\n" \ "Reset Timer (Tns-reset) number of retries\n" \ "Test Timer (Tns-test) timeout\n" \ "Alive Timer (Tns-alive) timeout\n" \ "Alive Timer (Tns-alive) number of retries\n" #define NS_ALLOC_SIZE 2048 #define NS_ALLOC_HEADROOM 20 enum ns_timeout { NS_TOUT_TNS_BLOCK, NS_TOUT_TNS_BLOCK_RETRIES, NS_TOUT_TNS_RESET, NS_TOUT_TNS_RESET_RETRIES, NS_TOUT_TNS_TEST, NS_TOUT_TNS_ALIVE, NS_TOUT_TNS_ALIVE_RETRIES, }; #define NSE_S_BLOCKED 0x0001 #define NSE_S_ALIVE 0x0002 #define NSE_S_RESET 0x0004 /*! \brief Osmocom NS link layer types */ enum gprs_ns_ll { GPRS_NS_LL_UDP, /*!< NS/UDP/IP */ GPRS_NS_LL_E1, /*!< NS/E1 */ GPRS_NS_LL_FR_GRE, /*!< NS/FR/GRE/IP */ }; /*! \brief Osmoco NS events */ enum gprs_ns_evt { GPRS_NS_EVT_UNIT_DATA, }; /*! \brief Osmocom NS VC create status */ enum gprs_ns_cs { GPRS_NS_CS_CREATED, /*!< A NSVC object has been created */ GPRS_NS_CS_FOUND, /*!< A NSVC object has been found */ GPRS_NS_CS_REJECTED, /*!< Rejected and answered message */ GPRS_NS_CS_SKIPPED, /*!< Skipped message */ GPRS_NS_CS_ERROR, /*!< Failed to process message */ }; struct gprs_nsvc; /*! \brief Osmocom GPRS callback function type */ typedef int gprs_ns_cb_t(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci); /*! \brief An instance of the NS protocol stack */ struct gprs_ns_inst { /*! \brief callback to the user for incoming UNIT DATA IND */ gprs_ns_cb_t *cb; /*! \brief linked lists of all NSVC in this instance */ struct llist_head gprs_nsvcs; /*! \brief a NSVC object that's needed to deal with packets for * unknown NSVC */ struct gprs_nsvc *unknown_nsvc; uint16_t timeout[NS_TIMERS_COUNT]; /*! \brief NS-over-IP specific bits */ struct { struct osmo_fd fd; uint32_t local_ip; uint16_t local_port; int dscp; } nsip; /*! \brief NS-over-FR-over-GRE-over-IP specific bits */ struct { struct osmo_fd fd; uint32_t local_ip; unsigned int enabled:1; } frgre; }; enum nsvc_timer_mode { /* standard timers */ NSVC_TIMER_TNS_TEST, NSVC_TIMER_TNS_ALIVE, NSVC_TIMER_TNS_RESET, _NSVC_TIMER_NR, }; /*! \brief Structure representing a single NS-VC */ struct gprs_nsvc { /*! \brief list of NS-VCs within NS Instance */ struct llist_head list; /*! \brief pointer to NS Instance */ struct gprs_ns_inst *nsi; uint16_t nsei; /*! \brief end-to-end significance */ uint16_t nsvci; /*! \brief uniquely identifies NS-VC at SGSN */ uint32_t state; uint32_t remote_state; struct osmo_timer_list timer; enum nsvc_timer_mode timer_mode; struct timeval timer_started; int alive_retries; unsigned int remote_end_is_sgsn:1; unsigned int persistent:1; unsigned int nsvci_is_valid:1; struct rate_ctr_group *ctrg; struct osmo_stat_item_group *statg; /*! \brief which link-layer are we based on? */ enum gprs_ns_ll ll; union { struct { struct sockaddr_in bts_addr; } ip; struct { struct sockaddr_in bts_addr; } frgre; }; }; /* Create a new NS protocol instance */ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx); /* Close a NS protocol instance */ void gprs_ns_close(struct gprs_ns_inst *nsi); /* Close and Destroy a NS protocol instance */ void gprs_ns_destroy(struct gprs_ns_inst *nsi); /* Listen for incoming GPRS packets via NS/UDP */ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi); /* Establish a connection (from the BSS) to the SGSN */ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi, struct sockaddr_in *dest, uint16_t nsei, uint16_t nsvci); struct sockaddr_in; /* main function for higher layers (BSSGP) to send NS messages */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg); int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause); int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause); int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc); /* Listen for incoming GPRS packets via NS/FR/GRE */ int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi); struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci); void gprs_nsvc_delete(struct gprs_nsvc *nsvc); struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei); struct gprs_nsvc *gprs_nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci); /* Initiate a RESET procedure (including timer start, ...)*/ int gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause); /* Add NS-specific VTY stuff */ int gprs_ns_vty_init(struct gprs_ns_inst *nsi); /* Resturn peer info as string (NOTE: the buffer is allocated statically) */ const char *gprs_ns_ll_str(struct gprs_nsvc *nsvc); /* Copy the link layer info from other into nsvc */ void gprs_ns_ll_copy(struct gprs_nsvc *nsvc, struct gprs_nsvc *other); /* Clear the link layer info (will never match a real link then) */ void gprs_ns_ll_clear(struct gprs_nsvc *nsvc); struct msgb *gprs_ns_msgb_alloc(void); enum signal_ns { S_NS_RESET, S_NS_BLOCK, S_NS_UNBLOCK, S_NS_ALIVE_EXP, /* Tns-alive expired more than N times */ S_NS_REPLACED, /* nsvc object is replaced (sets old_nsvc) */ S_NS_MISMATCH, /* got an unexpected IE (sets msg, pdu_type, ie_type) */ }; struct ns_signal_data { struct gprs_nsvc *nsvc; struct gprs_nsvc *old_nsvc; uint8_t cause; uint8_t pdu_type; uint8_t ie_type; struct msgb *msg; }; void gprs_ns_set_log_ss(int ss); /*! }@ */ libosmocore-0.9.0/include/osmocom/gprs/gprs_ns_frgre.h000066400000000000000000000001231261607044000231140ustar00rootroot00000000000000#pragma once int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); libosmocore-0.9.0/include/osmocom/gprs/protocol/000077500000000000000000000000001261607044000217505ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/gprs/protocol/gsm_08_16.h000066400000000000000000000042731261607044000235320ustar00rootroot00000000000000#pragma once /* GPRS Networks Service (NS) messages on the Gb interface * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) * 3GPP TS 48.016 version 6.5.0 Release 6 / ETSI TS 148 016 V6.5.0 (2005-11) */ #include /*! \addtogroup libgb * @{ */ /*! \file gprs_ns.h */ /*! \brief Common header of GPRS NS */ struct gprs_ns_hdr { uint8_t pdu_type; /*!< NS PDU type */ uint8_t data[0]; /*!< variable-length payload */ } __attribute__((packed)); /*! \brief NS PDU Type (TS 08.16, Section 10.3.7, Table 14) */ enum ns_pdu_type { NS_PDUT_UNITDATA = 0x00, NS_PDUT_RESET = 0x02, NS_PDUT_RESET_ACK = 0x03, NS_PDUT_BLOCK = 0x04, NS_PDUT_BLOCK_ACK = 0x05, NS_PDUT_UNBLOCK = 0x06, NS_PDUT_UNBLOCK_ACK = 0x07, NS_PDUT_STATUS = 0x08, NS_PDUT_ALIVE = 0x0a, NS_PDUT_ALIVE_ACK = 0x0b, /* TS 48.016 Section 10.3.7, Table 10.3.7.1 */ SNS_PDUT_ACK = 0x0c, SNS_PDUT_ADD = 0x0d, SNS_PDUT_CHANGE_WEIGHT = 0x0e, SNS_PDUT_CONFIG = 0x0f, SNS_PDUT_CONFIG_ACK = 0x10, SNS_PDUT_DELETE = 0x11, SNS_PDUT_SIZE = 0x12, SNS_PDUT_SIZE_ACK = 0x13, }; /*! \brief NS Control IE (TS 08.16, Section 10.3, Table 12) */ enum ns_ctrl_ie { NS_IE_CAUSE = 0x00, NS_IE_VCI = 0x01, NS_IE_PDU = 0x02, NS_IE_BVCI = 0x03, NS_IE_NSEI = 0x04, /* TS 48.016 Section 10.3, Table 10.3.1 */ NS_IE_IPv4_LIST = 0x05, NS_IE_IPv6_LIST = 0x06, NS_IE_MAX_NR_NSVC = 0x07, NS_IE_IPv4_EP_NR = 0x08, NS_IE_IPv6_EP_NR = 0x09, NS_IE_RESET_FLAG = 0x0a, NS_IE_IP_ADDR = 0x0b, }; /*! \brief NS Cause (TS 08.16, Section 10.3.2, Table 13) */ enum ns_cause { NS_CAUSE_TRANSIT_FAIL = 0x00, NS_CAUSE_OM_INTERVENTION = 0x01, NS_CAUSE_EQUIP_FAIL = 0x02, NS_CAUSE_NSVC_BLOCKED = 0x03, NS_CAUSE_NSVC_UNKNOWN = 0x04, NS_CAUSE_BVCI_UNKNOWN = 0x05, NS_CAUSE_SEM_INCORR_PDU = 0x08, NS_CAUSE_PDU_INCOMP_PSTATE = 0x0a, NS_CAUSE_PROTO_ERR_UNSPEC = 0x0b, NS_CAUSE_INVAL_ESSENT_IE = 0x0c, NS_CAUSE_MISSING_ESSENT_IE = 0x0d, /* TS 48.016 Section 10.3.2, Table 10.3.2.1 */ NS_CAUSE_INVAL_NR_IPv4_EP = 0x0e, NS_CAUSE_INVAL_NR_IPv6_EP = 0x0f, NS_CAUSE_INVAL_NR_NS_VC = 0x10, NS_CAUSE_INVAL_WEIGH = 0x11, NS_CAUSE_UNKN_IP_EP = 0x12, NS_CAUSE_UNKN_IP_ADDR = 0x13, NS_CAUSE_UNKN_IP_TEST_FAILED = 0x14, }; libosmocore-0.9.0/include/osmocom/gprs/protocol/gsm_08_18.h000066400000000000000000000105651261607044000235350ustar00rootroot00000000000000#pragma once #include /*! \brief Fixed BVCI definitions (Section 5.4.1) */ #define BVCI_SIGNALLING 0x0000 #define BVCI_PTM 0x0001 /*! \brief BSSGP PDU types (Section 11.3.26 / Table 11.27) */ enum bssgp_pdu_type { /* PDUs between RL and BSSGP SAPs */ BSSGP_PDUT_DL_UNITDATA = 0x00, BSSGP_PDUT_UL_UNITDATA = 0x01, BSSGP_PDUT_RA_CAPABILITY = 0x02, BSSGP_PDUT_PTM_UNITDATA = 0x03, /* PDUs between GMM SAPs */ BSSGP_PDUT_PAGING_PS = 0x06, BSSGP_PDUT_PAGING_CS = 0x07, BSSGP_PDUT_RA_CAPA_UDPATE = 0x08, BSSGP_PDUT_RA_CAPA_UPDATE_ACK = 0x09, BSSGP_PDUT_RADIO_STATUS = 0x0a, BSSGP_PDUT_SUSPEND = 0x0b, BSSGP_PDUT_SUSPEND_ACK = 0x0c, BSSGP_PDUT_SUSPEND_NACK = 0x0d, BSSGP_PDUT_RESUME = 0x0e, BSSGP_PDUT_RESUME_ACK = 0x0f, BSSGP_PDUT_RESUME_NACK = 0x10, /* PDus between NM SAPs */ BSSGP_PDUT_BVC_BLOCK = 0x20, BSSGP_PDUT_BVC_BLOCK_ACK = 0x21, BSSGP_PDUT_BVC_RESET = 0x22, BSSGP_PDUT_BVC_RESET_ACK = 0x23, BSSGP_PDUT_BVC_UNBLOCK = 0x24, BSSGP_PDUT_BVC_UNBLOCK_ACK = 0x25, BSSGP_PDUT_FLOW_CONTROL_BVC = 0x26, BSSGP_PDUT_FLOW_CONTROL_BVC_ACK = 0x27, BSSGP_PDUT_FLOW_CONTROL_MS = 0x28, BSSGP_PDUT_FLOW_CONTROL_MS_ACK = 0x29, BSSGP_PDUT_FLUSH_LL = 0x2a, BSSGP_PDUT_FLUSH_LL_ACK = 0x2b, BSSGP_PDUT_LLC_DISCARD = 0x2c, BSSGP_PDUT_SGSN_INVOKE_TRACE = 0x40, BSSGP_PDUT_STATUS = 0x41, /* PDUs between PFM SAP's */ BSSGP_PDUT_DOWNLOAD_BSS_PFC = 0x50, BSSGP_PDUT_CREATE_BSS_PFC = 0x51, BSSGP_PDUT_CREATE_BSS_PFC_ACK = 0x52, BSSGP_PDUT_CREATE_BSS_PFC_NACK = 0x53, BSSGP_PDUT_MODIFY_BSS_PFC = 0x54, BSSGP_PDUT_MODIFY_BSS_PFC_ACK = 0x55, BSSGP_PDUT_DELETE_BSS_PFC = 0x56, BSSGP_PDUT_DELETE_BSS_PFC_ACK = 0x57, }; /*! \brief BSSGP User-Data header (Section 10.2.1 and 10.2.2) */ struct bssgp_ud_hdr { uint8_t pdu_type; /*!< BSSGP PDU type */ uint32_t tlli; /*!< Temporary Link-Local Identifier */ uint8_t qos_profile[3]; /*!< QoS profile */ uint8_t data[0]; /* optional/conditional IEs as TLVs */ } __attribute__((packed)); /*! \brief BSSGP normal header */ struct bssgp_normal_hdr { uint8_t pdu_type; /*!< BSSGP PDU type */ uint8_t data[0]; /*!< optional/conditional IEs as TLVs */ }; /*! \brief BSSGP Information Element Identifiers */ enum bssgp_iei_type { BSSGP_IE_ALIGNMENT = 0x00, BSSGP_IE_BMAX_DEFAULT_MS = 0x01, BSSGP_IE_BSS_AREA_ID = 0x02, BSSGP_IE_BUCKET_LEAK_RATE = 0x03, BSSGP_IE_BVCI = 0x04, BSSGP_IE_BVC_BUCKET_SIZE = 0x05, BSSGP_IE_BVC_MEASUREMENT = 0x06, BSSGP_IE_CAUSE = 0x07, BSSGP_IE_CELL_ID = 0x08, BSSGP_IE_CHAN_NEEDED = 0x09, BSSGP_IE_DRX_PARAMS = 0x0a, BSSGP_IE_EMLPP_PRIO = 0x0b, BSSGP_IE_FLUSH_ACTION = 0x0c, BSSGP_IE_IMSI = 0x0d, BSSGP_IE_LLC_PDU = 0x0e, BSSGP_IE_LLC_FRAMES_DISCARDED = 0x0f, BSSGP_IE_LOCATION_AREA = 0x10, BSSGP_IE_MOBILE_ID = 0x11, BSSGP_IE_MS_BUCKET_SIZE = 0x12, BSSGP_IE_MS_RADIO_ACCESS_CAP = 0x13, BSSGP_IE_OMC_ID = 0x14, BSSGP_IE_PDU_IN_ERROR = 0x15, BSSGP_IE_PDU_LIFETIME = 0x16, BSSGP_IE_PRIORITY = 0x17, BSSGP_IE_QOS_PROFILE = 0x18, BSSGP_IE_RADIO_CAUSE = 0x19, BSSGP_IE_RA_CAP_UPD_CAUSE = 0x1a, BSSGP_IE_ROUTEING_AREA = 0x1b, BSSGP_IE_R_DEFAULT_MS = 0x1c, BSSGP_IE_SUSPEND_REF_NR = 0x1d, BSSGP_IE_TAG = 0x1e, BSSGP_IE_TLLI = 0x1f, BSSGP_IE_TMSI = 0x20, BSSGP_IE_TRACE_REFERENC = 0x21, BSSGP_IE_TRACE_TYPE = 0x22, BSSGP_IE_TRANSACTION_ID = 0x23, BSSGP_IE_TRIGGER_ID = 0x24, BSSGP_IE_NUM_OCT_AFF = 0x25, BSSGP_IE_LSA_ID_LIST = 0x26, BSSGP_IE_LSA_INFORMATION = 0x27, BSSGP_IE_PACKET_FLOW_ID = 0x28, BSSGP_IE_PACKET_FLOW_TIMER = 0x29, BSSGP_IE_AGG_BSS_QOS_PROFILE = 0x3a, BSSGP_IE_FEATURE_BITMAP = 0x3b, BSSGP_IE_BUCKET_FULL_RATIO = 0x3c, BSSGP_IE_SERVICE_UTRAN_CCO = 0x3d, }; /*! \brief Cause coding (Section 11.3.8 / Table 11.10) */ enum gprs_bssgp_cause { BSSGP_CAUSE_PROC_OVERLOAD = 0x00, BSSGP_CAUSE_EQUIP_FAIL = 0x01, BSSGP_CAUSE_TRASIT_NET_FAIL = 0x02, BSSGP_CAUSE_CAPA_GREATER_0KPBS = 0x03, BSSGP_CAUSE_UNKNOWN_MS = 0x04, BSSGP_CAUSE_UNKNOWN_BVCI = 0x05, BSSGP_CAUSE_CELL_TRAF_CONG = 0x06, BSSGP_CAUSE_SGSN_CONG = 0x07, BSSGP_CAUSE_OML_INTERV = 0x08, BSSGP_CAUSE_BVCI_BLOCKED = 0x09, BSSGP_CAUSE_PFC_CREATE_FAIL = 0x0a, BSSGP_CAUSE_SEM_INCORR_PDU = 0x20, BSSGP_CAUSE_INV_MAND_INF = 0x21, BSSGP_CAUSE_MISSING_MAND_IE = 0x22, BSSGP_CAUSE_MISSING_COND_IE = 0x23, BSSGP_CAUSE_UNEXP_COND_IE = 0x24, BSSGP_CAUSE_COND_IE_ERR = 0x25, BSSGP_CAUSE_PDU_INCOMP_STATE = 0x26, BSSGP_CAUSE_PROTO_ERR_UNSPEC = 0x27, BSSGP_CAUSE_PDU_INCOMP_FEAT = 0x28, }; libosmocore-0.9.0/include/osmocom/gsm/000077500000000000000000000000001261607044000177225ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/gsm/a5.h000066400000000000000000000033711261607044000204040ustar00rootroot00000000000000/* * a5.h * * Copyright (C) 2011 Sylvain Munaut * * 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. */ #pragma once #include #include /*! \defgroup a5 GSM A5 ciphering algorithm * @{ */ /*! \file gsm/a5.h * \brief Osmocom GSM A5 ciphering algorithm header */ /*! \brief Converts a frame number into the 22 bit number used in A5/x * \param[in] fn The true framenumber * \return 22 bit word */ static inline uint32_t osmo_a5_fn_count(uint32_t fn) { int t1 = fn / (26 * 51); int t2 = fn % 26; int t3 = fn % 51; return (t1 << 11) | (t3 << 5) | t2; } /* Notes: * - key must be 8 or 16 (for a5/4) bytes long (or NULL for A5/0) * - the dl and ul pointer must be either NULL or 114 bits long * - fn is the _real_ GSM frame number. * (converted internally to fn_count) */ int osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul); /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/abis_nm.h000066400000000000000000000032721261607044000215070ustar00rootroot00000000000000#pragma once /*! \defgroup oml A-bis OML * @{ */ #include #include #include /*! \file abis_nm.h */ extern const char abis_nm_ipa_magic[13]; extern const char abis_nm_osmo_magic[12]; enum abis_nm_msgtype; enum gsm_phys_chan_config; extern const enum abis_nm_msgtype abis_nm_reports[4]; extern const enum abis_nm_msgtype abis_nm_no_ack_nack[3]; extern const enum abis_nm_msgtype abis_nm_sw_load_msgs[9]; extern const enum abis_nm_msgtype abis_nm_nacks[33]; extern const struct value_string abis_nm_msg_disc_names[]; extern const struct value_string abis_nm_obj_class_names[]; extern const struct value_string abis_nm_adm_state_names[]; const char *abis_nm_nack_cause_name(uint8_t cause); const char *abis_nm_nack_name(uint8_t nack); const char *abis_nm_event_type_name(uint8_t cause); const char *abis_nm_severity_name(uint8_t cause); extern const struct tlv_definition abis_nm_att_tlvdef; const char *abis_nm_opstate_name(uint8_t os); const char *abis_nm_avail_name(uint8_t avail); const char *abis_nm_test_name(uint8_t test); extern const struct tlv_definition abis_nm_osmo_att_tlvdef; /*! \brief write a human-readable OML header to the debug log * \param[in] ss Logging sub-system * \param[in] foh A-bis OML FOM header */ #define abis_nm_debugp_foh(ss, foh) \ DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", \ get_value_string(abis_nm_obj_class_names, (foh)->obj_class), \ (foh)->obj_class, (foh)->obj_inst.bts_nr, (foh)->obj_inst.trx_nr, \ (foh)->obj_inst.ts_nr) int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan); enum gsm_phys_chan_config abis_nm_pchan4chcomb(uint8_t chcomb); /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/apn.h000066400000000000000000000005731261607044000206560ustar00rootroot00000000000000#pragma once #include /* 23.003 Section 9.1.1, excluding any terminating zero byte */ #define APN_NI_MAXLEN 63 /* 23.003 Section 9.1, excluding any terminating zero byte */ #define APN_MAXLEN 100 char *osmo_apn_qualify(unsigned int mcc, unsigned int mnc, const char *ni); char *osmo_apn_qualify_from_imsi(const char *imsi, const char *ni, int have_3dig_mnc); libosmocore-0.9.0/include/osmocom/gsm/comp128.h000066400000000000000000000005071261607044000212660ustar00rootroot00000000000000/* * COMP128 header * * See comp128.c for details */ #pragma once #include /* * 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(const uint8_t *ki, const uint8_t *srand, uint8_t *sres, uint8_t *kc); libosmocore-0.9.0/include/osmocom/gsm/comp128v23.h000066400000000000000000000007351261607044000216240ustar00rootroot00000000000000/* * COMP128v23 header * * See comp128v23.c for details */ #pragma once #include /* * Performs the COMP128 version 2 and 3 algorithm (used as A3/A8) * ki : uint8_t [16] * srand : uint8_t [16] * sres : uint8_t [4] * kc : uint8_t [8] * returns 1 if not version 2 or 3 specified */ int comp128v2(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc); int comp128v3(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc); libosmocore-0.9.0/include/osmocom/gsm/gan.h000066400000000000000000000002241261607044000206360ustar00rootroot00000000000000#pragma once #include extern const struct value_string gan_msgt_vals[]; static const struct value_string gan_pdisc_vals[]; libosmocore-0.9.0/include/osmocom/gsm/gsm0341.h000066400000000000000000000004251261607044000211720ustar00rootroot00000000000000#pragma once #include struct gsm341_ms_message * gsm0341_build_msg(void *ctx, uint8_t geo_scope, uint8_t msg_code, uint8_t update, uint16_t msg_id, uint8_t dcs, uint8_t page_total, uint8_t page_cur, uint8_t *data, uint8_t len); libosmocore-0.9.0/include/osmocom/gsm/gsm0411_smc.h000066400000000000000000000036701261607044000220370ustar00rootroot00000000000000#pragma once #include #include #define GSM411_MMSMS_EST_REQ 0x310 #define GSM411_MMSMS_EST_IND 0x312 #define GSM411_MMSMS_EST_CNF 0x311 #define GSM411_MMSMS_REL_REQ 0x320 #define GSM411_MMSMS_REL_IND 0x322 #define GSM411_MMSMS_DATA_REQ 0x330 #define GSM411_MMSMS_DATA_IND 0x332 #define GSM411_MMSMS_UNIT_DATA_REQ 0x340 #define GSM411_MMSMS_UNIT_DATA_IND 0x342 #define GSM411_MMSMS_ERR_IND 0x372 #define GSM411_MNSMS_ABORT_REQ 0x101 #define GSM411_MNSMS_DATA_REQ 0x102 #define GSM411_MNSMS_DATA_IND 0x103 #define GSM411_MNSMS_EST_REQ 0x104 #define GSM411_MNSMS_EST_IND 0x105 #define GSM411_MNSMS_ERROR_IND 0x106 #define GSM411_MNSMS_REL_REQ 0x107 struct gsm411_smc_inst { uint64_t id; /* a unique id for the SMS */ int network; /* is this a MO (0) or MT (1) transfer */ int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg); int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type); enum gsm411_cp_state cp_state; struct osmo_timer_list cp_timer; struct msgb *cp_msg; /* store pending message */ int cp_rel; /* store pending release */ int cp_retx; /* retry counter */ int cp_max_retr; /* maximum retry */ int cp_tc1; /* timer value TC1* */ }; extern const struct value_string gsm411_cp_cause_strs[]; /* init a new instance */ void gsm411_smc_init(struct gsm411_smc_inst *inst, uint64_t id, int network, int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg), int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type)); /* clear instance */ void gsm411_smc_clear(struct gsm411_smc_inst *inst); /* message from upper layer */ int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg); /* message from lower layer */ int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type); libosmocore-0.9.0/include/osmocom/gsm/gsm0411_smr.h000066400000000000000000000024151261607044000220520ustar00rootroot00000000000000#pragma once #include #define GSM411_SM_RL_DATA_REQ 0x401 #define GSM411_SM_RL_DATA_IND 0x402 #define GSM411_SM_RL_MEM_AVAIL_REQ 0x403 #define GSM411_SM_RL_MEM_AVAIL_IND 0x404 #define GSM411_SM_RL_REPORT_REQ 0x405 #define GSM411_SM_RL_REPORT_IND 0x406 struct gsm411_smr_inst { uint64_t id; /* a unique id for the SMS */ int network; /* is this a MO (0) or MT (1) transfer */ int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg); int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg); enum gsm411_rp_state rp_state; struct osmo_timer_list rp_timer; }; extern const struct value_string gsm411_rp_cause_strs[]; /* init a new instance */ void gsm411_smr_init(struct gsm411_smr_inst *inst, uint64_t id, int network, int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg), int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg)); /* clear instance */ void gsm411_smr_clear(struct gsm411_smr_inst *inst); /* message from upper layer */ int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg); /* message from lower layer */ int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg); libosmocore-0.9.0/include/osmocom/gsm/gsm0411_utils.h000066400000000000000000000021421261607044000224060ustar00rootroot00000000000000#pragma once #include /* Turn int into semi-octet representation: 98 => 0x89 */ uint8_t gsm411_bcdify(uint8_t value); /* Turn semi-octet representation into int: 0x89 => 98 */ uint8_t gsm411_unbcdify(uint8_t value); struct msgb *gsm411_msgb_alloc(void); /* Generate 03.40 TP-SCTS */ void gsm340_gen_scts(uint8_t *scts, time_t time); /* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ time_t gsm340_scts(uint8_t *scts); /* decode validity period. return minutes */ unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp); /* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs); /* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, uint8_t plan, const char *number); /* Prefix msg with a RP header */ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, uint8_t rp_msg_ref); /* Prefix msg with a 04.08/04.11 CP header */ int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, uint8_t msg_type); libosmocore-0.9.0/include/osmocom/gsm/gsm0480.h000066400000000000000000000021301261607044000211710ustar00rootroot00000000000000#pragma once #include #include #include #include #define MAX_LEN_USSD_STRING 31 /* deprecated */ struct ussd_request { char text[MAX_LEN_USSD_STRING + 1]; uint8_t transaction_id; uint8_t invoke_id; }; /* deprecated */ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *request) OSMO_DEPRECATED("Use gsm0480_decode_ss_request() instead"); struct ss_request { uint8_t opcode; uint8_t ss_code; uint8_t ussd_text[MAX_LEN_USSD_STRING + 1]; uint8_t transaction_id; uint8_t invoke_id; }; int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *request); struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text); struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text); struct msgb *gsm0480_create_notifySS(const char *text); int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id); int gsm0480_wrap_facility(struct msgb *msg); libosmocore-0.9.0/include/osmocom/gsm/gsm0502.h000066400000000000000000000016651261607044000212000ustar00rootroot00000000000000#pragma once #include #include #include /* Table 5 Clause 7 TS 05.02 */ static inline unsigned int gsm0502_get_n_pag_blocks(struct gsm48_control_channel_descr *chan_desc) { if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C) return 3 - chan_desc->bs_ag_blks_res; else return 9 - chan_desc->bs_ag_blks_res; } /* Chapter 6.5.2 of TS 05.02 */ static inline unsigned int gsm0502_get_ccch_group(uint64_t imsi, unsigned int bs_cc_chans, unsigned int n_pag_blocks) { return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks; } /* Chapter 6.5.2 of TS 05.02 */ static inline unsigned int gsm0502_get_paging_group(uint64_t imsi, unsigned int bs_cc_chans, int n_pag_blocks) { return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks; } unsigned int gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi); libosmocore-0.9.0/include/osmocom/gsm/gsm0808.h000066400000000000000000000041011261607044000211750ustar00rootroot00000000000000/* (C) 2009,2010 by Holger Hans Peter Freyther * (C) 2009,2010 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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. * */ #pragma once #include "tlv.h" struct msgb; struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, uint16_t ci); struct msgb *gsm0808_create_reset(void); struct msgb *gsm0808_create_reset_ack(void); struct msgb *gsm0808_create_clear_command(uint8_t reason); struct msgb *gsm0808_create_clear_complete(void); struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); struct msgb *gsm0808_create_cipher_reject(uint8_t cause); struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len); struct msgb *gsm0808_create_sapi_reject(uint8_t link_id); struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause, uint8_t chosen_channel, uint8_t encr_alg_id, uint8_t speech_mode); struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause); struct msgb *gsm0808_create_clear_rqst(uint8_t cause); struct msgb *gsm0808_create_dtap(struct msgb *msg, uint8_t link_id); void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id); const struct tlv_definition *gsm0808_att_tlvdef(void); const char *gsm0808_bssmap_name(uint8_t msg_type); const char *gsm0808_bssap_name(uint8_t msg_type); libosmocore-0.9.0/include/osmocom/gsm/gsm48.h000066400000000000000000000026131261607044000210370ustar00rootroot00000000000000#pragma once #include #include #include /* reserved according to GSM 03.03 § 2.4 */ #define GSM_RESERVED_TMSI 0xFFFFFFFF /* 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); int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, uint16_t *mnc, uint16_t *lac); 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); const char *gsm48_mi_type_name(uint8_t mi); /* 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); int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc); libosmocore-0.9.0/include/osmocom/gsm/gsm48_ie.h000066400000000000000000000104171261607044000215150ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include /* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */ int gsm48_decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv, int h_len); /* convert a ASCII phone number to 'called/calling/connect party BCD number' */ int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, int h_len, const char *input); /* decode 'bearer capability' */ int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, const uint8_t *lv); /* encode 'bearer capability' */ int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only, const struct gsm_mncc_bearer_cap *bcap); /* decode 'call control cap' */ int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv); /* encode 'call control cap' */ int gsm48_encode_cccap(struct msgb *msg, const struct gsm_mncc_cccap *ccap); /* decode 'called party BCD number' */ int gsm48_decode_called(struct gsm_mncc_number *called, const uint8_t *lv); /* encode 'called party BCD number' */ int gsm48_encode_called(struct msgb *msg, const struct gsm_mncc_number *called); /* decode callerid of various IEs */ int gsm48_decode_callerid(struct gsm_mncc_number *callerid, const uint8_t *lv); /* encode callerid of various IEs */ int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len, const struct gsm_mncc_number *callerid); /* decode 'cause' */ int gsm48_decode_cause(struct gsm_mncc_cause *cause, const uint8_t *lv); /* encode 'cause' */ int gsm48_encode_cause(struct msgb *msg, int lv_only, const struct gsm_mncc_cause *cause); /* decode 'calling number' */ int gsm48_decode_calling(struct gsm_mncc_number *calling, const uint8_t *lv); /* encode 'calling number' */ int gsm48_encode_calling(struct msgb *msg, const struct gsm_mncc_number *calling); /* decode 'connected number' */ int gsm48_decode_connected(struct gsm_mncc_number *connected, const uint8_t *lv); /* encode 'connected number' */ int gsm48_encode_connected(struct msgb *msg, const struct gsm_mncc_number *connected); /* decode 'redirecting number' */ int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting, const uint8_t *lv); /* encode 'redirecting number' */ int gsm48_encode_redirecting(struct msgb *msg, const struct gsm_mncc_number *redirecting); /* decode 'facility' */ int gsm48_decode_facility(struct gsm_mncc_facility *facility, const uint8_t *lv); /* encode 'facility' */ int gsm48_encode_facility(struct msgb *msg, int lv_only, const struct gsm_mncc_facility *facility); /* decode 'notify' */ int gsm48_decode_notify(int *notify, const uint8_t *v); /* encode 'notify' */ int gsm48_encode_notify(struct msgb *msg, int notify); /* decode 'signal' */ int gsm48_decode_signal(int *signal, const uint8_t *v); /* encode 'signal' */ int gsm48_encode_signal(struct msgb *msg, int signal); /* decode 'keypad' */ int gsm48_decode_keypad(int *keypad, const uint8_t *lv); /* encode 'keypad' */ int gsm48_encode_keypad(struct msgb *msg, int keypad); /* decode 'progress' */ int gsm48_decode_progress(struct gsm_mncc_progress *progress, const uint8_t *lv); /* encode 'progress' */ int gsm48_encode_progress(struct msgb *msg, int lv_only, const struct gsm_mncc_progress *p); /* decode 'user-user' */ int gsm48_decode_useruser(struct gsm_mncc_useruser *uu, const uint8_t *lv); /* encode 'useruser' */ int gsm48_encode_useruser(struct msgb *msg, int lv_only, const struct gsm_mncc_useruser *uu); /* decode 'ss version' */ int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv, const uint8_t *lv); /* encode 'ss version' */ int gsm48_encode_ssversion(struct msgb *msg, const struct gsm_mncc_ssversion *ssv); /* decode 'more data' does not require a function, because it has no value */ /* encode 'more data' */ int gsm48_encode_more(struct msgb *msg); /* structure of one frequency */ struct gsm_sysinfo_freq { /* if the frequency included in the sysinfo */ uint8_t mask; }; /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask, uint8_t frqt); libosmocore-0.9.0/include/osmocom/gsm/gsm_utils.h000066400000000000000000000152101261607044000221000ustar00rootroot00000000000000/* GSM utility functions, e.g. coding and decoding */ /* * (C) 2008 by Daniel Willmann * (C) 2009 by Holger Hans Peter Freyther * (C) 2009-2010 by Harald Welte * * 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. * */ #pragma once #include #include #include #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); /*! * \brief Decode a sequence of GSM 03.38 encoded 7 bit characters. * * \param decoded The destination buffer for the decoded characters. * \param n A maximum of n chars is written (incl. terminating \0). * Requires n >= 1. * \param user_data A pointer to the start of the packed 7bit character * sequence. * \param length The length of the input sequence (in octets). * * \returns the number of (8 bit) chars written excluding the terminating \0. * This is the same like strlen(decoded). */ int gsm_7bit_decode_n(char *decoded, size_t n, const uint8_t *user_data, uint8_t length); /*! * \brief Decode a sequence of 7 bit characters (USSD encoding). * * \see gsm_7bit_encode_n() */ int gsm_7bit_decode_n_ussd(char *decoded, size_t n, const uint8_t *user_data, uint8_t length); /*! * \brief Encode a text string into GSM 03.38 encoded 7 bit characters. * * \param result The destination buffer for the packed 7 bit sequence. * \param n A maximum of n octets is written. * \param data A pointer to the start of the \0 terminated 8 bit character * string. * \param octets_written Iff not NULL, *octets_written will be set to the * number of octets written to the result buffer. * * \returns the number of septets that have been created. */ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets_written); /*! * \brief Encode a text string into GSM 03.38 encoded 7 bit characters (USSD encoding). * * \see gsm_7bit_decode_n() */ int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets_written); /* the four functions below are helper functions and here for the unit test */ int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding); int gsm_septet_encode(uint8_t *result, const char *data); uint8_t gsm_get_octet_len(const uint8_t sept_len); int gsm_7bit_decode_n_hdr(char *decoded, size_t n, const uint8_t *user_data, uint8_t length, uint8_t ud_hdr_ind); unsigned int ms_class_gmsk_dbm(enum gsm_band band, int ms_class); 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 05.08 Chapter 8.1.4 */ int rxlev2dbm(uint8_t rxlev); uint8_t dbm2rxlev(int dbm); /* According to GSM 04.08 Chapter 10.5.1.6 */ static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) { switch (n) { case 0: return 1; case 1: return (cm2[0] & (1<<3)) ? 0 : 1; case 2: return (cm2[2] & (1<<0)) ? 1 : 0; case 3: return (cm2[2] & (1<<1)) ? 1 : 0; default: return 0; } } /* According to GSM 04.08 Chapter 10.5.2.29 */ static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; } static inline int rach_max_trans_raw2val(int raw) { const int tbl[4] = { 1, 2, 4, 7 }; return tbl[raw & 3]; } #define ARFCN_PCS 0x8000 #define ARFCN_UPLINK 0x4000 #define ARFCN_FLAG_MASK 0xf000 /* Reserve the upper 5 bits for flags */ enum gsm_band gsm_arfcn2band(uint16_t arfcn); /* Convert an ARFCN to the frequency in MHz * 10 */ uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); /* Convert a Frequency in MHz * 10 to ARFCN */ uint16_t gsm_freq102arfcn(uint16_t freq10, 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, TLLI_G_RNTI, TLLI_RAND_G_RNTI, }; /* TS 03.03 Chapter 2.6 */ int gprs_tlli_type(uint32_t tlli); uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type); /* Osmocom internal, not part of any gsm spec */ enum gsm_phys_chan_config { GSM_PCHAN_NONE, GSM_PCHAN_CCCH, GSM_PCHAN_CCCH_SDCCH4, GSM_PCHAN_TCH_F, GSM_PCHAN_TCH_H, GSM_PCHAN_SDCCH8_SACCH8C, GSM_PCHAN_PDCH, /* GPRS PDCH */ GSM_PCHAN_TCH_F_PDCH, /* TCH/F if used, PDCH otherwise */ GSM_PCHAN_UNKNOWN, GSM_PCHAN_CCCH_SDCCH4_CBCH, GSM_PCHAN_SDCCH8_SACCH8C_CBCH, _GSM_PCHAN_MAX }; /* Osmocom internal, not part of any gsm spec */ enum gsm_chan_t { GSM_LCHAN_NONE, GSM_LCHAN_SDCCH, GSM_LCHAN_TCH_F, GSM_LCHAN_TCH_H, GSM_LCHAN_UNKNOWN, GSM_LCHAN_CCCH, GSM_LCHAN_PDTCH, GSM_LCHAN_CBCH, _GSM_LCHAN_MAX }; /* Deprectated functions */ /* Limit encoding and decoding to use no more than this amount of buffer bytes */ #define GSM_7BIT_LEGACY_MAX_BUFFER_SIZE 0x10000 int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n() instead"); int gsm_7bit_decode_ussd(char *decoded, const uint8_t *user_data, uint8_t length) OSMO_DEPRECATED("Use gsm_7bit_decode_n_ussd() instead"); int gsm_7bit_encode(uint8_t *result, const char *data) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead"); int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n_ussd() instead"); int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets_written) OSMO_DEPRECATED("Use gsm_7bit_encode_n() instead"); libosmocore-0.9.0/include/osmocom/gsm/ipa.h000066400000000000000000000037421261607044000206520ustar00rootroot00000000000000#pragma once #include #include #include struct osmo_fd; /* internal (host-only) data structure */ struct ipaccess_unit { uint16_t site_id; uint16_t bts_id; uint16_t trx_id; char *unit_name; char *equipvers; char *swversion; uint8_t mac_addr[6]; char *location1; char *location2; char *serno; }; /* obtain the human-readable name of an IPA CCM ID TAG */ const char *ipa_ccm_idtag_name(uint8_t tag); /* parse a buffer of ID tags into a osmocom TLV style representation */ int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len); /* Is the TAG included in the length field? */ int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset); /* parse an Unit ID in string format into the 'ipaccess_unit' data structure */ int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data); /* fill a 'struct ipaccess_unit' based on a parsed IDTAG TLV */ int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud, const struct tlv_parsed *tp); /* Send an IPA message to the given FD */ int ipa_send(int fd, const void *msg, size_t msglen); /* Send an IPA CCM PONG via the given FD */ int ipa_ccm_send_pong(int fd); /* Send an IPA CCM ID_ACK via the given FD */ int ipa_ccm_send_id_ack(int fd); /* Send an IPA CCM ID_REQ via the given FD */ int ipa_ccm_send_id_req(int fd); /* Common handling of IPA CCM, BSC side */ int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd); /* Common handling of IPA CCM, BTS side */ int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd); /* prepend (push) an ipaccess_head_ext to the msgb */ void ipa_prepend_header_ext(struct msgb *msg, int proto); /* prepend (push) an ipaccess_head to the msgb */ void ipa_prepend_header(struct msgb *msg, int proto); struct msgb *ipa_msg_alloc(int headroom); int ipa_msg_recv(int fd, struct msgb **rmsg); int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg); libosmocore-0.9.0/include/osmocom/gsm/kasumi.h000066400000000000000000000033411261607044000213650ustar00rootroot00000000000000/* * KASUMI header * * See kasumi.c for details * The parameters are described in TS 135 202. */ #pragma once #include /*! \brief Single iteration of KASUMI cipher * \param[in] P Block, 64 bits to be processed in this round * \param[in] KLi1 Expanded subkeys * \param[in] KLi2 Expanded subkeys * \param[in] KOi1 Expanded subkeys * \param[in] KOi2 Expanded subkeys * \param[in] KOi3 Expanded subkeys * \param[in] KIi1 Expanded subkeys * \param[in] KIi2 Expanded subkeys * \param[in] KIi3 Expanded subkeys * \returns processed block of 64 bits */ uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3); /*! \brief Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD) * \param[in] CA * \param[in] cb * \param[in] cc * \param[in] cd * \param[in] ck 8-bytes long key * \param[out] co cl-dependent * \param[in] cl */ void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl); /*! \brief Expand key into set of subkeys - see TS 135 202 for details * \param[in] key (128 bits) as array of bytes * \param[out] KLi1 Expanded subkeys * \param[out] KLi2 Expanded subkeys * \param[out] KOi1 Expanded subkeys * \param[out] KOi2 Expanded subkeys * \param[out] KOi3 Expanded subkeys * \param[out] KIi1 Expanded subkeys * \param[out] KIi2 Expanded subkeys * \param[out] KIi3 Expanded subkeys */ void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3); libosmocore-0.9.0/include/osmocom/gsm/l1sap.h000066400000000000000000000100141261607044000211070ustar00rootroot00000000000000#pragma once #include /*! \brief PH-SAP related primitives (L1<->L2 SAP) */ enum osmo_ph_prim { PRIM_PH_DATA, /*!< \brief PH-DATA */ PRIM_PH_RACH, /*!< \brief PH-RANDOM_ACCESS */ PRIM_PH_CONN, /*!< \brief PH-CONNECT */ PRIM_PH_EMPTY_FRAME, /*!< \brief PH-EMPTY_FRAME */ PRIM_PH_RTS, /*!< \brief PH-RTS */ PRIM_MPH_INFO, /*!< \brief MPH-INFO */ PRIM_TCH, /*!< \brief TCH */ PRIM_TCH_RTS, /*!< \brief TCH */ }; /*! \brief PH-SAP related primitives (L1<->L2 SAP) */ enum osmo_mph_info_type { PRIM_INFO_TIME, /*!< \brief Current GSM time */ PRIM_INFO_MEAS, /*!< \brief Measurement indication */ PRIM_INFO_ACTIVATE, /*!< \brief Activation of channel */ PRIM_INFO_DEACTIVATE, /*!< \brief Deactivation of channel */ PRIM_INFO_MODIFY, /*!< \brief Mode Modify of channel */ PRIM_INFO_ACT_CIPH, /*!< \brief Activation of ciphering */ PRIM_INFO_DEACT_CIPH, /*!< \brief Deactivation of ciphering */ }; /*! \brief for PH-RANDOM_ACCESS.req */ struct ph_rach_req_param { uint8_t ra; /*!< \brief Random Access */ uint8_t ta; /*!< \brief Timing Advance */ uint8_t tx_power; /*!< \brief Transmit Power */ uint8_t is_combined_ccch;/*!< \brief Are we using a combined CCCH? */ uint16_t offset; /*!< \brief Timing Offset */ }; /*! \brief for PH-RANDOM_ACCESS.ind */ struct ph_rach_ind_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint8_t ra; /*!< \brief Random Access */ uint8_t acc_delay; /*!< \brief Delay in bit periods */ uint32_t fn; /*!< \brief GSM Frame Number at time of RA */ }; /*! \brief for PH-[UNIT]DATA.{req,ind} | PH-RTS.ind */ struct ph_data_param { uint8_t link_id; /*!< \brief Link Identifier (Like RSL) */ uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint32_t fn; /*!< \brief GSM Frame Number */ int8_t rssi; /*!< \brief RSSI of receivedindication */ }; /*! \brief for TCH.{req,ind} | TCH-RTS.ind */ struct ph_tch_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint32_t fn; /*!< \brief GSM Frame Number */ int8_t rssi; /*!< \brief RSSI of received indication */ }; /*! \brief for PH-CONN.ind */ struct ph_conn_ind_param { uint32_t fn; /*!< \brief GSM Frame Number */ }; /*! \brief for TIME MPH-INFO.ind */ struct info_time_ind_param { uint32_t fn; /*!< \brief GSM Frame Number */ }; /*! \brief for MEAS MPH-INFO.ind */ struct info_meas_ind_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint16_t ber10k; /*!< \brief BER in units of 0.01% */ int16_t ta_offs_qbits; /*!< \brief timing advance offset (in qbits) */ int16_t c_i_cb; /*!< \brief C/I ratio in 0.1 dB */ uint8_t is_sub:1; /*!< \brief flags */ uint8_t inv_rssi; /*!< \brief RSSI in dBm * -1 */ }; /*! \brief for {ACTIVATE,DEACTIVATE,MODIFY} MPH-INFO.req */ struct info_act_req_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint8_t sacch_only; /*!< \breif Only deactivate SACCH */ }; /*! \brief for {ACTIVATE,DEACTIVATE} MPH-INFO.cnf */ struct info_act_cnf_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint8_t cause; /*!< \brief RSL cause in case of nack */ }; /*! \brief for {ACTIVATE,DEACTIVATE} MPH-INFO.{req,cnf} */ struct info_ciph_req_param { uint8_t chan_nr; /*!< \brief Channel Number (Like RSL) */ uint8_t downlink; /*!< \brief Apply to downlink */ uint8_t uplink; /*!< \brief Apply to uplink */ }; /*! \brief for MPH-INFO.ind */ struct mph_info_param { enum osmo_mph_info_type type; /*!< \brief Info message type */ union { struct info_time_ind_param time_ind; struct info_meas_ind_param meas_ind; struct info_act_req_param act_req; struct info_act_cnf_param act_cnf; struct info_ciph_req_param ciph_req; } u; }; /*! \brief primitive header for PH-SAP primitives */ struct osmo_phsap_prim { struct osmo_prim_hdr oph; /*!< \brief generic primitive header */ union { struct ph_data_param data; struct ph_tch_param tch; struct ph_rach_req_param rach_req; struct ph_rach_ind_param rach_ind; struct ph_conn_ind_param conn_ind; struct mph_info_param info; } u; /*!< \brief request-specific data */ }; libosmocore-0.9.0/include/osmocom/gsm/lapd_core.h000066400000000000000000000127021261607044000220250ustar00rootroot00000000000000#pragma once #include #include #include #include /*! \defgroup lapd LAPD implementation common part * @{ */ /*! \file lapd_core.h * primitive related stuff */ /*! \brief LAPD related primitives (L2<->L3 SAP)*/ enum osmo_dl_prim { PRIM_DL_UNIT_DATA, /*!< \brief DL-UNIT-DATA */ PRIM_DL_DATA, /*!< \brief DL-DATA */ PRIM_DL_EST, /*!< \brief DL-ESTABLISH */ PRIM_DL_REL, /*!< \brief DL-RLEEASE */ PRIM_DL_SUSP, /*!< \brief DL-SUSPEND */ PRIM_DL_RES, /*!< \brief DL-RESUME */ PRIM_DL_RECON, /*!< \brief DL-RECONNECT */ PRIM_MDL_ERROR, /*!< \brief MDL-ERROR */ }; /* Uses the same values as RLL, so no conversion for GSM is required. */ #define MDL_CAUSE_T200_EXPIRED 0x01 #define MDL_CAUSE_REEST_REQ 0x02 #define MDL_CAUSE_UNSOL_UA_RESP 0x03 #define MDL_CAUSE_UNSOL_DM_RESP 0x04 #define MDL_CAUSE_UNSOL_DM_RESP_MF 0x05 #define MDL_CAUSE_UNSOL_SPRV_RESP 0x06 #define MDL_CAUSE_SEQ_ERR 0x07 #define MDL_CAUSE_UFRM_INC_PARAM 0x08 #define MDL_CAUSE_SFRM_INC_PARAM 0x09 #define MDL_CAUSE_IFRM_INC_MBITS 0x0a #define MDL_CAUSE_IFRM_INC_LEN 0x0b #define MDL_CAUSE_FRM_UNIMPL 0x0c #define MDL_CAUSE_SABM_MF 0x0d #define MDL_CAUSE_SABM_INFO_NOTALL 0x0e #define MDL_CAUSE_FRMR 0x0f /*! \brief for MDL-ERROR.ind */ struct mdl_error_ind_param { uint8_t cause; /*!< \brief generic cause value */ }; /*! \brief for DL-REL.req */ struct dl_rel_req_param { uint8_t mode; /*!< \brief release mode */ }; /*! \brief primitive header for LAPD DL-SAP primitives */ struct osmo_dlsap_prim { struct osmo_prim_hdr oph; /*!< \brief generic primitive header */ union { struct mdl_error_ind_param error_ind; struct dl_rel_req_param rel_req; } u; /*!< \brief request-specific data */ }; /*! \brief LAPD mode/role */ enum lapd_mode { LAPD_MODE_USER, /*!< \brief behave like user */ LAPD_MODE_NETWORK, /*!< \brief behave like network */ }; /*! \brief LAPD state (Figure B.2/Q.921)*/ enum lapd_state { LAPD_STATE_NULL = 0, LAPD_STATE_TEI_UNASS, LAPD_STATE_ASS_TEI_WAIT, LAPD_STATE_EST_TEI_WAIT, LAPD_STATE_IDLE, LAPD_STATE_SABM_SENT, LAPD_STATE_DISC_SENT, LAPD_STATE_MF_EST, LAPD_STATE_TIMER_RECOV, }; /*! \brief LAPD message format (I / S / U) */ enum lapd_format { LAPD_FORM_UKN = 0, LAPD_FORM_I, LAPD_FORM_S, LAPD_FORM_U, }; /*! \brief LAPD message context */ struct lapd_msg_ctx { struct lapd_datalink *dl; int n201; /* address */ uint8_t cr; uint8_t sapi; uint8_t tei; uint8_t lpd; /* control */ uint8_t format; uint8_t p_f; /* poll / final bit */ uint8_t n_send; uint8_t n_recv; uint8_t s_u; /* S or repectivly U function bits */ /* length */ int length; uint8_t more; }; struct lapd_cr_ent { uint8_t cmd; uint8_t resp; }; struct lapd_history { struct msgb *msg; /* message to be sent / NULL, if histoy is empty */ int more; /* if message is fragmented */ }; /*! \brief LAPD datalink */ struct lapd_datalink { int (*send_dlsap)(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); int (*send_ph_data_req)(struct lapd_msg_ctx *lctx, struct msgb *msg); int (*update_pending_frames)(struct lapd_msg_ctx *lctx); struct { /*! \brief filled-in once we set the lapd_mode above */ struct lapd_cr_ent loc2rem; struct lapd_cr_ent rem2loc; } cr; enum lapd_mode mode; /*!< \brief current mode of link */ int use_sabme; /*!< \brief use SABME instead of SABM */ int reestablish; /*!< \brief enable reestablish support */ int n200, n200_est_rel; /*!< \brief number of retranmissions */ struct lapd_msg_ctx lctx; /*!< \brief LAPD context */ int maxf; /*!< \brief maximum frame size (after defragmentation) */ uint8_t k; /*!< \brief maximum number of unacknowledged frames */ uint8_t v_range; /*!< \brief range of sequence numbers */ uint8_t v_send; /*!< \brief seq nr of next I frame to be transmitted */ uint8_t v_ack; /*!< \brief last frame ACKed by peer */ uint8_t v_recv; /*!< \brief seq nr of next I frame expected to be received */ uint32_t state; /*!< \brief LAPD state (\ref lapd_state) */ int seq_err_cond; /*!< \brief condition of sequence error */ uint8_t own_busy; /*!< \brief receiver busy on our side */ uint8_t peer_busy; /*!< \brief receiver busy on remote side */ int t200_sec, t200_usec; /*!< \brief retry timer (default 1 sec) */ int t203_sec, t203_usec; /*!< \brief retry timer (default 10 secs) */ struct osmo_timer_list t200; /*!< \brief T200 timer */ struct osmo_timer_list t203; /*!< \brief T203 timer */ uint8_t retrans_ctr; /*!< \brief re-transmission counter */ struct llist_head tx_queue; /*!< \brief frames to L1 */ struct llist_head send_queue; /*!< \brief frames from L3 */ struct msgb *send_buffer; /*!< \brief current frame transmitting */ int send_out; /*!< \brief how much was sent from send_buffer */ struct lapd_history *tx_hist; /*!< \brief tx history structure array */ uint8_t range_hist; /*!< \brief range of history buffer 2..2^n */ struct msgb *rcv_buffer; /*!< \brief buffer to assemble the received message */ struct msgb *cont_res; /*!< \brief buffer to store content resolution data on network side, to detect multiple phones on same channel */ }; void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf); void lapd_dl_exit(struct lapd_datalink *dl); void lapd_dl_reset(struct lapd_datalink *dl); int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode); int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx); int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/lapdm.h000066400000000000000000000073611261607044000211770ustar00rootroot00000000000000#pragma once #include #include /*! \defgroup lapdm LAPDm implementation according to GSM TS 04.06 * @{ */ /*! \file lapdm.h */ /*! \brief LAPDm mode/role */ enum lapdm_mode { LAPDM_MODE_MS, /*!< \brief behave like a MS (mobile phone) */ LAPDM_MODE_BTS, /*!< \brief behave like a BTS (network) */ }; struct lapdm_entity; /*! \brief LAPDm message context */ struct lapdm_msg_ctx { struct lapdm_datalink *dl; int lapdm_fmt; uint8_t chan_nr; uint8_t link_id; uint8_t ta_ind; /* TA indicated by network */ uint8_t tx_power_ind; /* MS power indicated by network */ }; /*! \brief LAPDm datalink like TS 04.06 / Section 3.5.2 */ struct lapdm_datalink { struct lapd_datalink dl; /* \brief common LAPD */ struct lapdm_msg_ctx mctx; /*!< \brief context of established connection */ struct lapdm_entity *entity; /*!< \brief LAPDm entity we are part of */ }; /*! \brief LAPDm datalink SAPIs */ enum lapdm_dl_sapi { DL_SAPI0 = 0, /*!< \brief SAPI 0 */ DL_SAPI3 = 1, /*!< \brief SAPI 1 */ _NR_DL_SAPI }; typedef int (*lapdm_cb_t)(struct msgb *msg, struct lapdm_entity *le, void *ctx); #define LAPDM_ENT_F_EMPTY_FRAME 0x0001 #define LAPDM_ENT_F_POLLING_ONLY 0x0002 /*! \brief a LAPDm Entity */ struct lapdm_entity { /*! \brief the SAPIs of the LAPDm entity */ struct lapdm_datalink datalink[_NR_DL_SAPI]; int last_tx_dequeue; /*!< \brief last entity that was dequeued */ int tx_pending; /*!< \brief currently a pending frame not confirmed by L1 */ enum lapdm_mode mode; /*!< \brief are we in BTS mode or MS mode */ unsigned int flags; void *l1_ctx; /*!< \brief context for layer1 instance */ void *l3_ctx; /*!< \brief context for layer3 instance */ osmo_prim_cb l1_prim_cb;/*!< \brief callback for sending prims to L1 */ lapdm_cb_t l3_cb; /*!< \brief callback for sending stuff to L3 */ /*! \brief pointer to \ref lapdm_channel of which we're part */ struct lapdm_channel *lapdm_ch; uint8_t ta; /* TA used and indicated to network */ uint8_t tx_power; /* MS power used and indicated to network */ }; /*! \brief the two lapdm_entities that form a GSM logical channel (ACCH + DCCH) */ struct lapdm_channel { struct llist_head list; /*!< \brief internal linked list */ char *name; /*!< \brief human-readable name */ struct lapdm_entity lapdm_acch; /*!< \brief Associated Control Channel */ struct lapdm_entity lapdm_dcch; /*!< \brief Dedicated Control Channel */ }; const char *get_rsl_name(int value); extern const char *lapdm_state_names[]; struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi); /* initialize a LAPDm entity */ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200); void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode); /* deinitialize a LAPDm entity */ void lapdm_entity_exit(struct lapdm_entity *le); void lapdm_channel_exit(struct lapdm_channel *lc); /* input into layer2 (from layer 1) */ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le); /* input into layer2 (from layer 3) */ int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc); void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx); void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx); int lapdm_entity_set_mode(struct lapdm_entity *le, enum lapdm_mode mode); int lapdm_channel_set_mode(struct lapdm_channel *lc, enum lapdm_mode mode); void lapdm_entity_reset(struct lapdm_entity *le); void lapdm_channel_reset(struct lapdm_channel *lc); void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags); void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags); int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp); /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/meas_rep.h000066400000000000000000000007471261607044000216760ustar00rootroot00000000000000#pragma once #include /* RX Level and RX Quality */ struct gsm_rx_lev_qual { uint8_t rx_lev; uint8_t rx_qual; }; /* unidirectional measumrement report */ struct gsm_meas_rep_unidir { struct gsm_rx_lev_qual full; struct gsm_rx_lev_qual sub; }; enum meas_rep_field { MEAS_REP_DL_RXLEV_FULL, MEAS_REP_DL_RXLEV_SUB, MEAS_REP_DL_RXQUAL_FULL, MEAS_REP_DL_RXQUAL_SUB, MEAS_REP_UL_RXLEV_FULL, MEAS_REP_UL_RXLEV_SUB, MEAS_REP_UL_RXQUAL_FULL, MEAS_REP_UL_RXQUAL_SUB, }; libosmocore-0.9.0/include/osmocom/gsm/mncc.h000066400000000000000000000032061261607044000210140ustar00rootroot00000000000000#pragma once #include #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 { enum gsm48_bcap_ra rate_adaption; enum gsm48_bcap_sig_access sig_access; int async; int nr_stop_bits; int nr_data_bits; enum gsm48_bcap_user_rate user_rate; enum gsm48_bcap_parity parity; enum gsm48_bcap_interm_rate interm_rate; enum gsm48_bcap_transp transp; enum gsm48_bcap_modem_type modem_type; } data; }; 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, }; libosmocore-0.9.0/include/osmocom/gsm/prim.h000066400000000000000000000003421261607044000210410ustar00rootroot00000000000000#pragma once #include /* enumeration of GSM related SAPs */ enum osmo_gsm_sap { SAP_GSM_PH = _SAP_GSM_BASE, SAP_GSM_DL, SAP_GSM_MDL, SAP_BSSGP_GMM, SAP_BSSGP_LL, SAP_BSSGP_NM, SAP_BSSGP_PFM, }; libosmocore-0.9.0/include/osmocom/gsm/protocol/000077500000000000000000000000001261607044000215635ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_03_40.h000066400000000000000000000012031261607044000233230ustar00rootroot00000000000000#pragma once /* GSM TS 03.40 */ /** * 9.1.2.5 Type Of Number */ enum { GSM340_TYPE_UNKNOWN = 0, GSM340_TYPE_INTERNATIONAL = 1, GSM340_TYPE_NATIONAL = 2, GSM340_TYPE_NETWORK = 3, GSM340_TYPE_SUBSCRIBER = 4, GSM340_TYPE_ALPHA_NUMERIC = 5, GSM340_TYPE_ABBREVIATED = 6, GSM340_TYPE_RESERVED = 7, }; /** * 9.1.2.5 Type of Numbering plan. * Applies for numbering plans (Unknown, International, National) */ enum { GSM340_PLAN_UNKNOWN = 0, GSM340_PLAN_ISDN = 1, GSM340_PLAN_DATA = 3, GSM340_PLAN_TELEX = 4, GSM340_PLAN_NATIONAL = 8, GSM340_PLAN_PRIVATE = 9, GSM340_PLAN_ERMES = 10, GSM340_PLAN_RESERVED = 15, }; libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_03_41.h000066400000000000000000000060401261607044000233300ustar00rootroot00000000000000#pragma once #include #include /* GSM TS 03.41 definitions also TS 23.041*/ #define GSM341_MAX_PAYLOAD (GSM412_MSG_LEN-sizeof(struct gsm341_ms_message)) #define GSM341_MAX_CHARS (GSM341_MAX_PAYLOAD*8/7) #define GSM341_7BIT_PADDING '\r' /* Chapter 9.3.2 */ struct gsm341_ms_message { struct { uint8_t code_hi:6; uint8_t gs:2; uint8_t update:4; uint8_t code_lo:4; } serial; uint16_t msg_id; struct { uint8_t language:4; uint8_t group:4; } dcs; struct { uint8_t total:4; uint8_t current:4; } page; uint8_t data[0]; } __attribute__((packed)); /* Chapter 9.4.1.3 */ struct gsm341_etws_message { struct { uint8_t code_hi:4; uint8_t popup:1; uint8_t alert:1; uint8_t gs:2; uint8_t update:4; uint8_t code_lo:4; } serial; uint16_t msg_id; uint16_t warning_type; uint8_t data[0]; } __attribute__((packed)); #define GSM341_MSG_CODE(ms) ((ms)->serial.code_lo | ((ms)->serial.code_hi << 4)) /* Section 9.3.2.1 - Geographical Scope */ #define GSM341_GS_CELL_WIDE_IMMED 0 #define GSM341_GS_PLMN_WIDE 1 #define GSM341_GS_LA_WIDE 2 #define GSM341_GS_CELL_WIDE 3 /* Section 9.4.1.2.2 */ #define GSM341_MSGID_EOTD_ASSISTANCE 0x03E8 #define GSM341_MSGID_DGPS_CORRECTION 0x03E9 #define GSM341_MSGID_DGPS_EPH_CLOCK_COR 0x03EA #define GSM341_MSGID_GPS_ALMANAC_OTHER 0x03EB #define GSM341_MSGID_ETWS_EARTHQUAKE 0x1100 #define GSM341_MSGID_ETWS_TSUNAMI 0x1101 #define GSM341_MSGID_ETWS_QUAKE_AND_TSUNAMI 0x1102 #define GSM341_MSGID_ETWS_TEST 0x1103 #define GSM341_MSGID_ETWS_OTHER 0x1104 #define GSM341_MSGID_ETWS_CMAS_PRESIDENTIAL 0x1112 #define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_OBSERVED 0x1113 #define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_LIKELY 0x1114 #define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_OBSERVED 0x1115 #define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_LIKELY 0x1116 #define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_OBSERVED 0x1117 #define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_LIKELY 0x1118 #define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_OBSERVED 0x1119 #define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_LIKELY 0x111A #define GSM341_MSGID_ETWS_CMAS_AMBER 0x111B #define GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST 0x111C #define GSM341_MSGID_ETWS_CMAS_EXERCISE 0x111D #define GSM341_MSGID_ETWS_CMAS_OPERATOR_DEFINED 0x111E #define GSM341_MSGID_ETWS_CMAS_PRESIDENTIAL_AL 0x111F #define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_OBSERVED_AL 0x1120 #define GSM341_MSGID_ETWS_CMAS_EXTREME_IMM_LIKELY_AL 0x1121 #define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_OBSERVED_AL 0x1122 #define GSM341_MSGID_ETWS_CMAS_EXTREME_EXP_LIKELY_AL 0x1123 #define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_OBSERVED_AL 0x1124 #define GSM341_MSGID_ETWS_CMAS_SEVERE_IMM_LIKELY_AL 0x1125 #define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_OBSERVED_AL 0x1126 #define GSM341_MSGID_ETWS_CMAS_SEVERE_EXP_LIKELY_AL 0x1127 #define GSM341_MSGID_ETWS_CMAS_AMBER_AL 0x1128 #define GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST_AL 0x1129 #define GSM341_MSGID_ETWS_CMAS_EXERCISE_AL 0x112A #define GSM341_MSGID_ETWS_CMAS_OPERATOR_DEFINED_AL 0x112B #define GSM341_MSGID_ETWS_EU_INFO_LOCAL_LANGUAGE 0x1900 libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_04_08.h000066400000000000000000001016741261607044000233450ustar00rootroot00000000000000#pragma once #include /* 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; } __attribute__ ((packed)) h1; struct { uint8_t arfcn_high:2, spare:2, h:1, tsc:3; uint8_t arfcn_low; } __attribute__ ((packed)) h0; } __attribute__ ((packed)); } __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 = 0x13, }; /* Chapter 9.1.2 */ struct gsm48_ass_cmd { /* Semantic is from 10.5.2.5a */ struct gsm48_chan_desc chan_desc; uint8_t power_command; uint8_t data[0]; } __attribute__((packed)); /* Chapter 9.1.13 */ struct gsm48_frq_redef { /* Semantic is from 10.5.2.5a */ struct gsm48_chan_desc chan_desc; uint8_t mob_alloc_len; uint8_t mob_alloc[0]; } __attribute__((packed)); /* Chapter 10.5.2.2 */ struct gsm48_cell_desc { uint8_t bcc:3, ncc:3, arfcn_hi:2; uint8_t arfcn_lo; } __attribute__((packed)); /* Chapter 9.1.15 */ struct gsm48_ho_cmd { struct gsm48_cell_desc cell_desc; struct gsm48_chan_desc chan_desc; uint8_t ho_ref; uint8_t power_command; uint8_t data[0]; } __attribute__((packed)); /* Chapter 9.1.18 */ struct gsm48_imm_ass { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t page_mode; struct gsm48_chan_desc chan_desc; struct gsm48_req_ref req_ref; uint8_t timing_advance; uint8_t mob_alloc_len; uint8_t mob_alloc[0]; } __attribute__ ((packed)); /* Chapter 9.1.25 */ struct gsm48_pag_resp { uint8_t spare:4, key_seq:4; uint32_t classmark2; uint8_t mi_len; uint8_t mi[0]; } __attribute__ ((packed)); /* Chapter 10.5.1.3 */ struct gsm48_loc_area_id { uint8_t digits[3]; /* BCD! */ uint16_t lac; } __attribute__ ((packed)); /* Section 9.2.2 */ struct gsm48_auth_req { uint8_t key_seq:4, spare:4; uint8_t rand[16]; } __attribute__ ((packed)); /* Section 9.2.3 */ struct gsm48_auth_resp { uint8_t sres[4]; } __attribute__ ((packed)); /* Section 9.2.15 */ struct gsm48_loc_upd_req { uint8_t type:4, key_seq:4; struct gsm48_loc_area_id lai; struct gsm48_classmark1 classmark1; uint8_t mi_len; uint8_t mi[0]; } __attribute__ ((packed)); /* Section 10.1 */ struct gsm48_hdr { uint8_t proto_discr; uint8_t msg_type; uint8_t data[0]; } __attribute__ ((packed)); /* Section 9.1.3x System information Type header */ struct gsm48_system_information_type_header { uint8_t l2_plen; uint8_t rr_protocol_discriminator :4, skip_indicator:4; uint8_t system_information; } __attribute__ ((packed)); /* Section 10.5.2.4 Cell Selection Parameters */ struct gsm48_cell_sel_par { uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */ cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */ uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */ neci:1, acs:1; } __attribute__ ((packed)); /* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */ struct gsm48_control_channel_descr { uint8_t ccch_conf :3, bs_ag_blks_res :3, att :1, spare1 :1; uint8_t bs_pa_mfrms : 3, spare2 :5; uint8_t t3212; } __attribute__ ((packed)); struct gsm48_cell_options { uint8_t radio_link_timeout:4, dtx:2, pwrc:1, spare:1; } __attribute__ ((packed)); /* Section 9.2.9 CM service request */ struct gsm48_service_request { uint8_t cm_service_type : 4, cipher_key_seq : 4; /* length + 3 bytes */ uint32_t classmark; uint8_t mi_len; uint8_t mi[0]; /* optional priority level */ } __attribute__ ((packed)); /* Section 9.1.31 System information Type 1 */ struct gsm48_system_information_type_1 { struct gsm48_system_information_type_header header; uint8_t cell_channel_description[16]; struct gsm48_rach_control rach_control; uint8_t rest_octets[0]; /* NCH position on the CCCH */ } __attribute__ ((packed)); /* Section 9.1.32 System information Type 2 */ struct gsm48_system_information_type_2 { struct gsm48_system_information_type_header header; uint8_t bcch_frequency_list[16]; uint8_t ncc_permitted; struct gsm48_rach_control rach_control; } __attribute__ ((packed)); /* Section 9.1.33 System information Type 2bis */ struct gsm48_system_information_type_2bis { struct gsm48_system_information_type_header header; uint8_t bcch_frequency_list[16]; struct gsm48_rach_control rach_control; uint8_t rest_octets[0]; } __attribute__ ((packed)); /* Section 9.1.34 System information Type 2ter */ struct gsm48_system_information_type_2ter { struct gsm48_system_information_type_header header; uint8_t ext_bcch_frequency_list[16]; uint8_t rest_octets[0]; } __attribute__ ((packed)); /* Section 9.1.35 System information Type 3 */ struct gsm48_system_information_type_3 { struct gsm48_system_information_type_header header; uint16_t cell_identity; struct gsm48_loc_area_id lai; struct gsm48_control_channel_descr control_channel_desc; struct gsm48_cell_options cell_options; struct gsm48_cell_sel_par cell_sel_par; struct gsm48_rach_control rach_control; uint8_t rest_octets[0]; } __attribute__ ((packed)); /* Section 9.1.36 System information Type 4 */ struct gsm48_system_information_type_4 { struct gsm48_system_information_type_header header; struct gsm48_loc_area_id lai; struct gsm48_cell_sel_par cell_sel_par; struct gsm48_rach_control rach_control; /* optional CBCH conditional CBCH... followed by mandantory SI 4 Reset Octets */ uint8_t data[0]; } __attribute__ ((packed)); /* Section 9.1.37 System information Type 5 */ struct gsm48_system_information_type_5 { uint8_t rr_protocol_discriminator :4, skip_indicator:4; uint8_t system_information; uint8_t bcch_frequency_list[16]; } __attribute__ ((packed)); /* Section 9.1.38 System information Type 5bis */ struct gsm48_system_information_type_5bis { uint8_t rr_protocol_discriminator :4, skip_indicator:4; uint8_t system_information; uint8_t bcch_frequency_list[16]; } __attribute__ ((packed)); /* Section 9.1.39 System information Type 5ter */ struct gsm48_system_information_type_5ter { uint8_t rr_protocol_discriminator :4, skip_indicator:4; uint8_t system_information; uint8_t bcch_frequency_list[16]; } __attribute__ ((packed)); /* Section 9.1.40 System information Type 6 */ struct gsm48_system_information_type_6 { uint8_t rr_protocol_discriminator :4, skip_indicator:4; uint8_t system_information; uint16_t cell_identity; struct gsm48_loc_area_id lai; struct gsm48_cell_options cell_options; uint8_t ncc_permitted; uint8_t rest_octets[0]; } __attribute__ ((packed)); /* Section 9.1.43a System Information type 13 */ struct gsm48_system_information_type_13 { struct gsm48_system_information_type_header header; uint8_t rest_octets[0]; } __attribute__ ((packed)); /* Section 9.2.12 IMSI Detach Indication */ struct gsm48_imsi_detach_ind { struct gsm48_classmark1 classmark1; uint8_t mi_len; uint8_t mi[0]; } __attribute__ ((packed)); /* Section 9.1.1 */ struct gsm48_add_ass { /* Semantic is from 10.5.2.5 */ struct gsm48_chan_desc chan_desc; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.3 */ struct gsm48_ass_cpl { uint8_t rr_cause; } __attribute__((packed)); /* Section 9.1.4 */ struct gsm48_ass_fail { uint8_t rr_cause; } __attribute__((packed)); /* Section 9.1.3 */ struct gsm48_ho_cpl { uint8_t rr_cause; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.4 */ struct gsm48_ho_fail { uint8_t rr_cause; } __attribute__((packed)); /* Section 9.1.7 */ struct gsm48_chan_rel { uint8_t rr_cause; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.9 */ struct gsm48_cip_mode_cmd { uint8_t sc:1, alg_id:3, cr:1, spare:3; } __attribute__((packed)); /* Section 9.1.11 */ struct gsm48_cm_change { uint8_t cm2_len; struct gsm48_classmark2 cm2; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.19 */ struct gsm48_imm_ass_ext { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t page_mode; struct gsm48_chan_desc chan_desc1; struct gsm48_req_ref req_ref1; uint8_t timing_advance1; struct gsm48_chan_desc chan_desc2; struct gsm48_req_ref req_ref2; uint8_t timing_advance2; uint8_t mob_alloc_len; uint8_t mob_alloc[0]; } __attribute__ ((packed)); /* Section 9.1.20 */ struct gsm48_imm_ass_rej { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t page_mode; struct gsm48_req_ref req_ref1; uint8_t wait_ind1; struct gsm48_req_ref req_ref2; uint8_t wait_ind2; struct gsm48_req_ref req_ref3; uint8_t wait_ind3; struct gsm48_req_ref req_ref4; uint8_t wait_ind4; uint8_t rest[0]; } __attribute__ ((packed)); /* Section 9.1.22 */ struct gsm48_paging1 { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t pag_mode:2, spare:2, cneed1:2, cneed2:2; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.23 */ struct gsm48_paging2 { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t pag_mode:2, spare:2, cneed1:2, cneed2:2; uint32_t tmsi1; uint32_t tmsi2; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.24 */ struct gsm48_paging3 { uint8_t l2_plen; uint8_t proto_discr; uint8_t msg_type; uint8_t pag_mode:2, spare:2, cneed1:2, cneed2:2; uint32_t tmsi1; uint32_t tmsi2; uint32_t tmsi3; uint32_t tmsi4; uint8_t cneed3:2, cneed4:2, spare2:4; uint8_t rest[0]; } __attribute__((packed)); /* Section 9.1.25 */ struct gsm48_pag_rsp { uint8_t key_seq:3, spare:5; uint8_t cm2_len; struct gsm48_classmark2 cm2; uint8_t data[0]; } __attribute__((packed)); /* Section 9.1.29 */ struct gsm48_rr_status { uint8_t rr_cause; } __attribute__((packed)); /* Section 10.2 + GSM 04.07 12.2.3.1.1 */ #define GSM48_PDISC_GROUP_CC 0x00 #define GSM48_PDISC_BCAST_CC 0x01 #define GSM48_PDISC_PDSS1 0x02 #define GSM48_PDISC_CC 0x03 #define GSM48_PDISC_PDSS2 0x04 #define GSM48_PDISC_MM 0x05 #define GSM48_PDISC_RR 0x06 #define GSM48_PDISC_MM_GPRS 0x08 #define GSM48_PDISC_SMS 0x09 #define GSM48_PDISC_SM_GPRS 0x0a #define GSM48_PDISC_NC_SS 0x0b #define GSM48_PDISC_LOC 0x0c #define GSM48_PDISC_MASK 0x0f #define GSM48_PDISC_USSD 0x11 /* Section 10.4 */ #define GSM48_MT_RR_INIT_REQ 0x3c #define GSM48_MT_RR_ADD_ASS 0x3b #define GSM48_MT_RR_IMM_ASS 0x3f #define GSM48_MT_RR_IMM_ASS_EXT 0x39 #define GSM48_MT_RR_IMM_ASS_REJ 0x3a #define GSM48_MT_RR_CIPH_M_CMD 0x35 #define GSM48_MT_RR_CIPH_M_COMPL 0x32 #define GSM48_MT_RR_CFG_CHG_CMD 0x30 #define GSM48_MT_RR_CFG_CHG_ACK 0x31 #define GSM48_MT_RR_CFG_CHG_REJ 0x33 #define GSM48_MT_RR_ASS_CMD 0x2e #define GSM48_MT_RR_ASS_COMPL 0x29 #define GSM48_MT_RR_ASS_FAIL 0x2f #define GSM48_MT_RR_HANDO_CMD 0x2b #define GSM48_MT_RR_HANDO_COMPL 0x2c #define GSM48_MT_RR_HANDO_FAIL 0x28 #define GSM48_MT_RR_HANDO_INFO 0x2d #define GSM48_MT_RR_CELL_CHG_ORDER 0x08 #define GSM48_MT_RR_PDCH_ASS_CMD 0x23 #define GSM48_MT_RR_CHAN_REL 0x0d #define GSM48_MT_RR_PART_REL 0x0a #define GSM48_MT_RR_PART_REL_COMP 0x0f #define GSM48_MT_RR_PAG_REQ_1 0x21 #define GSM48_MT_RR_PAG_REQ_2 0x22 #define GSM48_MT_RR_PAG_REQ_3 0x24 #define GSM48_MT_RR_PAG_RESP 0x27 #define GSM48_MT_RR_NOTIF_NCH 0x20 #define GSM48_MT_RR_NOTIF_FACCH 0x25 #define GSM48_MT_RR_NOTIF_RESP 0x26 #define GSM48_MT_RR_SYSINFO_8 0x18 #define GSM48_MT_RR_SYSINFO_1 0x19 #define GSM48_MT_RR_SYSINFO_2 0x1a #define GSM48_MT_RR_SYSINFO_3 0x1b #define GSM48_MT_RR_SYSINFO_4 0x1c #define GSM48_MT_RR_SYSINFO_5 0x1d #define GSM48_MT_RR_SYSINFO_6 0x1e #define GSM48_MT_RR_SYSINFO_7 0x1f #define GSM48_MT_RR_SYSINFO_2bis 0x02 #define GSM48_MT_RR_SYSINFO_2ter 0x03 #define GSM48_MT_RR_SYSINFO_5bis 0x05 #define GSM48_MT_RR_SYSINFO_5ter 0x06 #define GSM48_MT_RR_SYSINFO_9 0x04 #define GSM48_MT_RR_SYSINFO_13 0x00 #define GSM48_MT_RR_SYSINFO_16 0x3d #define GSM48_MT_RR_SYSINFO_17 0x3e #define GSM48_MT_RR_CHAN_MODE_MODIF 0x10 #define GSM48_MT_RR_STATUS 0x12 #define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17 #define GSM48_MT_RR_FREQ_REDEF 0x14 #define GSM48_MT_RR_MEAS_REP 0x15 #define GSM48_MT_RR_CLSM_CHG 0x16 #define GSM48_MT_RR_CLSM_ENQ 0x13 #define GSM48_MT_RR_EXT_MEAS_REP 0x36 #define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37 #define GSM48_MT_RR_GPRS_SUSP_REQ 0x34 #define GSM48_MT_RR_VGCS_UPL_GRANT 0x09 #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_NET_DST 0x49 /* 10.5.3.12 [24.008] */ #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 = 0x08, GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x09, GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x0a, 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, }; /* GSM 04.08 Bearer Capability: Rate Adaption */ enum gsm48_bcap_ra { GSM48_BCAP_RA_NONE = 0, GSM48_BCAP_RA_V110_X30 = 1, GSM48_BCAP_RA_X31 = 2, GSM48_BCAP_RA_OTHER = 3, }; /* GSM 04.08 Bearer Capability: Signalling access protocol */ enum gsm48_bcap_sig_access { GSM48_BCAP_SA_I440_I450 = 1, GSM48_BCAP_SA_X21 = 2, GSM48_BCAP_SA_X28_DP_IN = 3, GSM48_BCAP_SA_X28_DP_UN = 4, GSM48_BCAP_SA_X28_NDP = 5, GSM48_BCAP_SA_X32 = 6, }; /* GSM 04.08 Bearer Capability: User Rate */ enum gsm48_bcap_user_rate { GSM48_BCAP_UR_300 = 1, GSM48_BCAP_UR_1200 = 2, GSM48_BCAP_UR_2400 = 3, GSM48_BCAP_UR_4800 = 4, GSM48_BCAP_UR_9600 = 5, GSM48_BCAP_UR_12000 = 6, GSM48_BCAP_UR_1200_75 = 7, }; /* GSM 04.08 Bearer Capability: Parity */ enum gsm48_bcap_parity { GSM48_BCAP_PAR_ODD = 0, GSM48_BCAP_PAR_EVEN = 2, GSM48_BCAP_PAR_NONE = 3, GSM48_BCAP_PAR_ZERO = 4, GSM48_BCAP_PAR_ONE = 5, }; /* GSM 04.08 Bearer Capability: Intermediate Rate */ enum gsm48_bcap_interm_rate { GSM48_BCAP_IR_8k = 2, GSM48_BCAP_IR_16k = 3, }; /* GSM 04.08 Bearer Capability: Transparency */ enum gsm48_bcap_transp { GSM48_BCAP_TR_TRANSP = 0, GSM48_BCAP_TR_RLP = 1, GSM48_BCAP_TR_TR_PREF = 2, GSM48_BCAP_TR_RLP_PREF = 3, }; /* GSM 04.08 Bearer Capability: Modem Type */ enum gsm48_bcap_modem_type { GSM48_BCAP_MT_NONE = 0, GSM48_BCAP_MT_V21 = 1, GSM48_BCAP_MT_V22 = 2, GSM48_BCAP_MT_V22bis = 3, GSM48_BCAP_MT_V23 = 4, GSM48_BCAP_MT_V26ter = 5, GSM48_BCAP_MT_V32 = 6, GSM48_BCAP_MT_UNDEF = 7, GSM48_BCAP_MT_AUTO_1 = 8, }; /* GSM 04.08 Bearer Capability: Speech Version Indication */ enum gsm48_bcap_speech_ver { GSM48_BCAP_SV_FR = 0, GSM48_BCAP_SV_HR = 1, GSM48_BCAP_SV_EFR = 2, GSM48_BCAP_SV_AMR_F = 4, GSM48_BCAP_SV_AMR_H = 5, }; #define GSM48_TMSI_LEN 5 #define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) #define GSM48_MI_SIZE 32 /* Chapter 10.4.4.15 */ struct gsm48_ra_id { uint8_t digits[3]; /* MCC + MNC BCD digits */ uint16_t lac; /* Location Area Code */ uint8_t rac; /* Routing Area Code */ } __attribute__ ((packed)); #define GSM48_CELL_CHAN_DESC_SIZE 16 #define GSM_MACBLOCK_LEN 23 #define GSM_MACBLOCK_PADDING 0x2b libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_04_11.h000066400000000000000000000131571261607044000233350ustar00rootroot00000000000000#pragma once #include /* GSM TS 04.11 definitions */ /* Chapter 5.2.3: SMC-CS states at the user/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 user/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, GSM411_RPS_WAIT_FOR_RETRANS_T = 4, }; /* 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 /* TR1M - 10 */ #define GSM411_TMR_TC1A_SEC 30 /* TR1M - 10 */ /* 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_RESERVED 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 */ libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_04_12.h000066400000000000000000000013241261607044000233270ustar00rootroot00000000000000#pragma once #include /* GSM TS 04.12 definitions for Short Message Service Cell Broadcast */ #define GSM412_MSG_LEN 88 /* TS 04.12 Section 3.1 */ #define GSM412_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */ #define GSM412_SEQ_FST_BLOCK 0x0 #define GSM412_SEQ_SND_BLOCK 0x1 #define GSM412_SEQ_TRD_BLOCK 0x2 #define GSM412_SEQ_FTH_BLOCK 0x3 #define GSM412_SEQ_FST_SCHED_BLOCK 0x8 #define GSM412_SEQ_NULL_MSG 0xf struct gsm412_block_type { uint8_t seq_nr : 4, lb : 1, lpd : 2, spare : 1; } __attribute__((packed)); struct gsm412_sched_msg { uint8_t beg_slot_nr : 6, type : 2; uint8_t end_slot_nr : 6, spare1 : 1, spare2: 1; uint8_t cbsms_msg_map[6]; uint8_t data[0]; } __attribute__((packed)); libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_04_80.h000066400000000000000000000110671261607044000233410ustar00rootroot00000000000000#pragma once /* 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 libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_08_08.h000066400000000000000000000340231261607044000233420ustar00rootroot00000000000000/* From GSM08.08 / 3GPP TS 48.008 version 11.7.0 Release 11 */ #pragma once #include /* * 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, BSS_MAP_MSG_CHAN_MOD_RQST = 8, /* 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, BSS_MAP_MSG_INT_HANDOVER_REQUIRED = 0x70, BSS_MAP_MSG_INT_HANDOVER_REQUIRED_REJ = 0x71, BSS_MAP_MSG_INT_HANDOVER_CMD = 0x72, BSS_MAP_MSG_INT_HANDOVER_ENQUIRY = 0x73, /* 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, BSS_MAP_MSG_REROUTE_CMD = 0x78, BSS_MAP_MSG_REROUTE_COMPLETE = 0x79, /* 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, BSS_MAP_MSG_RESET_IP_RSRC = 0x3d, BSS_MAP_MSG_RESET_IP_RSRC_ACK = 0x3e, /* 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, BSS_MAP_MSG_VGCS_ADDL_INFO = 0x60, BSS_MAP_MSG_NOTIFICATION_DATA = 0x62, BSS_MAP_MSG_UPLINK_APP_DATA = 0x63, /* LOCAL SWITCHING */ BSS_MAP_MSG_LCLS_CONNECT_CTRL = 0x74, BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK = 0x75, BSS_MAP_MSG_LCLS_NOTIFICATION = 0x76, }; 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, GSM0808_IE_GERAN_CLASSMARK = 0x53, GSM0808_IE_GERAN_BSC_CONTAINER = 0x54, GSM0808_IE_NEW_BSS_TO_OLD_BSS_INFO = 0x61, GSM0800_IE_INTER_SYSTEM_INFO = 0x63, GSM0808_IE_SNA_ACCESS_INFO = 0x64, GSM0808_IE_VSTK_RAND_INFO = 0x65, GSM0808_IE_VSTK_INFO = 0x66, GSM0808_IE_PAGING_INFO = 0x67, GSM0808_IE_IMEI = 0x68, GSM0808_IE_VELOCITY_ESTIMATE = 0x55, GSM0808_IE_VGCS_FEATURE_FLAGS = 0x69, GSM0808_IE_TALKER_PRIORITY = 0x6a, GSM0808_IE_EMERGENCY_SET_INDICATION = 0x6b, GSM0808_IE_TALKER_IDENTITY = 0x6c, GSM0808_IE_CELL_ID_LIST_SEGMENT = 0x6d, GSM0808_IE_SMS_TO_VGCS = 0x6e, GSM0808_IE_VGCS_TALKER_MODE = 0x6f, GSM0808_IE_VGCS_VBS_CELL_STATUS = 0x70, GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS = 0x71, GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE = 0x72, GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS = 0x73, GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS = 0x74, GSM0808_IE_GANSS_ASSISTANCE_DATA = 0x75, GSM0808_IE_GANSS_POSITIONING_DATA = 0x76, GSM0808_IE_GANSS_LOCATION_TYPE = 0x77, GSM0808_IE_APP_DATA = 0x78, GSM0808_IE_DATA_IDENTITY = 0x79, GSM0808_IE_APP_DATA_INFO = 0x7a, GSM0808_IE_MSISDN = 0x7b, GSM0808_IE_AOIP_TRASP_ADDR = 0x7c, GSM0808_IE_SPEECH_CODEC_LIST = 0x7d, GSM0808_IE_SPEECH_CODEC = 0x7e, GSM0808_IE_CALL_ID = 0x7f, GSM0808_IE_CALL_ID_LIST = 0x80, GSM0808_IE_A_IF_SEL_FOR_RESET = 0x81, GSM0808_IE_KC_128 = 0x83, GSM0808_IE_CSG_IDENTIFIER = 0x84, GSM0808_IE_REDIR_ATTEMPT_FLAG = 0x85, GSM0808_IE_REROUTE_REJ_CAUSE = 0x86, GSM0808_IE_SEND_SEQ_NUM = 0x87, GSM0808_IE_REROUTE_COMPL_OUTCOME = 0x88, GSM0808_IE_GLOBAL_CALL_REF = 0x89, GSM0808_IE_LCLS_CONFIG = 0x8a, GSM0808_IE_LCLS_CONN_STATUS_CTRL = 0x8b, GSM0808_IE_LCLS_CORR_NOT_NEEDED = 0x8c, GSM0808_IE_LCLS_BSS_STATUS = 0x8d, GSM0808_IE_LCLS_BREAK_REQ = 0x8e, GSM0808_IE_CSFB_INDICATION = 0x8f, GSM0808_IE_CS_TO_PS_SRVCC = 0x90, GSM0808_IE_SRC_ENB_TO_TGT_ENB_TRANSP = 0x91, GSM0808_IE_CS_TO_PS_SRVCC_IND = 0x92, GSM0808_IE_CN_TO_MS_TRANSP_INFO = 0x93, GSM0808_IE_SELECTED_PLMN_ID = 0x94, GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID = 0x95, }; /* GSM 08.08 3.2.2.5 Cause */ 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_REDUCE_LOAD_IN_SERVING_CELL = 0x10, GSM0808_CAUSE_TRAFFIC_LOAD_IN_TGT_HIGHER_THAN_IN_SRC_CELL = 0x11, GSM0808_CAUSE_RELOCATION_TRIGGERED = 0x12, GSM0808_CAUSE_REQUSTED_OPT_NOT_AUTHORISED = 0x14, GSM0808_CAUSE_ALT_CHAN_CONFIG_REQUESTED = 0x15, GSM0808_CAUSE_RESP_TO_INT_HO_ENQ_MSG = 0x16, GSM0808_CAUSE_INT_HO_ENQUIRY_REJECT = 0x17, GSM0808_CAUSE_REDUNDANCY_LEVEL_NOT_ADEQUATE = 0x18, 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_DTM_HO_SGSN_FAILURE = 0x2a, GSM0808_CAUSE_DTM_HO_PS_ALLOC_FAILURE = 0x2b, 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_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL = 0x35, GSM0808_CAUSE_REQ_A_IF_TYPE_UNAVAIL = 0x36, GSM0808_CAUSE_INVALID_CSG_CELL = 0x37, GSM0808_CAUSE_REQ_REDUND_LEVEL_NOT_AVAIL = 0x3f, GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64, GSM0808_CAUSE_GERAN_IU_MODE_FAILURE = 0x41, GSM0808_CAUSE_INC_RELOC_NOT_SUPP_DT_PUESBINE_FEATURE = 0x42, GSM0808_CAUSE_ACCESS_RESTRICTED_DUE_TO_SHARED_NETWORKS = 0x43, GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP = 0x44, GSM0808_CAUSE_REQ_A_IF_TYPE_NOT_SUPP = 0x45, GSM0808_CAUSE_REQ_REDUND_LVL_NOT_SUPP = 0x46, 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_DTM_HO_INVALID_PS_IND = 0x56, GSM0808_CAUSE_CALL_ID_ALREADY_ALLOC = 0x57, GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96, GSM0808_CAUSE_VGCS_VBS_CALL_NON_EXISTANT = 0x61, GSM0808_CAUSE_DTM_HO_TIMER_EXPIRY = 0x62, }; /* 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, }; /* GSM 08.08 3.2.2.11 Channel Type */ 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, }; /* GSM 08.08 3.2.2.11 Channel Type */ 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, }; /* GSM 08.08 3.2.2.11 Channel Type */ 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, }; /* GSM 08.08 3.2.2.44 Chosen Encryption Algorithm */ enum gsm0808_chosen_enc_alg { GSM0808_ALG_ID_A5_0 = 0x01, GSM0808_ALG_ID_A5_1 = 0x02, GSM0808_ALG_ID_A5_2 = 0x03, GSM0808_ALG_ID_A5_3 = 0x04, GSM0808_ALG_ID_A5_4 = 0x05, GSM0808_ALG_ID_A5_5 = 0x06, GSM0808_ALG_ID_A5_6 = 0x07, GSM0808_ALG_ID_A5_7 = 0x08, }; /* GSM 08.08 3.2.2.85 Paging Information */ enum gsm0808_paging_info { GSM0808_PAGINF_FOR_MT_CALL = 0x00, GSM0808_PAGINF_FOR_SMS = 0x01, GSM0808_PAGINF_FOR_USSD = 0x02, }; libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_08_58.h000066400000000000000000000407341261607044000233550ustar00rootroot00000000000000#pragma once /* 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 * 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 /*! \addtogroup rsl * @{ */ /*! \file gsm_08_58.h */ /*! \brief RSL common header */ struct abis_rsl_common_hdr { uint8_t msg_discr; /*!< \brief message discriminator (ABIS_RSL_MDISC_*) */ uint8_t msg_type; /*!< \brief message type (\ref abis_rsl_msgtype) */ uint8_t data[0]; /*!< \brief actual payload data */ } __attribute__ ((packed)); /* \brief RSL RLL header (Chapter 8.3) */ struct abis_rsl_rll_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */ uint8_t chan_nr; /*!< \brief RSL channel number (value) */ uint8_t ie_link_id; /*!< \brief \ref RSL_IE_LINK_IDENT (tag) */ uint8_t link_id; /*!< \brief RSL link identifier (value) */ uint8_t data[0]; /*!< \brief message payload data */ } __attribute__ ((packed)); /* \brief RSL Dedicated Channel header (Chapter 8.3 and 8.4) */ struct abis_rsl_dchan_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */ uint8_t chan_nr; /*!< \brief RSL channel number (value) */ uint8_t data[0]; /*!< \brief message payload data */ } __attribute__ ((packed)); /* \brief RSL Common Channel header (Chapter 8.5) */ struct abis_rsl_cchan_hdr { struct abis_rsl_common_hdr c; uint8_t ie_chan; /*!< \brief \ref RSL_IE_CHAN_NR (tag) */ uint8_t chan_nr; /*!< \brief RSL channel number (value) */ uint8_t data[0]; /*!< \brief message payload data */ } __attribute__ ((packed)); /* Chapter 9.1 */ /* \brief RSL Message Discriminator: RLL */ #define ABIS_RSL_MDISC_RLL 0x02 /* \brief RSL Message Discriminator: Dedicated Channel */ #define ABIS_RSL_MDISC_DED_CHAN 0x08 /* \brief RSL Message Discriminator: Common Channel */ #define ABIS_RSL_MDISC_COM_CHAN 0x0c /* \brief RSL Message Discriminator: TRX Management */ #define ABIS_RSL_MDISC_TRX 0x10 /* \brief RSL Message Discriminator: Location Service */ #define ABIS_RSL_MDISC_LOC 0x20 /* \brief RSL Message Discriminator: ip.access */ #define ABIS_RSL_MDISC_IPACCESS 0x7e #define ABIS_RSL_MDISC_TRANSP 0x01 /* \brief Check if given RSL message discriminator is transparent */ #define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01) /* \brief RSL Message Tyoe (Chapter 9.1) */ enum abis_rsl_msgtype { /* Radio Link Layer Management */ RSL_MT_DATA_REQ = 0x01, RSL_MT_DATA_IND, RSL_MT_ERROR_IND, RSL_MT_EST_REQ, RSL_MT_EST_CONF, RSL_MT_EST_IND, RSL_MT_REL_REQ, RSL_MT_REL_CONF, RSL_MT_REL_IND, RSL_MT_UNIT_DATA_REQ, RSL_MT_UNIT_DATA_IND, /* 0x0b */ RSL_MT_SUSP_REQ, /* non-standard elements */ RSL_MT_SUSP_CONF, RSL_MT_RES_REQ, RSL_MT_RECON_REQ, /* 0x0f */ /* Common Channel Management / TRX Management */ RSL_MT_BCCH_INFO = 0x11, RSL_MT_CCCH_LOAD_IND, RSL_MT_CHAN_RQD, RSL_MT_DELETE_IND, RSL_MT_PAGING_CMD, RSL_MT_IMMEDIATE_ASSIGN_CMD, RSL_MT_SMS_BC_REQ, RSL_MT_CHAN_CONF, /* non-standard element */ /* empty */ RSL_MT_RF_RES_IND = 0x19, RSL_MT_SACCH_FILL, RSL_MT_OVERLOAD, RSL_MT_ERROR_REPORT, RSL_MT_SMS_BC_CMD, RSL_MT_CBCH_LOAD_IND, RSL_MT_NOT_CMD, /* 0x1f */ /* Dedicate Channel Management */ RSL_MT_CHAN_ACTIV = 0x21, RSL_MT_CHAN_ACTIV_ACK, RSL_MT_CHAN_ACTIV_NACK, RSL_MT_CONN_FAIL, RSL_MT_DEACTIVATE_SACCH, RSL_MT_ENCR_CMD, RSL_MT_HANDO_DET, RSL_MT_MEAS_RES, RSL_MT_MODE_MODIFY_REQ, RSL_MT_MODE_MODIFY_ACK, RSL_MT_MODE_MODIFY_NACK, RSL_MT_PHY_CONTEXT_REQ, RSL_MT_PHY_CONTEXT_CONF, RSL_MT_RF_CHAN_REL, RSL_MT_MS_POWER_CONTROL, RSL_MT_BS_POWER_CONTROL, /* 0x30 */ RSL_MT_PREPROC_CONFIG, RSL_MT_PREPROC_MEAS_RES, RSL_MT_RF_CHAN_REL_ACK, RSL_MT_SACCH_INFO_MODIFY, RSL_MT_TALKER_DET, RSL_MT_LISTENER_DET, RSL_MT_REMOTE_CODEC_CONF_REP, RSL_MT_RTD_REP, RSL_MT_PRE_HANDO_NOTIF, RSL_MT_MR_CODEC_MOD_REQ, RSL_MT_MR_CODEC_MOD_ACK, RSL_MT_MR_CODEC_MOD_NACK, RSL_MT_MR_CODEC_MOD_PER, RSL_MT_TFO_REP, RSL_MT_TFO_MOD_REQ, /* 0x3f */ RSL_MT_LOCATION_INFO = 0x41, /* ip.access specific RSL message types */ RSL_MT_IPAC_DIR_RETR_ENQ = 0x40, RSL_MT_IPAC_PDCH_ACT = 0x48, RSL_MT_IPAC_PDCH_ACT_ACK, RSL_MT_IPAC_PDCH_ACT_NACK, RSL_MT_IPAC_PDCH_DEACT = 0x4b, RSL_MT_IPAC_PDCH_DEACT_ACK, RSL_MT_IPAC_PDCH_DEACT_NACK, RSL_MT_IPAC_CONNECT_MUX = 0x50, RSL_MT_IPAC_CONNECT_MUX_ACK, RSL_MT_IPAC_CONNECT_MUX_NACK, RSL_MT_IPAC_BIND_MUX = 0x53, RSL_MT_IPAC_BIND_MUX_ACK, RSL_MT_IPAC_BIND_MUX_NACK, RSL_MT_IPAC_DISC_MUX = 0x56, RSL_MT_IPAC_DISC_MUX_ACK, RSL_MT_IPAC_DISC_MUX_NACK, RSL_MT_IPAC_CRCX = 0x70, /* Bind to local BTS RTP port */ RSL_MT_IPAC_CRCX_ACK, RSL_MT_IPAC_CRCX_NACK, RSL_MT_IPAC_MDCX = 0x73, RSL_MT_IPAC_MDCX_ACK, RSL_MT_IPAC_MDCX_NACK, RSL_MT_IPAC_DLCX_IND = 0x76, RSL_MT_IPAC_DLCX = 0x77, RSL_MT_IPAC_DLCX_ACK, RSL_MT_IPAC_DLCX_NACK, }; /*! \brief Siemens vendor-specific RSL message types */ 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, }; /*! \brief RSL Information Element Identifiers (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 /*! \brief RSL Channel Mode IF (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 /* non-transparent data */ #define RSL_CMOD_CSD_NT_43k5 0x74 #define RSL_CMOD_CSD_NT_28k8 0x71 #define RSL_CMOD_CSD_NT_14k5 0x58 #define RSL_CMOD_CSD_NT_12k0 0x50 #define RSL_CMOD_CSD_NT_6k0 0x51 /* legacy #defines with wrong name */ #define RSL_CMOD_SP_NT_14k5 RSL_CMOD_CSD_NT_14k5 #define RSL_CMOD_SP_NT_12k0 RSL_CMOD_CSD_NT_12k0 #define RSL_CMOD_SP_NT_6k0 RSL_CMOD_CSD_NT_6k0 /* transparent data */ #define RSL_CMOD_CSD_T_32000 0x38 #define RSL_CMOD_CSD_T_29000 0x39 #define RSL_CMOD_CSD_T_14400 0x18 #define RSL_CMOD_CSD_T_9600 0x10 #define RSL_CMOD_CSD_T_4800 0x11 #define RSL_CMOD_CSD_T_2400 0x12 #define RSL_CMOD_CSD_T_1200 0x13 #define RSL_CMOD_CSD_T_600 0x14 #define RSL_CMOD_CSD_T_1200_75 0x15 /*! \brief RSL Channel Identification IE (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 RSL_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 /*! \brief RSL Cell Broadcast Command (Chapter 9.3.45) */ struct rsl_ie_cb_cmd_type { uint8_t last_block:2; uint8_t spare:1; uint8_t def_bcast:1; uint8_t command:4; } __attribute__ ((packed)); /* ->command */ #define RSL_CB_CMD_TYPE_NORMAL 0x00 #define RSL_CB_CMD_TYPE_SCHEDULE 0x08 #define RSL_CB_CMD_TYPE_DEFAULT 0x0e #define RSL_CB_CMD_TYPE_NULL 0x0f /* ->def_bcast */ #define RSL_CB_CMD_DEFBCAST_NORMAL 0 #define RSL_CB_CMD_DEFBCAST_NULL 1 /* ->last_block */ #define RSL_CB_CMD_LASTBLOCK_4 0 #define RSL_CB_CMD_LASTBLOCK_1 1 #define RSL_CB_CMD_LASTBLOCK_2 2 #define RSL_CB_CMD_LASTBLOCK_3 3 /* Chapter 3.3.2.3 Brocast control channel */ /* CCCH-CONF, NC is not combined */ #define RSL_BCCH_CCCH_CONF_1_NC 0x00 #define RSL_BCCH_CCCH_CONF_1_C 0x01 #define RSL_BCCH_CCCH_CONF_2_NC 0x02 #define RSL_BCCH_CCCH_CONF_3_NC 0x04 #define RSL_BCCH_CCCH_CONF_4_NC 0x06 /* BS-PA-MFRMS */ #define RSL_BS_PA_MFRMS_2 0x00 #define RSL_BS_PA_MFRMS_3 0x01 #define RSL_BS_PA_MFRMS_4 0x02 #define RSL_BS_PA_MFRMS_5 0x03 #define RSL_BS_PA_MFRMS_6 0x04 #define RSL_BS_PA_MFRMS_7 0x05 #define RSL_BS_PA_MFRMS_8 0x06 #define RSL_BS_PA_MFRMS_9 0x07 /* RSL_IE_IPAC_RTP_PAYLOAD[2] */ enum rsl_ipac_rtp_payload { RSL_IPAC_RTP_GSM = 1, RSL_IPAC_RTP_EFR, RSL_IPAC_RTP_AMR, RSL_IPAC_RTP_CSD, RSL_IPAC_RTP_MUX, }; /* RSL_IE_IPAC_SPEECH_MODE, lower four bits */ enum rsl_ipac_speech_mode_s { RSL_IPAC_SPEECH_GSM_FR = 0, /* GSM FR (Type 1, FS) */ RSL_IPAC_SPEECH_GSM_EFR = 1, /* GSM EFR (Type 2, FS) */ RSL_IPAC_SPEECH_GSM_AMR_FR = 2, /* GSM AMR/FR (Type 3, FS) */ RSL_IPAC_SPEECH_GSM_HR = 3, /* GSM HR (Type 1, HS) */ RSL_IPAC_SPEECH_GSM_AMR_HR = 5, /* GSM AMR/hr (Type 3, HS) */ RSL_IPAC_SPEECH_AS_RTP = 0xf, /* As specified by RTP Payload IE */ }; /* RSL_IE_IPAC_SPEECH_MODE, upper four bits */ enum rsl_ipac_speech_mode_m { RSL_IPAC_SPEECH_M_RXTX = 0, /* Send and Receive */ RSL_IPAC_SPEECH_M_RX = 1, /* Receive only */ RSL_IPAC_SPEECH_M_TX = 2, /* Send only */ }; /* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */ enum rsl_ipac_rtp_csd_format_d { RSL_IPAC_RTP_CSD_EXT_TRAU = 0, RSL_IPAC_RTP_CSD_NON_TRAU = 1, RSL_IPAC_RTP_CSD_TRAU_BTS = 2, RSL_IPAC_RTP_CSD_IWF_FREE = 3, }; /* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */ enum rsl_ipac_rtp_csd_format_ir { RSL_IPAC_RTP_CSD_IR_8k = 0, RSL_IPAC_RTP_CSD_IR_16k = 1, RSL_IPAC_RTP_CSD_IR_32k = 2, RSL_IPAC_RTP_CSD_IR_64k = 3, }; /* Siemens vendor-specific RSL extensions */ struct rsl_mrpci { uint8_t power_class:3, vgcs_capable:1, vbs_capable:1, gsm_phase:2; } __attribute__ ((packed)); enum rsl_mrpci_pwrclass { RSL_MRPCI_PWRC_1 = 0, RSL_MRPCI_PWRC_2 = 1, RSL_MRPCI_PWRC_3 = 2, RSL_MRPCI_PWRC_4 = 3, RSL_MRPCI_PWRC_5 = 4, }; enum rsl_mrpci_phase { RSL_MRPCI_PHASE_1 = 0, /* reserved */ RSL_MRPCI_PHASE_2 = 2, RSL_MRPCI_PHASE_2PLUS = 3, }; /* 9.3.20 Release Mode */ enum rsl_rel_mode { RSL_REL_NORMAL = 0, RSL_REL_LOCAL_END = 1, }; /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_09_02.h000066400000000000000000000131261261607044000233360ustar00rootroot00000000000000#ifndef PROTO_GSM_09_02_H #define PROTO_GSM_09_02_H /* GSM TS 09.02 definitions (MAP) */ /* Section 17.7.4 */ /* SS-Status */ #define GSM0902_SS_STATUS_Q_BIT 0x08 #define GSM0902_SS_STATUS_P_BIT 0x04 #define GSM0902_SS_STATUS_R_BIT 0x02 #define GSM0902_SS_STATUS_A_BIT 0x01 /* SS-Data */ #define GSM0902_SS_DATA_SS_STATUS_TAG 0x84 #define GSM0902_SS_DATA_NBR_USER 0x85 /* SS-Info */ #define GSM0902_SS_INFO_FORW_INFO_TAG 0xA0 #define GSM0902_SS_INFO_CALL_BARR_INFO_TAG 0xA1 #define GSM0902_SS_INFO_SS_DATA_TAG 0xA3 /* InterrogateSS-Res */ #define GSM0902_SS_INTERR_SS_RES_SS_STATUS_TAG 0x80 #define GSM0902_SS_INTERR_SS_RES_BSG_LIST_TAG 0x81 #define GSM0902_SS_INTERR_SS_RES_FORW_FEAT_LIST_TAG 0x82 #define GSM0902_SS_INTERR_SS_RES_GEN_SERV_INFO_TAG 0x83 /* Section 17.7.5 */ /* Supplementary service codes */ #define GSM0902_SS_CODE_ALL_SS 0x00 #define GSM0902_SS_CODE_ALL_LINE_IDENTIFICATION_SS 0x10 #define GSM0902_SS_CODE_CLIP 0x11 #define GSM0902_SS_CODE_CLIR 0x12 #define GSM0902_SS_CODE_COLP 0x13 #define GSM0902_SS_CODE_COLR 0x14 #define GSM0902_SS_CODE_MCI 0x15 #define GSM0902_SS_CODE_ALL_NAME_IDENTIFICATION_SS 0x18 #define GSM0902_SS_CODE_CNAP 0x19 #define GSM0902_SS_CODE_ALL_FORWARDING_SS 0x20 #define GSM0902_SS_CODE_CFU 0x21 #define GSM0902_SS_CODE_ALL_COND_FORWARDING_SS 0x28 #define GSM0902_SS_CODE_CFB 0x29 #define GSM0902_SS_CODE_CFNRY 0x2A #define GSM0902_SS_CODE_CFNRC 0x2B #define GSM0902_SS_CODE_CD 0x24 #define GSM0902_SS_CODE_ALL_CALL_OFFERING_SS 0x30 #define GSM0902_SS_CODE_ECT 0x31 #define GSM0902_SS_CODE_MAH 0x32 #define GSM0902_SS_CODE_ALL_CALL_COMPLETION_SS 0x40 #define GSM0902_SS_CODE_CW 0x41 #define GSM0902_SS_CODE_HOLD 0x42 #define GSM0902_SS_CODE_CCBS_A 0x43 #define GSM0902_SS_CODE_CCBS_B 0x44 #define GSM0902_SS_CODE_MC 0x45 #define GSM0902_SS_CODE_ALL_MULTI_PARTY_SS 0x50 #define GSM0902_SS_CODE_MULTI_PTY 0x51 #define GSM0902_SS_CODE_ALL_COMMUNITY_OF_INTEREST_SS 0x60 #define GSM0902_SS_CODE_CUG 0x61 #define GSM0902_SS_CODE_ALL_CHARGING_SS 0x70 #define GSM0902_SS_CODE_AOCI 0x71 #define GSM0902_SS_CODE_AOCC 0x72 #define GSM0902_SS_CODE_ALL_ADDITIONAL_INFO_TRANSFER_SS 0x80 #define GSM0902_SS_CODE_UUS1 0x81 #define GSM0902_SS_CODE_UUS2 0x82 #define GSM0902_SS_CODE_UUS3 0x83 #define GSM0902_SS_CODE_ALL_BARRING_SS 0x90 #define GSM0902_SS_CODE_BARRING_OF_OUTGOING_CALLS 0x91 #define GSM0902_SS_CODE_BAOC 0x92 #define GSM0902_SS_CODE_BOIC 0x93 #define GSM0902_SS_CODE_BOIC_EX_HC 0x94 #define GSM0902_SS_CODE_BARRING_OF_INCOMING_CALLS 0x99 #define GSM0902_SS_CODE_BAIC 0x9A #define GSM0902_SS_CODE_BIC_ROAM 0x9B #define GSM0902_SS_CODE_ALL_PLMN_SPECIFIC_SS 0xF0 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_1 0xF1 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_2 0xF2 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_3 0xF3 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_4 0xF4 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_5 0xF5 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_6 0xF6 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_7 0xF7 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_8 0xF8 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_9 0xF9 #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_A 0xFA #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_B 0xFB #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_C 0xFC #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_D 0xFD #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_E 0xFE #define GSM0902_SS_CODE_PLMN_SPECIFIC_SS_F 0xFF #define GSM0902_SS_CODE_ALL_CALL_PRIORITY_SS 0xA0 #define GSM0902_SS_CODE_EMLPP 0xA1 #define GSM0902_SS_CODE_ALL_LCSPRIVACY_EXCEPTION 0xB0 #define GSM0902_SS_CODE_UNIVERSAL 0xB1 #define GSM0902_SS_CODE_CALL_SESSION_RELATED 0xB2 #define GSM0902_SS_CODE_CALL_SESSION_UNRELATED 0xB3 #define GSM0902_SS_CODE_PLMNOPERATOR 0xB4 #define GSM0902_SS_CODE_SERVICE_TYPE 0xB5 #define GSM0902_SS_CODE_ALL_MOLR_SS 0xC0 #define GSM0902_SS_CODE_BASIC_SELF_LOCATION 0xC1 #define GSM0902_SS_CODE_AUTONOMOUS_SELF_LOCATION 0xC2 #define GSM0902_SS_CODE_TRANSFER_TO_THIRD_PARTY 0xC3 /* Section 17.7.9 */ /* Teleservice codes */ #define GSM0902_TS_CODE_ALL_TELESERVICES 0x00 #define GSM0902_TS_CODE_ALL_SPEECH_TRANSMISSION_SERVICES 0x10 #define GSM0902_TS_CODE_TELEPHONY 0x11 #define GSM0902_TS_CODE_EMERGENCY_CALLS 0x12 #define GSM0902_TS_CODE_ALL_SHORT_MESSAGE_SERVICES 0x20 #define GSM0902_TS_CODE_SHORT_MESSAGE_MT_PP 0x21 #define GSM0902_TS_CODE_SHORT_MESSAGE_MO_PP 0x22 #define GSM0902_TS_CODE_ALL_FACSIMILE_TRANSMISSION_SERVICES 0x60 #define GSM0902_TS_CODE_FACSIMILE_GROUP3AND_ALTER_SPEECH 0x61 #define GSM0902_TS_CODE_AUTOMATIC_FACSIMILE_GROUP3 0x62 #define GSM0902_TS_CODE_FACSIMILE_GROUP4 0x63 #define GSM0902_TS_CODE_ALL_DATA_TELESERVICES 0x70 #define GSM0902_TS_CODE_ALL_TELESERVICES_EXEPT_SMS 0x80 #define GSM0902_TS_CODE_ALL_VOICE_GROUP_CALL_SERVICES 0x90 #define GSM0902_TS_CODE_VOICE_GROUP_CALL 0x91 #define GSM0902_TS_CODE_VOICE_BROADCAST_CALL 0x92 #define GSM0902_TS_CODE_ALL_PLMN_SPECIFIC_TS 0xD0 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_1 0xD1 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_2 0xD2 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_3 0xD3 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_4 0xD4 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_5 0xD5 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_6 0xD6 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_7 0xD7 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_8 0xD8 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_9 0xD9 #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_A 0xDA #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_B 0xDB #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_C 0xDC #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_D 0xDD #define GSM0902_TS_CODE_PLMN_SPECIFIC_TS_E 0xDE #endif /* PROTO_GSM_09_02_H */ libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_12_21.h000066400000000000000000000517071261607044000233400ustar00rootroot00000000000000#pragma once /* 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 * 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. * */ /*! \addtogroup oml * @{ */ /*! \file gsm_12_21.h */ #include #include /*! \brief generic header in front of every OML message according to TS 08.59 */ struct abis_om_hdr { /*! \brief Message Discriminator \ref abis_oml_mdisc */ uint8_t mdisc; /*! \brief Placement (like \ref ABIS_OM_PLACEMENT_ONLY) */ uint8_t placement; /*! \brief Sequence Number (if \ref ABIS_OM_PLACEMENT_MIDDLE) */ uint8_t sequence; /*! \brief Length in octets */ uint8_t length; /*! \brief actual payload data */ uint8_t data[0]; } __attribute__ ((packed)); /*! \brief Message Discriminator for Formatted Object Messages */ #define ABIS_OM_MDISC_FOM 0x80 /*! \brief Message Discriminator for Man Machine Interface */ #define ABIS_OM_MDISC_MMI 0x40 /*! \brief Message Discriminator for TRAU management */ #define ABIS_OM_MDISC_TRAU 0x20 /*! \brief Message Discriminator for Manufacturer Specific Messages */ #define ABIS_OM_MDISC_MANUF 0x10 /*! \brief Entire OML message is in the L2 frame */ #define ABIS_OM_PLACEMENT_ONLY 0x80 /*! \brief First fragment of OML message is in this L2 frame */ #define ABIS_OM_PLACEMENT_FIRST 0x40 /*! \brief Middle fragment of OML message is in this L2 frame */ #define ABIS_OM_PLACEMENT_MIDDLE 0x20 /*! \brief Last fragment of OML message is in this L2 frame */ #define ABIS_OM_PLACEMENT_LAST 0x10 /*! \brief OML Object Instance */ struct abis_om_obj_inst { uint8_t bts_nr; /*!< \brief BTS Number */ uint8_t trx_nr; /*!< \brief TRX Number */ uint8_t ts_nr; /*!< \brief Timeslot Number */ } __attribute__ ((packed)); /*! \brief OML Object Instance */ struct abis_om_fom_hdr { uint8_t msg_type; /*!< \brief Message Type (\ref abis_nm_msgtype) */ uint8_t obj_class; /*!< \brief Object Class (\ref abis_nm_obj_class) */ struct abis_om_obj_inst obj_inst; /*!< \brief Object Instance */ uint8_t data[0]; /*!< \brief Data */ } __attribute__ ((packed)); /*! \brief Size of the OML FOM header in octets */ #define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr)) /*! \brief OML Message Type (Section 9.1) */ 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, }; /*! \brief Siemens specific OML Message Types */ 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, }; /*! \brief ip.access specific OML Message Types */ 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, }; /*! \brief OML Object Class (Section 9.2) */ 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, }; /*! \brief OML Attributes / IEs (Section 9.4) */ 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- */ 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, /* osmocom (osmo-bts) specific attributes, used in combination * with the "org.osmocom" manufacturer identification */ NM_ATT_OSMO_REDUCEPOWER = 0xfe, /* TLV_TYPE_TV */ }; #define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE /*! \brief OML Administrative State (Section 9.4.4) */ enum abis_nm_adm_state { NM_STATE_LOCKED = 0x01, NM_STATE_UNLOCKED = 0x02, NM_STATE_SHUTDOWN = 0x03, NM_STATE_NULL = 0xff, }; /*! \brief OML Availability State (Section 9.4.7) */ 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, }; /*! \brief OML Operational State */ enum abis_nm_op_state { NM_OPSTATE_DISABLED = 1, NM_OPSTATE_ENABLED = 2, NM_OPSTATE_NULL = 0xff, }; /* \brief Channel Combination (Section 9.4.13) */ 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, }; /*! \brief Event Type (Section 9.4.16) */ 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, }; /*! \brief Perceived Severity (Section: 9.4.63) */ 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, }; /*! \brief Probable Cause Type (Section 9.4.43) */ enum abis_nm_pcause_type { NM_PCAUSE_T_X721 = 0x01, NM_PCAUSE_T_GSM = 0x02, NM_PCAUSE_T_MANUF = 0x03, }; /*! \brief NACK causes (Section 9.4.36) */ 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, }; /*! \brief Abis OML Channel (Section 9.4.1) */ struct abis_nm_channel { uint8_t attrib; uint8_t bts_port; /*!< \brief BTS port number */ uint8_t timeslot; /*!< \brief E1 timeslot */ uint8_t subslot; /*!< \brief E1 sub-slot */ } __attribute__ ((packed)); /*! \brief 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*/ }; /*! \brief Siemens BS11 TRX power */ 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, }; /*! \brief Siemens BS11 PLL mode */ enum abis_bs11_li_pll_mode { BS11_LI_PLL_LOCKED = 2, BS11_LI_PLL_STANDALONE = 3, }; /*! \brief Siemens BS11 E1 line configuration */ enum abis_bs11_line_cfg { BS11_LINE_CFG_STAR = 0x00, BS11_LINE_CFG_MULTIDROP = 0x01, BS11_LINE_CFG_LOOP = 0x02, }; /*! \brief Siemens BS11 boot phase */ 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, }; /*! \brief ip.access test number */ 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, }; /*! \brief 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, }; /*! \brief 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, }; /*! \brief ip.access IEI */ 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, }; /*! \brief ip.access NWL BCCH information type */ 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), }; /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/protocol/gsm_44_318.h000066400000000000000000000105421261607044000234260ustar00rootroot00000000000000#pragma once #include /* Definitions according to 3GPP TS 44.318 6.8.0 Release 6 */ /* Table 11.1.1.2.1: Protocol Discriminator */ enum gan_pdisc { GA_PDISC_RC = 0, GA_PDISC_CSR = 1, GA_PDISC_PSR = 2, }; /* Table 11.1.1.4.1: Message types for URR */ enum gan_msg_type { GA_MT_RC_DISCOVERY_REQUEST = 0x01, GA_MT_RC_DISCOVERY_ACCEPT = 0x02, GA_MT_RC_DISCOVERY_REJECT = 0x03, GA_MT_RC_REGISTER_REQUEST = 0x10, GA_MT_RC_REGISTER_ACCEPT = 0x11, GA_MT_RC_REGISTER_REDIRECT = 0x12, GA_MT_RC_REGISTER_REJECT = 0x13, GA_MT_RC_DEREGISTER = 0x14, GA_MT_RC_REGISTER_UPDATE_UL = 0x15, GA_MT_RC_REGISTER_UPDATE_DL = 0x16, GA_MT_RC_CELL_BCAST_INFO = 0x17, GA_MT_CSR_CIPH_MODE_CMD = 0x20, GA_MT_CSR_CIPH_MODE_COMPL = 0x21, GA_MT_CSR_ACT_CHAN = 0x30, GA_MT_CSR_ACT_CHAN_ACK = 0x31, GA_MT_CSR_ACT_CHAN_COMPL = 0x32, GA_MT_CSR_ACT_CHAN_FAIL = 0x33, GA_MT_CSR_CHAN_MODE_MOD = 0x34, GA_MT_CSR_CHAN_MODE_MOD_ACK = 0x35, GA_MT_CSR_RELEASE = 0x40, GA_MT_CSR_RELEASE_COMPL = 0x41, GA_MT_CSR_CLEAR_REQ = 0x42, GA_MT_CSR_HO_ACCESS = 0x50, GA_MT_CSR_HO_COMPL = 0x51, GA_MT_CSR_UL_QUAL_IND = 0x52, GA_MT_CSR_HO_INFO = 0x53, GA_MT_CSR_HO_CMD = 0x54, GA_MT_CSR_HO_FAIL = 0x55, GA_MT_CSR_PAGING_REQ = 0x60, GA_MT_CSR_PAGING_RESP = 0x61, GA_MT_CSR_UL_DIRECT_XFER = 0x70, GA_MT_CSR_DL_DIRECT_XFER = 0x72, GA_MT_CSR_STATUS = 0x73, GA_MT_RC_KEEPALIVE = 0x74, GA_MT_CSR_CM_ENQ = 0x75, GA_MT_CSR_CM_CHANGE = 0x76, GA_MT_PSR_GPRS_SUSPEND_REQ = 0x77, GA_RC_SYNC_INFO = 0x78, GA_CSR_UTRAN_CM_CHG = 0x79, GA_MT_CSR_REQUEST = 0x80, GA_MT_CSR_REQUEST_ACCEPT = 0x81, GA_MT_CSR_REQUEST_REJECT = 0x82, }; /* All tables in 10.1.x and 10.2.x / Table 11.2.1 */ enum gan_iei { GA_IE_MI = 1, GA_IE_GAN_RELEASE_IND = 2, GA_IE_RADIO_IE = 3, GA_IE_GERAN_CELL_ID = 4, GA_IE_LAC = 5, GA_IE_GERAN_COV_IND = 6, GA_IE_GAN_CM = 7, GA_IE_GEO_LOC = 8, GA_IE_DEF_SEGW_IP = 9, GA_IE_DEF_SEGW_FQDN = 10, GA_IE_REDIR_CTR = 11, GA_IE_DISCOV_REJ_CAUSE = 12, GA_IE_GANC_CELL_DESC = 13, GA_IE_GANC_CTRL_CH_DESC = 14, GA_IE_GERAN_CELL_ID_LIST= 15, GA_IE_TU3907_TIMER = 16, GA_IE_RR_STATE = 17, GA_IE_RAI = 18, GA_IE_GAN_BAND = 19, GA_IE_GARC_GACSR_STATE = 20, GA_IE_REG_REJ_CAUSE = 21, GA_IE_TU3906_TIMER = 22, GA_IE_TU3910_TIMER = 23, GA_IE_TU3902_TIMER = 24, GA_IE_L3_MSG = 26, GA_IE_CHAN_MODE = 27, GA_IE_MS_CLASSMARK2 = 28, GA_IE_RR_CAUSE = 29, GA_EI_CIPH_MODE_SET = 30, GA_IE_GPRS_RESUMPTION = 31, GA_IE_HO_FROM_GAN_CMD = 32, GA_IE_UL_QUAL_IND = 33, GA_IE_TLLI = 34, GA_IE_PFI = 35, GA_IE_SUSP_CAUSE = 36, GA_IE_TU3920_TIMER = 37, GA_IE_REQD_QOS = 38, GA_IE_P_DEACT_CAUSE = 39, GA_IE_REQD_UL_RATE = 40, GA_IE_RAC = 41, GA_IE_AP_LOCATION = 42, GA_IE_TU4001_TIMER = 43, GA_IE_LOC_STATUS = 44, GA_IE_CIPH_RESP = 45, GA_IE_CIPH_RAND = 46, GA_IE_CIPH_MAC = 47, GA_IE_CKSN = 48, GA_IE_SAPI_ID = 49, GA_IE_EST_CAUSE = 50, GA_IE_CHAN_NEEDED = 51, GA_IE_PDU_IN_ERROR = 52, GA_IE_SAMPLE_SIZE = 53, GA_IE_PAYLOAD_TYPE = 54, GA_IE_MULTIRATE_CONF = 55, GA_IE_MS_CLASSMARK3 = 56, GA_IE_LLC_PDU = 57, GA_IE_LOC_BLACKL_IND = 58, GA_IE_RESET_IND = 59, GA_IE_TU4003_TIMER = 60, GA_IE_AP_SERV_NAME = 61, GA_IE_SERV_ZONE_INFO = 62, GA_IE_RTP_RED_CONF = 63, GA_IE_UTRAN_CLASSMARK = 64, GA_IE_CM_ENQ_MASK = 65, GA_IE_UTRAN_CELLID_LIST = 66, GA_IE_SERV_GANC_TBL_IND = 67, GA_IE_AP_REG_IND = 68, GA_IE_GAN_PLMN_LIST = 69, GA_IE_REQD_GAN_SERV = 71, GA_IE_BCAST_CONTAINER = 72, GA_IE_3G_CELL_ID = 73, GA_IE_MS_RADIO_ID = 96, GA_IE_DEF_GANC_IP = 97, GA_IE_DEF_GANC_FQDN = 98, GA_IE_GPRS_IP_ADDR = 99, GA_IE_GPRS_UDP_PORT = 100, GA_IE_GANC_TCP_PORT = 103, GA_IE_RTP_UDP_PORT = 104, GA_IE_RTCP_UDP_PORT = 105, GA_IE_GERAN_RCV_SIGL_LIST = 106, GA_IE_UTRAN_RCV_SIGL_LIST = 107, }; /* 11.1.1 GA-RC and GA-CSR Message header IE */ struct gan_rc_csr_hdr { uint16_t len; uint8_t pdisc:4, skip_ind:4; uint8_t msg_type; uint8_t data[0]; } __attribute__((packed)); /* 11.2.14.1: GAN Control Channel Description IE */ struct gan_cch_desc_ie { uint8_t spare:1, ecmc:1, nmo:2, gprs:1, dtm:1, att:1, mscr:1; #if 0 uint8_t mscr:1, att:1, dtm:1, gprs:1, nmo:2, ecmc:1, spare:1; #endif uint8_t t3212; uint8_t rac; uint8_t sgsnr:1, ecmp:1, re:1, pfcfm:1, tgecs:2, spare2:2; #if 0 uint8_t spare2:2, tgecs:2, pfcfm:1, re:1, ecmp:1, sgsnr:1; #endif uint8_t access_class[2]; } __attribute__((packed)); libosmocore-0.9.0/include/osmocom/gsm/protocol/ipaccess.h000066400000000000000000000036031261607044000235300ustar00rootroot00000000000000#pragma once #include #define IPA_TCP_PORT_OML 3002 #define IPA_TCP_PORT_RSL 3003 struct ipaccess_head { uint16_t len; /* network byte order */ uint8_t proto; uint8_t data[0]; } __attribute__ ((packed)); struct ipaccess_head_ext { uint8_t proto; uint8_t data[0]; } __attribute__ ((packed)); enum ipaccess_proto { IPAC_PROTO_RSL = 0x00, IPAC_PROTO_IPACCESS = 0xfe, IPAC_PROTO_SCCP = 0xfd, IPAC_PROTO_OML = 0xff, /* OpenBSC extensions */ IPAC_PROTO_OSMO = 0xee, IPAC_PROTO_MGCP_OLD = 0xfc, }; enum ipaccess_proto_ext { IPAC_PROTO_EXT_CTRL = 0x00, IPAC_PROTO_EXT_MGCP = 0x01, IPAC_PROTO_EXT_LAC = 0x02, IPAC_PROTO_EXT_SMSC = 0x03, IPAC_PROTO_EXT_ORC = 0x04, /* OML Router Control */ IPAC_PROTO_EXT_GSUP = 0x05, /* GSUP GPRS extension */ IPAC_PROTO_EXT_OAP = 0x06, /* Osmocom Authn Protocol */ }; enum ipaccess_msgtype { IPAC_MSGT_PING = 0x00, IPAC_MSGT_PONG = 0x01, IPAC_MSGT_ID_GET = 0x04, IPAC_MSGT_ID_RESP = 0x05, IPAC_MSGT_ID_ACK = 0x06, /* OpenBSC extension */ IPAC_MSGT_SCCP_OLD = 0xff, }; enum ipaccess_id_tags { IPAC_IDTAG_SERNR = 0x00, IPAC_IDTAG_UNITNAME = 0x01, IPAC_IDTAG_LOCATION1 = 0x02, IPAC_IDTAG_LOCATION2 = 0x03, IPAC_IDTAG_EQUIPVERS = 0x04, IPAC_IDTAG_SWVERSION = 0x05, IPAC_IDTAG_IPADDR = 0x06, IPAC_IDTAG_MACADDR = 0x07, IPAC_IDTAG_UNIT = 0x08, }; /* * Firmware specific header */ struct sdp_firmware { char magic[4]; char more_magic[2]; uint16_t more_more_magic; uint32_t header_length; uint32_t file_length; char sw_part[20]; char text1[64]; char time[12]; char date[14]; char text2[10]; char version[20]; uint16_t table_offset; /* stuff i don't know */ } __attribute__((packed)); struct sdp_header_entry { uint16_t something1; char text1[64]; char time[12]; char date[14]; char text2[10]; char version[20]; uint32_t length; uint32_t addr1; uint32_t addr2; uint32_t start; } __attribute__((packed)); libosmocore-0.9.0/include/osmocom/gsm/protocol/smpp34_osmocom.h000066400000000000000000000024051261607044000246170ustar00rootroot00000000000000#pragma once /* Osmocom SMPP extensions */ /* Osmocom specific new TLV definitions */ /* ARFCN in 16-bit encoding, highest bit: PCS(1) / DCS(0) */ #define TLVID_osmo_arfcn 0x2300 /* Timing advance as uint8_t */ #define TLVID_osmo_ta 0x2301 /* Receive signal level (uplink) as int16_t in dBm */ #define TLVID_osmo_rxlev_ul 0x2302 /* Receive signal quality (uplink) as uint8_t */ #define TLVID_osmo_rxqual_ul 0x2303 /* Receive signal level (downlink) as int16_t in dBm */ #define TLVID_osmo_rxlev_dl 0x2304 /* Receive signal quality (downlink) as uint8_t */ #define TLVID_osmo_rxqual_dl 0x2305 /* IMEI of the subscriber, if known */ #define TLVID_osmo_imei 0x2306 /* MS Layer 1 Transmit Power */ #define TLVID_osmo_ms_l1_txpwr 0x2307 /* BTS Layer 1 Transmit Power */ #define TLVID_osmo_bts_l1_txpwr 0x2308 /* DELIVER_SM can contain the following optional Osmocom TLVs: * TLVID_osmo_arfcn * TLVID_osmo_ta * TLVID_osmo_rxlev_ul * TLVID_osmo_rxqual_ul * TLVID_osmo_rxlev_dl * TLVID_osmo_rxqual_dl * TLVID_osmo_imei */ /* SUBMIT_SM_RESP (transaction mode) can contain the following optional * Osmocom TLVs: * TLVID_osmo_arfcn * TLVID_osmo_ta * TLVID_osmo_rxlev_ul * TLVID_osmo_rxqual_ul * TLVID_osmo_rxlev_dl * TLVID_osmo_rxqual_dl * TLVID_osmo_imei */ libosmocore-0.9.0/include/osmocom/gsm/rsl.h000066400000000000000000000031761261607044000207020ustar00rootroot00000000000000#pragma once #include #include #include #include /*! \defgroup rsl RSL * @{ */ /*! \file rsl.h */ void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type); void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type); extern const struct tlv_definition rsl_att_tlvdef; /*! \brief Parse RSL TLV structure using \ref tlv_parse */ #define rsl_tlv_parse(dec, buf, len) \ tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) /* encode channel number as per Section 9.3.1 */ uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot); /* decode channel number as per Section 9.3.1 */ int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot); /* Turns channel number into a string */ const char *rsl_chan_nr_str(uint8_t chan_nr); const char *rsl_err_name(uint8_t err); const char *rsl_rlm_cause_name(uint8_t err); const char *rsl_msg_name(uint8_t err); const char *rsl_ipac_msg_name(uint8_t msg_type); /* Section 3.3.2.3 TS 05.02. I think this looks like a table */ int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); /* Push a RSL RLL header */ void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, uint8_t link_id, int transparent); /* Push a RSL RLL header with L3_INFO IE */ void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, uint8_t link_id, int transparent); /* Allocate msgb and fill with simple RSL RLL header */ struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, uint8_t link_id, int transparent); /*! @} */ libosmocore-0.9.0/include/osmocom/gsm/rxlev_stat.h000066400000000000000000000011101261607044000222570ustar00rootroot00000000000000#pragma once #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); libosmocore-0.9.0/include/osmocom/gsm/sysinfo.h000066400000000000000000000015341261607044000215700ustar00rootroot00000000000000#pragma once #include #include enum osmo_sysinfo_type { SYSINFO_TYPE_NONE, SYSINFO_TYPE_1, SYSINFO_TYPE_2, SYSINFO_TYPE_3, SYSINFO_TYPE_4, SYSINFO_TYPE_5, SYSINFO_TYPE_6, SYSINFO_TYPE_7, SYSINFO_TYPE_8, SYSINFO_TYPE_9, SYSINFO_TYPE_10, SYSINFO_TYPE_13, SYSINFO_TYPE_16, SYSINFO_TYPE_17, SYSINFO_TYPE_18, SYSINFO_TYPE_19, SYSINFO_TYPE_20, SYSINFO_TYPE_2bis, SYSINFO_TYPE_2ter, SYSINFO_TYPE_2quater, SYSINFO_TYPE_5bis, SYSINFO_TYPE_5ter, SYSINFO_TYPE_EMO, SYSINFO_TYPE_MEAS_INFO, /* FIXME all the various bis and ter */ _MAX_SYSINFO_TYPE }; typedef uint8_t sysinfo_buf_t[GSM_MACBLOCK_LEN]; extern const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE]; uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type); enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si); libosmocore-0.9.0/include/osmocom/gsm/tlv.h000066400000000000000000000270621261607044000207070ustar00rootroot00000000000000#pragma once #include #include #include /*! \defgroup tlv GSM L3 compatible TLV parser * @{ */ /*! \file tlv.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 vTvLV 8/16 8/16 N * 8 */ /*! \brief gross length of a LV type field */ #define LV_GROSS_LEN(x) (x+1) /*! \brief gross length of a TLV type field */ #define TLV_GROSS_LEN(x) (x+2) /*! \brief gross length of a TLV16 type field */ #define TLV16_GROSS_LEN(x) ((2*x)+2) /*! \brief gross length of a TL16V type field */ #define TL16V_GROSS_LEN(x) (x+3) /*! \brief gross length of a L16TV type field */ #define L16TV_GROSS_LEN(x) (x+3) /*! \brief maximum length of TLV of one byte length */ #define TVLV_MAX_ONEBYTE 0x7f /*! \brief gross length of a TVLV type field */ 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); } /*! \brief gross length of vTvL header (tag+len) */ static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len) { uint16_t ret = 2; if (tag > TVLV_MAX_ONEBYTE) ret++; if (len > TVLV_MAX_ONEBYTE) ret++; return ret; } /*! \brief gross length of vTvLV (tag+len+val) */ static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len) { uint16_t ret; if (len <= TVLV_MAX_ONEBYTE) return TLV_GROSS_LEN(len); else return TL16V_GROSS_LEN(len); if (tag > TVLV_MAX_ONEBYTE) ret += 1; return ret; } /* TLV generation */ /*! \brief put (append) a LV field */ 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; } /*! \brief put (append) a TLV field */ 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; } /*! \brief put (append) a TLV16 field */ 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; } /*! \brief put (append) a TL16V field */ 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; } /*! \brief put (append) a TvLV field */ 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; } /*! \brief put (append) a variable-length tag or variable-length length * */ static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag) { if (tag > TVLV_MAX_ONEBYTE) { /* two-byte TAG */ *buf++ = 0x80 | (tag >> 8); *buf++ = (tag & 0xff); } else *buf++ = tag; return buf; } /* \brief put (append) vTvL (GAN) field (tag + length)*/ static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len) { uint8_t *ret; ret = vt_gan_put(buf, tag); return vt_gan_put(ret, len); } /* \brief put (append) vTvLV (GAN) field (tag + length + val) */ static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len, const uint8_t *val) { uint8_t *ret; ret = vtvl_gan_put(buf, tag, len ); memcpy(ret, val, len); ret = buf + len; return ret; } /*! \brief put (append) a TLV16 field to \ref msgb */ 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); } /*! \brief put (append) a TL16V field to \ref msgb */ 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); } /*! \brief put (append) a TvLV field to \ref msgb */ 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); } /*! \brief put (append) a vTvLV field to \ref msgb */ static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag, uint16_t len, const uint8_t *val) { uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len)); return vtvlv_gan_put(buf, tag, len, val); } /*! \brief put (append) a L16TV field to \ref msgb */ 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; } /*! \brief put (append) a V field */ static inline uint8_t *v_put(uint8_t *buf, uint8_t val) { *buf++ = val; return buf; } /*! \brief put (append) a TV field */ static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, uint8_t val) { *buf++ = tag; *buf++ = val; return buf; } /*! \brief put (append) a TVfixed field */ static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag, unsigned int len, const uint8_t *val) { *buf++ = tag; memcpy(buf, val, len); return buf + len; } /*! \brief put (append) a TV16 field * \param[in,out] buf data buffer * \param[in] tag Tag value * \param[in] val Value (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; } /*! \brief put (append) a LV field to a \ref msgb * \returns pointer to first byte after newly-put information */ 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); } /*! \brief put (append) a TLV field to a \ref msgb * \returns pointer to first byte after newly-put information */ 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); } /*! \brief put (append) a TV field to a \ref msgb * \returns pointer to first byte after newly-put information */ 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); } /*! \brief put (append) a TVfixed field to a \ref msgb * \returns pointer to first byte after newly-put information */ static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag, unsigned int len, const uint8_t *val) { uint8_t *buf = msgb_put(msg, 1+len); return tv_fixed_put(buf, tag, len, val); } /*! \brief put (append) a V field to a \ref msgb * \returns pointer to first byte after newly-put information */ 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); } /*! \brief put (append) a TV16 field to a \ref msgb * \returns pointer to first byte after newly-put information */ 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); } /*! \brief push (prepend) a TLV field to a \ref msgb * \returns pointer to first byte of newly-pushed information */ 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)); tlv_put(buf, tag, len, val); return buf; } /*! \brief push (prepend) a TV field to a \ref msgb * \returns pointer to first byte of newly-pushed information */ static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val) { uint8_t *buf = msgb_push(msg, 2); tv_put(buf, tag, val); return buf; } /*! \brief push (prepend) a TV16 field to a \ref msgb * \returns pointer to first byte of newly-pushed information */ static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val) { uint8_t *buf = msgb_push(msg, 3); tv16_put(buf, tag, val); return buf; } /*! \brief push (prepend) a TvLV field to a \ref msgb * \returns pointer to first byte of newly-pushed information */ 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)); tvlv_put(buf, tag, len, val); return buf; } /* \brief push (prepend) a vTvL header to a \ref msgb */ static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag, uint16_t len) { uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len)); vtvl_gan_put(buf, tag, len); return buf; } static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag, uint16_t len, const uint8_t *val) { uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len)); vtvlv_gan_put(buf, tag, len, val); return buf; } /* TLV parsing */ /*! \brief Entry in a TLV parser array */ struct tlv_p_entry { uint16_t len; /*!< \brief length */ const uint8_t *val; /*!< \brief pointer to value */ }; /*! \brief TLV type */ enum tlv_type { TLV_TYPE_NONE, /*!< \brief no type */ TLV_TYPE_FIXED, /*!< \brief fixed-length value-only */ TLV_TYPE_T, /*!< \brief tag-only */ TLV_TYPE_TV, /*!< \brief tag-value (8bit) */ TLV_TYPE_TLV, /*!< \brief tag-length-value */ TLV_TYPE_TL16V, /*!< \brief tag, 16 bit length, value */ TLV_TYPE_TvLV, /*!< \brief tag, variable length, value */ TLV_TYPE_SINGLE_TV, /*!< \brief tag and value (both 4 bit) in 1 byte */ TLV_TYPE_vTvLV_GAN, /*!< \brief variable-length tag, variable-length length */ }; /*! \brief Definition of a single IE (Information Element) */ struct tlv_def { enum tlv_type type; /*!< \brief TLV type */ uint8_t fixed_len; /*!< \brief length in case of \ref TLV_TYPE_FIXED */ }; /*! \brief Definition of All 256 IE / TLV */ struct tlv_definition { struct tlv_def def[256]; }; /*! \brief result of the TLV parser */ struct tlv_parsed { struct tlv_p_entry lv[256]; }; extern struct tlv_definition tvlv_att_def; extern struct tlv_definition vtvlv_gan_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 #define TLVP_PRES_LEN(tp, tag, min_len) \ (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len) /*! \brief Align given TLV element with 16 bit value to an even address * \param[in] tp pointer to \ref tlv_parsed * \param[in] pos element to return * \returns aligned 16 bit value */ static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos) { uint16_t res; memcpy(&res, TLVP_VAL(tp, pos), sizeof(res)); return res; } /*! \brief Align given TLV element with 32 bit value to an address that is a multiple of 4 * \param[in] tp pointer to \ref tlv_parsed * \param[in] pos element to return * \returns aligned 32 bit value */ static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos) { uint32_t res; memcpy(&res, TLVP_VAL(tp, pos), sizeof(res)); return res; } /*! @} */ libosmocore-0.9.0/include/osmocom/sim/000077500000000000000000000000001261607044000177245ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/sim/sim.h000066400000000000000000000265061261607044000206760ustar00rootroot00000000000000#ifndef _OSMOCOM_SIM_H #define _OSMOCOM_SIM_H #include #include #define APDU_HDR_LEN 5 /*! * \file sim.h * \brief Routines for helping with SIM (ISO/IEC 7816-4 more generally) communication. */ /*! \brief command-response pairs cases * * Enumeration used to identify the APDU structure based on command-response pair case , as specified in ISO/IEC 7816-3:2006(E) §12.1. */ enum osim_apdu_case { APDU_CASE_1, /*!< command header, no command data field, no response data field */ APDU_CASE_2S, /*!< command header, no command data field, response data field (short) */ APDU_CASE_2E, /*!< command header, no command data field, response data field (extended) */ APDU_CASE_3S, /*!< command header, command data field (short), no response data field */ APDU_CASE_3E, /*!< command header, command data field (extended), no response data field */ APDU_CASE_4S, /*!< command header, command data field (short), response data field (short) */ APDU_CASE_4E /*!< command header, command data field (extended), response data field (extended) */ }; /*! \brief APDU/TPDU command header * * This structure encode an APDU/TPDU command header, as specified in ISO/IEC 7816-3:2006(E) §12.2 and §12.3. * The APDU (application layer) can be encoded as different TPDUs (transport layer), depending on the transport protocol used. * The TPDU encoding by T=1 of the APDU command header is identical to the APDU. * The TPDU encoding by T=0 of the APDU command header adds a Parameter 3 field, generally used instead of Lc/Le. * * @todo have different structures for APDU, TPDU by T=0, and TPDU by T=1. */ struct osim_apdu_cmd_hdr { uint8_t cla; /*!< CLASS byte */ uint8_t ins; /*!< INSTRUCTION byte */ uint8_t p1; /*!< Parameter 1 byte */ uint8_t p2; /*!< Parameter 2 byte */ uint8_t p3; /*!< Parameter 3 byte, used for TPDU by T=0 */ } __attribute__ ((packed)); #define msgb_apdu_dr(__x) /*! \brief APDU command body * * This structure encode a command body, as specified in ISO/IEC 7816-3:2006(E) §12.1. * The data and response contents should be provided along with this structure. */ struct osim_msgb_cb { enum osim_apdu_case apduc; /*!< command-response pair case, defining the encoding of Lc and Le */ uint16_t lc; /*!< number of bytes in the command data field Nc, which will encoded in 0, 1 or 3 bytes into Lc, depending on the case */ uint16_t le; /*!< maximum number of bytes expected in the response data field, which will encoded in 0, 1, 2 or 3 bytes into Le, depending on the case */ uint16_t sw; /*!< status word, composed of SW1 and SW2 bytes */ } __attribute__((__may_alias__)); #define OSIM_MSGB_CB(__msgb) ((struct osim_msgb_cb *)&((__msgb)->cb[0])) /*! \brief status word from msgb->cb */ #define msgb_apdu_case(__x) OSIM_MSGB_CB(__x)->apduc #define msgb_apdu_lc(__x) OSIM_MSGB_CB(__x)->lc #define msgb_apdu_le(__x) OSIM_MSGB_CB(__x)->le #define msgb_apdu_sw(__x) OSIM_MSGB_CB(__x)->sw /*! \brief pointer to the command header of the APDU */ #define msgb_apdu_h(__x) ((struct osim_apdu_cmd_hdr *)(__x)->l2h) #define msgb_apdu_dc(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr)) #define msgb_apdu_de(__x) ((__x)->l2h + sizeof(struct osim_apdu_cmd_hdr) + msgb_apdu_lc(__x)) /* FILES */ struct osim_file; struct osim_file_desc; struct osim_decoded_data; /*! \brief Operations for a given File */ struct osim_file_ops { /*! Parse binary file data into osim_decoded_data */ int (*parse)(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); /*! Encode osim_decoded_data into binary file */ struct msgb * (*encode)(const struct osim_file_desc *desc, const struct osim_decoded_data *decoded); }; enum osim_element_type { ELEM_T_NONE, ELEM_T_BOOL, /*!< a boolean flag */ ELEM_T_UINT8, /*!< unsigned integer */ ELEM_T_UINT16, /*!< unsigned integer */ ELEM_T_UINT32, /*!< unsigned integer */ ELEM_T_STRING, /*!< generic string */ ELEM_T_BCD, /*!< BCD encoded digits */ ELEM_T_BYTES, /*!< BCD encoded digits */ ELEM_T_GROUP, /*!< group container, has siblings */ }; enum osim_element_repr { ELEM_REPR_NONE, ELEM_REPR_DEC, ELEM_REPR_HEX, }; /*! \brief A single decoded element inside a file */ struct osim_decoded_element { struct llist_head list; enum osim_element_type type; enum osim_element_repr representation; const char *name; unsigned int length; union { uint8_t u8; uint16_t u16; uint32_t u32; uint8_t *buf; /*! A list of sibling decoded_items */ struct llist_head siblings; } u; }; /*! Decoded data for a single file, consisting of all decoded elements */ struct osim_decoded_data { /*! file to which we belong */ const struct osim_file *file; /*! list of 'struct decoded_element' */ struct llist_head decoded_elements; }; enum osim_file_type { TYPE_NONE, TYPE_DF, /*!< Dedicated File */ TYPE_ADF, /*!< Application Dedicated File */ TYPE_EF, /*!< Entry File */ TYPE_EF_INT, /*!< Internal Entry File */ }; enum osim_ef_type { EF_TYPE_TRANSP, /*!< Transparent EF */ EF_TYPE_RECORD_FIXED, /*!< Fixed-Size Record EF */ EF_TYPE_RECORD_CYCLIC, /*!< Cyclic Record EF */ EF_TYPE_KEY, /*!< Key file as used in TETRA */ }; #define F_OPTIONAL 0x0001 #define SFI_NONE 0xFF struct osim_file_desc { struct llist_head list; /*!< local element in list */ struct llist_head child_list; /*!< list of children EF in DF */ struct osim_file_desc *parent; /*!< parent DF */ enum osim_file_type type; /*!< Type of the file (EF, DF, ...) */ enum osim_ef_type ef_type; /*!< Type of the EF, if type == TYPE_EF */ uint16_t fid; /*!< File Identifier */ uint8_t sfid; /*!< Short File IDentifier */ const uint8_t *df_name; uint8_t df_name_len; const char *short_name; /*!< Short Name (like EF.ICCID) */ const char *long_name; /*!< Long / description */ unsigned int flags; struct osim_file_ops ops; /*!< Operations (parse/encode */ struct { size_t min; /*!< Minimum size of the file (transparent) or record in cyclic / linear file */ size_t rec; /*!< Recommended size */ } size; }; /*! \brief A single instance of a file: Descriptor and contents */ struct osim_file { /*! Descriptor for the file */ const struct osim_file_desc *desc; /*! Encoded file contents */ struct msgb *encoded_data; /*! Parsed/Decoded file contents */ struct osim_decoded_data *decoded_data; }; /*! Convenience macros for defining EF */ #define EF(pfid, sfi, pns, pflags, pnl, ptype, smin, srec, pdec, penc) \ { \ .fid = pfid, \ .sfid = sfi, \ .type = TYPE_EF, \ .ef_type = ptype, \ .short_name = pns, \ .long_name = pnl, \ .flags = pflags, \ .ops = { .encode = penc, .parse = pdec }, \ .size = { .min = smin, .rec = srec}, \ } /*! Convenience macros for defining EF */ #define EF_TRANSP(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \ EF(fid, sfi, ns, flags, nl, EF_TYPE_TRANSP, \ smin, srec, dec, enc) /*! Convenience macros for defining EF */ #define EF_TRANSP_N(fid, sfi, ns, flags, smin, srec, nl) \ EF_TRANSP(fid, sfi, ns, flags, smin, srec, \ nl, &default_decode, NULL) /*! Convenience macros for defining EF */ #define EF_CYCLIC(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \ EF(fid, sfi, ns, flags, nl, EF_TYPE_RECORD_CYCLIC, \ smin, srec, dec, enc) /*! Convenience macros for defining EF */ #define EF_CYCLIC_N(fid, sfi, ns, flags, smin, srec, nl) \ EF_CYCLIC(fid, sfi, ns, flags, smin, srec, nl, \ &default_decode, NULL) /*! Convenience macros for defining EF */ #define EF_LIN_FIX(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \ EF(fid, sfi, ns, flags, nl, EF_TYPE_RECORD_FIXED, \ smin, srec, dec, enc) /*! Convenience macros for defining EF */ #define EF_LIN_FIX_N(fid, sfi, ns, flags, smin, srec, nl) \ EF_LIN_FIX(fid, sfi, ns, flags, smin, srec, nl, \ &default_decode, NULL) /*! Convenience macros for defining EF */ #define EF_KEY(fid, sfi, ns, flags, smin, srec, nl, dec, enc) \ EF(fid, sfi, ns, flags, nl, EF_TYPE_KEY, \ smin, srec, dec, enc) /*! Convenience macros for defining EF */ #define EF_KEY_N(fid, sfi, ns, flags, smin, srec, nl) \ EF_KEY(fid, sfi, ns, flags, smin, srec, nl, \ &default_decode, NULL) struct osim_file_desc * osim_file_find_name(struct osim_file_desc *parent, const char *name); /* STATUS WORDS */ enum osim_card_sw_type { SW_TYPE_NONE, SW_TYPE_STR, }; enum osim_card_sw_class { SW_CLS_NONE, SW_CLS_OK, SW_CLS_POSTP, SW_CLS_WARN, SW_CLS_ERROR, }; /*! A card status word (SW) */ struct osim_card_sw { /*! status word code (2 bytes) */ uint16_t code; /*! status word mask (2 bytes), to match range/prefix of SW */ uint16_t mask; enum osim_card_sw_type type; enum osim_card_sw_class class; union { /*! Human-readable meaning of SW */ const char *str; } u; }; #define OSIM_CARD_SW_LAST (const struct osim_card_sw) { \ .code = 0, .mask = 0, .type = SW_TYPE_NONE, \ .class = SW_CLS_NONE, .u.str = NULL \ } /*! \brief A card profile (e.g. SIM card */ struct osim_card_profile { const char *name; /*! Descriptor for the MF (root directory */ struct osim_file_desc *mf; /*! Array of pointers to status words */ const struct osim_card_sw **sws; }; const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp, uint16_t sw); enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp, uint16_t sw_in); struct osim_card_hdl; char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in); extern const struct tlv_definition ts102221_fcp_tlv_def; extern const struct value_string ts102221_fcp_vals[14]; /* 11.1.1.3 */ enum ts102221_fcp_tag { UICC_FCP_T_FCP = 0x62, UICC_FCP_T_FILE_SIZE = 0x80, UICC_FCP_T_TOT_F_SIZE = 0x81, UICC_FCP_T_FILE_DESC = 0x82, UICC_FCP_T_FILE_ID = 0x83, UICC_FCP_T_DF_NAME = 0x84, UICC_FCP_T_SFID = 0x88, UICC_FCP_T_LIFEC_STS = 0x8A, UICC_FCP_T_SEC_ATTR_REFEXP= 0x8B, UICC_FCP_T_SEC_ATTR_COMP= 0x8C, UICC_FCP_T_PROPRIETARY = 0xA5, UICC_FCP_T_SEC_ATTR_EXP = 0xAB, UICC_FCP_T_PIN_STS_DO = 0xC6, }; struct msgb *osim_new_apdumsg(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint16_t lc, uint16_t le); /* CARD READERS */ struct osim_reader_ops; enum osim_proto { OSIM_PROTO_T0 = 0, OSIM_PROTO_T1 = 1, }; enum osim_reader_driver { OSIM_READER_DRV_PCSC = 0, OSIM_READER_DRV_OPENCT = 1, OSIM_READER_DRV_SERIAL = 2, }; struct osim_reader_hdl { /*! \brief member in global list of readers */ struct llist_head list; const struct osim_reader_ops *ops; uint32_t proto_supported; void *priv; /*! \brief current card, if any */ struct osim_card_hdl *card; }; struct osim_card_hdl { /*! \brief member in global list of cards */ struct llist_head list; /*! \brief reader through which card is accessed */ struct osim_reader_hdl *reader; /*! \brief card profile */ struct osim_card_profile *prof; /*! \brief card protocol */ enum osim_proto proto; /*! \brief list of channels for this card */ struct llist_head channels; }; struct osim_chan_hdl { /*! \brief linked to card->channels */ struct llist_head list; /*! \brief card to which this channel belongs */ struct osim_card_hdl *card; const struct osim_file_desc *cwd; }; /* reader.c */ int osim_transceive_apdu(struct osim_chan_hdl *st, struct msgb *amsg); struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver drv, int idx, const char *name, void *ctx); struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto); #endif /* _OSMOCOM_SIM_H */ libosmocore-0.9.0/include/osmocom/vty/000077500000000000000000000000001261607044000177565ustar00rootroot00000000000000libosmocore-0.9.0/include/osmocom/vty/buffer.h000066400000000000000000000076651261607044000214160ustar00rootroot00000000000000/* * 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. */ #pragma once #include /* 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 *, unsigned 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); libosmocore-0.9.0/include/osmocom/vty/command.h000066400000000000000000000331701261607044000215510ustar00rootroot00000000000000/* * 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. */ #pragma once #include #include #include "vector.h" /*! \defgroup command VTY Command * @{ */ /*! \file command.h */ /*! \brief Host configuration variable */ struct host { /*! \brief Host name of this router. */ char *name; /*! \brief Password for vty interface. */ char *password; char *password_encrypt; /*! \brief Enable password */ char *enable; char *enable_encrypt; /*! \brief System wide terminal lines. */ int lines; /*! \brief Log filename. */ char *logfile; /*! \brief config file name of this host */ char *config; /*! \brief Flags for services */ int advanced; int encrypt; /*! \brief Banner configuration. */ const char *motd; char *motdfile; /*! \brief VTY application information */ const struct vty_app_info *app_info; }; /*! \brief There are some command levels which called from command node. */ enum node_type { AUTH_NODE, /*!< \brief Authentication mode of vty interface. */ VIEW_NODE, /*!< \brief View node. Default mode of vty interface. */ AUTH_ENABLE_NODE, /*!< \brief Authentication mode for change enable. */ ENABLE_NODE, /*!< \brief Enable node. */ CONFIG_NODE, /*!< \brief Config node. Default mode of config file. */ SERVICE_NODE, /*!< \brief Service node. */ DEBUG_NODE, /*!< \brief Debug node. */ CFG_LOG_NODE, /*!< \brief Configure the logging */ CFG_STATS_NODE, /*!< \brief Configure the statistics */ VTY_NODE, /*!< \brief Vty node. */ L_E1INP_NODE, /*!< \brief E1 line in libosmo-abis. */ L_IPA_NODE, /*!< \brief IPA proxying commands in libosmo-abis. */ L_NS_NODE, /*!< \brief NS node in libosmo-gb. */ L_BSSGP_NODE, /*!< \brief BSSGP node in libosmo-gb. */ /* * When adding new nodes to the libosmocore project, these nodes can be * used to avoid ABI changes for unrelated projects. */ RESERVED1_NODE, /*!< \brief Reserved for later extensions */ RESERVED2_NODE, /*!< \brief Reserved for later extensions */ RESERVED3_NODE, /*!< \brief Reserved for later extensions */ RESERVED4_NODE, /*!< \brief Reserved for later extensions */ _LAST_OSMOVTY_NODE }; #include "vty.h" /*! \brief Node which has some commands and prompt string and * configuration function pointer . */ struct cmd_node { /*! \brief Node index */ int node; /*! \brief Prompt character at vty interface. */ const char *prompt; /*! \brief Is this node's configuration goes to vtysh ? */ int vtysh; /*! \brief Node's configuration write function */ int (*func) (struct vty *); /*! \brief Vector of this node's command list. */ vector cmd_vector; }; enum { CMD_ATTR_DEPRECATED = 1, CMD_ATTR_HIDDEN, }; /*! \brief Structure of a command element */ struct cmd_element { const char *string; /*!< \brief Command specification by string. */ int (*func) (struct cmd_element *, struct vty *, int, const char *[]); const char *doc; /*!< \brief Documentation of this command. */ int daemon; /*!< \brief Daemon to which this command belong. */ vector strvec; /*!< \brief Pointing out each description vector. */ unsigned int cmdsize; /*!< \brief Command index count. */ char *config; /*!< \brief Configuration string */ vector subconfig; /*!< \brief Sub configuration string */ unsigned char attr; /*!< \brief Command attributes */ }; /*! \brief Command description structure. */ struct desc { const char *cmd; /*!< \brief Command string. */ const char *str; /*!< \brief Command's description. */ }; /*! \brief 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 256 /* 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[]) /*! \brief Macro for defining a VTY node and function * \param[in] funcname Name of the function implementing the node * \param[in] cmdname Name of the command node * \param[in] cmdstr String with syntax of node * \param[in] helpstr String with help message of node */ #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) /*! \brief Macro for defining a non-static (global) VTY node and function * \param[in] funcname Name of the function implementing the node * \param[in] cmdname Name of the command node * \param[in] cmdstr String with syntax of node * \param[in] helpstr String with help message of node */ #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(int node_type); void install_element(int node_type, struct cmd_element *); void install_element_ve(struct cmd_element *cmd); void sort_node(void); /* This is similar to install_default() but it also creates * 'exit' and 'end' commands. */ void vty_install_default(int node_type); /* 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; extern struct cmd_element config_end_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; /*! @} */ libosmocore-0.9.0/include/osmocom/vty/logging.h000066400000000000000000000004031261607044000215520ustar00rootroot00000000000000#pragma once #define LOGGING_STR "Configure log message to this terminal\n" #define FILTER_STR "Filter log messages\n" struct log_info; void logging_vty_add_cmds(const struct log_info *cat); struct vty; struct log_target *osmo_log_vty2tgt(struct vty *vty); libosmocore-0.9.0/include/osmocom/vty/misc.h000066400000000000000000000012761261607044000210700ustar00rootroot00000000000000#pragma once #include #include #include #include #define VTY_DO_LOWER 1 char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals, const char *prefix, const char *sep, const char *end, int do_lower); void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, struct rate_ctr_group *ctrg); void vty_out_stat_item_group(struct vty *vty, const char *prefix, struct osmo_stat_item_group *statg); void vty_out_statistics_full(struct vty *vty, const char *prefix); int osmo_vty_write_config_file(const char *filename); int osmo_vty_save_config_file(void); libosmocore-0.9.0/include/osmocom/vty/ports.h000066400000000000000000000010621261607044000212750ustar00rootroot00000000000000#pragma once /* TCP port numbers used for VTY interfaces in osmocom projects */ /* 4238 used by osmo-bts control interface */ #define OSMO_VTY_PORT_PCU 4240 /* also: osmo_pcap_client */ #define OSMO_VTY_PORT_BTS 4241 /* also: osmo_pcap_server */ #define OSMO_VTY_PORT_NITB_BSC 4242 #define OSMO_VTY_PORT_BSC_MGCP 4243 #define OSMO_VTY_PORT_BSC_NAT 4244 #define OSMO_VTY_PORT_SGSN 4245 #define OSMO_VTY_PORT_GBPROXY 4246 #define OSMO_VTY_PORT_BB 4247 /* 4249-4251 used by control interface */ #define OSMO_VTY_PORT_BTSMGR 4252 #define OSMO_VTY_PORT_GTPHUB 4253 libosmocore-0.9.0/include/osmocom/vty/stats.h000066400000000000000000000000561261607044000212660ustar00rootroot00000000000000#pragma once void osmo_stats_vty_add_cmds(); libosmocore-0.9.0/include/osmocom/vty/telnet_interface.h000066400000000000000000000032611261607044000234440ustar00rootroot00000000000000/* minimalistic telnet/network interface it might turn into a wire interface */ /* (C) 2009 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. * */ #pragma once #include #include #include /*! \defgroup telnet_interface Telnet Interface * @{ */ /*! \file telnet_interface.h */ /*! \brief A telnet connection */ struct telnet_connection { /*! \brief linked list header for internal management */ struct llist_head entry; /*! \brief private data pointer passed through */ void *priv; /*! \brief filedsecriptor (socket ) */ struct osmo_fd fd; /*! \brief VTY instance associated with telnet connection */ struct vty *vty; /*! \brief logging target associated with this telnet connection */ struct log_target *dbg; }; int telnet_init(void *tall_ctx, void *priv, int port); int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port); void telnet_exit(void); /*! @} */ libosmocore-0.9.0/include/osmocom/vty/vector.h000066400000000000000000000040251261607044000214320ustar00rootroot00000000000000/* * 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. */ #pragma once /* 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; libosmocore-0.9.0/include/osmocom/vty/vty.h000066400000000000000000000116271261607044000207600ustar00rootroot00000000000000#pragma once #include #include /*! \defgroup vty VTY (Virtual TTY) interface * @{ */ /*! \file vty.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 /*! \brief 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 */ }; enum vty_type { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV }; /*! Internal representation of a single VTY */ struct vty { /*! \brief underlying file (if any) */ FILE *file; /*! \brief private data, specified by creator */ void *priv; /*! \brief File descripter of this vty. */ int fd; /*! \brief Is this vty connect to file or not */ enum vty_type type; /*! \brief Node status of this vty */ int node; /*! \brief Failure count */ int fail; /*! \brief Output buffer. */ struct buffer *obuf; /*! \brief Command input buffer */ char *buf; /*! \brief Command cursor point */ int cp; /*! \brief Command length */ int length; /*! \brief Command max length. */ int max; /*! \brief Histry of command */ char *hist[VTY_MAXHIST]; /*! \brief History lookup current point */ int hp; /*! \brief History insert end point */ int hindex; /*! \brief For current referencing point of interface, route-map, access-list etc... */ void *index; /*! \brief For multiple level index treatment such as key chain and key. */ void *index_sub; /*! \brief For escape character. */ unsigned char escape; /*! \brief Current vty status. */ enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status; /*! \brief IAC handling * * 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; /*! \brief 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): * <16-bit width> <16-bit height> */ #define TELNET_NAWS_SB_LEN 5 /*! \brief sub-negotiation buffer */ unsigned char sb_buf[TELNET_NAWS_SB_LEN]; /*! \brief How many subnegotiation characters have we received? * * We just drop those that do not fit in the buffer. */ size_t sb_len; /*! \brief Window width */ int width; /*! \brief Widnow height */ int height; /*! \brief Configure lines. */ int lines; int monitor; /*! \brief 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 const char *vty_newline(struct vty *vty) { return VTY_NEWLINE; } /*! Information an application registers with the VTY */ struct vty_app_info { /*! \brief name of the application */ const char *name; /*! \brief version string of the application */ const char *version; /*! \brief copyright string of the application */ const char *copyright; /*! \brief \ref talloc context */ void *tall_ctx; /*! \brief call-back for returning to parent n ode */ int (*go_parent_cb)(struct vty *vty); /*! \brief call-back to determine if node is config node */ int (*is_config_node)(struct vty *vty, int node); /*! \brief Check if the config is consistent before write */ int (*config_is_consistent)(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 *vty_current_index(struct vty *); int vty_current_node(struct vty *vty); int vty_go_parent(struct vty *vty); extern void *tall_vty_ctx; extern struct cmd_element cfg_description_cmd; extern struct cmd_element cfg_no_description_cmd; /** * signal handling */ enum signal_vty { S_VTY_EVENT, }; struct vty_signal_data { enum event event; int sock; struct vty *vty; }; /*! @} */ libosmocore-0.9.0/libosmocodec.pc.in000066400000000000000000000003531261607044000174310ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom Codec related utilities Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmocodec Cflags: -I${includedir}/ libosmocore-0.9.0/libosmocore.pc.in000066400000000000000000000003271261607044000173050ustar00rootroot00000000000000prefix=@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}/ libosmocore-0.9.0/libosmoctrl.pc.in000066400000000000000000000003441261607044000173200ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom Control Interface Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmoctrl Cflags: -I${includedir}/ libosmocore-0.9.0/libosmogb.pc.in000066400000000000000000000004221261607044000167410ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom GPRS Gb Library Description: Osmocom GPRS Gb Interface (NS/BSSGP) Library Version: @VERSION@ Libs: -L${libdir} -losmogb -losmovty Cflags: -I${includedir}/ -fno-strict-aliasing libosmocore-0.9.0/libosmogsm.pc.in000066400000000000000000000003311261607044000171360ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom GSM Core Library Description: GSM Core Library Version: @VERSION@ Libs: -L${libdir} -losmogsm Cflags: -I${includedir}/ libosmocore-0.9.0/libosmosim.pc.in000066400000000000000000000003541261607044000171450ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom SIM card related utilities Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmosim Cflags: -I${includedir}/ libosmocore-0.9.0/libosmovty.pc.in000066400000000000000000000003371261607044000172000ustar00rootroot00000000000000prefix=@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}/ libosmocore-0.9.0/m4/000077500000000000000000000000001261607044000143555ustar00rootroot00000000000000libosmocore-0.9.0/m4/DUMMY000066400000000000000000000000251261607044000151700ustar00rootroot00000000000000Dummply placeholder. libosmocore-0.9.0/src/000077500000000000000000000000001261607044000146245ustar00rootroot00000000000000libosmocore-0.9.0/src/Makefile.am000066400000000000000000000023711261607044000166630ustar00rootroot00000000000000# 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=6:0:0 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall lib_LTLIBRARIES = libosmocore.la libosmocore_la_LIBADD = $(BACKTRACE_LIB) libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c bits.c \ bitvec.c statistics.c \ write_queue.c utils.c socket.c \ logging.c logging_syslog.c rate_ctr.c \ gsmtap_util.c crc16.c panic.c backtrace.c \ conv.c application.c rbtree.c strrb.c \ loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \ macaddr.c stat_item.c stats.c BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c if ENABLE_PLUGIN libosmocore_la_SOURCES += plugin.c libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) $(LIBRARY_DL) -no-undefined else libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined endif if ENABLE_TALLOC libosmocore_la_SOURCES += talloc.c else libosmocore_la_LIBADD += -ltalloc endif if ENABLE_MSGFILE libosmocore_la_SOURCES += msgfile.c endif if ENABLE_SERIAL libosmocore_la_SOURCES += serial.c endif crc%gen.c: crcXXgen.c.tpl $(AM_V_GEN)sed -e's/XX/$*/g' $< > $@ libosmocore-0.9.0/src/application.c000066400000000000000000000116061261607044000172770ustar00rootroot00000000000000/* Utility functions to setup applications */ /* * (C) 2010 by Harald Welte * (C) 2011 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /*! \file application.c * \brief Routines for helping with the osmocom application setup. */ /*! \mainpage libosmocore Documentation * \section sec_intro Introduction * This library is a collection of common code used in various * sub-projects inside the Osmocom family of projects. It includes a * logging framework, select() loop abstraction, timers with callbacks, * bit vectors, bit packing/unpacking, convolutional decoding, GSMTAP, a * generic plugin interface, statistics counters, memory allocator, * socket abstraction, message buffers, etc. * \n\n * Please note that C language projects inside Osmocom are typically * single-threaded event-loop state machine designs. As such, * routines in libosmocore are not thread-safe. If you must use them in * a multi-threaded context, you have to add your own locking. * * \section sec_copyright Copyright and License * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n * All rights reserved. \n\n * The source code of libosmocore is licensed 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.\n * See or COPYING included in the source * code package istelf.\n * The information detailed here is provided AS IS with NO WARRANTY OF * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. * \n\n * * \section sec_contact Contact and Support * Community-based support is available at the OpenBSC mailing list * \n * Commercial support options available upon request from * */ #include #include #include #include #include #include #include #include struct log_target *osmo_stderr_target; static void sighup_hdlr(int signal) { log_targets_reopen(); } /*! \brief Ignore \ref SIGPIPE, \ref SIGALRM, \ref SIGHUP and \ref SIGIO */ void osmo_init_ignore_signals(void) { /* Signals that by default would terminate */ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif signal(SIGALRM, SIG_IGN); #ifdef SIGHUP signal(SIGHUP, &sighup_hdlr); #endif #ifdef SIGIO signal(SIGIO, SIG_IGN); #endif } /*! \brief Initialize the osmocom logging framework * \param[in] log_info Array of available logging sub-systems * \returns 0 on success, -1 in case of error * * This function initializes the osmocom logging systems. It also * creates the default (stderr) logging target. */ int osmo_init_logging(const struct log_info *log_info) { log_init(log_info, NULL); osmo_stderr_target = log_target_create_stderr(); if (!osmo_stderr_target) return -1; log_add_target(osmo_stderr_target); log_set_all_filter(osmo_stderr_target, 1); return 0; } /*! \brief Turn the current process into a background daemon * * This function will fork the process, exit the parent and set umask, * create a new session, close stdin/stdout/stderr and chdir to /tmp */ int osmo_daemonize(void) { int rc; pid_t pid, sid; /* Check if parent PID == init, in which case we are already a daemon */ if (getppid() == 1) return -EEXIST; /* Fork from the parent process */ pid = fork(); if (pid < 0) { /* some error happened */ return pid; } if (pid > 0) { /* if we have received a positive PID, then we are the parent * and can exit */ exit(0); } /* FIXME: do we really want this? */ umask(0); /* Create a new session and set process group ID */ sid = setsid(); if (sid < 0) return sid; /* Change to the /tmp directory, which prevents the CWD from being locked * and unable to remove it */ rc = chdir("/tmp"); if (rc < 0) return rc; /* Redirect stdio to /dev/null */ /* since C89/C99 says stderr is a macro, we can safely do this! */ #ifdef stderr freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); #endif return 0; } libosmocore-0.9.0/src/backtrace.c000066400000000000000000000046201261607044000167110ustar00rootroot00000000000000/* * (C) 2009 by Holger Hans Peter Freyther * (C) 2012 by Harald Welte * * 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. * */ /*! \file backtrace.c * \brief Routines realted to generating call back traces */ #include #include #include #include #include "config.h" #ifdef HAVE_EXECINFO_H #include static void _osmo_backtrace(int use_printf, int subsys, int level) { int i, nptrs; void *buffer[100]; char **strings; nptrs = backtrace(buffer, ARRAY_SIZE(buffer)); if (use_printf) printf("backtrace() returned %d addresses\n", nptrs); else LOGP(subsys, level, "backtrace() returned %d addresses\n", nptrs); strings = backtrace_symbols(buffer, nptrs); if (!strings) return; for (i = 1; i < nptrs; i++) { if (use_printf) printf("%s\n", strings[i]); else LOGP(subsys, level, "\t%s\n", strings[i]); } free(strings); } /*! \brief Generate and print a call back-trace * * This function will generate a function call back-trace of the * current process and print it to stdout. */ void osmo_generate_backtrace(void) { _osmo_backtrace(1, 0, 0); } /*! \brief Generate and log a call back-trace * * This function will generate a function call back-trace of the * current process and log it to the specified subsystem and * level using the libosmocore logging subsystem */ void osmo_log_backtrace(int subsys, int level) { _osmo_backtrace(0, subsys, level); } #else void osmo_generate_backtrace(void) { printf("This platform has no backtrace function\n"); } void osmo_log_backtrace(int subsys, int level) { LOGP(subsys, level, "This platform has no backtrace function\n"); } #endif libosmocore-0.9.0/src/bits.c000066400000000000000000000147521261607044000157420ustar00rootroot00000000000000/* * (C) 2011 by Harald Welte * (C) 2011 by Sylvain Munaut * * 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 #include /*! \addtogroup bits * @{ */ /*! \file bits.c * \brief Osmocom bit level support code */ /*! \brief convert unpacked bits to packed bits, return length in bytes * \param[out] out output buffer of packed bits * \param[in] in input buffer of unpacked bits * \param[in] num_bits number of bits */ int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits) { unsigned int i; uint8_t curbyte = 0; pbit_t *outptr = out; for (i = 0; i < num_bits; i++) { uint8_t bitnum = 7 - (i % 8); curbyte |= (in[i] << bitnum); if(i % 8 == 7){ *outptr++ = curbyte; curbyte = 0; } } /* we have a non-modulo-8 bitcount */ if (i % 8) *outptr++ = curbyte; return outptr - out; } /*! \brief convert packed bits to unpacked bits, return length in bytes * \param[out] out output buffer of unpacked bits * \param[in] in input buffer of packed bits * \param[in] num_bits number of bits */ int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits) { unsigned int i; ubit_t *cur = out; ubit_t *limit = out + num_bits; for (i = 0; i < (num_bits/8)+1; i++) { pbit_t byte = in[i]; *cur++ = (byte >> 7) & 1; if (cur >= limit) break; *cur++ = (byte >> 6) & 1; if (cur >= limit) break; *cur++ = (byte >> 5) & 1; if (cur >= limit) break; *cur++ = (byte >> 4) & 1; if (cur >= limit) break; *cur++ = (byte >> 3) & 1; if (cur >= limit) break; *cur++ = (byte >> 2) & 1; if (cur >= limit) break; *cur++ = (byte >> 1) & 1; if (cur >= limit) break; *cur++ = (byte >> 0) & 1; if (cur >= limit) break; } return cur - out; } /*! \brief convert unpacked bits to packed bits (extended options) * \param[out] out output buffer of packed bits * \param[in] out_ofs offset into output buffer * \param[in] in input buffer of unpacked bits * \param[in] in_ofs offset into input buffer * \param[in] num_bits number of bits * \param[in] lsb_mode Encode bits in LSB orde instead of MSB * \returns length in bytes (max written offset of output buffer + 1) */ int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs, const ubit_t *in, unsigned int in_ofs, unsigned int num_bits, int lsb_mode) { int i, op, bn; for (i=0; i>3] |= 1 << bn; else out[op>>3] &= ~(1 << bn); } return ((out_ofs + num_bits - 1) >> 3) + 1; } /*! \brief convert packed bits to unpacked bits (extended options) * \param[out] out output buffer of unpacked bits * \param[in] out_ofs offset into output buffer * \param[in] in input buffer of packed bits * \param[in] in_ofs offset into input buffer * \param[in] num_bits number of bits * \param[in] lsb_mode Encode bits in LSB orde instead of MSB * \returns length in bytes (max written offset of output buffer + 1) */ int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs, const pbit_t *in, unsigned int in_ofs, unsigned int num_bits, int lsb_mode) { int i, ip, bn; for (i=0; i>3] & (1<> 1; if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8; if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16; return x; } /*! \brief reverse the bit-order in each byte of a dword * \param[in] x 32bit input value * \returns 32bit value where bits of each byte have been reversed * * See Chapter 7 "Hackers Delight" */ uint32_t osmo_revbytebits_32(uint32_t x) { x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1; x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2; x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4; return x; } /*! \brief reverse the bit order in a byte * \param[in] x 8bit input value * \returns 8bit value where bits order has been reversed * * See Chapter 7 "Hackers Delight" */ uint32_t osmo_revbytebits_8(uint8_t x) { x = (x & 0x55) << 1 | (x & 0xAA) >> 1; x = (x & 0x33) << 2 | (x & 0xCC) >> 2; x = (x & 0x0F) << 4 | (x & 0xF0) >> 4; return x; } /*! \brief reverse bit-order of each byte in a buffer * \param[in] buf buffer containing bytes to be bit-reversed * \param[in] len length of buffer in bytes * * This function reverses the bits in each byte of the buffer */ void osmo_revbytebits_buf(uint8_t *buf, int len) { unsigned int i; unsigned int unaligned_cnt; int len_remain = len; unaligned_cnt = ((unsigned long)buf & 3); for (i = 0; i < unaligned_cnt; i++) { buf[i] = osmo_revbytebits_8(buf[i]); len_remain--; if (len_remain <= 0) return; } for (i = unaligned_cnt; i + 3 < len; i += 4) { uint32_t *cur = (uint32_t *) (buf + i); *cur = osmo_revbytebits_32(*cur); len_remain -= 4; } for (i = len - len_remain; i < len; i++) { buf[i] = osmo_revbytebits_8(buf[i]); len_remain--; } } /*! @} */ libosmocore-0.9.0/src/bitvec.c000066400000000000000000000134571261607044000162560ustar00rootroot00000000000000/* bit vector utility routines */ /* (C) 2009 by Harald Welte * * 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. * */ /*! \addtogroup bitvec * @{ */ /*! \file bitvec.c * \brief Osmocom bit vector abstraction */ #include #include #include #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; } /*! \brief check if the bit is 0 or 1 for a given position inside a bitvec * \param[in] bv the bit vector on which to check * \param[in] bitnr the bit number inside the bit vector to check * \returns */ 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; } /*! \brief check if the bit is L or H for a given position inside a bitvec * \param[in] bv the bit vector on which to check * \param[in] bitnr the bit number inside the bit vector to check */ enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, unsigned int bitnr) { unsigned int bytenum = bytenum_from_bitnum(bitnr); unsigned int bitnum = 7 - (bitnr % 8); uint8_t bitval; if (bytenum >= bv->data_len) return -EINVAL; bitval = bitval2mask(H, bitnum); if ((bv->data[bytenum] & (1 << bitnum)) == bitval) return H; return L; } /*! \brief get the Nth set bit inside the bit vector * \param[in] bv the bit vector to use * \param[in] n the bit number to get * \returns the bit number (offset) of the Nth set bit in \a bv */ 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; } /*! \brief set a bit at given position in a bit vector * \param[in] bv bit vector on which to operate * \param[in] bitnr number of bit to be set * \param[in] bit value to which the bit is to be set */ 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; } /*! \brief set the next bit inside a bitvec * \param[in] bv bit vector to be used * \param[in] bit value of the bit to be set */ 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; } /*! \brief 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; } /*! \brief set multiple bits (based on array of bitvals) at current pos * \param[in] bv bit vector * \param[in] bits array of \ref bit_value * \param[in] count number of bits to set */ 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; } /*! \brief 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; } /*! \brief 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; } /*! \brief 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; } /*! \brief find first bit set in bit vector */ int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val) { unsigned int i; for (i = n; i < bv->data_len*8; i++) { if (bitvec_get_bit_pos(bv, i) == val) return i; } return -1; } /*! @} */ libosmocore-0.9.0/src/codec/000077500000000000000000000000001261607044000157015ustar00rootroot00000000000000libosmocore-0.9.0/src/codec/Makefile.am000066400000000000000000000006431261607044000177400ustar00rootroot00000000000000# 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 AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = -Wall lib_LTLIBRARIES = libosmocodec.la libosmocodec_la_SOURCES = gsm610.c gsm620.c gsm660.c gsm690.c libosmocodec_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined libosmocore-0.9.0/src/codec/gsm610.c000066400000000000000000000143361261607044000170710ustar00rootroot00000000000000/* GSM 06.10 - GSM FR codec */ /* * (C) 2010 Sylvain Munaut * * 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 /* GSM FR - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 2. * It's also GSM 06.10 Table A.2.1a * * It converts between serial parameter output by the encoder and the * order needed before channel encoding. */ const uint16_t gsm610_bitorder[260] = { 0, /* LARc0:5 */ 47, /* Xmaxc0:5 */ 103, /* Xmaxc1:5 */ 159, /* Xmaxc2:5 */ 215, /* Xmaxc3:5 */ 1, /* LARc0:4 */ 6, /* LARc1:5 */ 12, /* LARc2:4 */ 2, /* LARc0:3 */ 7, /* LARc1:4 */ 13, /* LARc2:3 */ 17, /* LARc3:4 */ 36, /* Nc0:6 */ 92, /* Nc1:6 */ 148, /* Nc2:6 */ 204, /* Nc3:6 */ 48, /* Xmaxc0:4 */ 104, /* Xmaxc1:4 */ 160, /* Xmaxc2:4 */ 216, /* Xmaxc3:4 */ 8, /* LARc1:3 */ 22, /* LARc4:3 */ 26, /* LARc5:3 */ 37, /* Nc0:5 */ 93, /* Nc1:5 */ 149, /* Nc2:5 */ 205, /* Nc3:5 */ 38, /* Nc0:4 */ 94, /* Nc1:4 */ 150, /* Nc2:4 */ 206, /* Nc3:4 */ 39, /* Nc0:3 */ 95, /* Nc1:3 */ 151, /* Nc2:3 */ 207, /* Nc3:3 */ 40, /* Nc0:2 */ 96, /* Nc1:2 */ 152, /* Nc2:2 */ 208, /* Nc3:2 */ 49, /* Xmaxc0:3 */ 105, /* Xmaxc1:3 */ 161, /* Xmaxc2:3 */ 217, /* Xmaxc3:3 */ 3, /* LARc0:2 */ 18, /* LARc3:3 */ 30, /* LARc6:2 */ 41, /* Nc0:1 */ 97, /* Nc1:1 */ 153, /* Nc2:1 */ 209, /* Nc3:1 */ 23, /* LARc4:2 */ 27, /* LARc5:2 */ 43, /* bc0:1 */ 99, /* bc1:1 */ 155, /* bc2:1 */ 211, /* bc3:1 */ 42, /* Nc0:0 */ 98, /* Nc1:0 */ 154, /* Nc2:0 */ 210, /* Nc3:0 */ 45, /* Mc0:1 */ 101, /* Mc1:1 */ 157, /* Mc2:1 */ 213, /* Mc3:1 */ 4, /* LARc0:1 */ 9, /* LARc1:2 */ 14, /* LARc2:2 */ 33, /* LARc7:2 */ 19, /* LARc3:2 */ 24, /* LARc4:1 */ 31, /* LARc6:1 */ 44, /* bc0:0 */ 100, /* bc1:0 */ 156, /* bc2:0 */ 212, /* bc3:0 */ 50, /* Xmaxc0:2 */ 106, /* Xmaxc1:2 */ 162, /* Xmaxc2:2 */ 218, /* Xmaxc3:2 */ 53, /* xmc0_0:2 */ 56, /* xmc0_1:2 */ 59, /* xmc0_2:2 */ 62, /* xmc0_3:2 */ 65, /* xmc0_4:2 */ 68, /* xmc0_5:2 */ 71, /* xmc0_6:2 */ 74, /* xmc0_7:2 */ 77, /* xmc0_8:2 */ 80, /* xmc0_9:2 */ 83, /* xmc0_10:2 */ 86, /* xmc0_11:2 */ 89, /* xmc0_12:2 */ 109, /* xmc1_0:2 */ 112, /* xmc1_1:2 */ 115, /* xmc1_2:2 */ 118, /* xmc1_3:2 */ 121, /* xmc1_4:2 */ 124, /* xmc1_5:2 */ 127, /* xmc1_6:2 */ 130, /* xmc1_7:2 */ 133, /* xmc1_8:2 */ 136, /* xmc1_9:2 */ 139, /* xmc1_10:2 */ 142, /* xmc1_11:2 */ 145, /* xmc1_12:2 */ 165, /* xmc2_0:2 */ 168, /* xmc2_1:2 */ 171, /* xmc2_2:2 */ 174, /* xmc2_3:2 */ 177, /* xmc2_4:2 */ 180, /* xmc2_5:2 */ 183, /* xmc2_6:2 */ 186, /* xmc2_7:2 */ 189, /* xmc2_8:2 */ 192, /* xmc2_9:2 */ 195, /* xmc2_10:2 */ 198, /* xmc2_11:2 */ 201, /* xmc2_12:2 */ 221, /* xmc3_0:2 */ 224, /* xmc3_1:2 */ 227, /* xmc3_2:2 */ 230, /* xmc3_3:2 */ 233, /* xmc3_4:2 */ 236, /* xmc3_5:2 */ 239, /* xmc3_6:2 */ 242, /* xmc3_7:2 */ 245, /* xmc3_8:2 */ 248, /* xmc3_9:2 */ 251, /* xmc3_10:2 */ 254, /* xmc3_11:2 */ 257, /* xmc3_12:2 */ 46, /* Mc0:0 */ 102, /* Mc1:0 */ 158, /* Mc2:0 */ 214, /* Mc3:0 */ 51, /* Xmaxc0:1 */ 107, /* Xmaxc1:1 */ 163, /* Xmaxc2:1 */ 219, /* Xmaxc3:1 */ 54, /* xmc0_0:1 */ 57, /* xmc0_1:1 */ 60, /* xmc0_2:1 */ 63, /* xmc0_3:1 */ 66, /* xmc0_4:1 */ 69, /* xmc0_5:1 */ 72, /* xmc0_6:1 */ 75, /* xmc0_7:1 */ 78, /* xmc0_8:1 */ 81, /* xmc0_9:1 */ 84, /* xmc0_10:1 */ 87, /* xmc0_11:1 */ 90, /* xmc0_12:1 */ 110, /* xmc1_0:1 */ 113, /* xmc1_1:1 */ 116, /* xmc1_2:1 */ 119, /* xmc1_3:1 */ 122, /* xmc1_4:1 */ 125, /* xmc1_5:1 */ 128, /* xmc1_6:1 */ 131, /* xmc1_7:1 */ 134, /* xmc1_8:1 */ 137, /* xmc1_9:1 */ 140, /* xmc1_10:1 */ 143, /* xmc1_11:1 */ 146, /* xmc1_12:1 */ 166, /* xmc2_0:1 */ 169, /* xmc2_1:1 */ 172, /* xmc2_2:1 */ 175, /* xmc2_3:1 */ 178, /* xmc2_4:1 */ 181, /* xmc2_5:1 */ 184, /* xmc2_6:1 */ 187, /* xmc2_7:1 */ 190, /* xmc2_8:1 */ 193, /* xmc2_9:1 */ 196, /* xmc2_10:1 */ 199, /* xmc2_11:1 */ 202, /* xmc2_12:1 */ 222, /* xmc3_0:1 */ 225, /* xmc3_1:1 */ 228, /* xmc3_2:1 */ 231, /* xmc3_3:1 */ 234, /* xmc3_4:1 */ 237, /* xmc3_5:1 */ 240, /* xmc3_6:1 */ 243, /* xmc3_7:1 */ 246, /* xmc3_8:1 */ 249, /* xmc3_9:1 */ 252, /* xmc3_10:1 */ 255, /* xmc3_11:1 */ 258, /* xmc3_12:1 */ 5, /* LARc0:0 */ 10, /* LARc1:1 */ 15, /* LARc2:1 */ 28, /* LARc5:1 */ 32, /* LARc6:0 */ 34, /* LARc7:1 */ 35, /* LARc7:0 */ 16, /* LARc2:0 */ 20, /* LARc3:1 */ 21, /* LARc3:0 */ 25, /* LARc4:0 */ 52, /* Xmaxc0:0 */ 108, /* Xmaxc1:0 */ 164, /* Xmaxc2:0 */ 220, /* Xmaxc3:0 */ 55, /* xmc0_0:0 */ 58, /* xmc0_1:0 */ 61, /* xmc0_2:0 */ 64, /* xmc0_3:0 */ 67, /* xmc0_4:0 */ 70, /* xmc0_5:0 */ 73, /* xmc0_6:0 */ 76, /* xmc0_7:0 */ 79, /* xmc0_8:0 */ 82, /* xmc0_9:0 */ 85, /* xmc0_10:0 */ 88, /* xmc0_11:0 */ 91, /* xmc0_12:0 */ 111, /* xmc1_0:0 */ 114, /* xmc1_1:0 */ 117, /* xmc1_2:0 */ 120, /* xmc1_3:0 */ 123, /* xmc1_4:0 */ 126, /* xmc1_5:0 */ 129, /* xmc1_6:0 */ 132, /* xmc1_7:0 */ 135, /* xmc1_8:0 */ 138, /* xmc1_9:0 */ 141, /* xmc1_10:0 */ 144, /* xmc1_11:0 */ 147, /* xmc1_12:0 */ 167, /* xmc2_0:0 */ 170, /* xmc2_1:0 */ 173, /* xmc2_2:0 */ 176, /* xmc2_3:0 */ 179, /* xmc2_4:0 */ 182, /* xmc2_5:0 */ 185, /* xmc2_6:0 */ 188, /* xmc2_7:0 */ 191, /* xmc2_8:0 */ 194, /* xmc2_9:0 */ 197, /* xmc2_10:0 */ 200, /* xmc2_11:0 */ 203, /* xmc2_12:0 */ 223, /* xmc3_0:0 */ 226, /* xmc3_1:0 */ 229, /* xmc3_2:0 */ 232, /* xmc3_3:0 */ 235, /* xmc3_4:0 */ 238, /* xmc3_5:0 */ 241, /* xmc3_6:0 */ 244, /* xmc3_7:0 */ 247, /* xmc3_8:0 */ 250, /* xmc3_9:0 */ 253, /* xmc3_10:0 */ 256, /* xmc3_11:0 */ 259, /* xmc3_12:0 */ 11, /* LARc1:0 */ 29, /* LARc5:0 */ }; libosmocore-0.9.0/src/codec/gsm620.c000066400000000000000000000134711261607044000170710ustar00rootroot00000000000000/* GSM 06.20 - GSM HR codec */ /* * (C) 2010 Sylvain Munaut * * 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 /* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */ /* This array encode mapping between GSM 05.03 Table 3a (bits * ordering before channel coding on TCH) and GSM 06.20 Table B.1 * (bit ordering on A-bis */ const uint16_t gsm620_unvoiced_bitorder[112] = { 3, /* R0:1 */ 25, /* LPC 3:7 */ 52, /* GSP 0-1:2 */ 71, /* GSP 0-2:2 */ 90, /* GSP 0-3:2 */ 109, /* GSP 0-4:2 */ 15, /* LPC 1:0 */ 19, /* LPC 2:5 */ 20, /* LPC 2:4 */ 21, /* LPC 2:3 */ 22, /* LPC 2:2 */ 23, /* LPC 2:1 */ 26, /* LPC 3:6 */ 27, /* LPC 3:5 */ 28, /* LPC 3:4 */ 29, /* LPC 3:3 */ 30, /* LPC 3:2 */ 31, /* LPC 3:1 */ 61, /* Code 1-2:0 */ 62, /* Code 2-2:6 */ 63, /* Code 2-2:5 */ 64, /* Code 2-2:4 */ 65, /* Code 2-2:3 */ 66, /* Code 2-2:2 */ 67, /* Code 2-2:1 */ 68, /* Code 2-2:0 */ 74, /* Code 1-3:6 */ 75, /* Code 1-3:5 */ 76, /* Code 1-3:4 */ 77, /* Code 1-3:3 */ 78, /* Code 1-3:2 */ 79, /* Code 1-3:1 */ 80, /* Code 1-3:0 */ 81, /* Code 2-3:6 */ 82, /* Code 2-3:5 */ 83, /* Code 2-3:4 */ 84, /* Code 2-3:3 */ 32, /* LPC 3:0 */ 4, /* R0:0 */ 33, /* INT-LPC:0 */ 60, /* Code 1-2:1 */ 59, /* Code 1-2:2 */ 58, /* Code 1-2:3 */ 57, /* Code 1-2:4 */ 56, /* Code 1-2:5 */ 55, /* Code 1-2:6 */ 49, /* Code 2-1:0 */ 48, /* Code 2-1:1 */ 47, /* Code 2-1:2 */ 46, /* Code 2-1:3 */ 45, /* Code 2-1:4 */ 44, /* Code 2-1:5 */ 43, /* Code 2-1:6 */ 42, /* Code 1-1:0 */ 41, /* Code 1-1:1 */ 40, /* Code 1-1:2 */ 39, /* Code 1-1:3 */ 38, /* Code 1-1:4 */ 37, /* Code 1-1:5 */ 36, /* Code 1-1:6 */ 111, /* GSP 0-4:0 */ 92, /* GSP 0-3:0 */ 73, /* GSP 0-2:0 */ 54, /* GSP 0-1:0 */ 24, /* LPC 2:0 */ 110, /* GSP 0-4:1 */ 91, /* GSP 0-3:1 */ 72, /* GSP 0-2:1 */ 53, /* GSP 0-1:1 */ 14, /* LPC 1:1 */ 13, /* LPC 1:2 */ 12, /* LPC 1:3 */ 11, /* LPC 1:4 */ 10, /* LPC 1:5 */ 108, /* GSP 0-4:3 */ 89, /* GSP 0-3:3 */ 70, /* GSP 0-2:3 */ 51, /* GSP 0-1:3 */ 16, /* LPC 2:8 */ 17, /* LPC 2:7 */ 18, /* LPC 2:6 */ 107, /* GSP 0-4:4 */ 88, /* GSP 0-3:4 */ 69, /* GSP 0-2:4 */ 50, /* GSP 0-1:4 */ 9, /* LPC 1:6 */ 8, /* LPC 1:7 */ 7, /* LPC 1:8 */ 6, /* LPC 1:9 */ 2, /* R0:2 */ 5, /* LPC 1:10 */ 1, /* R0:3 */ 0, /* R0:4 */ 35, /* Mode:0 */ 34, /* Mode:1 */ 106, /* Code 2-4:0 */ 105, /* Code 2-4:1 */ 104, /* Code 2-4:2 */ 103, /* Code 2-4:3 */ 102, /* Code 2-4:4 */ 101, /* Code 2-4:5 */ 100, /* Code 2-4:6 */ 99, /* Code 1-4:0 */ 98, /* Code 1-4:1 */ 97, /* Code 1-4:2 */ 96, /* Code 1-4:3 */ 95, /* Code 1-4:4 */ 94, /* Code 1-4:5 */ 93, /* Code 1-4:6 */ 87, /* Code 2-3:0 */ 86, /* Code 2-3:1 */ 85, /* Code 2-3:2 */ }; /* GSM HR voiced (mode=1,2,3) frames - subjective importance bit ordering */ /* This array encode mapping between GSM 05.03 Table 3b (bits * ordering before channel coding on TCH) and GSM 06.20 Table B.2 * (bit ordering on A-bis */ const uint16_t gsm620_voiced_bitorder[112] = { 13, /* LPC 1:2 */ 14, /* LPC 1:1 */ 18, /* LPC 2:6 */ 19, /* LPC 2:5 */ 20, /* LPC 2:4 */ 53, /* GSP 0-1:4 */ 71, /* GSP 0-2:4 */ 89, /* GSP 0-3:4 */ 107, /* GSP 0-4:4 */ 54, /* GSP 0-1:3 */ 72, /* GSP 0-2:3 */ 90, /* GSP 0-3:3 */ 108, /* GSP 0-4:3 */ 55, /* GSP 0-1:2 */ 73, /* GSP 0-2:2 */ 91, /* GSP 0-3:2 */ 109, /* GSP 0-4:2 */ 44, /* Code 1:8 */ 45, /* Code 1:7 */ 46, /* Code 1:6 */ 47, /* Code 1:5 */ 48, /* Code 1:4 */ 49, /* Code 1:3 */ 50, /* Code 1:2 */ 51, /* Code 1:1 */ 52, /* Code 1:0 */ 62, /* Code 2:8 */ 63, /* Code 2:7 */ 64, /* Code 2:6 */ 65, /* Code 2:5 */ 68, /* Code 2:2 */ 69, /* Code 2:1 */ 70, /* Code 2:0 */ 80, /* Code 3:8 */ 66, /* Code 2:4 */ 67, /* Code 2:3 */ 56, /* GSP 0-1:1 */ 74, /* GSP 0-2:1 */ 92, /* GSP 0-3:1 */ 110, /* GSP 0-4:1 */ 57, /* GSP 0-1:0 */ 75, /* GSP 0-2:0 */ 93, /* GSP 0-3:0 */ 111, /* GSP 0-4:0 */ 33, /* INT-LPC:0 */ 24, /* LPC 2:0 */ 32, /* LPC 3:0 */ 97, /* LAG 4:0 */ 31, /* LPC 3:1 */ 23, /* LPC 2:1 */ 96, /* LAG 4:1 */ 79, /* LAG 3:0 */ 61, /* LAG 2:0 */ 43, /* LAG 1:0 */ 95, /* LAG 4:2 */ 78, /* LAG 3:1 */ 60, /* LAG 2:1 */ 42, /* LAG 1:1 */ 30, /* LPC 3:2 */ 29, /* LPC 3:3 */ 28, /* LPC 3:4 */ 22, /* LPC 2:2 */ 27, /* LPC 3:5 */ 26, /* LPC 3:6 */ 21, /* LPC 2:3 */ 4, /* R0:0 */ 25, /* LPC 3:7 */ 15, /* LPC 1:0 */ 94, /* LAG 4:3 */ 77, /* LAG 3:2 */ 59, /* LAG 2:2 */ 41, /* LAG 1:2 */ 3, /* R0:1 */ 76, /* LAG 3:3 */ 58, /* LAG 2:3 */ 40, /* LAG 1:3 */ 39, /* LAG 1:4 */ 17, /* LPC 2:7 */ 16, /* LPC 2:8 */ 12, /* LPC 1:3 */ 11, /* LPC 1:4 */ 10, /* LPC 1:5 */ 9, /* LPC 1:6 */ 2, /* R0:2 */ 38, /* LAG 1:5 */ 37, /* LAG 1:6 */ 36, /* LAG 1:7 */ 8, /* LPC 1:7 */ 7, /* LPC 1:8 */ 6, /* LPC 1:9 */ 5, /* LPC 1:10 */ 1, /* R0:3 */ 0, /* R0:4 */ 35, /* Mode:0 */ 34, /* Mode:1 */ 106, /* Code 4:0 */ 105, /* Code 4:1 */ 104, /* Code 4:2 */ 103, /* Code 4:3 */ 102, /* Code 4:4 */ 101, /* Code 4:5 */ 100, /* Code 4:6 */ 99, /* Code 4:7 */ 98, /* Code 4:8 */ 88, /* Code 3:0 */ 87, /* Code 3:1 */ 86, /* Code 3:2 */ 85, /* Code 3:3 */ 84, /* Code 3:4 */ 83, /* Code 3:5 */ 82, /* Code 3:6 */ 81, /* Code 3:7 */ }; libosmocore-0.9.0/src/codec/gsm660.c000066400000000000000000000243741261607044000171010ustar00rootroot00000000000000/* GSM 06.60 - GSM EFR Codec */ /* * (C) 2010 Sylvain Munaut * * 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 /* GSM EFR - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 6. * * It converts between serial parameter output (as described in * GSM 06.60 Table 6 and GSM 05.03 Table 5) and the order needed * before channel encoding. CRC poly and bit repetition must be * applied prior to this table, as in GSM 05.03 3.1.1, to get 260 * bits from a 244 bits raw EFR frame. */ const uint16_t gsm660_bitorder[260] = { 38, 39, 40, 41, 42, 43, /* 0 -> LTP-LAG 1: b8..b3 */ 145, 146, 147, 148, 149, 150, /* 6 -> LTP-LAG 3: b8..b3 */ 93, 94, /* 12 -> LTP-LAG 2: b5..b4 */ 200, 201, /* 14 -> LTP-LAG 4: b5..b4 */ 47, /* 16 -> LTP-GAIN 1: b3 */ 88, /* 17 -> FCB-GAIN 1: b4 */ 99, /* 18 -> LTP-GAIN 2: b3 */ 140, /* 19 -> FCB-GAIN 2: b4 */ 44, /* 20 -> LTP-LAG 1: b2 */ 151, /* 21 -> LTP-LAG 3: b2 */ 95, /* 22 -> LTP-LAG 2: b3 */ 202, /* 23 -> LTP-LAG 4: b3 */ 1, 2, /* 24 -> LPC 1: b5..b4 */ 7, /* 26 -> LPC 2: b7 */ 9, /* 27 -> LPC 2: b5 */ 17, 18, /* 28 -> LPC 3: b6..b5 */ 23, /* 30 -> LPC 3: b0 */ 45, 46, /* 31 -> LTP-LAG 1: b1..b0 */ 152, 153, /* 33 -> LTP-LAG 3: b1..b0 */ 96, /* 35 -> LTP-LAG 2: b2 */ 203, /* 36 -> LTP-LAG 4: b2 */ 3, 4, /* 37 -> LPC 1: b3..b2 */ 10, 11, /* 39 -> LPC 2: b4..b3 */ 15, /* 41 -> LPC 3: b8 */ 8, /* 42 -> LPC 2: b6 */ 5, 6, /* 43 -> LPC 1: b1..b0 */ 12, /* 45 -> LPC 2: b2 */ 16, /* 46 -> LPC 3: b7 */ 19, /* 47 -> LPC 3: b4 */ 97, /* 48 -> LTP-LAG 2: b1 */ 204, /* 49 -> LTP-LAG 4: b1 */ 0, /* 50 -> LPC 1: b6 */ 13, 14, /* 51 -> LPC 2: b1..b0 */ 20, /* 53 -> LPC 3: b3 */ 24, 25, /* 54 -> LPC 4: b7..b6 */ 27, /* 56 -> LPC 4: b4 */ 154, /* 57 -> LTP-GAIN 3: b3 */ 206, /* 58 -> LTP-GAIN 4: b3 */ 195, /* 59 -> FCB-GAIN 3: b4 */ 247, /* 60 -> FCB-GAIN 4: b4 */ 89, /* 61 -> FCB-GAIN 1: b3 */ 141, /* 62 -> FCB-GAIN 2: b3 */ 196, /* 63 -> FCB-GAIN 3: b3 */ 248, /* 64 -> FCB-GAIN 4: b3 */ 252, 253, 254, 255, 256, 257, 258, 259, /* 65 -> CRC-POLY: b7..b0 */ 48, /* 73 -> LTP-GAIN 1: b2 */ 100, /* 74 -> LTP-GAIN 2: b2 */ 155, /* 75 -> LTP-GAIN 3: b2 */ 207, /* 76 -> LTP-GAIN 4: b2 */ 21, 22, /* 77 -> LPC 3: b2..b1 */ 26, /* 79 -> LPC 4: b5 */ 28, /* 80 -> LPC 4: b3 */ 51, /* 81 -> PULSE 1_1: b3 */ 55, /* 82 -> PULSE 1_2: b3 */ 59, /* 83 -> PULSE 1_3: b3 */ 63, /* 84 -> PULSE 1_4: b3 */ 67, /* 85 -> PULSE 1_5: b3 */ 103, /* 86 -> PULSE 2_1: b3 */ 107, /* 87 -> PULSE 2_2: b3 */ 111, /* 88 -> PULSE 2_3: b3 */ 115, /* 89 -> PULSE 2_4: b3 */ 119, /* 90 -> PULSE 2_5: b3 */ 158, /* 91 -> PULSE 3_1: b3 */ 162, /* 92 -> PULSE 3_2: b3 */ 166, /* 93 -> PULSE 3_3: b3 */ 170, /* 94 -> PULSE 3_4: b3 */ 174, /* 95 -> PULSE 3_5: b3 */ 210, /* 96 -> PULSE 4_1: b3 */ 214, /* 97 -> PULSE 4_2: b3 */ 218, /* 98 -> PULSE 4_3: b3 */ 222, /* 99 -> PULSE 4_4: b3 */ 226, /* 100 -> PULSE 4_5: b3 */ 90, /* 101 -> FCB-GAIN 1: b2 */ 142, /* 102 -> FCB-GAIN 2: b2 */ 197, /* 103 -> FCB-GAIN 3: b2 */ 249, /* 104 -> FCB-GAIN 4: b2 */ 49, /* 105 -> LTP-GAIN 1: b1 */ 101, /* 106 -> LTP-GAIN 2: b1 */ 156, /* 107 -> LTP-GAIN 3: b1 */ 208, /* 108 -> LTP-GAIN 4: b1 */ 29, 30, 31, /* 109 -> LPC 4: b2..b0 */ 32, 33, 34, 35, /* 112 -> LPC 5: b5..b2 */ 98, /* 116 -> LTP-LAG 2: b0 */ 205, /* 117 -> LTP-LAG 4: b0 */ 52, /* 118 -> PULSE 1_1: b2 */ 56, /* 119 -> PULSE 1_2: b2 */ 60, /* 120 -> PULSE 1_3: b2 */ 64, /* 121 -> PULSE 1_4: b2 */ 68, /* 122 -> PULSE 1_5: b2 */ 104, /* 123 -> PULSE 2_1: b2 */ 108, /* 124 -> PULSE 2_2: b2 */ 112, /* 125 -> PULSE 2_3: b2 */ 116, /* 126 -> PULSE 2_4: b2 */ 120, /* 127 -> PULSE 2_5: b2 */ 159, /* 128 -> PULSE 3_1: b2 */ 163, /* 129 -> PULSE 3_2: b2 */ 167, /* 130 -> PULSE 3_3: b2 */ 171, /* 131 -> PULSE 3_4: b2 */ 175, /* 132 -> PULSE 3_5: b2 */ 211, /* 133 -> PULSE 4_1: b2 */ 215, /* 134 -> PULSE 4_2: b2 */ 219, /* 135 -> PULSE 4_3: b2 */ 223, /* 136 -> PULSE 4_4: b2 */ 227, /* 137 -> PULSE 4_5: b2 */ 53, /* 138 -> PULSE 1_1: b1 */ 57, /* 139 -> PULSE 1_2: b1 */ 61, /* 140 -> PULSE 1_3: b1 */ 65, /* 141 -> PULSE 1_4: b1 */ 105, /* 142 -> PULSE 2_1: b1 */ 109, /* 143 -> PULSE 2_2: b1 */ 113, /* 144 -> PULSE 2_3: b1 */ 117, /* 145 -> PULSE 2_4: b1 */ 160, /* 146 -> PULSE 3_1: b1 */ 164, /* 147 -> PULSE 3_2: b1 */ 168, /* 148 -> PULSE 3_3: b1 */ 172, /* 149 -> PULSE 3_4: b1 */ 212, /* 150 -> PULSE 4_1: b1 */ 220, /* 151 -> PULSE 4_3: b1 */ 224, /* 152 -> PULSE 4_4: b1 */ 91, /* 153 -> FCB-GAIN 1: b1 */ 143, /* 154 -> FCB-GAIN 2: b1 */ 198, /* 155 -> FCB-GAIN 3: b1 */ 250, /* 156 -> FCB-GAIN 4: b1 */ 50, /* 157 -> LTP-GAIN 1: b0 */ 102, /* 158 -> LTP-GAIN 2: b0 */ 157, /* 159 -> LTP-GAIN 3: b0 */ 209, /* 160 -> LTP-GAIN 4: b0 */ 92, /* 161 -> FCB-GAIN 1: b0 */ 144, /* 162 -> FCB-GAIN 2: b0 */ 199, /* 163 -> FCB-GAIN 3: b0 */ 251, /* 164 -> FCB-GAIN 4: b0 */ 54, /* 165 -> PULSE 1_1: b0 */ 58, /* 166 -> PULSE 1_2: b0 */ 62, /* 167 -> PULSE 1_3: b0 */ 66, /* 168 -> PULSE 1_4: b0 */ 106, /* 169 -> PULSE 2_1: b0 */ 110, /* 170 -> PULSE 2_2: b0 */ 114, /* 171 -> PULSE 2_3: b0 */ 118, /* 172 -> PULSE 2_4: b0 */ 161, /* 173 -> PULSE 3_1: b0 */ 165, /* 174 -> PULSE 3_2: b0 */ 169, /* 175 -> PULSE 3_3: b0 */ 173, /* 176 -> PULSE 3_4: b0 */ 213, /* 177 -> PULSE 4_1: b0 */ 221, /* 178 -> PULSE 4_3: b0 */ 225, /* 179 -> PULSE 4_4: b0 */ 36, 37, /* 180 -> LPC 5: b1..b0 */ 69, /* 182 -> PULSE 1_5: b1 */ 71, 72, /* 183 -> PULSE 1_5: b1..b1 */ 121, /* 185 -> PULSE 2_5: b1 */ 123, 124, /* 186 -> PULSE 2_5: b1..b1 */ 176, /* 188 -> PULSE 3_5: b1 */ 178, 179, /* 189 -> PULSE 3_5: b1..b1 */ 228, /* 191 -> PULSE 4_5: b1 */ 230, 231, /* 192 -> PULSE 4_5: b1..b1 */ 216, 217, /* 194 -> PULSE 4_2: b1..b0 */ 70, /* 196 -> PULSE 1_5: b0 */ 122, /* 197 -> PULSE 2_5: b0 */ 177, /* 198 -> PULSE 3_5: b0 */ 229, /* 199 -> PULSE 4_5: b0 */ 73, /* 200 -> PULSE 1_6: b2 */ 76, /* 201 -> PULSE 1_7: b2 */ 79, /* 202 -> PULSE 1_8: b2 */ 82, /* 203 -> PULSE 1_9: b2 */ 85, /* 204 -> PULSE 1_10: b2 */ 125, /* 205 -> PULSE 2_6: b2 */ 128, /* 206 -> PULSE 2_7: b2 */ 131, /* 207 -> PULSE 2_8: b2 */ 134, /* 208 -> PULSE 2_9: b2 */ 137, /* 209 -> PULSE 2_10: b2 */ 180, /* 210 -> PULSE 3_6: b2 */ 183, /* 211 -> PULSE 3_7: b2 */ 186, /* 212 -> PULSE 3_8: b2 */ 189, /* 213 -> PULSE 3_9: b2 */ 192, /* 214 -> PULSE 3_10: b2 */ 232, /* 215 -> PULSE 4_6: b2 */ 235, /* 216 -> PULSE 4_7: b2 */ 238, /* 217 -> PULSE 4_8: b2 */ 241, /* 218 -> PULSE 4_9: b2 */ 244, /* 219 -> PULSE 4_10: b2 */ 74, /* 220 -> PULSE 1_6: b1 */ 77, /* 221 -> PULSE 1_7: b1 */ 80, /* 222 -> PULSE 1_8: b1 */ 83, /* 223 -> PULSE 1_9: b1 */ 86, /* 224 -> PULSE 1_10: b1 */ 126, /* 225 -> PULSE 2_6: b1 */ 129, /* 226 -> PULSE 2_7: b1 */ 132, /* 227 -> PULSE 2_8: b1 */ 135, /* 228 -> PULSE 2_9: b1 */ 138, /* 229 -> PULSE 2_10: b1 */ 181, /* 230 -> PULSE 3_6: b1 */ 184, /* 231 -> PULSE 3_7: b1 */ 187, /* 232 -> PULSE 3_8: b1 */ 190, /* 233 -> PULSE 3_9: b1 */ 193, /* 234 -> PULSE 3_10: b1 */ 233, /* 235 -> PULSE 4_6: b1 */ 236, /* 236 -> PULSE 4_7: b1 */ 239, /* 237 -> PULSE 4_8: b1 */ 242, /* 238 -> PULSE 4_9: b1 */ 245, /* 239 -> PULSE 4_10: b1 */ 75, /* 240 -> PULSE 1_6: b0 */ 78, /* 241 -> PULSE 1_7: b0 */ 81, /* 242 -> PULSE 1_8: b0 */ 84, /* 243 -> PULSE 1_9: b0 */ 87, /* 244 -> PULSE 1_10: b0 */ 127, /* 245 -> PULSE 2_6: b0 */ 130, /* 246 -> PULSE 2_7: b0 */ 133, /* 247 -> PULSE 2_8: b0 */ 136, /* 248 -> PULSE 2_9: b0 */ 139, /* 249 -> PULSE 2_10: b0 */ 182, /* 250 -> PULSE 3_6: b0 */ 185, /* 251 -> PULSE 3_7: b0 */ 188, /* 252 -> PULSE 3_8: b0 */ 191, /* 253 -> PULSE 3_9: b0 */ 194, /* 254 -> PULSE 3_10: b0 */ 234, /* 255 -> PULSE 4_6: b0 */ 237, /* 256 -> PULSE 4_7: b0 */ 240, /* 257 -> PULSE 4_8: b0 */ 243, /* 258 -> PULSE 4_9: b0 */ 246, /* 259 -> PULSE 4_10: b0 */ }; libosmocore-0.9.0/src/codec/gsm690.c000066400000000000000000000207721261607044000171020ustar00rootroot00000000000000/* GSM 06.90 - GSM AMR Codec */ /* * (C) 2010 Sylvain Munaut * * 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 /* * These table map between the raw encoder parameter output and * the format used before channel coding. Both in GSM and in various * file/network format (same tables used in several specs). */ /* AMR 12.2 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 7 * It's also TS 26.101 Table B.8 */ const uint16_t gsm690_12_2_bitorder[244] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 23, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 38, 141, 39, 142, 40, 143, 41, 144, 42, 145, 43, 146, 44, 147, 45, 148, 46, 149, 47, 97, 150, 200, 48, 98, 151, 201, 49, 99, 152, 202, 86, 136, 189, 239, 87, 137, 190, 240, 88, 138, 191, 241, 91, 194, 92, 195, 93, 196, 94, 197, 95, 198, 29, 30, 31, 32, 33, 34, 35, 50, 100, 153, 203, 89, 139, 192, 242, 51, 101, 154, 204, 55, 105, 158, 208, 90, 140, 193, 243, 59, 109, 162, 212, 63, 113, 166, 216, 67, 117, 170, 220, 36, 37, 54, 53, 52, 58, 57, 56, 62, 61, 60, 66, 65, 64, 70, 69, 68, 104, 103, 102, 108, 107, 106, 112, 111, 110, 116, 115, 114, 120, 119, 118, 157, 156, 155, 161, 160, 159, 165, 164, 163, 169, 168, 167, 173, 172, 171, 207, 206, 205, 211, 210, 209, 215, 214, 213, 219, 218, 217, 223, 222, 221, 73, 72, 71, 76, 75, 74, 79, 78, 77, 82, 81, 80, 85, 84, 83, 123, 122, 121, 126, 125, 124, 129, 128, 127, 132, 131, 130, 135, 134, 133, 176, 175, 174, 179, 178, 177, 182, 181, 180, 185, 184, 183, 188, 187, 186, 226, 225, 224, 229, 228, 227, 232, 231, 230, 235, 234, 233, 238, 237, 236, 96, 199, }; /* AMR 10.2 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 8 * It's also TS 26.101 Table B.7 */ const uint16_t gsm690_10_2_bitorder[204] = { 7, 6, 5, 4, 3, 2, 1, 0, 16, 15, 14, 13, 12, 11, 10, 9, 8, 26, 27, 28, 29, 30, 31, 115, 116, 117, 118, 119, 120, 72, 73, 161, 162, 65, 68, 69, 108, 111, 112, 154, 157, 158, 197, 200, 201, 32, 33, 121, 122, 74, 75, 163, 164, 66, 109, 155, 198, 19, 23, 21, 22, 18, 17, 20, 24, 25, 37, 36, 35, 34, 80, 79, 78, 77, 126, 125, 124, 123, 169, 168, 167, 166, 70, 67, 71, 113, 110, 114, 159, 156, 160, 202, 199, 203, 76, 165, 81, 82, 92, 91, 93, 83, 95, 85, 84, 94, 101, 102, 96, 104, 86, 103, 87, 97, 127, 128, 138, 137, 139, 129, 141, 131, 130, 140, 147, 148, 142, 150, 132, 149, 133, 143, 170, 171, 181, 180, 182, 172, 184, 174, 173, 183, 190, 191, 185, 193, 175, 192, 176, 186, 38, 39, 49, 48, 50, 40, 52, 42, 41, 51, 58, 59, 53, 61, 43, 60, 44, 54, 194, 179, 189, 196, 177, 195, 178, 187, 188, 151, 136, 146, 153, 134, 152, 135, 144, 145, 105, 90, 100, 107, 88, 106, 89, 98, 99, 62, 47, 57, 64, 45, 63, 46, 55, 56, }; /* AMR 7.95 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 9 * It's also TS 26.101 Table B.6 */ const uint16_t gsm690_7_95_bitorder[159] = { 8, 7, 6, 5, 4, 3, 2, 14, 16, 9, 10, 12, 13, 15, 11, 17, 20, 22, 24, 23, 19, 18, 21, 56, 88, 122, 154, 57, 89, 123, 155, 58, 90, 124, 156, 52, 84, 118, 150, 53, 85, 119, 151, 27, 93, 28, 94, 29, 95, 30, 96, 31, 97, 61, 127, 62, 128, 63, 129, 59, 91, 125, 157, 32, 98, 64, 130, 1, 0, 25, 26, 33, 99, 34, 100, 65, 131, 66, 132, 54, 86, 120, 152, 60, 92, 126, 158, 55, 87, 121, 153, 117, 116, 115, 46, 78, 112, 144, 43, 75, 109, 141, 40, 72, 106, 138, 36, 68, 102, 134, 114, 149, 148, 147, 146, 83, 82, 81, 80, 51, 50, 49, 48, 47, 45, 44, 42, 39, 35, 79, 77, 76, 74, 71, 67, 113, 111, 110, 108, 105, 101, 145, 143, 142, 140, 137, 133, 41, 73, 107, 139, 37, 69, 103, 135, 38, 70, 104, 136, }; /* AMR 7.4 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 10 * It's also TS 26.101 Table B.5 */ const uint16_t gsm690_7_4_bitorder[148] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 26, 87, 27, 88, 28, 89, 29, 90, 30, 91, 51, 80, 112, 141, 52, 81, 113, 142, 54, 83, 115, 144, 55, 84, 116, 145, 58, 119, 59, 120, 21, 22, 23, 17, 18, 19, 31, 60, 92, 121, 56, 85, 117, 146, 20, 24, 25, 50, 79, 111, 140, 57, 86, 118, 147, 49, 78, 110, 139, 48, 77, 53, 82, 114, 143, 109, 138, 47, 76, 108, 137, 32, 33, 61, 62, 93, 94, 122, 123, 41, 42, 43, 44, 45, 46, 70, 71, 72, 73, 74, 75, 102, 103, 104, 105, 106, 107, 131, 132, 133, 134, 135, 136, 34, 63, 95, 124, 35, 64, 96, 125, 36, 65, 97, 126, 37, 66, 98, 127, 38, 67, 99, 128, 39, 68, 100, 129, 40, 69, 101, 130, }; /* AMR 6.7 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 11 * It's also TS 26.101 Table B.4 */ const uint16_t gsm690_6_7_bitorder[134] = { 0, 1, 4, 3, 5, 6, 13, 7, 2, 8, 9, 11, 15, 12, 14, 10, 28, 82, 29, 83, 27, 81, 26, 80, 30, 84, 16, 55, 109, 56, 110, 31, 85, 57, 111, 48, 73, 102, 127, 32, 86, 51, 76, 105, 130, 52, 77, 106, 131, 58, 112, 33, 87, 19, 23, 53, 78, 107, 132, 21, 22, 18, 17, 20, 24, 25, 50, 75, 104, 129, 47, 72, 101, 126, 54, 79, 108, 133, 46, 71, 100, 125, 128, 103, 74, 49, 45, 70, 99, 124, 42, 67, 96, 121, 39, 64, 93, 118, 38, 63, 92, 117, 35, 60, 89, 114, 34, 59, 88, 113, 44, 69, 98, 123, 43, 68, 97, 122, 41, 66, 95, 120, 40, 65, 94, 119, 37, 62, 91, 116, 36, 61, 90, 115, }; /* AMR 5.9 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 12 * It's also TS 26.101 Table B.3 */ const uint16_t gsm690_5_9_bitorder[118] = { 0, 1, 4, 5, 3, 6, 7, 2, 13, 15, 8, 9, 11, 12, 14, 10, 16, 28, 74, 29, 75, 27, 73, 26, 72, 30, 76, 51, 97, 50, 71, 96, 117, 31, 77, 52, 98, 49, 70, 95, 116, 53, 99, 32, 78, 33, 79, 48, 69, 94, 115, 47, 68, 93, 114, 46, 67, 92, 113, 19, 21, 23, 22, 18, 17, 20, 24, 111, 43, 89, 110, 64, 65, 44, 90, 25, 45, 66, 91, 112, 54, 100, 40, 61, 86, 107, 39, 60, 85, 106, 36, 57, 82, 103, 35, 56, 81, 102, 34, 55, 80, 101, 42, 63, 88, 109, 41, 62, 87, 108, 38, 59, 84, 105, 37, 58, 83, 104, }; /* AMR 5.15 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 13 * It's also TS 26.101 Table B.2 */ const uint16_t gsm690_5_15_bitorder[103] = { 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 24, 25, 26, 27, 46, 65, 84, 45, 44, 43, 64, 63, 62, 83, 82, 81, 102, 101, 100, 42, 61, 80, 99, 28, 47, 66, 85, 18, 41, 60, 79, 98, 29, 48, 67, 17, 20, 22, 40, 59, 78, 97, 21, 30, 49, 68, 86, 19, 16, 87, 39, 38, 58, 57, 77, 35, 54, 73, 92, 76, 96, 95, 36, 55, 74, 93, 32, 51, 33, 52, 70, 71, 89, 90, 31, 50, 69, 88, 37, 56, 75, 94, 34, 53, 72, 91, }; /* AMR 4.75 kbits - subjective importance bit ordering */ /* This array encodes GSM 05.03 Table 14 * It's also TS 26.101 Table B.1 */ const uint16_t gsm690_4_75_bitorder[95] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 23, 24, 25, 26, 27, 28, 48, 49, 61, 62, 82, 83, 47, 46, 45, 44, 81, 80, 79, 78, 17, 18, 20, 22, 77, 76, 75, 74, 29, 30, 43, 42, 41, 40, 38, 39, 16, 19, 21, 50, 51, 59, 60, 63, 64, 72, 73, 84, 85, 93, 94, 32, 33, 35, 36, 53, 54, 56, 57, 66, 67, 69, 70, 87, 88, 90, 91, 34, 55, 68, 89, 37, 58, 71, 92, 31, 52, 65, 86, }; libosmocore-0.9.0/src/conv.c000066400000000000000000000341761261607044000157500ustar00rootroot00000000000000/* * conv.c * * Generic convolutional encoding / decoding * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \addtogroup conv * @{ */ /*! \file conv.c * Osmocom convolutional encoder and decoder */ #include "config.h" #ifdef HAVE_ALLOCA_H #include #endif #include #include #include #include #include /* ------------------------------------------------------------------------ */ /* Common */ /* ------------------------------------------------------------------------ */ int osmo_conv_get_input_length(const struct osmo_conv_code *code, int len) { return len <= 0 ? code->len : len; } int osmo_conv_get_output_length(const struct osmo_conv_code *code, int len) { int pbits, in_len, out_len; /* Input length */ in_len = osmo_conv_get_input_length(code, len); /* Output length */ out_len = in_len * code->N; if (code->term == CONV_TERM_FLUSH) out_len += code->N * (code->K - 1); /* Count punctured bits */ if (code->puncture) { for (pbits=0; code->puncture[pbits] >= 0; pbits++); out_len -= pbits; } return out_len; } /* ------------------------------------------------------------------------ */ /* Encoding */ /* ------------------------------------------------------------------------ */ /*! \brief Initialize a convolutional encoder * \param[in,out] encoder Encoder state to initialize * \param[in] code Description of convolutional code */ void osmo_conv_encode_init(struct osmo_conv_encoder *encoder, const struct osmo_conv_code *code) { memset(encoder, 0x00, sizeof(struct osmo_conv_encoder)); encoder->code = code; } void osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder, const ubit_t *input) { int i; uint8_t state = 0; for (i=0; i<(encoder->code->K-1); i++) state = (state << 1) | input[i]; encoder->state = state; } static inline int _conv_encode_do_output(struct osmo_conv_encoder *encoder, uint8_t out, ubit_t *output) { const struct osmo_conv_code *code = encoder->code; int o_idx = 0; int j; if (code->puncture) { for (j=0; jN; j++) { int bit_no = code->N - j - 1; int r_idx = encoder->i_idx * code->N + j; if (code->puncture[encoder->p_idx] == r_idx) encoder->p_idx++; else output[o_idx++] = (out >> bit_no) & 1; } } else { for (j=0; jN; j++) { int bit_no = code->N - j - 1; output[o_idx++] = (out >> bit_no) & 1; } } return o_idx; } int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder, const ubit_t *input, ubit_t *output, int n) { const struct osmo_conv_code *code = encoder->code; uint8_t state; int i; int o_idx; o_idx = 0; state = encoder->state; for (i=0; inext_output[state][bit]; state = code->next_state[state][bit]; o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); encoder->i_idx++; } encoder->state = state; return o_idx; } int osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output) { const struct osmo_conv_code *code = encoder->code; uint8_t state; int n; int i; int o_idx; n = code->K - 1; o_idx = 0; state = encoder->state; for (i=0; inext_term_output) { out = code->next_term_output[state]; state = code->next_term_state[state]; } else { out = code->next_output[state][0]; state = code->next_state[state][0]; } o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]); encoder->i_idx++; } encoder->state = state; return o_idx; } /*! \brief All-in-one convolutional encoding function * \param[in] code description of convolutional code to be used * \param[in] input array of unpacked bits (uncoded) * \param[out] output array of unpacked bits (encoded) * \return Number of produced output bits * * This is an all-in-one function, taking care of * \ref osmo_conv_init, \ref osmo_conv_encode_load_state, * \ref osmo_conv_encode_raw and \ref osmo_conv_encode_flush as needed. */ int osmo_conv_encode(const struct osmo_conv_code *code, const ubit_t *input, ubit_t *output) { struct osmo_conv_encoder encoder; int l; osmo_conv_encode_init(&encoder, code); if (code->term == CONV_TERM_TAIL_BITING) { int eidx = code->len - code->K + 1; osmo_conv_encode_load_state(&encoder, &input[eidx]); } l = osmo_conv_encode_raw(&encoder, input, output, code->len); if (code->term == CONV_TERM_FLUSH) l += osmo_conv_encode_flush(&encoder, &output[l]); return l; } /* ------------------------------------------------------------------------ */ /* Decoding (viterbi) */ /* ------------------------------------------------------------------------ */ #define MAX_AE 0x00ffffff void osmo_conv_decode_init(struct osmo_conv_decoder *decoder, const struct osmo_conv_code *code, int len, int start_state) { int n_states; /* Init */ if (len <= 0) len = code->len; n_states = 1 << (code->K - 1); memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); decoder->code = code; decoder->n_states = n_states; decoder->len = len; /* Allocate arrays */ decoder->ae = malloc(sizeof(unsigned int) * n_states); decoder->ae_next = malloc(sizeof(unsigned int) * n_states); decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1)); /* Classic reset */ osmo_conv_decode_reset(decoder, start_state); } void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state) { int i; /* Reset indexes */ decoder->o_idx = 0; decoder->p_idx = 0; /* Initial error */ if (start_state < 0) { /* All states possible */ memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states); } else { /* Fixed start state */ for (i=0; in_states; i++) { decoder->ae[i] = (i == start_state) ? 0 : MAX_AE; } } } void osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder) { int i; unsigned int min_ae = MAX_AE; /* Reset indexes */ decoder->o_idx = 0; decoder->p_idx = 0; /* Initial error normalize (remove constant) */ for (i=0; in_states; i++) { if (decoder->ae[i] < min_ae) min_ae = decoder->ae[i]; } for (i=0; in_states; i++) decoder->ae[i] -= min_ae; } void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder) { free(decoder->ae); free(decoder->ae_next); free(decoder->state_history); memset(decoder, 0x00, sizeof(struct osmo_conv_decoder)); } int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder, const sbit_t *input, int n) { const struct osmo_conv_code *code = decoder->code; int i, s, b, j; int n_states; unsigned int *ae; unsigned int *ae_next; uint8_t *state_history; sbit_t *in_sym; int i_idx, p_idx; /* Prepare */ n_states = decoder->n_states; ae = decoder->ae; ae_next = decoder->ae_next; state_history = &decoder->state_history[n_states * decoder->o_idx]; in_sym = alloca(sizeof(sbit_t) * code->N); i_idx = 0; p_idx = decoder->p_idx; /* Scan the treillis */ for (i=0; ipuncture) { /* Hard way ... */ for (j=0; jN; j++) { int idx = ((decoder->o_idx + i) * code->N) + j; if (idx == code->puncture[p_idx]) { in_sym[j] = 0; /* Undefined */ p_idx++; } else { in_sym[j] = input[i_idx]; i_idx++; } } } else { /* Easy, just copy N bits */ memcpy(in_sym, &input[i_idx], code->N); i_idx += code->N; } /* Scan all state */ for (s=0; snext_output[s][b]; uint8_t state = code->next_state[s][b]; /* New error for this path */ nae = ae[s]; /* start from last error */ m = 1 << (code->N - 1); /* mask for 'out' bit selection */ for (j=0; jN; j++) { int is = (int)in_sym[j]; if (is) { ov = (out & m) ? -127 : 127; /* sbit_t value for it */ e = is - ov; /* raw error for this bit */ nae += (e * e) >> 9; /* acc the squared/scaled value */ } m >>= 1; /* next mask bit */ } /* Is it survivor ? */ if (ae_next[state] > nae) { ae_next[state] = nae; state_history[(n_states * i) + state] = s; } } } /* Copy accumulated error */ memcpy(ae, ae_next, sizeof(unsigned int) * n_states); } /* Update decoder state */ decoder->p_idx = p_idx; decoder->o_idx += n; return i_idx; } int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder, const sbit_t *input) { const struct osmo_conv_code *code = decoder->code; int i, s, j; int n_states; unsigned int *ae; unsigned int *ae_next; uint8_t *state_history; sbit_t *in_sym; int i_idx, p_idx; /* Prepare */ n_states = decoder->n_states; ae = decoder->ae; ae_next = decoder->ae_next; state_history = &decoder->state_history[n_states * decoder->o_idx]; in_sym = alloca(sizeof(sbit_t) * code->N); i_idx = 0; p_idx = decoder->p_idx; /* Scan the treillis */ for (i=0; iK-1; i++) { /* Reset next accumulated error */ for (s=0; spuncture) { /* Hard way ... */ for (j=0; jN; j++) { int idx = ((decoder->o_idx + i) * code->N) + j; if (idx == code->puncture[p_idx]) { in_sym[j] = 0; /* Undefined */ p_idx++; } else { in_sym[j] = input[i_idx]; i_idx++; } } } else { /* Easy, just copy N bits */ memcpy(in_sym, &input[i_idx], code->N); i_idx += code->N; } /* Scan all state */ for (s=0; snext_term_output) { out = code->next_term_output[s]; state = code->next_term_state[s]; } else { out = code->next_output[s][0]; state = code->next_state[s][0]; } /* New error for this path */ nae = ae[s]; /* start from last error */ m = 1 << (code->N - 1); /* mask for 'out' bit selection */ for (j=0; jN; j++) { int is = (int)in_sym[j]; if (is) { ov = (out & m) ? -127 : 127; /* sbit_t value for it */ e = is - ov; /* raw error for this bit */ nae += (e * e) >> 9; /* acc the squared/scaled value */ } m >>= 1; /* next mask bit */ } /* Is it survivor ? */ if (ae_next[state] > nae) { ae_next[state] = nae; state_history[(n_states * i) + state] = s; } } /* Copy accumulated error */ memcpy(ae, ae_next, sizeof(unsigned int) * n_states); } /* Update decoder state */ decoder->p_idx = p_idx; decoder->o_idx += code->K - 1; return i_idx; } int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder, ubit_t *output, int has_flush, int end_state) { const struct osmo_conv_code *code = decoder->code; int min_ae; uint8_t min_state, cur_state; int i, s, n; uint8_t *sh_ptr; /* End state ? */ if (end_state < 0) { /* Find state with least error */ min_ae = MAX_AE; min_state = 0xff; for (s=0; sn_states; s++) { if (decoder->ae[s] < min_ae) { min_ae = decoder->ae[s]; min_state = s; } } if (min_state == 0xff) return -1; } else { min_state = (uint8_t) end_state; min_ae = decoder->ae[end_state]; } /* Traceback */ cur_state = min_state; n = decoder->o_idx; sh_ptr = &decoder->state_history[decoder->n_states * (n-1)]; /* No output for the K-1 termination input bits */ if (has_flush) { for (i=0; iK-1; i++) { cur_state = sh_ptr[cur_state]; sh_ptr -= decoder->n_states; } n -= code->K - 1; } /* Generate output backward */ for (i=n-1; i>=0; i--) { min_state = cur_state; cur_state = sh_ptr[cur_state]; sh_ptr -= decoder->n_states; if (code->next_state[cur_state][0] == min_state) output[i] = 0; else output[i] = 1; } return min_ae; } /*! \brief All-in-one convolutional decoding function * \param[in] code description of convolutional code to be used * \param[in] input array of soft bits (coded) * \param[out] output array of unpacked bits (decoded) * * This is an all-in-one function, taking care of * \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan, * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and * \ref osmo_conv_decode_deinit. */ int osmo_conv_decode(const struct osmo_conv_code *code, const sbit_t *input, ubit_t *output) { struct osmo_conv_decoder decoder; int rv, l; osmo_conv_decode_init(&decoder, code, 0, 0); if (code->term == CONV_TERM_TAIL_BITING) { osmo_conv_decode_scan(&decoder, input, code->len); osmo_conv_decode_rewind(&decoder); } l = osmo_conv_decode_scan(&decoder, input, code->len); if (code->term == CONV_TERM_FLUSH) l = osmo_conv_decode_flush(&decoder, &input[l]); rv = osmo_conv_decode_get_output(&decoder, output, code->term == CONV_TERM_FLUSH, /* has_flush */ code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */ ); osmo_conv_decode_deinit(&decoder); return rv; } /*! @} */ libosmocore-0.9.0/src/crc16.c000066400000000000000000000053441261607044000157140ustar00rootroot00000000000000/* * This was copied from the linux kernel and adjusted for our types. */ /* * crc16.c * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #include /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ uint16_t const osmo_crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; /** * crc16 - compute the CRC-16 for the data buffer * @crc: previous CRC value * @buffer: data pointer * @len: number of bytes in the buffer * * Returns the updated CRC value. */ uint16_t osmo_crc16(uint16_t crc, uint8_t const *buffer, size_t len) { while (len--) crc = osmo_crc16_byte(crc, *buffer++); return crc; } libosmocore-0.9.0/src/crcXXgen.c.tpl000066400000000000000000000061611261607044000173130ustar00rootroot00000000000000/* * crcXXgen.c * * Generic CRC routines (for max XX bits poly) * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \addtogroup crcgen * @{ */ /*! \file crcXXgen.c * Osmocom generic CRC routines (for max XX bits poly) */ #include #include #include /*! \brief Compute the CRC value of a given array of hard-bits * \param[in] code The CRC code description to apply * \param[in] in Array of hard bits * \param[in] len Length of the array of hard bits * \returns The CRC value */ uintXX_t osmo_crcXXgen_compute_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len) { const uintXX_t poly = code->poly; uintXX_t crc = code->init; int i, n = code->bits-1; for (i=0; ibits) - 1; } crc ^= code->remainder; return crc; } /*! \brief Checks the CRC value of a given array of hard-bits * \param[in] code The CRC code description to apply * \param[in] in Array of hard bits * \param[in] len Length of the array of hard bits * \param[in] crc_bits Array of hard bits with the alleged CRC * \returns 0 if CRC matches. 1 in case of error. * * The crc_bits array must have a length of code->len */ int osmo_crcXXgen_check_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len, const ubit_t *crc_bits) { uintXX_t crc; int i; crc = osmo_crcXXgen_compute_bits(code, in, len); for (i=0; ibits; i++) if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1)) return 1; return 0; } /*! \brief Computes and writes the CRC value of a given array of bits * \param[in] code The CRC code description to apply * \param[in] in Array of hard bits * \param[in] len Length of the array of hard bits * \param[in] crc_bits Array of hard bits to write the computed CRC to * * The crc_bits array must have a length of code->len */ void osmo_crcXXgen_set_bits(const struct osmo_crcXXgen_code *code, const ubit_t *in, int len, ubit_t *crc_bits) { uintXX_t crc; int i; crc = osmo_crcXXgen_compute_bits(code, in, len); for (i=0; ibits; i++) crc_bits[i] = ((crc >> (code->bits-i-1)) & 1); } /*! @} */ /* vim: set syntax=c: */ libosmocore-0.9.0/src/ctrl/000077500000000000000000000000001261607044000155705ustar00rootroot00000000000000libosmocore-0.9.0/src/ctrl/Makefile.am000066400000000000000000000011321261607044000176210ustar00rootroot00000000000000# 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 AM_CFLAGS = -Wall $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include lib_LTLIBRARIES = libosmoctrl.la libosmoctrl_la_SOURCES = control_cmd.c control_if.c libosmoctrl_la_LDFLAGS = $(LTLDFLAGS_OSMOCTRL) -version-info $(LIBVERSION) -no-undefined libosmoctrl_la_LIBADD = \ $(top_builddir)/src/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ $(top_builddir)/src/vty/libosmovty.la libosmocore-0.9.0/src/ctrl/control_cmd.c000066400000000000000000000301741261607044000202440ustar00rootroot00000000000000/* SNMP-like status interface * * (C) 2010-2011 by Daniel Willmann * (C) 2010-2011 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 #include #include #include #include #include #include #include #include #include #include #include extern vector ctrl_node_vec; static struct ctrl_cmd_map ccm[] = { {"GET", CTRL_TYPE_GET}, {"SET", CTRL_TYPE_SET}, {"GET_REPLY", CTRL_TYPE_GET_REPLY}, {"SET_REPLY", CTRL_TYPE_SET_REPLY}, {"TRAP", CTRL_TYPE_TRAP}, {"ERROR", CTRL_TYPE_ERROR}, {NULL} }; static int ctrl_cmd_str2type(char *s) { int i; for (i=0; ccm[i].cmd != NULL; i++) { if (strcasecmp(s, ccm[i].cmd) == 0) return ccm[i].type; } return CTRL_TYPE_UNKNOWN; } static char *ctrl_cmd_type2str(int type) { int i; for (i=0; ccm[i].cmd != NULL; i++) { if (ccm[i].type == type) return ccm[i].cmd; } return NULL; } /* Functions from libosmocom */ extern vector cmd_make_descvec(const char *string, const char *descstr); /* Get the ctrl_cmd_element that matches this command */ static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node) { int index, j; const char *desc; struct ctrl_cmd_element *cmd_el; struct ctrl_cmd_struct *cmd_desc; char *str; for (index = 0; index < vector_active(node); index++) { if ((cmd_el = vector_slot(node, index))) { cmd_desc = &cmd_el->strcmd; if (cmd_desc->nr_commands > vector_active(vline)) continue; for (j =0; j < vector_active(vline) && j < cmd_desc->nr_commands; j++) { str = vector_slot(vline, j); desc = cmd_desc->command[j]; if (desc[0] == '*') return cmd_el; /* Partial match */ if (strcmp(desc, str) != 0) break; } /* We went through all the elements and all matched */ if (j == cmd_desc->nr_commands) return cmd_el; } } return NULL; } int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data) { int ret = CTRL_CMD_ERROR; struct ctrl_cmd_element *cmd_el; if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) { command->reply = "Trying to execute something not GET or SET"; goto out; } if ((command->type == CTRL_TYPE_SET) && (!command->value)) { command->reply = "SET without a value"; goto out; } if (!vline) goto out; cmd_el = ctrl_cmd_get_element_match(vline, node); if (!cmd_el) { command->reply = "Command not found"; goto out; } if (command->type == CTRL_TYPE_SET) { if (!cmd_el->set) { command->reply = "SET not implemented"; goto out; } if (cmd_el->verify) { if ((ret = cmd_el->verify(command, command->value, data))) { ret = CTRL_CMD_ERROR; /* If verify() set an appropriate error message, don't change it. */ if (!command->reply) command->reply = "Value failed verification."; goto out; } } ret = cmd_el->set(command, data); goto out; } else if (command->type == CTRL_TYPE_GET) { if (!cmd_el->get) { command->reply = "GET not implemented"; goto out; } ret = cmd_el->get(command, data); goto out; } out: if (ret == CTRL_CMD_REPLY) { if (command->type == CTRL_TYPE_SET) { command->type = CTRL_TYPE_SET_REPLY; } else if (command->type == CTRL_TYPE_GET) { command->type = CTRL_TYPE_GET_REPLY; } } else if (ret == CTRL_CMD_ERROR) { command->type = CTRL_TYPE_ERROR; } return ret; } static void add_word(struct ctrl_cmd_struct *cmd, const char *start, const char *end) { if (!cmd->command) { cmd->command = talloc_zero_array(tall_vty_vec_ctx, char*, 1); cmd->nr_commands = 0; } else { cmd->command = talloc_realloc(tall_vty_vec_ctx, cmd->command, char*, cmd->nr_commands + 1); } cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command, start, end - start); } static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name) { const char *cur, *word; for (cur = name, word = NULL; cur[0] != '\0'; ++cur) { /* warn about optionals */ if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') { LOGP(DLCTRL, LOGL_ERROR, "Optionals are not supported in '%s'\n", name); goto failure; } if (isspace(cur[0])) { if (word) { add_word(cmd, word, cur); word = NULL; } continue; } if (!word) word = cur; } if (word) add_word(cmd, word, cur); return; failure: cmd->nr_commands = 0; talloc_free(cmd->command); } int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd) { vector cmds_vec; cmds_vec = vector_lookup_ensure(ctrl_node_vec, node); if (!cmds_vec) { cmds_vec = vector_init(5); if (!cmds_vec) { LOGP(DLCTRL, LOGL_ERROR, "vector_init failed.\n"); return -ENOMEM; } vector_set_index(ctrl_node_vec, node, cmds_vec); } vector_set(cmds_vec, cmd); create_cmd_struct(&cmd->strcmd, cmd->name); return 0; } struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type) { struct ctrl_cmd *cmd; cmd = talloc_zero(ctx, struct ctrl_cmd); if (!cmd) return NULL; cmd->type = type; return cmd; } struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd) { struct ctrl_cmd *cmd2; cmd2 = talloc_zero(ctx, struct ctrl_cmd); if (!cmd2) return NULL; cmd2->type = cmd->type; if (cmd->id) { cmd2->id = talloc_strdup(cmd2, cmd->id); if (!cmd2->id) goto err; } if (cmd->variable) { cmd2->variable = talloc_strdup(cmd2, cmd->variable); if (!cmd2->variable) goto err; } if (cmd->value) { cmd2->value = talloc_strdup(cmd2, cmd->value); if (!cmd2->value) goto err; } if (cmd->reply) { cmd2->reply = talloc_strdup(cmd2, cmd->reply); if (!cmd2->reply) goto err; } return cmd2; err: talloc_free(cmd2); return NULL; } struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg) { char *str, *tmp, *saveptr = NULL; char *var, *val; struct ctrl_cmd *cmd; cmd = talloc_zero(ctx, struct ctrl_cmd); if (!cmd) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n"); return NULL; } /* Make sure input is NULL terminated */ msgb_put_u8(msg, 0); str = (char *) msg->l2h; tmp = strtok_r(str, " ", &saveptr); if (!tmp) { cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Request malformed"; goto err; } cmd->type = ctrl_cmd_str2type(tmp); if (cmd->type == CTRL_TYPE_UNKNOWN) { cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Request type unknown"; goto err; } tmp = strtok_r(NULL, " ", &saveptr); if (!tmp) { cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Missing ID"; goto err; } cmd->id = talloc_strdup(cmd, tmp); if (!cmd->id) goto oom; switch (cmd->type) { case CTRL_TYPE_GET: var = strtok_r(NULL, " ", &saveptr); if (!var) { cmd->type = CTRL_TYPE_ERROR; cmd->reply = "GET incomplete"; LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n"); goto err; } cmd->variable = talloc_strdup(cmd, var); LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable); break; case CTRL_TYPE_SET: var = strtok_r(NULL, " ", &saveptr); val = strtok_r(NULL, "\n", &saveptr); if (!var || !val) { cmd->type = CTRL_TYPE_ERROR; cmd->reply = "SET incomplete"; LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n"); goto err; } cmd->variable = talloc_strdup(cmd, var); cmd->value = talloc_strdup(cmd, val); if (!cmd->variable || !cmd->value) goto oom; LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value); break; case CTRL_TYPE_GET_REPLY: case CTRL_TYPE_SET_REPLY: case CTRL_TYPE_TRAP: var = strtok_r(NULL, " ", &saveptr); val = strtok_r(NULL, " ", &saveptr); if (!var || !val) { cmd->type = CTRL_TYPE_ERROR; cmd->reply = "Trap/Reply incomplete"; LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n"); goto err; } cmd->variable = talloc_strdup(cmd, var); cmd->reply = talloc_strdup(cmd, val); if (!cmd->variable || !cmd->reply) goto oom; LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply); break; case CTRL_TYPE_ERROR: var = strtok_r(NULL, "\0", &saveptr); if (!var) { cmd->reply = ""; goto err; } cmd->reply = talloc_strdup(cmd, var); if (!cmd->reply) goto oom; LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply); break; case CTRL_TYPE_UNKNOWN: default: cmd->type = CTRL_TYPE_ERROR; cmd->reply = "Unknown type"; goto err; } return cmd; oom: cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "OOM"; err: talloc_free(cmd); return NULL; } struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd) { struct msgb *msg; char *type, *tmp; if (!cmd->id) return NULL; msg = msgb_alloc_headroom(4096, 128, "ctrl command make"); if (!msg) return NULL; type = ctrl_cmd_type2str(cmd->type); switch (cmd->type) { case CTRL_TYPE_GET: if (!cmd->variable) goto err; tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable); if (!tmp) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); goto err; } msg->l2h = msgb_put(msg, strlen(tmp)); memcpy(msg->l2h, tmp, strlen(tmp)); talloc_free(tmp); break; case CTRL_TYPE_SET: if (!cmd->variable || !cmd->value) goto err; tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, cmd->value); if (!tmp) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); goto err; } msg->l2h = msgb_put(msg, strlen(tmp)); memcpy(msg->l2h, tmp, strlen(tmp)); talloc_free(tmp); break; case CTRL_TYPE_GET_REPLY: case CTRL_TYPE_SET_REPLY: case CTRL_TYPE_TRAP: if (!cmd->variable || !cmd->reply) goto err; tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable, cmd->reply); if (!tmp) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); goto err; } msg->l2h = msgb_put(msg, strlen(tmp)); memcpy(msg->l2h, tmp, strlen(tmp)); talloc_free(tmp); break; case CTRL_TYPE_ERROR: if (!cmd->reply) goto err; tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->reply); if (!tmp) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n"); goto err; } msg->l2h = msgb_put(msg, strlen(tmp)); memcpy(msg->l2h, tmp, strlen(tmp)); talloc_free(tmp); break; default: LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type); goto err; break; } return msg; err: msgb_free(msg); return NULL; } struct ctrl_cmd_def * ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs) { struct ctrl_cmd_def *cd; if (!cmd->ccon) return NULL; cd = talloc_zero(ctx, struct ctrl_cmd_def); cd->cmd = cmd; cd->data = data; /* add to per-connection list of deferred commands */ llist_add(&cd->list, &cmd->ccon->def_cmds); return cd; } int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd) { /* luckily we're still alive */ if (cd->cmd) return 0; /* if we are a zombie, make sure we really die */ llist_del(&cd->list); talloc_free(cd); return 1; } int ctrl_cmd_def_send(struct ctrl_cmd_def *cd) { struct ctrl_cmd *cmd = cd->cmd; int rc; /* Deferred commands can only be responses to GET/SET or ERROR, but * never TRAP or anything else */ switch (cmd->type) { case CTRL_TYPE_GET: cmd->type = CTRL_TYPE_GET_REPLY; break; case CTRL_TYPE_SET: cmd->type = CTRL_TYPE_SET_REPLY; break; default: cmd->type = CTRL_TYPE_ERROR; } rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd); talloc_free(cmd); llist_del(&cd->list); talloc_free(cd); return rc; } libosmocore-0.9.0/src/ctrl/control_if.c000066400000000000000000000367731261607044000201120ustar00rootroot00000000000000/* SNMP-like status interface * * (C) 2010-2011 by Daniel Willmann * (C) 2010-2011 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 "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_NETINET_TCP_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include vector ctrl_node_vec; int ctrl_parse_get_num(vector vline, int i, long *num) { char *token, *tmp; if (i >= vector_active(vline)) return 0; token = vector_slot(vline, i); errno = 0; if (token[0] == '\0') return 0; *num = strtol(token, &tmp, 10); if (tmp[0] != '\0' || errno != 0) return 0; return 1; } /* Send command to all */ int ctrl_cmd_send_to_all(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd) { struct ctrl_connection *ccon; int ret = 0; llist_for_each_entry(ccon, &ctrl->ccon_list, list_entry) { if (ccon == cmd->ccon) continue; if (ctrl_cmd_send(&ccon->write_queue, cmd)) ret++; } return ret; } int ctrl_cmd_send(struct osmo_wqueue *queue, struct ctrl_cmd *cmd) { int ret; struct msgb *msg; msg = ctrl_cmd_make(cmd); if (!msg) { LOGP(DLCTRL, LOGL_ERROR, "Could not generate msg\n"); return -1; } ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); ipa_prepend_header(msg, IPAC_PROTO_OSMO); ret = osmo_wqueue_enqueue(queue, msg); if (ret != 0) { LOGP(DLCTRL, LOGL_ERROR, "Failed to enqueue the command.\n"); msgb_free(msg); } return ret; } struct ctrl_cmd *ctrl_cmd_trap(struct ctrl_cmd *cmd) { struct ctrl_cmd *trap; trap = ctrl_cmd_cpy(cmd, cmd); if (!trap) return NULL; trap->ccon = cmd->ccon; trap->type = CTRL_TYPE_TRAP; return trap; } static void control_close_conn(struct ctrl_connection *ccon) { struct ctrl_cmd_def *cd, *cd2; osmo_wqueue_clear(&ccon->write_queue); close(ccon->write_queue.bfd.fd); osmo_fd_unregister(&ccon->write_queue.bfd); llist_del(&ccon->list_entry); if (ccon->closed_cb) ccon->closed_cb(ccon); msgb_free(ccon->pending_msg); /* clean up deferred commands */ llist_for_each_entry_safe(cd, cd2, &ccon->def_cmds, list) { /* delete from list of def_cmds for this ccon */ llist_del(&cd->list); /* not strictly needed as this is a slave to the ccon which we * are about to free anyway */ talloc_free(cd->cmd); /* set the CMD to null, this is the indication to the user that * the connection for this command has gone */ cd->cmd = NULL; } talloc_free(ccon); } int ctrl_cmd_handle(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void *data) { char *request; int i, j, ret, node; vector vline, cmdvec, cmds_vec; ret = CTRL_CMD_ERROR; cmd->reply = NULL; node = CTRL_NODE_ROOT; cmd->node = data; request = talloc_strdup(cmd, cmd->variable); if (!request) goto err; for (i=0;ireply = "cmd_make_strvec failed."; goto err; } for (i=0;ilookup) rc = ctrl->lookup(data, vline, &node, &cmd->node, &i); else rc = 0; if (rc == 1) { /* do nothing */ } else if (rc == -ENODEV) goto err_missing; else if (rc == -ERANGE) goto err_index; else { /* If we're here the rest must be the command */ cmdvec = vector_init(vector_active(vline)-i); for (j=i; jreply = "Command not found."; vector_free(cmdvec); break; } ret = ctrl_cmd_exec(cmdvec, cmd, cmds_vec, data); vector_free(cmdvec); break; } if (i+1 == vector_active(vline)) cmd->reply = "Command not present."; } cmd_free_strvec(vline); err: if (!cmd->reply) { if (ret == CTRL_CMD_ERROR) { cmd->reply = "An error has occured."; LOGP(DLCTRL, LOGL_NOTICE, "%s: cmd->reply has not been set (ERROR).\n", cmd->variable); } else if (ret == CTRL_CMD_REPLY) { LOGP(DLCTRL, LOGL_NOTICE, "%s: cmd->reply has not been set (type = %d).\n", cmd->variable, cmd->type); cmd->reply = ""; } else { cmd->reply = "Command has been handled."; } } if (ret == CTRL_CMD_ERROR) cmd->type = CTRL_TYPE_ERROR; return ret; err_missing: cmd_free_strvec(vline); cmd->type = CTRL_TYPE_ERROR; cmd->reply = "Error while resolving object"; return ret; err_index: cmd_free_strvec(vline); cmd->type = CTRL_TYPE_ERROR; cmd->reply = "Error while parsing the index."; return ret; } static int handle_control_read(struct osmo_fd * bfd) { int ret = -1; struct osmo_wqueue *queue; struct ctrl_connection *ccon; struct ipaccess_head *iph; struct ipaccess_head_ext *iph_ext; struct msgb *msg = NULL; struct ctrl_cmd *cmd; struct ctrl_handle *ctrl = bfd->data; queue = container_of(bfd, struct osmo_wqueue, bfd); ccon = container_of(queue, struct ctrl_connection, write_queue); ret = ipa_msg_recv_buffered(bfd->fd, &msg, &ccon->pending_msg); if (ret <= 0) { if (ret == -EAGAIN) return 0; if (ret == 0) LOGP(DLCTRL, LOGL_INFO, "The control connection was closed\n"); else LOGP(DLCTRL, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); goto err; } if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { LOGP(DLCTRL, LOGL_ERROR, "The message is too short.\n"); goto err; } iph = (struct ipaccess_head *) msg->data; if (iph->proto != IPAC_PROTO_OSMO) { LOGP(DLCTRL, LOGL_ERROR, "Protocol mismatch. We got 0x%x\n", iph->proto); goto err; } iph_ext = (struct ipaccess_head_ext *) iph->data; if (iph_ext->proto != IPAC_PROTO_EXT_CTRL) { LOGP(DLCTRL, LOGL_ERROR, "Extended protocol mismatch. We got 0x%x\n", iph_ext->proto); goto err; } msg->l2h = iph_ext->data; cmd = ctrl_cmd_parse(ccon, msg); if (cmd) { cmd->ccon = ccon; if (ctrl_cmd_handle(ctrl, cmd, ctrl->data) != CTRL_CMD_HANDLED) { ctrl_cmd_send(queue, cmd); talloc_free(cmd); } } else { cmd = talloc_zero(ccon, struct ctrl_cmd); if (!cmd) goto err; LOGP(DLCTRL, LOGL_ERROR, "Command parser error.\n"); cmd->type = CTRL_TYPE_ERROR; cmd->id = "err"; cmd->reply = "Command parser error."; ctrl_cmd_send(queue, cmd); talloc_free(cmd); } msgb_free(msg); return 0; err: control_close_conn(ccon); msgb_free(msg); return ret; } static int control_write_cb(struct osmo_fd *bfd, struct msgb *msg) { int rc; rc = write(bfd->fd, msg->data, msg->len); if (rc != msg->len) LOGP(DLCTRL, LOGL_ERROR, "Failed to write message to the control connection.\n"); return rc; } static struct ctrl_connection *ctrl_connection_alloc(void *ctx) { struct ctrl_connection *ccon = talloc_zero(ctx, struct ctrl_connection); if (!ccon) return NULL; osmo_wqueue_init(&ccon->write_queue, 100); /* Error handling here? */ INIT_LLIST_HEAD(&ccon->cmds); INIT_LLIST_HEAD(&ccon->def_cmds); return ccon; } static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) { int ret, fd, on; struct ctrl_handle *ctrl; struct ctrl_connection *ccon; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); if (!(what & BSC_FD_READ)) return 0; fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); if (fd < 0) { perror("accept"); return fd; } LOGP(DLCTRL, LOGL_INFO, "accept()ed new control connection from %s\n", inet_ntoa(sa.sin_addr)); #ifdef TCP_NODELAY on = 1; ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); if (ret != 0) { LOGP(DLCTRL, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); close(fd); return ret; } #endif ccon = ctrl_connection_alloc(listen_bfd->data); if (!ccon) { LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n"); close(fd); return -1; } ctrl = listen_bfd->data; ccon->write_queue.bfd.data = ctrl; ccon->write_queue.bfd.fd = fd; ccon->write_queue.bfd.when = BSC_FD_READ; ccon->write_queue.read_cb = handle_control_read; ccon->write_queue.write_cb = control_write_cb; ret = osmo_fd_register(&ccon->write_queue.bfd); if (ret < 0) { LOGP(DLCTRL, LOGL_ERROR, "Could not register FD.\n"); close(ccon->write_queue.bfd.fd); talloc_free(ccon); } llist_add(&ccon->list_entry, &ctrl->ccon_list); return ret; } static uint64_t get_rate_ctr_value(const struct rate_ctr *ctr, int intv) { if (intv >= RATE_CTR_INTV_NUM) return 0; /* Absolute value */ if (intv == -1) { return ctr->current; } else { return ctr->intv[intv].rate; } } static char *get_all_rate_ctr_in_group(void *ctx, const struct rate_ctr_group *ctrg, int intv) { int i; char *counters = talloc_strdup(ctx, ""); if (!counters) return NULL; for (i=0;idesc->num_ctr;i++) { counters = talloc_asprintf_append(counters, "\n%s.%u.%s %"PRIu64, ctrg->desc->group_name_prefix, ctrg->idx, ctrg->desc->ctr_desc[i].name, get_rate_ctr_value(&ctrg->ctr[i], intv)); if (!counters) return NULL; } return counters; } static int get_rate_ctr_group(const char *ctr_group, int intv, struct ctrl_cmd *cmd) { int i; char *counters; struct rate_ctr_group *ctrg; cmd->reply = talloc_asprintf(cmd, "All counters in group %s", ctr_group); if (!cmd->reply) goto oom; for (i=0;;i++) { ctrg = rate_ctr_get_group_by_name_idx(ctr_group, i); if (!ctrg) break; counters = get_all_rate_ctr_in_group(cmd, ctrg, intv); if (!counters) goto oom; cmd->reply = talloc_asprintf_append(cmd->reply, "%s", counters); talloc_free(counters); if (!cmd->reply) goto oom; } /* We found no counter group by that name */ if (i == 0) { cmd->reply = talloc_asprintf(cmd, "No counter group with name %s.", ctr_group); return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; oom: cmd->reply = "OOM."; return CTRL_CMD_ERROR; } static int get_rate_ctr_group_idx(const struct rate_ctr_group *ctrg, int intv, struct ctrl_cmd *cmd) { char *counters; counters = get_all_rate_ctr_in_group(cmd, ctrg, intv); if (!counters) goto oom; cmd->reply = talloc_asprintf(cmd, "All counters in %s.%u%s", ctrg->desc->group_name_prefix, ctrg->idx, counters); talloc_free(counters); if (!cmd->reply) goto oom; return CTRL_CMD_REPLY; oom: cmd->reply = "OOM."; return CTRL_CMD_ERROR; } /* rate_ctr */ CTRL_CMD_DEFINE(rate_ctr, "rate_ctr *"); static int get_rate_ctr(struct ctrl_cmd *cmd, void *data) { int intv; unsigned int idx; char *ctr_group, *ctr_idx, *ctr_name, *tmp, *dup, *saveptr, *interval; struct rate_ctr_group *ctrg; const struct rate_ctr *ctr; dup = talloc_strdup(cmd, cmd->variable); if (!dup) goto oom; /* Skip over possible prefixes (net.) */ tmp = strstr(dup, "rate_ctr"); if (!tmp) { talloc_free(dup); cmd->reply = "rate_ctr not a token in rate_ctr command!"; goto err; } strtok_r(tmp, ".", &saveptr); interval = strtok_r(NULL, ".", &saveptr); if (!interval) { talloc_free(dup); cmd->reply = "Missing interval."; goto err; } if (!strcmp(interval, "abs")) { intv = -1; } else if (!strcmp(interval, "per_sec")) { intv = RATE_CTR_INTV_SEC; } else if (!strcmp(interval, "per_min")) { intv = RATE_CTR_INTV_MIN; } else if (!strcmp(interval, "per_hour")) { intv = RATE_CTR_INTV_HOUR; } else if (!strcmp(interval, "per_day")) { intv = RATE_CTR_INTV_DAY; } else { talloc_free(dup); cmd->reply = "Wrong interval."; goto err; } ctr_group = strtok_r(NULL, ".", &saveptr); tmp = strtok_r(NULL, ".", &saveptr); if (!ctr_group || !tmp) { talloc_free(dup); cmd->reply = "Counter group must be of form a.b"; goto err; } ctr_group[strlen(ctr_group)] = '.'; ctr_idx = strtok_r(NULL, ".", &saveptr); if (!ctr_idx) { talloc_free(dup); return get_rate_ctr_group(ctr_group, intv, cmd); } idx = atoi(ctr_idx); ctrg = rate_ctr_get_group_by_name_idx(ctr_group, idx); if (!ctrg) { talloc_free(dup); cmd->reply = "Counter group not found."; goto err; } ctr_name = strtok_r(NULL, "\0", &saveptr); if (!ctr_name) { talloc_free(dup); return get_rate_ctr_group_idx(ctrg, intv, cmd); } ctr = rate_ctr_get_by_name(ctrg, ctr_name); if (!ctr) { cmd->reply = "Counter name not found."; talloc_free(dup); goto err; } talloc_free(dup); cmd->reply = talloc_asprintf(cmd, "%"PRIu64, get_rate_ctr_value(ctr, intv)); if (!cmd->reply) goto oom; return CTRL_CMD_REPLY; oom: cmd->reply = "OOM"; err: return CTRL_CMD_ERROR; } static int set_rate_ctr(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Can't set rate counter."; return CTRL_CMD_ERROR; } static int verify_rate_ctr(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } /* counter */ CTRL_CMD_DEFINE(counter, "counter *"); static int get_counter(struct ctrl_cmd *cmd, void *data) { char *ctr_name, *tmp, *dup, *saveptr; struct osmo_counter *counter; cmd->reply = "OOM"; dup = talloc_strdup(cmd, cmd->variable); if (!dup) goto err; tmp = strstr(dup, "counter"); if (!tmp) { talloc_free(dup); goto err; } strtok_r(tmp, ".", &saveptr); ctr_name = strtok_r(NULL, "\0", &saveptr); if (!ctr_name) goto err; counter = osmo_counter_get_by_name(ctr_name); if (!counter) { cmd->reply = "Counter name not found."; talloc_free(dup); goto err; } talloc_free(dup); cmd->reply = talloc_asprintf(cmd, "%lu", counter->value); if (!cmd->reply) { cmd->reply = "OOM"; goto err; } return CTRL_CMD_REPLY; err: return CTRL_CMD_ERROR; } static int set_counter(struct ctrl_cmd *cmd, void *data) { cmd->reply = "Can't set counter."; return CTRL_CMD_ERROR; } static int verify_counter(struct ctrl_cmd *cmd, const char *value, void *data) { return 0; } struct ctrl_handle *ctrl_interface_setup(void *data, uint16_t port, ctrl_cmd_lookup lookup) { int ret; struct ctrl_handle *ctrl; ctrl = talloc_zero(data, struct ctrl_handle); if (!ctrl) return NULL; INIT_LLIST_HEAD(&ctrl->ccon_list); ctrl->data = data; ctrl->lookup = lookup; ctrl_node_vec = vector_init(5); if (!ctrl_node_vec) goto err; /* Listen for control connections */ ctrl->listen_fd.cb = listen_fd_cb; ctrl->listen_fd.data = ctrl; ret = osmo_sock_init_ofd(&ctrl->listen_fd, AF_INET, SOCK_STREAM, IPPROTO_TCP, "127.0.0.1", port, OSMO_SOCK_F_BIND); if (ret < 0) goto err_vec; ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_rate_ctr); if (ret) goto err_vec; ret = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_counter); if (ret) goto err_vec; return ctrl; err_vec: vector_free(ctrl_node_vec); ctrl_node_vec = NULL; err: talloc_free(ctrl); return NULL; } libosmocore-0.9.0/src/gb/000077500000000000000000000000001261607044000152145ustar00rootroot00000000000000libosmocore-0.9.0/src/gb/Makefile.am000066400000000000000000000015471261607044000172570ustar00rootroot00000000000000# 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=4:0:0 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} -fno-strict-aliasing # FIXME: this should eventually go into a milenage/Makefile.am noinst_HEADERS = common_vty.h if ENABLE_GB lib_LTLIBRARIES = libosmogb.la libosmogb_la_LDFLAGS = $(LTLDFLAGS_OSMOGB) -version-info $(LIBVERSION) libosmogb_la_LIBADD = \ $(top_builddir)/src/libosmocore.la \ $(top_builddir)/src/vty/libosmovty.la \ $(top_builddir)/src/gsm/libosmogsm.la libosmogb_la_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c \ gprs_bssgp_bss.c common_vty.c endif EXTRA_DIST = libosmogb.map libosmocore-0.9.0/src/gb/common_vty.c000066400000000000000000000043561261607044000175620ustar00rootroot00000000000000/* OpenBSC VTY common helpers */ /* (C) 2009-2012 by Harald Welte * (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, see . * */ #include #include #include #include #include #include #include #include #include #include "common_vty.h" /* Down vty node level. */ gDEFUN(libgb_exit, libgb_exit_cmd, "exit", "Exit current mode and down to previous mode\n") { switch (vty->node) { case L_NS_NODE: case L_BSSGP_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; default: break; } return CMD_SUCCESS; } /* End of configuration. */ gDEFUN(libgb_end, libgb_end_cmd, "end", "End current mode and change to enable mode.") { switch (vty->node) { case L_NS_NODE: case L_BSSGP_NODE: vty_config_unlock(vty); vty->node = ENABLE_NODE; vty->index = NULL; vty->index_sub = NULL; break; default: break; } return CMD_SUCCESS; } int gprs_log_filter_fn(const struct log_context *ctx, struct log_target *tar) { const struct gprs_nsvc *nsvc = ctx->ctx[GPRS_CTX_NSVC]; const struct gprs_bvc *bvc = ctx->ctx[GPRS_CTX_BVC]; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << FLT_NSVC)) != 0 && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) return 1; /* Filter on the NS Virtual Connection */ if ((tar->filter_map & (1 << FLT_BVC)) != 0 && bvc && (bvc == tar->filter_data[FLT_BVC])) return 1; return 0; } int DNS, DBSSGP; libosmocore-0.9.0/src/gb/common_vty.h000066400000000000000000000004231261607044000175560ustar00rootroot00000000000000#include #include extern int DNS, DBSSGP; enum log_filter { _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ FLT_NSVC = 1, FLT_BVC = 2, }; extern struct cmd_element libgb_exit_cmd; extern struct cmd_element libgb_end_cmd; libosmocore-0.9.0/src/gb/gprs_bssgp.c000066400000000000000000001110061261607044000175300ustar00rootroot00000000000000/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ /* (C) 2009-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * TODO: * o properly count incoming BVC-RESET packets in counter group * o set log context as early as possible for outgoing packets */ #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" void *bssgp_tall_ctx = NULL; static const struct rate_ctr_desc bssgp_ctr_description[] = { { "packets.in", "Packets at BSSGP Level ( In)" }, { "packets.out","Packets at BSSGP Level (Out)" }, { "bytes.in", "Bytes at BSSGP Level ( In)" }, { "bytes.out", "Bytes at BSSGP Level (Out)" }, { "blocked", "BVC Blocking count" }, { "discarded", "BVC LLC Discarded count" }, { "status", "BVC Status count" }, }; static const struct rate_ctr_group_desc bssgp_ctrg_desc = { .group_name_prefix = "bssgp.bss_ctx", .group_description = "BSSGP Peer Statistics", .num_ctr = ARRAY_SIZE(bssgp_ctr_description), .ctr_desc = bssgp_ctr_description, .class_id = OSMO_STATS_CLASS_PEER, }; LLIST_HEAD(bssgp_bvc_ctxts); static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv); /* Find a BTS Context based on parsed RA ID and Cell ID */ struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid) { struct bssgp_bvc_ctx *bctx; llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) && bctx->cell_id == cid) return bctx; } return NULL; } /* Find a BTS context based on BVCI+NSEI tuple */ struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei) { struct bssgp_bvc_ctx *bctx; llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { if (bctx->nsei == nsei && bctx->bvci == bvci) return bctx; } return NULL; } struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei) { struct bssgp_bvc_ctx *ctx; ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx); if (!ctx) return NULL; ctx->bvci = bvci; ctx->nsei = nsei; /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */ ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci); ctx->fc = talloc_zero(ctx, struct bssgp_flow_control); /* cofigure for 2Mbit, 30 packets in queue */ bssgp_fc_init(ctx->fc, 100000, 2*1024*1024/8, 30, &_bssgp_tx_dl_ud); llist_add(&ctx->list, &bssgp_bvc_ctxts); return ctx; } /* Chapter 10.4.5: Flow Control BVC ACK */ static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); msgb_nsei(msg) = nsei; msgb_bvci(msg) = ns_bvci; bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* 10.3.7 SUSPEND-ACK PDU */ int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id, uint8_t suspend_ref) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint32_t _tlli; uint8_t ra[6]; msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK; _tlli = htonl(tlli); msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* 10.3.8 SUSPEND-NACK PDU */ int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id, uint8_t *cause) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint32_t _tlli; uint8_t ra[6]; msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; _tlli = htonl(tlli); msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); if (cause) msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* 10.3.10 RESUME-ACK PDU */ int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint32_t _tlli; uint8_t ra[6]; msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_RESUME_ACK; _tlli = htonl(tlli); msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* 10.3.11 RESUME-NACK PDU */ int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id, uint8_t *cause) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint32_t _tlli; uint8_t ra[6]; msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; _tlli = htonl(tlli); msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); if (cause) msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); return gprs_ns_sendmsg(bssgp_nsi, msg); } uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf) { /* 6 octets RAC */ gsm48_parse_ra(raid, buf); /* 2 octets CID */ return ntohs(*(uint16_t *) (buf+6)); } int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid, uint16_t cid) { uint16_t *out_cid = (uint16_t *) (buf + 6); /* 6 octets RAC */ gsm48_construct_ra(buf, raid); /* 2 octets CID */ *out_cid = htons(cid); return 8; } /* Chapter 8.4 BVC-Reset Procedure */ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp, uint16_t ns_bvci) { struct osmo_bssgp_prim nmp; struct bssgp_bvc_ctx *bctx; uint16_t nsei = msgb_nsei(msg); uint16_t bvci; bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci, bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE))); /* look-up or create the BTS context for this BVC */ bctx = btsctx_by_bvci_nsei(bvci, nsei); if (!bctx) bctx = btsctx_alloc(bvci, nsei); /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */ bctx->state &= ~BVC_S_BLOCKED; /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS * informs us about its RAC + Cell ID, so we can create a mapping */ if (bvci != 0 && bvci != 1) { if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET " "missing mandatory IE\n", bvci); return -EINVAL; } /* actually extract RAC / CID */ bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID)); LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n", bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac, bctx->ra_id.rac, bctx->cell_id, bvci); } /* Send NM_BVC_RESET.ind to NM */ memset(&nmp, 0, sizeof(nmp)); nmp.nsei = nsei; nmp.bvci = bvci; nmp.tp = tp; nmp.ra_id = &bctx->ra_id; osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_RESET, PRIM_OP_INDICATION, msg); bssgp_prim_cb(&nmp.oph, NULL); /* Acknowledge the RESET to the BTS */ bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, nsei, bvci, ns_bvci); return 0; } static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim nmp; uint16_t bvci; struct bssgp_bvc_ctx *ptp_ctx; bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); if (bvci == BVCI_SIGNALLING) { /* 8.3.2: Signalling BVC shall never be blocked */ LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " "received block for signalling BVC!?!\n", msgb_nsei(msg), msgb_bvci(msg)); return 0; } LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci); ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); if (!ptp_ctx) return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); ptp_ctx->state |= BVC_S_BLOCKED; rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]); /* Send NM_BVC_BLOCK.ind to NM */ memset(&nmp, 0, sizeof(nmp)); nmp.nsei = msgb_nsei(msg); nmp.bvci = bvci; nmp.tp = tp; osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_BLOCK, PRIM_OP_INDICATION, msg); bssgp_prim_cb(&nmp.oph, NULL); /* We always acknowledge the BLOCKing */ return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg), bvci, msgb_bvci(msg)); }; static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim nmp; uint16_t bvci; struct bssgp_bvc_ctx *ptp_ctx; bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); if (bvci == BVCI_SIGNALLING) { /* 8.3.2: Signalling BVC shall never be blocked */ LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " "received unblock for signalling BVC!?!\n", msgb_nsei(msg), msgb_bvci(msg)); return 0; } DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci); ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); if (!ptp_ctx) return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); ptp_ctx->state &= ~BVC_S_BLOCKED; /* Send NM_BVC_UNBLOCK.ind to NM */ memset(&nmp, 0, sizeof(nmp)); nmp.nsei = msgb_nsei(msg); nmp.bvci = bvci; nmp.tp = tp; osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_UNBLOCK, PRIM_OP_INDICATION, msg); bssgp_prim_cb(&nmp.oph, NULL); /* We always acknowledge the unBLOCKing */ return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg), bvci, msgb_bvci(msg)); }; /* Uplink unit-data */ static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *ctx) { struct osmo_bssgp_prim gbp; struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); /* extract TLLI and parse TLV IEs */ msgb_tlli(msg) = ntohl(budh->tlli); DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg)); /* Cell ID and LLC_PDU are the only mandatory IE */ if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) || !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD " "missing mandatory IE\n", msgb_tlli(msg)); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } /* store pointer to LLC header and CELL ID in msgb->cb */ msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU); msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID); /* Send BSSGP_UL_UD.ind to NM */ memset(&gbp, 0, sizeof(gbp)); gbp.nsei = ctx->nsei; gbp.bvci = ctx->bvci; gbp.tlli = msgb_tlli(msg); gbp.tp = tp; osmo_prim_init(&gbp.oph, SAP_BSSGP_LL, PRIM_BSSGP_UL_UD, PRIM_OP_INDICATION, msg); return bssgp_prim_cb(&gbp.oph, NULL); } static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim gbp; struct gprs_ra_id raid; uint32_t tlli; uint16_t ns_bvci = msgb_bvci(msg); int rc; if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND " "missing mandatory IE\n", ns_bvci); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n", ns_bvci, tlli); gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); /* Inform GMM about the SUSPEND request */ memset(&gbp, 0, sizeof(gbp)); gbp.nsei = msgb_nsei(msg); gbp.bvci = ns_bvci; gbp.tlli = tlli; gbp.ra_id = &raid; osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND, PRIM_OP_REQUEST, msg); rc = bssgp_prim_cb(&gbp.oph, NULL); if (rc < 0) return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL); bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0); return 0; } static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp) { struct osmo_bssgp_prim gbp; struct gprs_ra_id raid; uint32_t tlli; uint8_t suspend_ref; uint16_t ns_bvci = msgb_bvci(msg); int rc; if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) || !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME " "missing mandatory IE\n", ns_bvci); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR); DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ns_bvci, tlli); gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); /* Inform GMM about the RESUME request */ memset(&gbp, 0, sizeof(gbp)); gbp.nsei = msgb_nsei(msg); gbp.bvci = ns_bvci; gbp.tlli = tlli; gbp.ra_id = &raid; gbp.u.resume.suspend_ref = suspend_ref; osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_RESUME, PRIM_OP_REQUEST, msg); rc = bssgp_prim_cb(&gbp.oph, NULL); if (rc < 0) return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid, NULL); bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid); return 0; } static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *ctx) { struct osmo_bssgp_prim nmp; uint32_t tlli = 0; if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) || !TLVP_PRESENT(tp, BSSGP_IE_BVCI) || !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED " "missing mandatory IE\n", ctx->bvci); } if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n", ctx->bvci, tlli); rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]); /* send NM_LLC_DISCARDED to NM */ memset(&nmp, 0, sizeof(nmp)); nmp.nsei = msgb_nsei(msg); nmp.bvci = ctx->bvci; nmp.tlli = tlli; nmp.tp = tp; osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_LLC_DISCARDED, PRIM_OP_INDICATION, msg); return bssgp_prim_cb(&nmp.oph, NULL); } int bssgp_rx_status(struct msgb *msg, struct tlv_parsed *tp, uint16_t bvci, struct bssgp_bvc_ctx *bctx) { struct osmo_bssgp_prim nmp; enum gprs_bssgp_cause cause; if (!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS " "missing mandatory IE\n", bvci); cause = BSSGP_CAUSE_PROTO_ERR_UNSPEC; } else { cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE); } LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx BVC STATUS, cause=%s\n", bvci, bssgp_cause_str(cause)); if (cause == BSSGP_CAUSE_BVCI_BLOCKED || cause == BSSGP_CAUSE_UNKNOWN_BVCI) { if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx STATUS cause=%s " "missing conditional BVCI IE\n", bvci, bssgp_cause_str(cause)); } if (bctx) rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_STATUS]); /* send NM_STATUS to NM */ memset(&nmp, 0, sizeof(nmp)); nmp.nsei = msgb_nsei(msg); nmp.bvci = bvci; nmp.tp = tp; osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_STATUS, PRIM_OP_INDICATION, msg); return bssgp_prim_cb(&nmp.oph, NULL); } /* One element (msgb) in a BSSGP Flow Control queue */ struct bssgp_fc_queue_element { /* linked list of queue elements */ struct llist_head list; /* The message that we have enqueued */ struct msgb *msg; /* Length of the LLC PDU part of the contained message */ uint32_t llc_pdu_len; /* private pointer passed to the flow control out_cb function */ void *priv; }; static int fc_queue_timer_cfg(struct bssgp_flow_control *fc); static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_len); static void fc_timer_cb(void *data) { struct bssgp_flow_control *fc = data; struct bssgp_fc_queue_element *fcqe; struct timeval time_now; /* if the queue is empty, we return without sending something * and without re-starting the timer */ if (llist_empty(&fc->queue)) return; /* get the first entry from the queue */ fcqe = llist_entry(fc->queue.next, struct bssgp_fc_queue_element, list); if (bssgp_fc_needs_queueing(fc, fcqe->llc_pdu_len)) { LOGP(DBSSGP, LOGL_NOTICE, "BSSGP-FC: fc_timer_cb() but still " "not able to send PDU of %u bytes\n", fcqe->llc_pdu_len); /* make sure we re-start the timer */ fc_queue_timer_cfg(fc); return; } /* remove from the queue */ llist_del(&fcqe->list); fc->queue_depth--; /* record the time we transmitted this PDU */ gettimeofday(&time_now, NULL); fc->time_last_pdu = time_now; /* call the output callback for this FC instance */ fc->out_cb(fcqe->priv, fcqe->msg, fcqe->llc_pdu_len, NULL); /* we expect that out_cb will in the end free the msgb once * it is no longer needed */ /* but we have to free the queue element ourselves */ talloc_free(fcqe); /* re-configure the timer for the next PDU */ fc_queue_timer_cfg(fc); } /* configure/schedule the flow control timer to expire once the bucket * will have leaked a sufficient number of bytes to transmit the next * PDU in the queue */ static int fc_queue_timer_cfg(struct bssgp_flow_control *fc) { struct bssgp_fc_queue_element *fcqe; uint32_t msecs; if (llist_empty(&fc->queue)) return 0; fcqe = llist_entry(fc->queue.next, struct bssgp_fc_queue_element, list); if (fc->bucket_leak_rate != 0) { /* Calculate the point in time at which we will have leaked * a sufficient number of bytes from the bucket to transmit * the first PDU in the queue */ msecs = (fcqe->llc_pdu_len * 1000) / fc->bucket_leak_rate; /* FIXME: add that time to fc->time_last_pdu and subtract it from * current time */ fc->timer.data = fc; fc->timer.cb = &fc_timer_cb; osmo_timer_schedule(&fc->timer, msecs / 1000, (msecs % 1000) * 1000); } else { /* If the PCU is telling us to not send any more data at all, * there's no point starting a timer. */ } return 0; } /* Enqueue a PDU in the flow control queue for delayed transmission */ static int fc_enqueue(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv) { struct bssgp_fc_queue_element *fcqe; if (fc->queue_depth >= fc->max_queue_depth) return -ENOSPC; fcqe = talloc_zero(fc, struct bssgp_fc_queue_element); if (!fcqe) return -ENOMEM; fcqe->msg = msg; fcqe->llc_pdu_len = llc_pdu_len; fcqe->priv = priv; llist_add_tail(&fcqe->list, &fc->queue); fc->queue_depth++; /* re-configure the timer for dequeueing the pdu */ fc_queue_timer_cfg(fc); return 0; } /* According to Section 8.2 */ static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_len) { struct timeval time_now, time_diff; int64_t bucket_predicted; uint32_t csecs_elapsed, leaked; /* B' = B + L(p) - (Tc - Tp)*R */ /* compute number of centi-seconds that have elapsed since transmitting * the last PDU (Tc - Tp) */ gettimeofday(&time_now, NULL); timersub(&time_now, &fc->time_last_pdu, &time_diff); csecs_elapsed = time_diff.tv_sec*100 + time_diff.tv_usec/10000; /* compute number of bytes that have leaked in the elapsed number * of centi-seconds */ leaked = csecs_elapsed * (fc->bucket_leak_rate / 100); /* add the current PDU length to the last bucket level */ bucket_predicted = fc->bucket_counter + pdu_len; /* ... and subtract the number of leaked bytes */ bucket_predicted -= leaked; if (bucket_predicted < pdu_len) { /* this is just to make sure the bucket doesn't underflow */ bucket_predicted = pdu_len; goto pass; } if (bucket_predicted <= fc->bucket_size_max) { /* the bucket is not full yet, we can pass the packet */ fc->bucket_counter = bucket_predicted; goto pass; } /* bucket is full, PDU needs to be delayed */ return 1; pass: /* if we reach here, the PDU can pass */ return 0; } /* output callback for BVC flow control */ static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv) { return gprs_ns_sendmsg(bssgp_nsi, msg); } /* input function of the flow control implementation, called first * for the MM flow control, and then as the MM flow control output * callback in order to perform BVC flow control */ int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv) { struct timeval time_now; if (llc_pdu_len > fc->bucket_size_max) { LOGP(DBSSGP, LOGL_NOTICE, "Single PDU (size=%u) is larger " "than maximum bucket size (%u)!\n", llc_pdu_len, fc->bucket_size_max); msgb_free(msg); return -EIO; } if (bssgp_fc_needs_queueing(fc, llc_pdu_len)) { return fc_enqueue(fc, msg, llc_pdu_len, priv); } else { /* record the time we transmitted this PDU */ gettimeofday(&time_now, NULL); fc->time_last_pdu = time_now; return fc->out_cb(priv, msg, llc_pdu_len, NULL); } } /* Initialize the Flow Control structure */ void bssgp_fc_init(struct bssgp_flow_control *fc, uint32_t bucket_size_max, uint32_t bucket_leak_rate, uint32_t max_queue_depth, int (*out_cb)(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv)) { fc->out_cb = out_cb; fc->bucket_size_max = bucket_size_max; fc->bucket_leak_rate = bucket_leak_rate; fc->max_queue_depth = max_queue_depth; INIT_LLIST_HEAD(&fc->queue); gettimeofday(&fc->time_last_pdu, NULL); } /* Initialize the Flow Control parameters for a new MS according to * default values for the BVC specified by BVCI and NSEI */ int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci, uint16_t nsei, uint32_t max_queue_depth) { struct bssgp_bvc_ctx *ctx; ctx = btsctx_by_bvci_nsei(bvci, nsei); if (!ctx) return -ENODEV; /* output call-back of per-MS FC is per-CTX FC */ bssgp_fc_init(fc_ms, ctx->bmax_default_ms, ctx->r_default_ms, max_queue_depth, bssgp_fc_in); return 0; } static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx) { uint32_t old_leak_rate = bctx->fc->bucket_leak_rate; uint32_t old_r_def_ms = bctx->r_default_ms; DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n", bctx->bvci); if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) || !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) || !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) || !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) || !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC " "missing mandatory IE\n", bctx->bvci); return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } /* 11.3.5 Bucket Size in 100 octets unit */ bctx->fc->bucket_size_max = 100 * ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVC_BUCKET_SIZE)); /* 11.3.4 Bucket Leak Rate in 100 bits/sec unit */ bctx->fc->bucket_leak_rate = 100 * ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8; /* 11.3.2 in octets */ bctx->bmax_default_ms = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BMAX_DEFAULT_MS)); /* 11.3.32 Bucket Leak rate in 100bits/sec unit */ bctx->r_default_ms = 100 * ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_R_DEFAULT_MS)) / 8; if (old_leak_rate != 0 && bctx->fc->bucket_leak_rate == 0) LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak " "rate of 0, stopping all DL GPRS!\n"); else if (old_leak_rate == 0 && bctx->fc->bucket_leak_rate != 0) LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak " "rate of != 0, restarting all DL GPRS!\n"); if (old_r_def_ms != 0 && bctx->r_default_ms == 0) LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default " "bucket leak rate of 0, stopping DL GPRS!\n"); else if (old_r_def_ms == 0 && bctx->r_default_ms != 0) LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default " "bucket leak rate != 0, restarting DL GPRS!\n"); /* reconfigure the timer for flow control based on new values */ fc_queue_timer_cfg(bctx->fc); /* Send FLOW_CONTROL_BVC_ACK */ return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG), msgb_bvci(msg)); } /* Receive a BSSGP PDU from a BSS on a PTP BVCI */ static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; int rc = 0; OSMO_ASSERT(pdu_type != BSSGP_PDUT_STATUS); /* If traffic is received on a BVC that is marked as blocked, the * received PDU shall not be accepted and a STATUS PDU (Cause value: * BVC Blocked) shall be sent to the peer entity on the signalling BVC */ if (bctx->state & BVC_S_BLOCKED) { uint16_t bvci = msgb_bvci(msg); return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg); } switch (pdu_type) { case BSSGP_PDUT_UL_UNITDATA: /* some LLC data from the MS */ rc = bssgp_rx_ul_ud(msg, tp, bctx); break; case BSSGP_PDUT_RA_CAPABILITY: /* BSS requests RA capability or IMSI */ DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n", bctx->bvci); /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */ /* FIXME: send RA_CAPA_UPDATE_ACK */ break; case BSSGP_PDUT_RADIO_STATUS: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci); /* BSS informs us of some exception */ /* FIXME: send GMM_RADIO_STATUS.ind to GMM */ break; case BSSGP_PDUT_FLOW_CONTROL_BVC: /* BSS informs us of available bandwidth in Gb interface */ rc = bssgp_rx_fc_bvc(msg, tp, bctx); break; case BSSGP_PDUT_FLOW_CONTROL_MS: /* BSS informs us of available bandwidth to one MS */ DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n", bctx->bvci); /* FIXME: actually implement flow control */ /* FIXME: Send FLOW_CONTROL_MS_ACK */ break; case BSSGP_PDUT_STATUS: /* This is already handled in bssgp_rcvmsg() */ break; case BSSGP_PDUT_DOWNLOAD_BSS_PFC: case BSSGP_PDUT_CREATE_BSS_PFC_ACK: case BSSGP_PDUT_CREATE_BSS_PFC_NACK: case BSSGP_PDUT_MODIFY_BSS_PFC: case BSSGP_PDUT_DELETE_BSS_PFC_ACK: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] " "implemented\n", bctx->bvci, pdu_type); rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); break; /* those only exist in the SGSN -> BSS direction */ case BSSGP_PDUT_DL_UNITDATA: case BSSGP_PDUT_PAGING_PS: case BSSGP_PDUT_PAGING_CS: case BSSGP_PDUT_RA_CAPA_UPDATE_ACK: case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: case BSSGP_PDUT_FLOW_CONTROL_MS_ACK: DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists " "in DL\n", bctx->bvci, pdu_type); bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); rc = -EINVAL; break; default: DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type); rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); break; } return rc; } /* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_bvc_ctx *bctx) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); uint8_t pdu_type = bgph->pdu_type; int rc = 0; uint16_t ns_bvci = msgb_bvci(msg); uint16_t bvci = bctx ? bctx->bvci : ns_bvci; switch (bgph->pdu_type) { case BSSGP_PDUT_SUSPEND: /* MS wants to suspend */ rc = bssgp_rx_suspend(msg, tp); break; case BSSGP_PDUT_RESUME: /* MS wants to resume */ rc = bssgp_rx_resume(msg, tp); break; case BSSGP_PDUT_FLUSH_LL_ACK: /* BSS informs us it has performed LL FLUSH */ DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bvci); /* FIXME: send NM_FLUSH_LL.res to NM */ break; case BSSGP_PDUT_LLC_DISCARD: /* BSS informs that some LLC PDU's have been discarded */ if (!bctx) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx LLC-DISCARD missing mandatory BVCI\n"); goto err_mand_ie; } rc = bssgp_rx_llc_disc(msg, tp, bctx); break; case BSSGP_PDUT_BVC_BLOCK: /* BSS tells us that BVC shall be blocked */ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK " "missing mandatory IE\n"); goto err_mand_ie; } rc = bssgp_rx_bvc_block(msg, tp); break; case BSSGP_PDUT_BVC_UNBLOCK: /* BSS tells us that BVC shall be unblocked */ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK " "missing mandatory IE\n"); goto err_mand_ie; } rc = bssgp_rx_bvc_unblock(msg, tp); break; case BSSGP_PDUT_BVC_RESET: /* BSS tells us that BVC init is required */ if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET " "missing mandatory IE\n"); goto err_mand_ie; } rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci); break; case BSSGP_PDUT_STATUS: /* This is already handled in bssgp_rcvmsg() */ break; /* those only exist in the SGSN -> BSS direction */ case BSSGP_PDUT_PAGING_PS: case BSSGP_PDUT_PAGING_CS: case BSSGP_PDUT_SUSPEND_ACK: case BSSGP_PDUT_SUSPEND_NACK: case BSSGP_PDUT_RESUME_ACK: case BSSGP_PDUT_RESUME_NACK: case BSSGP_PDUT_FLUSH_LL: case BSSGP_PDUT_BVC_BLOCK_ACK: case BSSGP_PDUT_BVC_UNBLOCK_ACK: case BSSGP_PDUT_SGSN_INVOKE_TRACE: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists " "in DL\n", bvci, pdu_type); bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); rc = -EINVAL; break; default: DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bvci, pdu_type); rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); break; } return rc; err_mand_ie: return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); } /* We expect msgb_bssgph() to point to the BSSGP header */ int bssgp_rcvmsg(struct msgb *msg) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); struct tlv_parsed tp; struct bssgp_bvc_ctx *bctx; uint8_t pdu_type = bgph->pdu_type; uint16_t ns_bvci = msgb_bvci(msg); uint16_t bvci = ns_bvci; int data_len; int rc = 0; /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */ /* UNITDATA BSSGP headers have TLLI in front */ if (pdu_type != BSSGP_PDUT_UL_UNITDATA && pdu_type != BSSGP_PDUT_DL_UNITDATA) { data_len = msgb_bssgp_len(msg) - sizeof(*bgph); rc = bssgp_tlv_parse(&tp, bgph->data, data_len); } else { data_len = msgb_bssgp_len(msg) - sizeof(*budh); rc = bssgp_tlv_parse(&tp, budh->data, data_len); } if (bvci == BVCI_SIGNALLING && TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); /* look-up or create the BTS context for this BVC */ bctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); if (bctx) { log_set_context(GPRS_CTX_BVC, bctx); rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]); rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], msgb_bssgp_len(msg)); } /* Always handle STATUS PDUs, even if they contain an invalid BVCI or * are otherwise unexpected */ if (pdu_type == BSSGP_PDUT_STATUS) /* Some exception has occurred */ return bssgp_rx_status(msg, &tp, bvci, bctx); /* Only a RESET PDU can create a new BVC context, otherwise it must be * registered if a BVCI is given. */ if (!bctx && bvci != BVCI_SIGNALLING && pdu_type != BSSGP_PDUT_BVC_RESET) { LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU " "type %u for unknown BVCI\n", msgb_nsei(msg), bvci, pdu_type); return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); } if (ns_bvci == BVCI_SIGNALLING) rc = bssgp_rx_sign(msg, &tp, bctx); else if (ns_bvci == BVCI_PTM) rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); else if (bctx) rc = bssgp_rx_ptp(msg, &tp, bctx); else LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Cannot handle PDU type %u for " "unknown BVCI, NS BVCI %u\n", msgb_nsei(msg), bvci, pdu_type, ns_bvci); return rc; } int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime, struct bssgp_dl_ud_par *dup) { struct bssgp_bvc_ctx *bctx; struct bssgp_ud_hdr *budh; uint8_t llc_pdu_tlv_hdr_len = 2; uint8_t *llc_pdu_tlv; uint16_t msg_len = msg->len; uint16_t bvci = msgb_bvci(msg); uint16_t nsei = msgb_nsei(msg); uint16_t _pdu_lifetime = htons(pdu_lifetime); /* centi-seconds */ uint16_t drx_params; OSMO_ASSERT(dup != NULL); /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */ if (bvci <= BVCI_PTM ) { LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n", bvci); msgb_free(msg); return -EINVAL; } bctx = btsctx_by_bvci_nsei(bvci, nsei); if (!bctx) { LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to unknown BVCI %u\n", bvci); msgb_free(msg); return -ENODEV; } if (msg->len > TVLV_MAX_ONEBYTE) llc_pdu_tlv_hdr_len += 1; /* prepend the tag and length of the LLC-PDU TLV */ llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len); llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU; if (llc_pdu_tlv_hdr_len > 2) { llc_pdu_tlv[1] = msg_len >> 8; llc_pdu_tlv[2] = msg_len & 0xff; } else { llc_pdu_tlv[1] = msg_len & 0x7f; llc_pdu_tlv[1] |= 0x80; } /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */ /* Old TLLI to help BSS map from old->new */ if (dup->tlli) { uint32_t tlli = htonl(*dup->tlli); msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli); } /* IMSI */ if (dup->imsi && strlen(dup->imsi)) { uint8_t mi[10]; int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi); if (imsi_len > 2) msgb_tvlv_push(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2); } /* DRX parameters */ drx_params = htons(dup->drx_parms); msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2, (uint8_t *) &drx_params); /* FIXME: Priority */ /* MS Radio Access Capability */ if (dup->ms_ra_cap.len) msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP, dup->ms_ra_cap.len, dup->ms_ra_cap.v); /* prepend the pdu lifetime */ msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&_pdu_lifetime); /* prepend the QoS profile, TLLI and pdu type */ budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); memcpy(budh->qos_profile, dup->qos_profile, sizeof(budh->qos_profile)); budh->tlli = htonl(msgb_tlli(msg)); budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]); rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len); /* Identifiers down: BVCI, NSEI (in msgb->cb) */ /* check if we have to go through per-ms flow control or can go * directly to the per-BSS flow control */ if (dup->fc) return bssgp_fc_in(dup->fc, msg, msg_len, bctx->fc); else return bssgp_fc_in(bctx->fc, msg, msg_len, NULL); } /* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */ int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci, struct bssgp_paging_info *pinfo) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t drx_params = htons(pinfo->drx_params); uint8_t mi[10]; int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi); uint8_t ra[6]; if (imsi_len < 2) return -EINVAL; msgb_nsei(msg) = nsei; msgb_bvci(msg) = ns_bvci; if (pinfo->mode == BSSGP_PAGING_PS) bgph->pdu_type = BSSGP_PDUT_PAGING_PS; else bgph->pdu_type = BSSGP_PDUT_PAGING_CS; /* IMSI */ msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2); /* DRX Parameters */ msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2, (uint8_t *) &drx_params); /* Scope */ switch (pinfo->scope) { case BSSGP_PAGING_BSS_AREA: { uint8_t null = 0; msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null); } break; case BSSGP_PAGING_LOCATION_AREA: gsm48_construct_ra(ra, &pinfo->raid); msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra); break; case BSSGP_PAGING_ROUTEING_AREA: gsm48_construct_ra(ra, &pinfo->raid); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); break; case BSSGP_PAGING_BVCI: { uint16_t bvci = htons(pinfo->bvci); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci); } break; } /* QoS profile mandatory for PS */ if (pinfo->mode == BSSGP_PAGING_PS) msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos); /* Optional (P-)TMSI */ if (pinfo->ptmsi) { uint32_t ptmsi = htonl(*pinfo->ptmsi); msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi); } return gprs_ns_sendmsg(bssgp_nsi, msg); } void bssgp_set_log_ss(int ss) { DBSSGP = ss; } libosmocore-0.9.0/src/gb/gprs_bssgp_bss.c000066400000000000000000000416401261607044000204050ustar00rootroot00000000000000/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ /* (C) 2009-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" #define GSM_IMSI_LENGTH 17 uint8_t *bssgp_msgb_tlli_put(struct msgb *msg, uint32_t tlli) { uint32_t _tlli = htonl(tlli); return msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); } /*! \brief GMM-SUSPEND.req (Chapter 10.3.6) */ int bssgp_tx_suspend(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint8_t ra[6]; LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx SUSPEND (TLLI=0x%04x)\n", tlli); msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_SUSPEND; bssgp_msgb_tlli_put(msg, tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief GMM-RESUME.req (Chapter 10.3.9) */ int bssgp_tx_resume(uint16_t nsei, uint32_t tlli, const struct gprs_ra_id *ra_id, uint8_t suspend_ref) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint8_t ra[6]; LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=0) Tx RESUME (TLLI=0x%04x)\n", tlli); msgb_nsei(msg) = nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_RESUME; bssgp_msgb_tlli_put(msg, tlli); gsm48_construct_ra(ra, ra_id); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit RA-CAPABILITY-UPDATE (10.3.3) */ int bssgp_tx_ra_capa_upd(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RA-CAPA-UPD (TLLI=0x%04x)\n", bctx->bvci, tlli); /* set NSEI and BVCI in msgb cb */ msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = bctx->bvci; bgph->pdu_type = BSSGP_PDUT_RA_CAPA_UDPATE; bssgp_msgb_tlli_put(msg, tlli); msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* first common part of RADIO-STATUS */ static struct msgb *common_tx_radio_status(struct bssgp_bvc_ctx *bctx) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx RADIO-STATUS ", bctx->bvci); /* set NSEI and BVCI in msgb cb */ msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = bctx->bvci; bgph->pdu_type = BSSGP_PDUT_RADIO_STATUS; return msg; } /* second common part of RADIO-STATUS */ static int common_tx_radio_status2(struct msgb *msg, uint8_t cause) { msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); LOGPC(DBSSGP, LOGL_NOTICE, "CAUSE=%u\n", cause); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit RADIO-STATUS for TLLI (10.3.5) */ int bssgp_tx_radio_status_tlli(struct bssgp_bvc_ctx *bctx, uint8_t cause, uint32_t tlli) { struct msgb *msg = common_tx_radio_status(bctx); if (!msg) return -ENOMEM; bssgp_msgb_tlli_put(msg, tlli); LOGPC(DBSSGP, LOGL_NOTICE, "TLLI=0x%08x ", tlli); return common_tx_radio_status2(msg, cause); } /*! \brief Transmit RADIO-STATUS for TMSI (10.3.5) */ int bssgp_tx_radio_status_tmsi(struct bssgp_bvc_ctx *bctx, uint8_t cause, uint32_t tmsi) { struct msgb *msg = common_tx_radio_status(bctx); uint32_t _tmsi = htonl(tmsi); if (!msg) return -ENOMEM; msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *)&_tmsi); LOGPC(DBSSGP, LOGL_NOTICE, "TMSI=0x%08x ", tmsi); return common_tx_radio_status2(msg, cause); } /*! \brief Transmit RADIO-STATUS for IMSI (10.3.5) */ int bssgp_tx_radio_status_imsi(struct bssgp_bvc_ctx *bctx, uint8_t cause, const char *imsi) { struct msgb *msg = common_tx_radio_status(bctx); uint8_t mi[10]; int imsi_len = gsm48_generate_mid_from_imsi(mi, imsi); if (!msg) return -ENOMEM; /* strip the MI type and length values (2 bytes) */ if (imsi_len > 2) msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2); LOGPC(DBSSGP, LOGL_NOTICE, "IMSI=%s ", imsi); return common_tx_radio_status2(msg, cause); } /*! \brief Transmit FLUSH-LL-ACK (Chapter 10.4.2) */ int bssgp_tx_flush_ll_ack(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t action, uint16_t bvci_new, uint32_t num_octets) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci_new = htons(bvci_new); uint32_t _oct_aff = htonl(num_octets & 0xFFFFFF); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_FLUSH_LL_ACK; bssgp_msgb_tlli_put(msg, tlli); msgb_tvlv_put(msg, BSSGP_IE_FLUSH_ACTION, 1, &action); if (action == 1) /* transferred */ msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci_new); msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, (uint8_t *) &_oct_aff); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit LLC-DISCARDED (Chapter 10.4.3) */ int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t num_frames, uint32_t num_octets) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci = htons(bctx->bvci); uint32_t _oct_aff = htonl(num_octets & 0xFFFFFF); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx LLC-DISCARDED " "TLLI=0x%04x, FRAMES=%u, OCTETS=%u\n", bctx->bvci, tlli, num_frames, num_octets); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_LLC_DISCARD; bssgp_msgb_tlli_put(msg, tlli); msgb_tvlv_put(msg, BSSGP_IE_LLC_FRAMES_DISCARDED, 1, &num_frames); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); msgb_tvlv_put(msg, BSSGP_IE_NUM_OCT_AFF, 3, ((uint8_t *) &_oct_aff) + 1); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit a BVC-BLOCK message (Chapter 10.4.8) */ int bssgp_tx_bvc_block(struct bssgp_bvc_ctx *bctx, uint8_t cause) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci = htons(bctx->bvci); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK " "CAUSE=%u\n", bctx->bvci, cause); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK; msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit a BVC-UNBLOCK message (Chapter 10.4.10) */ int bssgp_tx_bvc_unblock(struct bssgp_bvc_ctx *bctx) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci = htons(bctx->bvci); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-BLOCK\n", bctx->bvci); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK; msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit a BVC-RESET message (Chapter 10.4.12) */ int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci = htons(bvci); LOGP(DBSSGP, LOGL_NOTICE, "BSSGP (BVCI=%u) Tx BVC-RESET " "CAUSE=%u\n", bvci, cause); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = 0; /* Signalling */ bgph->pdu_type = BSSGP_PDUT_BVC_RESET; msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); if (bvci != BVCI_PTM) { uint8_t bssgp_cid[8]; bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id); msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid); } /* Optional: Feature Bitmap */ return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit a FLOW_CONTROL-BVC (Chapter 10.4.4) * \param[in] bctx BVC Context * \param[in] tag Additional tag to identify acknowledge * \param[in] bucket_size Maximum bucket size in octets * \param[in] bucket_leak_rate Bucket leak rate in octets/sec * \param[in] bmax_default_ms Maximum bucket size default for MS * \param[in] r_default_ms Bucket leak rate default for MS in octets/sec * \param[in] bucket_full_ratio Ratio (in percent) of queue filling * \param[in] queue_delay_ms Average queuing delay in milliseconds */ int bssgp_tx_fc_bvc(struct bssgp_bvc_ctx *bctx, uint8_t tag, uint32_t bucket_size, uint32_t bucket_leak_rate, uint32_t bmax_default_ms, uint32_t r_default_ms, uint8_t *bucket_full_ratio, uint32_t *queue_delay_ms) { struct msgb *msg; struct bssgp_normal_hdr *bgph; uint16_t e_bucket_size, e_leak_rate, e_bmax_default_ms, e_r_default_ms; uint16_t e_queue_delay = 0; /* to make gcc happy */ if ((bucket_size / 100) > 0xffff) return -EINVAL; e_bucket_size = htons(bucket_size / 100); if ((bucket_leak_rate * 8 / 100) > 0xffff) return -EINVAL; e_leak_rate = htons((bucket_leak_rate * 8) / 100); if ((bmax_default_ms / 100) > 0xffff) return -EINVAL; e_bmax_default_ms = htons(bmax_default_ms / 100); if ((r_default_ms * 8 / 100) > 0xffff) return -EINVAL; e_r_default_ms = htons((r_default_ms * 8) / 100); if (queue_delay_ms) { if ((*queue_delay_ms / 10) > 60000) return -EINVAL; else if (*queue_delay_ms == 0xFFFFFFFF) e_queue_delay = 0xFFFF; else e_queue_delay = htons(*queue_delay_ms / 10); } msg = bssgp_msgb_alloc(); bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = bctx->bvci; bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC; msgb_tvlv_put(msg, BSSGP_IE_TAG, sizeof(tag), (uint8_t *)&tag); msgb_tvlv_put(msg, BSSGP_IE_BVC_BUCKET_SIZE, sizeof(e_bucket_size), (uint8_t *) &e_bucket_size); msgb_tvlv_put(msg, BSSGP_IE_BUCKET_LEAK_RATE, sizeof(e_leak_rate), (uint8_t *) &e_leak_rate); msgb_tvlv_put(msg, BSSGP_IE_BMAX_DEFAULT_MS, sizeof(e_bmax_default_ms), (uint8_t *) &e_bmax_default_ms); msgb_tvlv_put(msg, BSSGP_IE_R_DEFAULT_MS, sizeof(e_r_default_ms), (uint8_t *) &e_r_default_ms); if (bucket_full_ratio) msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, bucket_full_ratio); if (queue_delay_ms) msgb_tvlv_put(msg, BSSGP_IE_BVC_MEASUREMENT, sizeof(e_queue_delay), (uint8_t *) &e_queue_delay); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief Transmit a FLOW_CONTROL-MS (Chapter 10.4.6) * \param[in] bctx BVC Context * \param[in] tlli TLLI to identify MS * \param[in] tag Additional tag to identify acknowledge * \param[in] ms_bucket_size Maximum bucket size in octets * \param[in] bucket_leak_rate Bucket leak rate in octets/sec * \param[in] bucket_full_ratio Ratio (in percent) of queue filling */ int bssgp_tx_fc_ms(struct bssgp_bvc_ctx *bctx, uint32_t tlli, uint8_t tag, uint32_t ms_bucket_size, uint32_t bucket_leak_rate, uint8_t *bucket_full_ratio) { struct msgb *msg; struct bssgp_normal_hdr *bgph; uint16_t e_bucket_size, e_leak_rate; uint32_t e_tlli; if ((ms_bucket_size / 100) > 0xffff) return -EINVAL; e_bucket_size = ms_bucket_size / 100; if ((bucket_leak_rate * 8 / 100) > 0xffff) return -EINVAL; e_leak_rate = (bucket_leak_rate * 8) / 100; msg = bssgp_msgb_alloc(); bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = bctx->bvci; bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_MS; e_tlli = htonl(tlli); msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(e_tlli), (uint8_t *)&e_tlli); msgb_tvlv_put(msg, BSSGP_IE_TAG, sizeof(tag), (uint8_t *)&tag); msgb_tvlv_put(msg, BSSGP_IE_MS_BUCKET_SIZE, sizeof(e_bucket_size), (uint8_t *) &e_bucket_size); msgb_tvlv_put(msg, BSSGP_IE_BUCKET_LEAK_RATE, sizeof(e_leak_rate), (uint8_t *) &e_leak_rate); if (bucket_full_ratio) msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, bucket_full_ratio); return gprs_ns_sendmsg(bssgp_nsi, msg); } /*! \brief RL-UL-UNITDATA.req (Chapter 10.2.2) * \param[in] bctx BVC Context * \param[in] tlli TLLI to identify MS * \param[in] qos_profile Pointer to three octests of QoS profile * \param[in] llc_pdu msgb pointer containing UL Unitdata IE payload */ int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli, const uint8_t *qos_profile, struct msgb *llc_pdu) { struct msgb *msg = llc_pdu; uint8_t bssgp_cid[8]; uint8_t bssgp_align[3] = {0, 0, 0}; struct bssgp_ud_hdr *budh; int align = sizeof(*budh); /* FIXME: Optional LSA Identifier List, PFI */ /* Cell Identifier */ bssgp_create_cell_id(bssgp_cid, &bctx->ra_id, bctx->cell_id); align += 2; /* add T+L */ align += sizeof(bssgp_cid); /* First push alignment IE */ align += 2; /* add T+L */ align = (4 - align) & 3; /* how many octest are required to align? */ msgb_tvlv_push(msg, BSSGP_IE_ALIGNMENT, align, bssgp_align); /* Push other IEs */ msgb_tvlv_push(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid); /* User Data Header */ budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); budh->tlli = htonl(tlli); memcpy(budh->qos_profile, qos_profile, 3); budh->pdu_type = BSSGP_PDUT_UL_UNITDATA; /* set NSEI and BVCI in msgb cb */ msgb_nsei(msg) = bctx->nsei; msgb_bvci(msg) = bctx->bvci; rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]); rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* Parse a single GMM-PAGING.req to a given NSEI/NS-BVCI */ int bssgp_rx_paging(struct bssgp_paging_info *pinfo, struct msgb *msg) { struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); struct tlv_parsed tp; uint8_t ra[6]; int rc, data_len; memset(ra, 0, sizeof(ra)); data_len = msgb_bssgp_len(msg) - sizeof(*bgph); rc = bssgp_tlv_parse(&tp, bgph->data, data_len); if (rc < 0) goto err_mand_ie; switch (bgph->pdu_type) { case BSSGP_PDUT_PAGING_PS: pinfo->mode = BSSGP_PAGING_PS; break; case BSSGP_PDUT_PAGING_CS: pinfo->mode = BSSGP_PAGING_CS; break; default: return -EINVAL; } /* IMSI */ if (!TLVP_PRESENT(&tp, BSSGP_IE_IMSI)) goto err_mand_ie; if (!pinfo->imsi) pinfo->imsi = talloc_zero_size(pinfo, GSM_IMSI_LENGTH); gsm48_mi_to_string(pinfo->imsi, GSM_IMSI_LENGTH, TLVP_VAL(&tp, BSSGP_IE_IMSI), TLVP_LEN(&tp, BSSGP_IE_IMSI)); /* DRX Parameters */ if (!TLVP_PRESENT(&tp, BSSGP_IE_DRX_PARAMS)) goto err_mand_ie; pinfo->drx_params = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_DRX_PARAMS)); /* Scope */ if (TLVP_PRESENT(&tp, BSSGP_IE_BSS_AREA_ID)) { pinfo->scope = BSSGP_PAGING_BSS_AREA; } else if (TLVP_PRESENT(&tp, BSSGP_IE_LOCATION_AREA)) { pinfo->scope = BSSGP_PAGING_LOCATION_AREA; memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_LOCATION_AREA), TLVP_LEN(&tp, BSSGP_IE_LOCATION_AREA)); gsm48_parse_ra(&pinfo->raid, ra); } else if (TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) { pinfo->scope = BSSGP_PAGING_ROUTEING_AREA; memcpy(ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), TLVP_LEN(&tp, BSSGP_IE_ROUTEING_AREA)); gsm48_parse_ra(&pinfo->raid, ra); } else if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { pinfo->scope = BSSGP_PAGING_BVCI; pinfo->bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI)); } else return -EINVAL; /* QoS profile mandatory for PS */ if (pinfo->mode == BSSGP_PAGING_PS) { if (!TLVP_PRESENT(&tp, BSSGP_IE_QOS_PROFILE)) goto err_cond_ie; if (TLVP_LEN(&tp, BSSGP_IE_QOS_PROFILE) < 3) goto err; memcpy(&pinfo->qos, TLVP_VAL(&tp, BSSGP_IE_QOS_PROFILE), 3); } /* Optional (P-)TMSI */ if (TLVP_PRESENT(&tp, BSSGP_IE_TMSI) && TLVP_LEN(&tp, BSSGP_IE_TMSI) >= 4) if (!pinfo->ptmsi) pinfo->ptmsi = talloc_zero_size(pinfo, sizeof(uint32_t)); *(pinfo->ptmsi) = ntohl(*(uint32_t *) TLVP_VAL(&tp, BSSGP_IE_TMSI)); return 0; err_mand_ie: err_cond_ie: err: /* FIXME */ return 0; } libosmocore-0.9.0/src/gb/gprs_bssgp_util.c000066400000000000000000000111031261607044000205620ustar00rootroot00000000000000/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ /* (C) 2009-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include "common_vty.h" struct gprs_ns_inst *bssgp_nsi; /* BSSGP Protocol specific, not implementation specific */ /* FIXME: This needs to go into libosmocore after finished */ /* Chapter 11.3.9 / Table 11.10: Cause coding */ static const struct value_string bssgp_cause_strings[] = { { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" }, { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" }, { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" }, { BSSGP_CAUSE_CAPA_GREATER_0KPBS,"Transmission capacity modified" }, { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" }, { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" }, { BSSGP_CAUSE_CELL_TRAF_CONG, "Cell traffic congestion" }, { BSSGP_CAUSE_SGSN_CONG, "SGSN congestion" }, { BSSGP_CAUSE_OML_INTERV, "O&M intervention" }, { BSSGP_CAUSE_BVCI_BLOCKED, "BVCI blocked" }, { BSSGP_CAUSE_PFC_CREATE_FAIL, "PFC create failure" }, { BSSGP_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, { BSSGP_CAUSE_INV_MAND_INF, "Invalid mandatory information" }, { BSSGP_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" }, { BSSGP_CAUSE_MISSING_COND_IE, "Missing conditional IE" }, { BSSGP_CAUSE_UNEXP_COND_IE, "Unexpected conditional IE" }, { BSSGP_CAUSE_COND_IE_ERR, "Conditional IE error" }, { BSSGP_CAUSE_PDU_INCOMP_STATE, "PDU incompatible with protocol state" }, { BSSGP_CAUSE_PROTO_ERR_UNSPEC, "Protocol error - unspecified" }, { BSSGP_CAUSE_PDU_INCOMP_FEAT, "PDU not compatible with feature set" }, { 0, NULL }, }; const char *bssgp_cause_str(enum gprs_bssgp_cause cause) { return get_value_string(bssgp_cause_strings, cause); } struct msgb *bssgp_msgb_alloc(void) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "BSSGP"); /* TODO: Add handling of msg == NULL to this function and to all callers */ OSMO_ASSERT(msg != NULL); msgb_bssgph(msg) = msg->data; return msg; } /* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, uint16_t bvci, uint16_t ns_bvci) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); uint16_t _bvci; msgb_nsei(msg) = nsei; msgb_bvci(msg) = ns_bvci; bgph->pdu_type = pdu_type; _bvci = htons(bvci); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); return gprs_ns_sendmsg(bssgp_nsi, msg); } /* Chapter 10.4.14: Status */ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg) { struct msgb *msg = bssgp_msgb_alloc(); struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); /* GSM 08.18, 10.4.14.1: The BVCI must be included if (and only if) the cause is either "BVCI blocked" or "BVCI unknown" */ if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED) { if (bvci == NULL) LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: " "missing conditional BVCI\n", bssgp_cause_str(cause)); } else { if (bvci != NULL) LOGP(DBSSGP, LOGL_ERROR, "BSSGP Tx STATUS, cause=%s: " "unexpected conditional BVCI\n", bssgp_cause_str(cause)); } LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n", bvci ? *bvci : 0, bssgp_cause_str(cause)); msgb_nsei(msg) = msgb_nsei(orig_msg); msgb_bvci(msg) = 0; bgph->pdu_type = BSSGP_PDUT_STATUS; msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); if (bvci) { uint16_t _bvci = htons(*bvci); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); } msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg)); return gprs_ns_sendmsg(bssgp_nsi, msg); } libosmocore-0.9.0/src/gb/gprs_bssgp_vty.c000066400000000000000000000117231261607044000204370ustar00rootroot00000000000000/* VTY interface for our GPRS BSS Gateway Protocol (BSSGP) implementation */ /* (C) 2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" /* FIXME: this should go to some common file as it is copied * in vty_interface.c of the BSC */ static const struct value_string gprs_bssgp_timer_strs[] = { { 0, NULL } }; static void log_set_bvc_filter(struct log_target *target, struct bssgp_bvc_ctx *bctx) { if (bctx) { target->filter_map |= (1 << FLT_BVC); target->filter_data[FLT_BVC] = bctx; } else if (target->filter_data[FLT_NSVC]) { target->filter_map = ~(1 << FLT_BVC); target->filter_data[FLT_BVC] = NULL; } } static struct cmd_node bssgp_node = { L_BSSGP_NODE, "%s(bssgp)#", 1, }; static int config_write_bssgp(struct vty *vty) { vty_out(vty, "bssgp%s", VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_bssgp, cfg_bssgp_cmd, "bssgp", "Configure the GPRS BSS Gateway Protocol") { vty->node = L_BSSGP_NODE; return CMD_SUCCESS; } static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats) { vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %u-%u-%u-%u, CID: %u, " "STATE: %s%s", bvc->nsei, bvc->bvci, bvc->ra_id.mcc, bvc->ra_id.mnc, bvc->ra_id.lac, bvc->ra_id.rac, bvc->cell_id, bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED", VTY_NEWLINE); if (stats) { struct bssgp_flow_control *fc = bvc->fc; vty_out_rate_ctr_group(vty, " ", bvc->ctrg); if (fc) vty_out(vty, "FC-BVC(bucket_max: %uoct, leak_rate: " "%uoct/s, cur_tokens: %uoct, max_q_d: %u, " "cur_q_d: %u)%s", fc->bucket_size_max, fc->bucket_leak_rate, fc->bucket_counter, fc->max_queue_depth, fc->queue_depth, VTY_NEWLINE); } } static void dump_bssgp(struct vty *vty, int stats) { struct bssgp_bvc_ctx *bvc; llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { dump_bvc(vty, bvc, stats); } } #define BSSGP_STR "Show information about the BSSGP protocol\n" DEFUN(show_bssgp, show_bssgp_cmd, "show bssgp", SHOW_STR BSSGP_STR) { dump_bssgp(vty, 0); return CMD_SUCCESS; } DEFUN(show_bssgp_stats, show_bssgp_stats_cmd, "show bssgp stats", SHOW_STR BSSGP_STR "Include statistics\n") { dump_bssgp(vty, 1); return CMD_SUCCESS; } DEFUN(show_bvc, show_bvc_cmd, "show bssgp nsei <0-65535> [stats]", SHOW_STR BSSGP_STR "Show all BVCs on one NSE\n" "The NSEI\n" "Include Statistics\n") { struct bssgp_bvc_ctx *bvc; uint16_t nsei = atoi(argv[0]); int show_stats = 0; if (argc >= 2) show_stats = 1; llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { if (bvc->nsei != nsei) continue; dump_bvc(vty, bvc, show_stats); } return CMD_SUCCESS; } DEFUN(logging_fltr_bvc, logging_fltr_bvc_cmd, "logging filter bvc nsei <0-65535> bvci <0-65535>", LOGGING_STR FILTER_STR "Filter based on BSSGP Virtual Connection\n" "NSEI of the BVC to be filtered\n" "Network Service Entity Identifier (NSEI)\n" "BVCI of the BVC to be filtered\n" "BSSGP Virtual Connection Identifier (BVCI)\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); struct bssgp_bvc_ctx *bvc; uint16_t nsei = atoi(argv[0]); uint16_t bvci = atoi(argv[1]); if (!tgt) return CMD_WARNING; bvc = btsctx_by_bvci_nsei(bvci, nsei); if (!bvc) { vty_out(vty, "No BVC by that identifier%s", VTY_NEWLINE); return CMD_WARNING; } log_set_bvc_filter(tgt, bvc); return CMD_SUCCESS; } int bssgp_vty_init(void) { install_element_ve(&show_bssgp_cmd); install_element_ve(&show_bssgp_stats_cmd); install_element_ve(&show_bvc_cmd); install_element_ve(&logging_fltr_bvc_cmd); install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd); install_element(CONFIG_NODE, &cfg_bssgp_cmd); install_node(&bssgp_node, config_write_bssgp); install_default(L_BSSGP_NODE); install_element(L_BSSGP_NODE, &libgb_exit_cmd); install_element(L_BSSGP_NODE, &libgb_end_cmd); //install_element(L_BSSGP_NODE, &cfg_bssgp_timer_cmd); return 0; } libosmocore-0.9.0/src/gb/gprs_ns.c000066400000000000000000001320571261607044000170430ustar00rootroot00000000000000/* GPRS Networks Service (NS) messages on the Gb interface * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ /* (C) 2009-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /*! \addtogroup libgb * @{ */ /*! \file gprs_ns.c */ /*! * GPRS Networks Service (NS) messages on the Gb interface * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) * * Some introduction into NS: NS is used typically on top of frame relay, * but in the ip.access world it is encapsulated in UDP packets. It serves * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't * do much, apart from providing congestion notification and status indication. * * Terms: * NS Network Service * NSVC NS Virtual Connection * NSEI NS Entity Identifier * NSVL NS Virtual Link * NSVLI NS Virtual Link Identifier * BVC BSSGP Virtual Connection * BVCI BSSGP Virtual Connection Identifier * NSVCG NS Virtual Connection Goup * Blocked NS-VC cannot be used for user traffic * Alive Ability of a NS-VC to provide communication * * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. * NS then has to firgure out which NSVC's are responsible for this BVCI. * Those mappings are administratively configured. */ /* This implementation has the following limitations: * o Only one NS-VC for each NSE: No load-sharing function * o NSVCI 65535 and 65534 are reserved for internal use * o Only UDP is supported as of now, no frame relay support * o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented * o There are no BLOCK and UNBLOCK timers (yet?) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" static const struct tlv_definition ns_att_tlvdef = { .def = { [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, }, }; enum ns_ctr { NS_CTR_PKTS_IN, NS_CTR_PKTS_OUT, NS_CTR_BYTES_IN, NS_CTR_BYTES_OUT, NS_CTR_BLOCKED, NS_CTR_DEAD, NS_CTR_REPLACED, NS_CTR_NSEI_CHG, NS_CTR_INV_VCI, NS_CTR_INV_NSEI, NS_CTR_LOST_ALIVE, NS_CTR_LOST_RESET, }; static const struct rate_ctr_desc nsvc_ctr_description[] = { { "packets.in", "Packets at NS Level ( In)" }, { "packets.out","Packets at NS Level (Out)" }, { "bytes.in", "Bytes at NS Level ( In)" }, { "bytes.out", "Bytes at NS Level (Out)" }, { "blocked", "NS-VC Block count " }, { "dead", "NS-VC gone dead count " }, { "replaced", "NS-VC replaced other count" }, { "nsei-chg", "NS-VC changed NSEI count " }, { "inv-nsvci", "NS-VCI was invalid count " }, { "inv-nsei", "NSEI was invalid count " }, { "lost.alive", "ALIVE ACK missing count " }, { "lost.reset", "RESET ACK missing count " }, }; static const struct rate_ctr_group_desc nsvc_ctrg_desc = { .group_name_prefix = "ns.nsvc", .group_description = "NSVC Peer Statistics", .num_ctr = ARRAY_SIZE(nsvc_ctr_description), .ctr_desc = nsvc_ctr_description, }; enum ns_stat { NS_STAT_ALIVE_DELAY, }; static const struct osmo_stat_item_desc nsvc_stat_description[] = { { "alive.delay", "ALIVE reponse time ", "ms", 16, 0 }, }; static const struct osmo_stat_item_group_desc nsvc_statg_desc = { .group_name_prefix = "ns.nsvc", .group_description = "NSVC Peer Statistics", .num_items = ARRAY_SIZE(nsvc_stat_description), .item_desc = nsvc_stat_description, .class_id = OSMO_STATS_CLASS_PEER, }; #define CHECK_TX_RC(rc, nsvc) \ if (rc < 0) \ LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", \ rc, gprs_ns_ll_str(nsvc)); struct msgb *gprs_ns_msgb_alloc(void) { struct msgb *msg = msgb_alloc_headroom(NS_ALLOC_SIZE, NS_ALLOC_HEADROOM, "GPRS/NS"); if (!msg) { LOGP(DNS, LOGL_ERROR, "Failed to allocate NS message of size %d\n", NS_ALLOC_SIZE); } return msg; } /*! \brief Lookup struct gprs_nsvc based on NSVCI * \param[in] nsi NS instance in which to search * \param[in] nsvci NSVCI to be searched * \returns gprs_nsvc of respective NSVCI */ struct gprs_nsvc *gprs_nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (nsvc->nsvci == nsvci) return nsvc; } return NULL; } /*! \brief Lookup struct gprs_nsvc based on NSEI * \param[in] nsi NS instance in which to search * \param[in] nsei NSEI to be searched * \returns first gprs_nsvc of respective NSEI */ struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (nsvc->nsei == nsei) return nsvc; } return NULL; } static struct gprs_nsvc *gprs_active_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (nsvc->nsei == nsei) { if (!(nsvc->state & NSE_S_BLOCKED) && nsvc->state & NSE_S_ALIVE) return nsvc; } } return NULL; } /* Lookup struct gprs_nsvc based on remote peer socket addr */ static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, struct sockaddr_in *sin) { struct gprs_nsvc *nsvc; llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (nsvc->ip.bts_addr.sin_addr.s_addr == sin->sin_addr.s_addr && nsvc->ip.bts_addr.sin_port == sin->sin_port) return nsvc; } return NULL; } static void gprs_ns_timer_cb(void *data); struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) { struct gprs_nsvc *nsvc; LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci); nsvc = talloc_zero(nsi, struct gprs_nsvc); nsvc->nsvci = nsvci; nsvc->nsvci_is_valid = 1; /* before RESET procedure: BLOCKED and DEAD */ nsvc->state = NSE_S_BLOCKED; nsvc->nsi = nsi; nsvc->timer.cb = gprs_ns_timer_cb; nsvc->timer.data = nsvc; nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci); nsvc->statg = osmo_stat_item_group_alloc(nsvc, &nsvc_statg_desc, nsvci); llist_add(&nsvc->list, &nsi->gprs_nsvcs); return nsvc; } /*! \brief Delete given NS-VC * \param[in] nsvc gprs_nsvc to be deleted */ void gprs_nsvc_delete(struct gprs_nsvc *nsvc) { if (osmo_timer_pending(&nsvc->timer)) osmo_timer_del(&nsvc->timer); llist_del(&nsvc->list); rate_ctr_group_free(nsvc->ctrg); talloc_free(nsvc); } static void ns_osmo_signal_dispatch(struct gprs_nsvc *nsvc, unsigned int signal, uint8_t cause) { struct ns_signal_data nssd = {0}; nssd.nsvc = nsvc; nssd.cause = cause; osmo_signal_dispatch(SS_L_NS, signal, &nssd); } static void ns_osmo_signal_dispatch_mismatch(struct gprs_nsvc *nsvc, struct msgb *msg, uint8_t pdu_type, uint8_t ie_type) { struct ns_signal_data nssd = {0}; nssd.nsvc = nsvc; nssd.pdu_type = pdu_type; nssd.ie_type = ie_type; nssd.msg = msg; osmo_signal_dispatch(SS_L_NS, S_NS_MISMATCH, &nssd); } static void ns_osmo_signal_dispatch_replaced(struct gprs_nsvc *nsvc, struct gprs_nsvc *old_nsvc) { struct ns_signal_data nssd = {0}; nssd.nsvc = nsvc; nssd.old_nsvc = old_nsvc; osmo_signal_dispatch(SS_L_NS, S_NS_REPLACED, &nssd); } /* Section 10.3.2, Table 13 */ static const struct value_string ns_cause_str[] = { { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" }, { NS_CAUSE_OM_INTERVENTION, "O&M intervention" }, { NS_CAUSE_EQUIP_FAIL, "Equipment failure" }, { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" }, { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" }, { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" }, { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" }, { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" }, { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" }, { 0, NULL } }; /*! \brief Obtain a human-readable string for NS cause value */ const char *gprs_ns_cause_str(enum ns_cause cause) { return get_value_string(ns_cause_str, cause); } static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); extern int grps_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) { int ret; log_set_context(GPRS_CTX_NSVC, nsvc); /* Increment number of Uplink bytes */ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]); rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg)); switch (nsvc->ll) { case GPRS_NS_LL_UDP: ret = nsip_sendmsg(nsvc, msg); if (ret < 0) LOGP(DNS, LOGL_INFO, "failed to send NS message via UDP: %s\n", strerror(-ret)); break; case GPRS_NS_LL_FR_GRE: ret = gprs_ns_frgre_sendmsg(nsvc, msg); if (ret < 0) LOGP(DNS, LOGL_INFO, "failed to send NS message via FR/GRE: %s\n", strerror(-ret)); break; default: LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->ll); msgb_free(msg); ret = -EIO; break; } return ret; } static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) { struct msgb *msg = gprs_ns_msgb_alloc(); struct gprs_ns_hdr *nsh; log_set_context(GPRS_CTX_NSVC, nsvc); if (!msg) return -ENOMEM; msg->l2h = msgb_put(msg, sizeof(*nsh)); nsh = (struct gprs_ns_hdr *) msg->l2h; nsh->pdu_type = pdu_type; return gprs_ns_tx(nsvc, msg); } /*! \brief Transmit a NS-RESET on a given NSVC * \param[in] nsvc NS-VC used for transmission * \paam[in] cause Numeric NS cause value */ int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) { struct msgb *msg = gprs_ns_msgb_alloc(); struct gprs_ns_hdr *nsh; uint16_t nsvci = htons(nsvc->nsvci); uint16_t nsei = htons(nsvc->nsei); log_set_context(GPRS_CTX_NSVC, nsvc); if (!msg) return -ENOMEM; LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n", nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); msg->l2h = msgb_put(msg, sizeof(*nsh)); nsh = (struct gprs_ns_hdr *) msg->l2h; nsh->pdu_type = NS_PDUT_RESET; msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); return gprs_ns_tx(nsvc, msg); } /*! \brief Transmit a NS-STATUS on a given NSVC * \param[in] nsvc NS-VC to be used for transmission * \param[in] cause Numeric NS cause value * \param[in] bvci BVCI to be reset within NSVC * \param[in] orig_msg message causing the STATUS */ int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, uint16_t bvci, struct msgb *orig_msg) { struct msgb *msg = gprs_ns_msgb_alloc(); struct gprs_ns_hdr *nsh; uint16_t nsvci = htons(nsvc->nsvci); log_set_context(GPRS_CTX_NSVC, nsvc); bvci = htons(bvci); if (!msg) return -ENOMEM; LOGP(DNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n", nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); msg->l2h = msgb_put(msg, sizeof(*nsh)); nsh = (struct gprs_ns_hdr *) msg->l2h; nsh->pdu_type = NS_PDUT_STATUS; msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); /* Section 9.2.7.1: Static conditions for NS-VCI */ if (cause == NS_CAUSE_NSVC_BLOCKED || cause == NS_CAUSE_NSVC_UNKNOWN) msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); /* Section 9.2.7.2: Static conditions for NS PDU */ switch (cause) { case NS_CAUSE_SEM_INCORR_PDU: case NS_CAUSE_PDU_INCOMP_PSTATE: case NS_CAUSE_PROTO_ERR_UNSPEC: case NS_CAUSE_INVAL_ESSENT_IE: case NS_CAUSE_MISSING_ESSENT_IE: msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg), orig_msg->l2h); break; default: break; } /* Section 9.2.7.3: Static conditions for BVCI */ if (cause == NS_CAUSE_BVCI_UNKNOWN) msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci); return gprs_ns_tx(nsvc, msg); } /*! \brief Transmit a NS-BLOCK on a tiven NS-VC * \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted * \param[in] cause Numeric NS Cause value * \returns 0 in case of success */ int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) { struct msgb *msg = gprs_ns_msgb_alloc(); struct gprs_ns_hdr *nsh; uint16_t nsvci = htons(nsvc->nsvci); log_set_context(GPRS_CTX_NSVC, nsvc); if (!msg) return -ENOMEM; LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n", nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); /* be conservative and mark it as blocked even now! */ nsvc->state |= NSE_S_BLOCKED; rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); msg->l2h = msgb_put(msg, sizeof(*nsh)); nsh = (struct gprs_ns_hdr *) msg->l2h; nsh->pdu_type = NS_PDUT_BLOCK; msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); return gprs_ns_tx(nsvc, msg); } /*! \brief Transmit a NS-UNBLOCK on a given NS-VC * \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted * \returns 0 in case of success */ int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) { log_set_context(GPRS_CTX_NSVC, nsvc); LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); } /*! \brief Transmit a NS-ALIVE on a given NS-VC * \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted * \returns 0 in case of success */ int gprs_ns_tx_alive(struct gprs_nsvc *nsvc) { log_set_context(GPRS_CTX_NSVC, nsvc); LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); } /*! \brief Transmit a NS-ALIVE-ACK on a given NS-VC * \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted * \returns 0 in case of success */ int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc) { log_set_context(GPRS_CTX_NSVC, nsvc); LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); } static const enum ns_timeout timer_mode_tout[_NSVC_TIMER_NR] = { [NSVC_TIMER_TNS_RESET] = NS_TOUT_TNS_RESET, [NSVC_TIMER_TNS_ALIVE] = NS_TOUT_TNS_ALIVE, [NSVC_TIMER_TNS_TEST] = NS_TOUT_TNS_TEST, }; static const struct value_string timer_mode_strs[] = { { NSVC_TIMER_TNS_RESET, "tns-reset" }, { NSVC_TIMER_TNS_ALIVE, "tns-alive" }, { NSVC_TIMER_TNS_TEST, "tns-test" }, { 0, NULL } }; static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) { enum ns_timeout tout = timer_mode_tout[mode]; unsigned int seconds = nsvc->nsi->timeout[tout]; log_set_context(GPRS_CTX_NSVC, nsvc); DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n", nsvc->nsei, get_value_string(timer_mode_strs, mode), seconds); if (osmo_timer_pending(&nsvc->timer)) osmo_timer_del(&nsvc->timer); gettimeofday(&nsvc->timer_started, NULL); nsvc->timer_mode = mode; osmo_timer_schedule(&nsvc->timer, seconds, 0); } static int nsvc_timer_elapsed_ms(struct gprs_nsvc *nsvc) { struct timeval now, elapsed; gettimeofday(&now, NULL); timersub(&now, &nsvc->timer_started, &elapsed); return 1000 * elapsed.tv_sec + elapsed.tv_usec / 1000; } static void gprs_ns_timer_cb(void *data) { struct gprs_nsvc *nsvc = data; enum ns_timeout tout = timer_mode_tout[nsvc->timer_mode]; unsigned int seconds = nsvc->nsi->timeout[tout]; log_set_context(GPRS_CTX_NSVC, nsvc); DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n", nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode), seconds); switch (nsvc->timer_mode) { case NSVC_TIMER_TNS_ALIVE: /* Tns-alive case: we expired without response ! */ rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_ALIVE]); nsvc->alive_retries++; if (nsvc->alive_retries > nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) { /* mark as dead and blocked */ nsvc->state = NSE_S_BLOCKED; rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]); LOGP(DNS, LOGL_NOTICE, "NSEI=%u Tns-alive expired more then " "%u times, blocking NS-VC\n", nsvc->nsei, nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]); ns_osmo_signal_dispatch(nsvc, S_NS_ALIVE_EXP, 0); ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); return; } /* Tns-test case: send NS-ALIVE PDU */ gprs_ns_tx_alive(nsvc); /* start Tns-alive timer */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_TEST: /* Tns-test case: send NS-ALIVE PDU */ gprs_ns_tx_alive(nsvc); /* start Tns-alive timer (transition into faster * alive retransmissions) */ nsvc->alive_retries = 0; nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); break; case NSVC_TIMER_TNS_RESET: rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_LOST_RESET]); /* Chapter 7.3: Re-send the RESET */ gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); /* Re-start Tns-reset timer */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); break; case _NSVC_TIMER_NR: break; } } /* Section 9.2.6 */ static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) { struct msgb *msg = gprs_ns_msgb_alloc(); struct gprs_ns_hdr *nsh; uint16_t nsvci, nsei; log_set_context(GPRS_CTX_NSVC, nsvc); if (!msg) return -ENOMEM; nsvci = htons(nsvc->nsvci); nsei = htons(nsvc->nsei); msg->l2h = msgb_put(msg, sizeof(*nsh)); nsh = (struct gprs_ns_hdr *) msg->l2h; nsh->pdu_type = NS_PDUT_RESET_ACK; LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", nsvc->nsei, nsvc->nsvci); msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei); return gprs_ns_tx(nsvc, msg); } /*! \brief High-level function for transmitting a NS-UNITDATA messsage * \param[in] nsi NS-instance on which we shall transmit * \param[in] msg struct msgb to be trasnmitted * * This function obtains the NS-VC by the msgb_nsei(msg) and then checks * if the NS-VC is ALIVEV and not BLOCKED. After that, it adds a NS * header for the NS-UNITDATA message type and sends it off. * * Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { struct gprs_nsvc *nsvc; struct gprs_ns_hdr *nsh; uint16_t bvci = msgb_bvci(msg); nsvc = gprs_active_nsvc_by_nsei(nsi, msgb_nsei(msg)); if (!nsvc) { int rc; if (gprs_nsvc_by_nsei(nsi, msgb_nsei(msg))) { LOGP(DNS, LOGL_ERROR, "All NS-VCs for NSEI %u are either dead or blocked!\n", msgb_nsei(msg)); rc = -EBUSY; } else { LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u " "to NS-VC!\n", msgb_nsei(msg)); rc = -EINVAL; } msgb_free(msg); return rc; } log_set_context(GPRS_CTX_NSVC, nsvc); msg->l2h = msgb_push(msg, sizeof(*nsh) + 3); nsh = (struct gprs_ns_hdr *) msg->l2h; if (!nsh) { LOGP(DNS, LOGL_ERROR, "Not enough headroom for NS header\n"); msgb_free(msg); return -EIO; } nsh->pdu_type = NS_PDUT_UNITDATA; /* spare octet in data[0] */ nsh->data[1] = bvci >> 8; nsh->data[2] = bvci & 0xff; return gprs_ns_tx(nsvc, msg); } /* Section 9.2.10: receive side */ static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; uint16_t bvci; if (nsvc->state & NSE_S_BLOCKED) return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED, 0, msg); /* spare octet in data[0] */ bvci = nsh->data[1] << 8 | nsh->data[2]; msgb_bssgph(msg) = &nsh->data[3]; msgb_bvci(msg) = bvci; /* call upper layer (BSSGP) */ return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); } /* Section 9.2.7 */ static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct tlv_parsed tp; uint8_t cause; int rc; LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx NS STATUS ", nsvc->nsei); rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg) - sizeof(*nsh), 0, 0); if (rc < 0) { LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse\n"); LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS STATUS: " "Error during TLV Parse\n", nsvc->nsei); return rc; } if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { LOGPC(DNS, LOGL_INFO, "missing cause IE\n"); return -EINVAL; } cause = *TLVP_VAL(&tp, NS_IE_CAUSE); LOGPC(DNS, LOGL_NOTICE, "cause=%s\n", gprs_ns_cause_str(cause)); return 0; } /* Replace a nsvc object with another based on NSVCI. * This function replaces looks for a NSVC with the given NSVCI and replaces it * if possible and necessary. If replaced, the former value of *nsvc is * returned in *old_nsvc. * \return != 0 if *nsvc points to a matching NSVC. */ static int gprs_nsvc_replace_if_found(uint16_t nsvci, struct gprs_nsvc **nsvc, struct gprs_nsvc **old_nsvc) { struct gprs_nsvc *matching_nsvc; if ((*nsvc)->nsvci == nsvci) { *old_nsvc = NULL; return 1; } matching_nsvc = gprs_nsvc_by_nsvci((*nsvc)->nsi, nsvci); if (!matching_nsvc) return 0; /* The NS-VCI is already used by this NS-VC */ char *old_peer; /* Exchange the NS-VC objects */ *old_nsvc = *nsvc; *nsvc = matching_nsvc; /* Do logging */ old_peer = talloc_strdup(*old_nsvc, gprs_ns_ll_str(*old_nsvc)); LOGP(DNS, LOGL_INFO, "NS-VC changed link (NSVCI=%u) from %s to %s\n", nsvci, old_peer, gprs_ns_ll_str(*nsvc)); talloc_free(old_peer); return 1; } /* Section 7.3 */ static int gprs_ns_rx_reset(struct gprs_nsvc **nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct tlv_parsed tp; uint8_t cause; uint16_t nsvci, nsei; struct gprs_nsvc *orig_nsvc = NULL; int rc; rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg) - sizeof(*nsh), 0, 0); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET " "Error during TLV Parse\n", (*nsvc)->nsei); return rc; } if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); gprs_ns_tx_status(*nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); return -EINVAL; } cause = *(uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); nsvci = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_VCI)); nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI)); LOGP(DNS, LOGL_INFO, "NSVCI=%u%s Rx NS RESET (NSEI=%u, NSVCI=%u, cause=%s)\n", (*nsvc)->nsvci, (*nsvc)->nsvci_is_valid ? "" : "(invalid)", nsei, nsvci, gprs_ns_cause_str(cause)); if ((*nsvc)->nsvci_is_valid && (*nsvc)->nsvci != nsvci) { if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) { /* The incoming RESET doesn't match the NSVCI. Send an * appropriate RESET_ACK and ignore the RESET. * See 3GPP TS 08.16, 7.3.1, 2nd paragraph. */ ns_osmo_signal_dispatch_mismatch(*nsvc, msg, NS_PDUT_RESET, NS_IE_VCI); rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]); gprs_ns_tx_reset_ack(*nsvc); return 0; } /* NS-VCI has changed */ if (!gprs_nsvc_replace_if_found(nsvci, nsvc, &orig_nsvc)) { LOGP(DNS, LOGL_INFO, "Creating NS-VC %d replacing %d " "at %s\n", nsvci, (*nsvc)->nsvci, gprs_ns_ll_str(*nsvc)); orig_nsvc = *nsvc; *nsvc = gprs_nsvc_create((*nsvc)->nsi, nsvci); (*nsvc)->nsei = nsei; } } if ((*nsvc)->nsvci_is_valid && (*nsvc)->nsei != nsei) { if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) { /* The incoming RESET doesn't match the NSEI. Send an * appropriate RESET_ACK and ignore the RESET. * See 3GPP TS 08.16, 7.3.1, 3rd paragraph. */ ns_osmo_signal_dispatch_mismatch(*nsvc, msg, NS_PDUT_RESET, NS_IE_NSEI); rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]); rc = gprs_ns_tx_reset_ack(*nsvc); CHECK_TX_RC(rc, *nsvc); return 0; } /* NSEI has changed */ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]); (*nsvc)->nsei = nsei; } /* Mark NS-VC as blocked and alive */ (*nsvc)->state = NSE_S_BLOCKED | NSE_S_ALIVE; if (orig_nsvc) { rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]); ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc); /* Update the ll info fields */ gprs_ns_ll_copy(*nsvc, orig_nsvc); gprs_ns_ll_clear(orig_nsvc); } else { (*nsvc)->nsei = nsei; (*nsvc)->nsvci = nsvci; (*nsvc)->nsvci_is_valid = 1; } /* inform interested parties about the fact that this NSVC * has received RESET */ ns_osmo_signal_dispatch(*nsvc, S_NS_RESET, cause); rc = gprs_ns_tx_reset_ack(*nsvc); /* start the test procedure */ gprs_ns_tx_simple((*nsvc), NS_PDUT_ALIVE); nsvc_start_timer((*nsvc), NSVC_TIMER_TNS_TEST); return rc; } static int gprs_ns_rx_reset_ack(struct gprs_nsvc **nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct tlv_parsed tp; uint16_t nsvci, nsei; struct gprs_nsvc *orig_nsvc = NULL; int rc; rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg) - sizeof(*nsh), 0, 0); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET ACK " "Error during TLV Parse\n", (*nsvc)->nsei); return rc; } if (!TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { LOGP(DNS, LOGL_ERROR, "NS RESET ACK Missing mandatory IE\n"); rc = gprs_ns_tx_status(*nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); CHECK_TX_RC(rc, *nsvc); return -EINVAL; } nsvci = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_VCI)); nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI)); LOGP(DNS, LOGL_INFO, "NSVCI=%u%s Rx NS RESET ACK (NSEI=%u, NSVCI=%u)\n", (*nsvc)->nsvci, (*nsvc)->nsvci_is_valid ? "" : "(invalid)", nsei, nsvci); if (!((*nsvc)->state & NSE_S_RESET)) { /* Not waiting for a RESET_ACK on this NS-VC, ignore it. * See 3GPP TS 08.16, 7.3.1, 5th paragraph. */ LOGP(DNS, LOGL_ERROR, "NS RESET ACK Discarding unexpected message for " "NS-VCI %d from SGSN NSEI=%d\n", nsvci, nsei); return 0; } if (!(*nsvc)->nsvci_is_valid) { LOGP(DNS, LOGL_NOTICE, "NS RESET ACK Uninitialised NS-VC (%u) for " "NS-VCI %d, NSEI=%d from %s\n", (*nsvc)->nsvci, nsvci, nsei, gprs_ns_ll_str(*nsvc)); return -EINVAL; } if ((*nsvc)->nsvci != nsvci) { /* NS-VCI has changed */ /* if !0, use another NSVC object that matches the NSVCI */ int use_other_nsvc; /* Only do this with BSS peers */ use_other_nsvc = !(*nsvc)->remote_end_is_sgsn && !(*nsvc)->persistent; if (use_other_nsvc) /* Update *nsvc to point to the right NSVC object */ use_other_nsvc = gprs_nsvc_replace_if_found(nsvci, nsvc, &orig_nsvc); if (!use_other_nsvc) { /* The incoming RESET_ACK doesn't match the NSVCI. * See 3GPP TS 08.16, 7.3.1, 4th paragraph. */ ns_osmo_signal_dispatch_mismatch(*nsvc, msg, NS_PDUT_RESET_ACK, NS_IE_VCI); rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_VCI]); LOGP(DNS, LOGL_ERROR, "NS RESET ACK Unknown NS-VCI %d (%s NSEI=%d) " "from %s\n", nsvci, (*nsvc)->remote_end_is_sgsn ? "SGSN" : "BSS", nsei, gprs_ns_ll_str(*nsvc)); return -EINVAL; } /* Notify others */ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_REPLACED]); ns_osmo_signal_dispatch_replaced(*nsvc, orig_nsvc); /* Update the ll info fields */ gprs_ns_ll_copy(*nsvc, orig_nsvc); gprs_ns_ll_clear(orig_nsvc); } else if ((*nsvc)->nsei != nsei) { if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) { /* The incoming RESET_ACK doesn't match the NSEI. * See 3GPP TS 08.16, 7.3.1, 4th paragraph. */ ns_osmo_signal_dispatch_mismatch(*nsvc, msg, NS_PDUT_RESET_ACK, NS_IE_NSEI); rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_INV_NSEI]); LOGP(DNS, LOGL_ERROR, "NS RESET ACK Unknown NSEI %d (NS-VCI=%u) from %s\n", nsei, nsvci, gprs_ns_ll_str(*nsvc)); return -EINVAL; } /* NSEI has changed */ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_NSEI_CHG]); (*nsvc)->nsei = nsei; } /* Mark NS-VC as blocked and alive */ (*nsvc)->state = NSE_S_BLOCKED | NSE_S_ALIVE; (*nsvc)->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_BLOCKED]); if ((*nsvc)->persistent || (*nsvc)->remote_end_is_sgsn) { /* stop RESET timer */ osmo_timer_del(&(*nsvc)->timer); } /* Initiate TEST proc.: Send ALIVE and start timer */ rc = gprs_ns_tx_simple(*nsvc, NS_PDUT_ALIVE); nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST); return rc; } static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; struct tlv_parsed tp; uint8_t *cause; int rc; LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); nsvc->state |= NSE_S_BLOCKED; rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg) - sizeof(*nsh), 0, 0); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS BLOCK " "Error during TLV Parse\n", nsvc->nsei); return rc; } if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI)) { LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); return -EINVAL; } cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); ns_osmo_signal_dispatch(nsvc, S_NS_BLOCK, *cause); rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); } int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg, struct gprs_nsvc *fallback_nsvc, struct gprs_nsvc **new_nsvc); int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg, struct gprs_nsvc **nsvc); /*! \brief Receive incoming NS message from underlying transport layer * \param nsi NS instance to which the data belongs * \param[in] msg message buffer containing newly-received data * \param[in] saddr socketaddr from which data was received * \param[in] ll link-layer type in which data was received * \returns 0 in case of success, < 0 in case of error * * This is the main entry point int othe NS imlementation where frames * from the underlying transport (normally UDP) enter. */ int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll) { struct gprs_nsvc *nsvc; int rc = 0; /* look up the NSVC based on source address */ nsvc = nsvc_by_rem_addr(nsi, saddr); if (!nsvc) { struct gprs_nsvc *fallback_nsvc; fallback_nsvc = nsi->unknown_nsvc; log_set_context(GPRS_CTX_NSVC, fallback_nsvc); fallback_nsvc->ip.bts_addr = *saddr; fallback_nsvc->ll = ll; rc = gprs_ns_vc_create(nsi, msg, fallback_nsvc, &nsvc); if (rc < 0) return rc; rc = 0; } if (nsvc) rc = gprs_ns_process_msg(nsi, msg, &nsvc); return rc; } const char *gprs_ns_ll_str(struct gprs_nsvc *nsvc) { static char buf[80]; snprintf(buf, sizeof(buf), "%s:%u", inet_ntoa(nsvc->ip.bts_addr.sin_addr), ntohs(nsvc->ip.bts_addr.sin_port)); buf[sizeof(buf) - 1] = '\0'; return buf; } void gprs_ns_ll_copy(struct gprs_nsvc *nsvc, struct gprs_nsvc *other) { nsvc->ll = other->ll; switch (nsvc->ll) { case GPRS_NS_LL_UDP: nsvc->ip = other->ip; break; case GPRS_NS_LL_FR_GRE: nsvc->frgre = other->frgre; break; default: break; } } void gprs_ns_ll_clear(struct gprs_nsvc *nsvc) { switch (nsvc->ll) { case GPRS_NS_LL_UDP: nsvc->ip.bts_addr.sin_addr.s_addr = INADDR_ANY; nsvc->ip.bts_addr.sin_port = 0; break; case GPRS_NS_LL_FR_GRE: nsvc->frgre.bts_addr.sin_addr.s_addr = INADDR_ANY; nsvc->frgre.bts_addr.sin_port = 0; break; default: break; } } /*! \brief Create/get NS-VC independently from underlying transport layer * \param nsi NS instance to which the data belongs * \param[in] msg message buffer containing newly-received data * \param[in] fallback_nsvc is used to send error messages back to the peer * and to initialise the ll info of a created NS-VC object * \param[out] new_nsvc contains a pointer to a NS-VC object if one has * been created or found * \returns < 0 in case of error, GPRS_NS_CS_SKIPPED if a message has been * skipped, GPRS_NS_CS_REJECTED if a message has been rejected and * answered accordingly, GPRS_NS_CS_CREATED if a new NS-VC object * has been created and registered, and GPRS_NS_CS_FOUND if an * existing NS-VC object has been found with the same NSEI. * * This contains the initial NS automaton state (NS-VC not yet attached). */ int gprs_ns_vc_create(struct gprs_ns_inst *nsi, struct msgb *msg, struct gprs_nsvc *fallback_nsvc, struct gprs_nsvc **new_nsvc) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; struct gprs_nsvc *existing_nsvc; struct tlv_parsed tp; uint16_t nsvci; uint16_t nsei; int rc; if (nsh->pdu_type == NS_PDUT_STATUS) { /* Do not respond, see 3GPP TS 08.16, 7.5.1 */ LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s " "for non-existing NS-VC\n", gprs_ns_ll_str(fallback_nsvc)); return GPRS_NS_CS_SKIPPED; } if (nsh->pdu_type == NS_PDUT_ALIVE_ACK) { /* Ignore this, see 3GPP TS 08.16, 7.4.1 */ LOGP(DNS, LOGL_INFO, "Ignoring NS ALIVE ACK from %s " "for non-existing NS-VC\n", gprs_ns_ll_str(fallback_nsvc)); return GPRS_NS_CS_SKIPPED; } if (nsh->pdu_type == NS_PDUT_RESET_ACK) { /* Ignore this, see 3GPP TS 08.16, 7.3.1 */ LOGP(DNS, LOGL_INFO, "Ignoring NS RESET ACK from %s " "for non-existing NS-VC\n", gprs_ns_ll_str(fallback_nsvc)); return GPRS_NS_CS_SKIPPED; } /* Only the RESET procedure creates a new NSVC */ if (nsh->pdu_type != NS_PDUT_RESET) { /* Since we have no NSVC, we have to use a fake */ log_set_context(GPRS_CTX_NSVC, fallback_nsvc); LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " "from %s for non-existing NS-VC\n", nsh->pdu_type, gprs_ns_ll_str(fallback_nsvc)); fallback_nsvc->nsvci = fallback_nsvc->nsei = 0xfffe; fallback_nsvc->nsvci_is_valid = 0; fallback_nsvc->state = NSE_S_ALIVE; rc = gprs_ns_tx_status(fallback_nsvc, NS_CAUSE_PDU_INCOMP_PSTATE, 0, msg); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", rc, gprs_ns_ll_str(fallback_nsvc)); return rc; } return GPRS_NS_CS_REJECTED; } rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, msgb_l2len(msg) - sizeof(*nsh), 0, 0); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during " "TLV Parse\n", rc); return rc; } if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || !TLVP_PRESENT(&tp, NS_IE_VCI) || !TLVP_PRESENT(&tp, NS_IE_NSEI)) { LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); rc = gprs_ns_tx_status(fallback_nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); CHECK_TX_RC(rc, fallback_nsvc); return -EINVAL; } nsvci = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_VCI)); nsei = ntohs(*(uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI)); /* Check if we already know this NSVCI, the remote end might * simply have changed addresses, or it is a SGSN */ existing_nsvc = gprs_nsvc_by_nsvci(nsi, nsvci); if (!existing_nsvc) { *new_nsvc = gprs_nsvc_create(nsi, 0xffff); (*new_nsvc)->nsvci_is_valid = 0; log_set_context(GPRS_CTX_NSVC, *new_nsvc); gprs_ns_ll_copy(*new_nsvc, fallback_nsvc); LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s\n", gprs_ns_ll_str(fallback_nsvc)); return GPRS_NS_CS_CREATED; } /* Check NSEI */ if (existing_nsvc->nsei != nsei) { LOGP(DNS, LOGL_NOTICE, "NS-VC changed NSEI (NSVCI=%u) from %u to %u\n", nsvci, existing_nsvc->nsei, nsei); /* Override old NSEI */ existing_nsvc->nsei = nsei; /* Do statistics */ rate_ctr_inc(&existing_nsvc->ctrg->ctr[NS_CTR_NSEI_CHG]); } *new_nsvc = existing_nsvc; gprs_ns_ll_copy(*new_nsvc, fallback_nsvc); return GPRS_NS_CS_FOUND; } /*! \brief Process NS message independently from underlying transport layer * \param nsi NS instance to which the data belongs * \param[in] msg message buffer containing newly-received data * \param[inout] nsvc refers to the virtual connection, may be modified when * processing a NS_RESET * \returns 0 in case of success, < 0 in case of error * * This contains the main NS automaton. */ int gprs_ns_process_msg(struct gprs_ns_inst *nsi, struct msgb *msg, struct gprs_nsvc **nsvc) { struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; int rc = 0; msgb_nsei(msg) = (*nsvc)->nsei; log_set_context(GPRS_CTX_NSVC, *nsvc); /* Increment number of Incoming bytes */ rate_ctr_inc(&(*nsvc)->ctrg->ctr[NS_CTR_PKTS_IN]); rate_ctr_add(&(*nsvc)->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg)); switch (nsh->pdu_type) { case NS_PDUT_ALIVE: /* If we're dead and blocked and suddenly receive a * NS-ALIVE out of the blue, we might have been re-started * and should send a NS-RESET to make sure everything recovers * fine. */ if ((*nsvc)->state == NSE_S_BLOCKED) rc = gprs_nsvc_reset((*nsvc), NS_CAUSE_PDU_INCOMP_PSTATE); else if (!((*nsvc)->state & NSE_S_RESET)) rc = gprs_ns_tx_alive_ack(*nsvc); break; case NS_PDUT_ALIVE_ACK: if ((*nsvc)->timer_mode == NSVC_TIMER_TNS_ALIVE) osmo_stat_item_set((*nsvc)->statg->items[NS_STAT_ALIVE_DELAY], nsvc_timer_elapsed_ms(*nsvc)); /* stop Tns-alive and start Tns-test */ nsvc_start_timer(*nsvc, NSVC_TIMER_TNS_TEST); if ((*nsvc)->remote_end_is_sgsn) { /* FIXME: this should be one level higher */ if ((*nsvc)->state & NSE_S_BLOCKED) rc = gprs_ns_tx_unblock(*nsvc); } break; case NS_PDUT_UNITDATA: /* actual user data */ rc = gprs_ns_rx_unitdata(*nsvc, msg); break; case NS_PDUT_STATUS: rc = gprs_ns_rx_status(*nsvc, msg); break; case NS_PDUT_RESET: rc = gprs_ns_rx_reset(nsvc, msg); break; case NS_PDUT_RESET_ACK: rc = gprs_ns_rx_reset_ack(nsvc, msg); break; case NS_PDUT_UNBLOCK: /* Section 7.2: unblocking procedure */ LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", (*nsvc)->nsei); (*nsvc)->state &= ~NSE_S_BLOCKED; ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0); rc = gprs_ns_tx_simple(*nsvc, NS_PDUT_UNBLOCK_ACK); break; case NS_PDUT_UNBLOCK_ACK: LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", (*nsvc)->nsei); /* mark NS-VC as unblocked + active */ (*nsvc)->state = NSE_S_ALIVE; (*nsvc)->remote_state = NSE_S_ALIVE; ns_osmo_signal_dispatch(*nsvc, S_NS_UNBLOCK, 0); break; case NS_PDUT_BLOCK: rc = gprs_ns_rx_block(*nsvc, msg); break; case NS_PDUT_BLOCK_ACK: LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", (*nsvc)->nsei); /* mark remote NS-VC as blocked + active */ (*nsvc)->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; break; default: LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", (*nsvc)->nsei, nsh->pdu_type); rc = -EINVAL; break; } return rc; } /*! \brief Create a new GPRS NS instance * \param[in] cb Call-back function for incoming BSSGP data * \returns dynamically allocated gprs_ns_inst */ struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb, void *ctx) { struct gprs_ns_inst *nsi = talloc_zero(ctx, struct gprs_ns_inst); nsi->cb = cb; INIT_LLIST_HEAD(&nsi->gprs_nsvcs); nsi->timeout[NS_TOUT_TNS_BLOCK] = 3; nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3; nsi->timeout[NS_TOUT_TNS_RESET] = 3; nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3; nsi->timeout[NS_TOUT_TNS_TEST] = 30; nsi->timeout[NS_TOUT_TNS_ALIVE] = 3; nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10; /* Create the dummy NSVC that we use for sending * messages to non-existant/unknown NS-VC's */ nsi->unknown_nsvc = gprs_nsvc_create(nsi, 0xfffe); nsi->unknown_nsvc->nsvci_is_valid = 0; llist_del(&nsi->unknown_nsvc->list); INIT_LLIST_HEAD(&nsi->unknown_nsvc->list); return nsi; } void gprs_ns_close(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc, *nsvc2; gprs_nsvc_delete(nsi->unknown_nsvc); /* delete all NSVCs and clear their timers */ llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) gprs_nsvc_delete(nsvc); /* close socket and unregister */ if (nsi->nsip.fd.data) { close(nsi->nsip.fd.fd); osmo_fd_unregister(&nsi->nsip.fd); nsi->nsip.fd.data = NULL; } } /*! \brief Destroy an entire NS instance * \param nsi gprs_ns_inst that is to be destroyed * * This function releases all resources associated with the * NS-instance. */ void gprs_ns_destroy(struct gprs_ns_inst *nsi) { gprs_ns_close(nsi); /* free the NSI */ talloc_free(nsi); } /* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ /* Read a single NS-over-IP message */ static struct msgb *read_nsip_msg(struct osmo_fd *bfd, int *error, struct sockaddr_in *saddr) { struct msgb *msg = gprs_ns_msgb_alloc(); int ret = 0; socklen_t saddr_len = sizeof(*saddr); if (!msg) { *error = -ENOMEM; return NULL; } ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0, (struct sockaddr *)saddr, &saddr_len); if (ret < 0) { LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n", strerror(errno)); msgb_free(msg); *error = ret; return NULL; } else if (ret == 0) { msgb_free(msg); *error = ret; return NULL; } msg->l2h = msg->data; msgb_put(msg, ret); return msg; } static int handle_nsip_read(struct osmo_fd *bfd) { int error; struct sockaddr_in saddr; struct gprs_ns_inst *nsi = bfd->data; struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); if (!msg) return error; error = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_UDP); msgb_free(msg); return error; } static int handle_nsip_write(struct osmo_fd *bfd) { /* FIXME: actually send the data here instead of nsip_sendmsg() */ return -EIO; } static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) { int rc; struct gprs_ns_inst *nsi = nsvc->nsi; struct sockaddr_in *daddr = &nsvc->ip.bts_addr; rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, (struct sockaddr *)daddr, sizeof(*daddr)); msgb_free(msg); return rc; } /* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ static int nsip_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_nsip_read(bfd); if (what & BSC_FD_WRITE) rc = handle_nsip_write(bfd); return rc; } /*! \brief Create a listening socket for GPRS NS/UDP/IP * \param[in] nsi NS protocol instance to listen * \returns >=0 (fd) in case of success, negative in case of error * * A call to this function will create a UDP socket bound to the port * number and IP address specified in the NS protocol instance. The * file descriptor of the socket will be stored in nsi->nsip.fd. */ int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi) { struct in_addr in; int ret; in.s_addr = htonl(nsi->nsip.local_ip); nsi->nsip.fd.cb = nsip_fd_cb; nsi->nsip.fd.data = nsi; ret = osmo_sock_init_ofd(&nsi->nsip.fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, inet_ntoa(in), nsi->nsip.local_port, OSMO_SOCK_F_BIND); if (ret < 0) return ret; ret = setsockopt(nsi->nsip.fd.fd, IPPROTO_IP, IP_TOS, &nsi->nsip.dscp, sizeof(nsi->nsip.dscp)); if (ret < 0) LOGP(DNS, LOGL_ERROR, "Failed to set the DSCP to %d with ret(%d) errno(%d)\n", nsi->nsip.dscp, ret, errno); return ret; } /*! \brief Initiate a RESET procedure * \param[in] nsvc NS-VC in which to start the procedure * \param[in] cause Numeric NS cause value * * This is a high-level function initiating a NS-RESET procedure. It * will not only send a NS-RESET, but also set the state to BLOCKED and * start the Tns-reset timer. */ int gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause) { int rc; LOGP(DNS, LOGL_INFO, "NSEI=%u RESET procedure based on API request\n", nsvc->nsei); /* Mark NS-VC locally as blocked and dead */ nsvc->state = NSE_S_BLOCKED | NSE_S_RESET; /* Send NS-RESET PDU */ rc = gprs_ns_tx_reset(nsvc, cause); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", nsvc->nsei); } /* Start Tns-reset */ nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); return rc; } /*! \brief Establish a NS connection (from the BSS) to the SGSN * \param nsi NS-instance * \param[in] dest Destination IP/Port * \param[in] nsei NSEI of the to-be-established NS-VC * \param[in] nsvci NSVCI of the to-be-established NS-VC * \returns struct gprs_nsvc representing the new NS-VC * * This function will establish a single NS/UDP/IP connection in uplink * (BSS to SGSN) direction. */ struct gprs_nsvc *gprs_ns_nsip_connect(struct gprs_ns_inst *nsi, struct sockaddr_in *dest, uint16_t nsei, uint16_t nsvci) { struct gprs_nsvc *nsvc; nsvc = nsvc_by_rem_addr(nsi, dest); if (!nsvc) nsvc = gprs_nsvc_create(nsi, nsvci); nsvc->ip.bts_addr = *dest; nsvc->nsei = nsei; nsvc->remote_end_is_sgsn = 1; gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); return nsvc; } void gprs_ns_set_log_ss(int ss) { DNS = ss; } /*! }@ */ libosmocore-0.9.0/src/gb/gprs_ns_frgre.c000066400000000000000000000210161261607044000202200ustar00rootroot00000000000000/* GPRS Networks Service (NS) messages on the Gb interface * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ /* NS-over-FR-over-GRE implementation */ /* (C) 2009-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" #define GRE_PTYPE_FR 0x6559 #define GRE_PTYPE_IPv4 0x0800 #define GRE_PTYPE_KAR 0x0000 /* keepalive response */ #ifndef IPPROTO_GRE # define IPPROTO_GRE 47 #endif struct gre_hdr { uint16_t flags; uint16_t ptype; } __attribute__ ((packed)); #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__) /** * On BSD the IPv4 struct is called struct ip and instead of iXX * the members are called ip_XX. One could change this code to use * struct ip but that would require to define _BSD_SOURCE and that * might have other complications. Instead make sure struct iphdr * is present on FreeBSD. The below is taken from GLIBC. * * The GNU C 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 2.1 of the License, or (at your option) any later version. */ struct iphdr { #if BYTE_ORDER == LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif BYTE_ORDER == BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; #endif /* IPv4 messages inside the GRE tunnel might be GRE keepalives */ static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg, struct iphdr *iph, struct gre_hdr *greh) { struct gprs_ns_inst *nsi = bfd->data; int gre_payload_len; struct iphdr *inner_iph; struct gre_hdr *inner_greh; struct sockaddr_in daddr; struct in_addr ia; gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh)); inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh)); if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) { LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n"); return -EIO; } if (inner_iph->saddr != iph->daddr || inner_iph->daddr != iph->saddr) { LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong tunnel addresses\n"); return -EIO; } if (inner_iph->protocol != IPPROTO_GRE) { LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n"); return -EIO; } inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4); if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) { LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n"); return -EIO; } /* Actually send the response back */ daddr.sin_family = AF_INET; daddr.sin_addr.s_addr = inner_iph->daddr; daddr.sin_port = IPPROTO_GRE; ia.s_addr = iph->saddr; LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n", inet_ntoa(ia)); return sendto(nsi->frgre.fd.fd, inner_greh, gre_payload_len - inner_iph->ihl*4, 0, (struct sockaddr *)&daddr, sizeof(daddr)); } static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error, struct sockaddr_in *saddr) { struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx"); int ret = 0; socklen_t saddr_len = sizeof(*saddr); struct iphdr *iph; struct gre_hdr *greh; uint8_t *frh; uint16_t dlci; if (!msg) { *error = -ENOMEM; return NULL; } ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, (struct sockaddr *)saddr, &saddr_len); if (ret < 0) { LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", strerror(errno)); *error = ret; goto out_err; } else if (ret == 0) { *error = ret; goto out_err; } msgb_put(msg, ret); if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) { LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); *error = -EIO; goto out_err; } iph = (struct iphdr *) msg->data; if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) { LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); *error = -EIO; goto out_err; } greh = (struct gre_hdr *) (msg->data + iph->ihl*4); if (greh->flags) { LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n", ntohs(greh->flags)); } switch (ntohs(greh->ptype)) { case GRE_PTYPE_IPv4: /* IPv4 messages might be GRE keepalives */ *error = handle_rx_gre_ipv4(bfd, msg, iph, greh); goto out_err; break; case GRE_PTYPE_FR: /* continue as usual */ break; default: LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n", ntohs(greh->ptype)); *error = -EIO; goto out_err; break; } if (msg->len < sizeof(*greh) + 2) { LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len); *error = -EIO; goto out_err; } frh = (uint8_t *)greh + sizeof(*greh); if (frh[0] & 0x01) { LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n"); *error = -EIO; goto out_err; } dlci = ((frh[0] & 0xfc) << 2); if ((frh[1] & 0x0f) != 0x01) { LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n", frh[1]); *error = -EIO; goto out_err; } dlci |= (frh[1] >> 4); msg->l2h = frh+2; /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */ saddr->sin_port = htons(dlci); return msg; out_err: msgb_free(msg); return NULL; } int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll); static int handle_nsfrgre_read(struct osmo_fd *bfd) { int rc; struct sockaddr_in saddr; struct gprs_ns_inst *nsi = bfd->data; struct msgb *msg; uint16_t dlci; msg = read_nsfrgre_msg(bfd, &rc, &saddr); if (!msg) return rc; dlci = ntohs(saddr.sin_port); if (dlci == 0 || dlci == 1023) { LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n", dlci); rc = 0; goto out; } rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE); out: msgb_free(msg); return rc; } static int handle_nsfrgre_write(struct osmo_fd *bfd) { /* FIXME: actually send the data here instead of nsip_sendmsg() */ return -EIO; } int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) { int rc; struct gprs_ns_inst *nsi = nsvc->nsi; struct sockaddr_in daddr; uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port); uint8_t *frh; struct gre_hdr *greh; /* Build socket address for the packet destionation */ daddr.sin_family = AF_INET; daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr; daddr.sin_port = IPPROTO_GRE; /* Prepend the FR header */ frh = msgb_push(msg, 2); frh[0] = (dlci >> 2) & 0xfc; frh[1] = ((dlci & 0xf)<<4) | 0x01; /* Prepend the GRE header */ greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh)); greh->flags = 0; greh->ptype = htons(GRE_PTYPE_FR); rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0, (struct sockaddr *)&daddr, sizeof(daddr)); msgb_free(msg); return rc; } static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_nsfrgre_read(bfd); if (what & BSC_FD_WRITE) rc = handle_nsfrgre_write(bfd); return rc; } int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi) { struct in_addr in; int rc; in.s_addr = htonl(nsi->frgre.local_ip); /* Make sure we close any existing socket before changing it */ if (nsi->frgre.fd.fd) close(nsi->frgre.fd.fd); if (!nsi->frgre.enabled) return 0; nsi->frgre.fd.cb = nsfrgre_fd_cb; nsi->frgre.fd.data = nsi; rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW, IPPROTO_GRE, inet_ntoa(in), 0, OSMO_SOCK_F_BIND); if (rc < 0) { LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n", strerror(errno)); return rc; } nsi->frgre.fd.data = nsi; return rc; } libosmocore-0.9.0/src/gb/gprs_ns_vty.c000066400000000000000000000361531261607044000177450ustar00rootroot00000000000000/* VTY interface for our GPRS Networks Service (NS) implementation */ /* (C) 2009-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common_vty.h" static struct gprs_ns_inst *vty_nsi = NULL; /* FIXME: this should go to some common file as it is copied * in vty_interface.c of the BSC */ static const struct value_string gprs_ns_timer_strs[] = { { 0, "tns-block" }, { 1, "tns-block-retries" }, { 2, "tns-reset" }, { 3, "tns-reset-retries" }, { 4, "tns-test" }, { 5, "tns-alive" }, { 6, "tns-alive-retries" }, { 0, NULL } }; static void log_set_nsvc_filter(struct log_target *target, struct gprs_nsvc *nsvc) { if (nsvc) { target->filter_map |= (1 << FLT_NSVC); target->filter_data[FLT_NSVC] = nsvc; } else if (target->filter_data[FLT_NSVC]) { target->filter_map = ~(1 << FLT_NSVC); target->filter_data[FLT_NSVC] = NULL; } } static struct cmd_node ns_node = { L_NS_NODE, "%s(config-ns)# ", 1, }; static int config_write_ns(struct vty *vty) { struct gprs_nsvc *nsvc; unsigned int i; struct in_addr ia; vty_out(vty, "ns%s", VTY_NEWLINE); llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) { if (!nsvc->persistent) continue; vty_out(vty, " nse %u nsvci %u%s", nsvc->nsei, nsvc->nsvci, VTY_NEWLINE); vty_out(vty, " nse %u remote-role %s%s", nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss", VTY_NEWLINE); switch (nsvc->ll) { case GPRS_NS_LL_UDP: vty_out(vty, " nse %u encapsulation udp%s", nsvc->nsei, VTY_NEWLINE); vty_out(vty, " nse %u remote-ip %s%s", nsvc->nsei, inet_ntoa(nsvc->ip.bts_addr.sin_addr), VTY_NEWLINE); vty_out(vty, " nse %u remote-port %u%s", nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port), VTY_NEWLINE); break; case GPRS_NS_LL_FR_GRE: vty_out(vty, " nse %u encapsulation framerelay-gre%s", nsvc->nsei, VTY_NEWLINE); vty_out(vty, " nse %u remote-ip %s%s", nsvc->nsei, inet_ntoa(nsvc->frgre.bts_addr.sin_addr), VTY_NEWLINE); vty_out(vty, " nse %u fr-dlci %u%s", nsvc->nsei, ntohs(nsvc->frgre.bts_addr.sin_port), VTY_NEWLINE); default: break; } } for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++) vty_out(vty, " timer %s %u%s", get_value_string(gprs_ns_timer_strs, i), vty_nsi->timeout[i], VTY_NEWLINE); if (vty_nsi->nsip.local_ip) { ia.s_addr = htonl(vty_nsi->nsip.local_ip); vty_out(vty, " encapsulation udp local-ip %s%s", inet_ntoa(ia), VTY_NEWLINE); } if (vty_nsi->nsip.local_port) vty_out(vty, " encapsulation udp local-port %u%s", vty_nsi->nsip.local_port, VTY_NEWLINE); if (vty_nsi->nsip.dscp) vty_out(vty, " encapsulation udp dscp %d%s", vty_nsi->nsip.dscp, VTY_NEWLINE); vty_out(vty, " encapsulation framerelay-gre enabled %u%s", vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE); if (vty_nsi->frgre.local_ip) { ia.s_addr = htonl(vty_nsi->frgre.local_ip); vty_out(vty, " encapsulation framerelay-gre local-ip %s%s", inet_ntoa(ia), VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(cfg_ns, cfg_ns_cmd, "ns", "Configure the GPRS Network Service") { vty->node = L_NS_NODE; return CMD_SUCCESS; } static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats) { vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s", nsvc->nsei, nsvc->nsvci, nsvc->remote_end_is_sgsn ? "SGSN" : "BSS", nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD", nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED"); if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE) vty_out(vty, ", %s %15s:%u", nsvc->ll == GPRS_NS_LL_UDP ? "UDP " : "FR-GRE", inet_ntoa(nsvc->ip.bts_addr.sin_addr), ntohs(nsvc->ip.bts_addr.sin_port)); vty_out(vty, "%s", VTY_NEWLINE); if (stats) { vty_out_rate_ctr_group(vty, " ", nsvc->ctrg); vty_out_stat_item_group(vty, " ", nsvc->statg); } } static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats) { struct gprs_nsvc *nsvc; struct in_addr ia; ia.s_addr = htonl(vty_nsi->nsip.local_ip); vty_out(vty, "Encapsulation NS-UDP-IP Local IP: %s, UDP Port: %u%s", inet_ntoa(ia), vty_nsi->nsip.local_port, VTY_NEWLINE); ia.s_addr = htonl(vty_nsi->frgre.local_ip); vty_out(vty, "Encapsulation NS-FR-GRE-IP Local IP: %s%s", inet_ntoa(ia), VTY_NEWLINE); llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { if (nsvc == nsi->unknown_nsvc) continue; dump_nse(vty, nsvc, stats); } } DEFUN(show_ns, show_ns_cmd, "show ns", SHOW_STR "Display information about the NS protocol") { struct gprs_ns_inst *nsi = vty_nsi; dump_ns(vty, nsi, 0); return CMD_SUCCESS; } DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats", SHOW_STR "Display information about the NS protocol\n" "Include statistics\n") { struct gprs_ns_inst *nsi = vty_nsi; dump_ns(vty, nsi, 1); return CMD_SUCCESS; } DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]", SHOW_STR "Display information about the NS protocol\n" "Select one NSE by its NSE Identifier\n" "Select one NSE by its NS-VC Identifier\n" "The Identifier of selected type\n" "Include Statistics\n") { struct gprs_ns_inst *nsi = vty_nsi; struct gprs_nsvc *nsvc; uint16_t id = atoi(argv[1]); int show_stats = 0; if (!strcmp(argv[0], "nsei")) nsvc = gprs_nsvc_by_nsei(nsi, id); else nsvc = gprs_nsvc_by_nsvci(nsi, id); if (!nsvc) { vty_out(vty, "No such NS Entity%s", VTY_NEWLINE); return CMD_WARNING; } if (argc >= 3) show_stats = 1; dump_nse(vty, nsvc, show_stats); return CMD_SUCCESS; } #define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n" DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd, "nse <0-65535> nsvci <0-65534>", NSE_CMD_STR "NS Virtual Connection\n" "NS Virtual Connection ID (NSVCI)\n" ) { uint16_t nsei = atoi(argv[0]); uint16_t nsvci = atoi(argv[1]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { nsvc = gprs_nsvc_create(vty_nsi, nsvci); nsvc->nsei = nsei; } nsvc->nsvci = nsvci; /* All NSVCs that are explicitly configured by VTY are * marked as persistent so we can write them to the config * file at some later point */ nsvc->persistent = 1; return CMD_SUCCESS; } DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd, "nse <0-65535> remote-ip A.B.C.D", NSE_CMD_STR "Remote IP Address\n" "Remote IP Address\n") { uint16_t nsei = atoi(argv[0]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr); return CMD_SUCCESS; } DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd, "nse <0-65535> remote-port <0-65535>", NSE_CMD_STR "Remote UDP Port\n" "Remote UDP Port Number\n") { uint16_t nsei = atoi(argv[0]); uint16_t port = atoi(argv[1]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } if (nsvc->ll != GPRS_NS_LL_UDP) { vty_out(vty, "Cannot set UDP Port on non-UDP NSE%s", VTY_NEWLINE); return CMD_WARNING; } nsvc->ip.bts_addr.sin_port = htons(port); return CMD_SUCCESS; } DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd, "nse <0-65535> fr-dlci <16-1007>", NSE_CMD_STR "Frame Relay DLCI\n" "Frame Relay DLCI Number\n") { uint16_t nsei = atoi(argv[0]); uint16_t dlci = atoi(argv[1]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } if (nsvc->ll != GPRS_NS_LL_FR_GRE) { vty_out(vty, "Cannot set FR DLCI on non-FR NSE%s", VTY_NEWLINE); return CMD_WARNING; } nsvc->frgre.bts_addr.sin_port = htons(dlci); return CMD_SUCCESS; } DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd, "nse <0-65535> encapsulation (udp|framerelay-gre)", NSE_CMD_STR "Encapsulation for NS\n" "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n") { uint16_t nsei = atoi(argv[0]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[1], "udp")) nsvc->ll = GPRS_NS_LL_UDP; else nsvc->ll = GPRS_NS_LL_FR_GRE; return CMD_SUCCESS; } DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd, "nse <0-65535> remote-role (sgsn|bss)", NSE_CMD_STR "Remote NSE Role\n" "Remote Peer is SGSN\n" "Remote Peer is BSS\n") { uint16_t nsei = atoi(argv[0]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[1], "sgsn")) nsvc->remote_end_is_sgsn = 1; else nsvc->remote_end_is_sgsn = 0; return CMD_SUCCESS; } DEFUN(cfg_no_nse, cfg_no_nse_cmd, "no nse <0-65535>", "Delete Persistent NS Entity\n" "Delete " NSE_CMD_STR) { uint16_t nsei = atoi(argv[0]); struct gprs_nsvc *nsvc; nsvc = gprs_nsvc_by_nsei(vty_nsi, nsei); if (!nsvc) { vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); return CMD_WARNING; } if (!nsvc->persistent) { vty_out(vty, "NSEI %u is not a persistent NSE%s", nsei, VTY_NEWLINE); return CMD_WARNING; } nsvc->persistent = 0; return CMD_SUCCESS; } DEFUN(cfg_ns_timer, cfg_ns_timer_cmd, "timer " NS_TIMERS " <0-65535>", "Network Service Timer\n" NS_TIMERS_HELP "Timer Value\n") { int idx = get_string_value(gprs_ns_timer_strs, argv[0]); int val = atoi(argv[1]); if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout)) return CMD_WARNING; vty_nsi->timeout[idx] = val; return CMD_SUCCESS; } #define ENCAPS_STR "NS encapsulation options\n" DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd, "encapsulation udp local-ip A.B.C.D", ENCAPS_STR "NS over UDP Encapsulation\n" "Set the IP address on which we listen for NS/UDP\n" "IP Address\n") { struct in_addr ia; inet_aton(argv[0], &ia); vty_nsi->nsip.local_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd, "encapsulation udp local-port <0-65535>", ENCAPS_STR "NS over UDP Encapsulation\n" "Set the UDP port on which we listen for NS/UDP\n" "UDP port number\n") { unsigned int port = atoi(argv[0]); vty_nsi->nsip.local_port = port; return CMD_SUCCESS; } DEFUN(cfg_nsip_dscp, cfg_nsip_dscp_cmd, "encapsulation udp dscp <0-255>", ENCAPS_STR "NS over UDP Encapsulation\n" "Set DSCP/TOS on the UDP socket\n" "DSCP Value\n") { int dscp = atoi(argv[0]); vty_nsi->nsip.dscp = dscp; return CMD_SUCCESS; } DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd, "encapsulation framerelay-gre local-ip A.B.C.D", ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" "Set the IP address on which we listen for NS/FR/GRE\n" "IP Address\n") { struct in_addr ia; if (!vty_nsi->frgre.enabled) { vty_out(vty, "FR/GRE is not enabled%s", VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[0], &ia); vty_nsi->frgre.local_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd, "encapsulation framerelay-gre enabled (1|0)", ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" "Enable or disable Frame Relay over GRE\n" "Enable\n" "Disable\n") { int enabled = atoi(argv[0]); vty_nsi->frgre.enabled = enabled; return CMD_SUCCESS; } DEFUN(nsvc_nsei, nsvc_nsei_cmd, "nsvc (nsei|nsvci) <0-65535> (block|unblock|reset)", "Perform an operation on a NSVC\n" "NSEI to identify NS-VC Identifier (NS-VCI)\n" "NS-VC Identifier (NS-VCI)\n" "The NSEI\n" "Initiate BLOCK procedure\n" "Initiate UNBLOCK procedure\n" "Initiate RESET procedure\n") { const char *id_type = argv[0]; uint16_t id = atoi(argv[1]); const char *operation = argv[2]; struct gprs_nsvc *nsvc; if (!strcmp(id_type, "nsei")) nsvc = gprs_nsvc_by_nsei(vty_nsi, id); else if (!strcmp(id_type, "nsvci")) nsvc = gprs_nsvc_by_nsvci(vty_nsi, id); else { vty_out(vty, "%%No such id_type '%s'%s", id_type, VTY_NEWLINE); return CMD_WARNING; } if (!nsvc) { vty_out(vty, "No such %s (%u)%s", id_type, id, VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(operation, "block")) gprs_ns_tx_block(nsvc, NS_CAUSE_OM_INTERVENTION); else if (!strcmp(operation, "unblock")) gprs_ns_tx_unblock(nsvc); else if (!strcmp(operation, "reset")) gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); else return CMD_WARNING; return CMD_SUCCESS; } DEFUN(logging_fltr_nsvc, logging_fltr_nsvc_cmd, "logging filter nsvc (nsei|nsvci) <0-65535>", LOGGING_STR FILTER_STR "Filter based on NS Virtual Connection\n" "Identify NS-VC by NSEI\n" "Identify NS-VC by NSVCI\n" "Numeric identifier\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); struct gprs_nsvc *nsvc; uint16_t id = atoi(argv[1]); if (!tgt) return CMD_WARNING; if (!strcmp(argv[0], "nsei")) nsvc = gprs_nsvc_by_nsei(vty_nsi, id); else nsvc = gprs_nsvc_by_nsvci(vty_nsi, id); if (!nsvc) { vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE); return CMD_WARNING; } log_set_nsvc_filter(tgt, nsvc); return CMD_SUCCESS; } int gprs_ns_vty_init(struct gprs_ns_inst *nsi) { vty_nsi = nsi; install_element_ve(&show_ns_cmd); install_element_ve(&show_ns_stats_cmd); install_element_ve(&show_nse_cmd); install_element_ve(&logging_fltr_nsvc_cmd); install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd); install_element(CONFIG_NODE, &cfg_ns_cmd); install_node(&ns_node, config_write_ns); install_default(L_NS_NODE); install_element(L_NS_NODE, &libgb_exit_cmd); install_element(L_NS_NODE, &libgb_end_cmd); install_element(L_NS_NODE, &cfg_nse_nsvci_cmd); install_element(L_NS_NODE, &cfg_nse_remoteip_cmd); install_element(L_NS_NODE, &cfg_nse_remoteport_cmd); install_element(L_NS_NODE, &cfg_nse_fr_dlci_cmd); install_element(L_NS_NODE, &cfg_nse_encaps_cmd); install_element(L_NS_NODE, &cfg_nse_remoterole_cmd); install_element(L_NS_NODE, &cfg_no_nse_cmd); install_element(L_NS_NODE, &cfg_ns_timer_cmd); install_element(L_NS_NODE, &cfg_nsip_local_ip_cmd); install_element(L_NS_NODE, &cfg_nsip_local_port_cmd); install_element(L_NS_NODE, &cfg_nsip_dscp_cmd); install_element(L_NS_NODE, &cfg_frgre_enable_cmd); install_element(L_NS_NODE, &cfg_frgre_local_ip_cmd); install_element(ENABLE_NODE, &nsvc_nsei_cmd); return 0; } libosmocore-0.9.0/src/gb/libosmogb.map000066400000000000000000000024321261607044000176710ustar00rootroot00000000000000LIBOSMOGB_1.0 { global: bssgp_cause_str; bssgp_create_cell_id; bssgp_fc_in; bssgp_fc_init; bssgp_fc_ms_init; bssgp_msgb_alloc; bssgp_msgb_tlli_put; bssgp_parse_cell_id; bssgp_tx_bvc_block; bssgp_tx_bvc_reset; bssgp_tx_bvc_unblock; bssgp_tx_fc_bvc; bssgp_tx_fc_ms; bssgp_tx_flush_ll_ack; bssgp_tx_llc_discarded; bssgp_tx_ra_capa_upd; bssgp_tx_radio_status_imsi; bssgp_tx_radio_status_tlli; bssgp_tx_radio_status_tmsi; bssgp_tx_resume; bssgp_tx_resume_ack; bssgp_tx_resume_nack; bssgp_tx_simple_bvci; bssgp_tx_status; bssgp_tx_suspend; bssgp_tx_suspend_ack; bssgp_tx_suspend_nack; bssgp_tx_ul_ud; bssgp_rcvmsg; bssgp_rx_paging; bssgp_set_log_ss; bssgp_tx_dl_ud; bssgp_tx_paging; bssgp_vty_init; bssgp_nsi; gprs_ns_cause_str; gprs_ns_destroy; gprs_ns_close; gprs_ns_frgre_listen; gprs_ns_frgre_sendmsg; gprs_ns_instantiate; gprs_ns_nsip_listen; gprs_ns_nsip_connect; gprs_ns_rcvmsg; gprs_ns_sendmsg; gprs_ns_set_log_ss; gprs_ns_tx_alive; gprs_ns_tx_alive_ack; gprs_ns_tx_block; gprs_ns_tx_reset; gprs_ns_tx_status; gprs_ns_tx_unblock; gprs_ns_vty_init; gprs_ns_ll_str; gprs_ns_ll_copy; gprs_ns_ll_clear; gprs_ns_msgb_alloc; gprs_nsvc_create; gprs_nsvc_delete; gprs_nsvc_reset; gprs_nsvc_by_nsvci; gprs_nsvc_by_nsei; gprs_log_filter_fn; btsctx_alloc; btsctx_by_bvci_nsei; btsctx_by_raid_cid; local: *; }; libosmocore-0.9.0/src/gsm/000077500000000000000000000000001261607044000154125ustar00rootroot00000000000000libosmocore-0.9.0/src/gsm/Makefile.am000066400000000000000000000024621261607044000174520ustar00rootroot00000000000000# 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=6:0:1 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall ${GCC_FVISIBILITY_HIDDEN} # FIXME: this should eventually go into a milenage/Makefile.am noinst_HEADERS = milenage/aes.h milenage/aes_i.h milenage/aes_wrap.h \ milenage/common.h milenage/crypto.h milenage/includes.h \ milenage/milenage.h noinst_LTLIBRARIES = libgsmint.la lib_LTLIBRARIES = libosmogsm.la libgsmint_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \ gsm_utils.c rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \ gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \ gsm0411_utils.c gsm0411_smc.c gsm0411_smr.c \ lapd_core.c lapdm.c kasumi.c \ auth_core.c auth_comp128v1.c auth_comp128v23.c \ auth_milenage.c milenage/aes-encblock.c \ milenage/aes-internal.c milenage/aes-internal-enc.c \ milenage/milenage.c gan.c ipa.c gsm0341.c apn.c libgsmint_la_LDFLAGS = -no-undefined libgsmint_la_LIBADD = ../libosmocore.la libosmogsm_la_SOURCES = libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined libosmogsm_la_LIBADD = libgsmint.la EXTRA_DIST = libosmogsm.map libosmocore-0.9.0/src/gsm/a5.c000066400000000000000000000263111261607044000160660ustar00rootroot00000000000000/* * a5.c * * Full reimplementation of A5/1,2 (split and threadsafe) * * The logic behind the algorithm is taken from "A pedagogical implementation * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by * Marc Briceno, Ian Goldberg, and David Wagner. * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \addtogroup a5 * @{ */ /*! \file gsm/a5.c * \brief Osmocom GSM A5 ciphering algorithm implementation */ #include #include #include #include #include /* Somme OS (like Nuttx) don't have ENOTSUP */ #ifndef ENOTSUP #define ENOTSUP EINVAL #endif /* ------------------------------------------------------------------------ */ /* A5/3&4 */ /* ------------------------------------------------------------------------ */ /*! \brief Generate a GSM A5/4 cipher stream * \param[in] key 16 byte array for the key (as received from the SIM) * \param[in] fn Frame number * \param[out] dl Pointer to array of ubits to return Downlink cipher stream * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion * * Either (or both) of dl/ul should be NULL if not needed. * * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202 * with slight simplifications (CE hardcoded to 0). */ void _a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct) { uint8_t i, gamma[32], uplink[15]; uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn; if (ul) { _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228); for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6); osmo_pbit2ubit(ul, uplink, 114); } if (dl) { _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114); osmo_pbit2ubit(dl, gamma, 114); } } /*! \brief Generate a GSM A5/3 cipher stream * \param[in] key 8 byte array for the key (as received from the SIM) * \param[in] fn Frame number * \param[out] dl Pointer to array of ubits to return Downlink cipher stream * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion * * Either (or both) of dl/ul should be NULL if not needed. * * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202 * with slight simplifications (CE hardcoded to 0). */ void _a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct) { uint8_t ck[16]; memcpy(ck, key, 8); memcpy(ck + 8, key, 8); /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */ _a5_4(ck, fn, dl, ul, fn_correct); } /*! \brief Main method to generate a A5/x cipher stream * \param[in] n Which A5/x method to use * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM) * \param[in] fn Frame number * \param[out] dl Pointer to array of ubits to return Downlink cipher stream * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * \returns 0 for success, -ENOTSUP for invalid cipher selection. * * Currently A5/[0-4] are supported. * Either (or both) of dl/ul can be NULL if not needed. */ int osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) { switch (n) { case 0: if (dl) memset(dl, 0x00, 114); if (ul) memset(ul, 0x00, 114); break; case 1: osmo_a5_1(key, fn, dl, ul); break; case 2: osmo_a5_2(key, fn, dl, ul); break; case 3: _a5_3(key, fn, dl, ul, true); break; case 4: _a5_4(key, fn, dl, ul, true); break; default: /* a5/[5..7] not supported here/yet */ return -ENOTSUP; } return 0; } /* ------------------------------------------------------------------------ */ /* A5/1&2 common stuff */ /* ------------------------------------------------------------------------ */ #define A5_R1_LEN 19 #define A5_R2_LEN 22 #define A5_R3_LEN 23 #define A5_R4_LEN 17 /* A5/2 only */ #define A5_R1_MASK ((1<> 16; x ^= x >> 8; x ^= x >> 4; x &= 0xf; return (0x6996 >> x) & 1; } /*! \brief Compute majority bit from 3 taps * \param[in] v1 LFSR state ANDed with tap-bit * \param[in] v2 LFSR state ANDed with tap-bit * \param[in] v3 LFSR state ANDed with tap-bit * \return The majority bit (0 or 1) */ static inline uint32_t _a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3) { return (!!v1 + !!v2 + !!v3) >= 2; } /*! \brief Compute the next LFSR state * \param[in] r Current state * \param[in] mask LFSR mask * \param[in] taps LFSR taps * \return Next state */ static inline uint32_t _a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps) { return ((r << 1) & mask) | _a5_12_parity(r & taps); } /* ------------------------------------------------------------------------ */ /* A5/1 */ /* ------------------------------------------------------------------------ */ #define A51_R1_CLKBIT 0x000100 #define A51_R2_CLKBIT 0x000400 #define A51_R3_CLKBIT 0x000400 /*! \brief GSM A5/1 Clocking function * \param[in] r Register state * \param[in] force Non-zero value disable conditional clocking */ static inline void _a5_1_clock(uint32_t r[], int force) { int cb[3], maj; cb[0] = !!(r[0] & A51_R1_CLKBIT); cb[1] = !!(r[1] & A51_R2_CLKBIT); cb[2] = !!(r[2] & A51_R3_CLKBIT); maj = _a5_12_majority(cb[0], cb[1], cb[2]); if (force || (maj == cb[0])) r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS); if (force || (maj == cb[1])) r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS); if (force || (maj == cb[2])) r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS); } /*! \brief GSM A5/1 Output function * \param[in] r Register state * \return The A5/1 output function bit */ static inline uint8_t _a5_1_get_output(uint32_t r[]) { return (r[0] >> (A5_R1_LEN-1)) ^ (r[1] >> (A5_R2_LEN-1)) ^ (r[2] >> (A5_R3_LEN-1)); } /*! \brief Generate a GSM A5/1 cipher stream * \param[in] key 8 byte array for the key (as received from the SIM) * \param[in] fn Frame number * \param[out] dl Pointer to array of ubits to return Downlink cipher stream * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * * Either (or both) of dl/ul can be NULL if not needed. */ void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) { uint32_t r[3] = {0, 0, 0}; uint32_t fn_count; uint32_t b; int i; /* Key load */ for (i=0; i<64; i++) { b = ( key[7 - (i>>3)] >> (i&7) ) & 1; _a5_1_clock(r, 1); r[0] ^= b; r[1] ^= b; r[2] ^= b; } /* Frame count load */ fn_count = osmo_a5_fn_count(fn); for (i=0; i<22; i++) { b = (fn_count >> i) & 1; _a5_1_clock(r, 1); r[0] ^= b; r[1] ^= b; r[2] ^= b; } /* Mix */ for (i=0; i<100; i++) { _a5_1_clock(r, 0); } /* Output */ for (i=0; i<114; i++) { _a5_1_clock(r, 0); if (dl) dl[i] = _a5_1_get_output(r); } for (i=0; i<114; i++) { _a5_1_clock(r, 0); if (ul) ul[i] = _a5_1_get_output(r); } } /* ------------------------------------------------------------------------ */ /* A5/2 */ /* ------------------------------------------------------------------------ */ #define A52_R4_CLKBIT0 0x000400 #define A52_R4_CLKBIT1 0x000008 #define A52_R4_CLKBIT2 0x000080 /*! \brief GSM A5/2 Clocking function * \param[in] r Register state * \param[in] force Non-zero value disable conditional clocking */ static inline void _a5_2_clock(uint32_t r[], int force) { int cb[3], maj; cb[0] = !!(r[3] & A52_R4_CLKBIT0); cb[1] = !!(r[3] & A52_R4_CLKBIT1); cb[2] = !!(r[3] & A52_R4_CLKBIT2); maj = (cb[0] + cb[1] + cb[2]) >= 2; if (force || (maj == cb[0])) r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS); if (force || (maj == cb[1])) r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS); if (force || (maj == cb[2])) r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS); r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS); } /*! \brief GSM A5/2 Output function * \param[in] r Register state * \return The A5/2 output function bit */ static inline uint8_t _a5_2_get_output(uint32_t r[]) { uint8_t b; b = (r[0] >> (A5_R1_LEN-1)) ^ (r[1] >> (A5_R2_LEN-1)) ^ (r[2] >> (A5_R3_LEN-1)) ^ _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^ _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^ _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000); return b; } /*! \brief Generate a GSM A5/1 cipher stream * \param[in] key 8 byte array for the key (as received from the SIM) * \param[in] fn Frame number * \param[out] dl Pointer to array of ubits to return Downlink cipher stream * \param[out] ul Pointer to array of ubits to return Uplink cipher stream * * Either (or both) of dl/ul can be NULL if not needed. */ void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) { uint32_t r[4] = {0, 0, 0, 0}; uint32_t fn_count; uint32_t b; int i; /* Key load */ for (i=0; i<64; i++) { b = ( key[7 - (i>>3)] >> (i&7) ) & 1; _a5_2_clock(r, 1); r[0] ^= b; r[1] ^= b; r[2] ^= b; r[3] ^= b; } /* Frame count load */ fn_count = osmo_a5_fn_count(fn); for (i=0; i<22; i++) { b = (fn_count >> i) & 1; _a5_2_clock(r, 1); r[0] ^= b; r[1] ^= b; r[2] ^= b; r[3] ^= b; } r[0] |= 1 << 15; r[1] |= 1 << 16; r[2] |= 1 << 18; r[3] |= 1 << 10; /* Mix */ for (i=0; i<99; i++) { _a5_2_clock(r, 0); } /* Output */ for (i=0; i<114; i++) { _a5_2_clock(r, 0); if (dl) dl[i] = _a5_2_get_output(r); } for (i=0; i<114; i++) { _a5_2_clock(r, 0); if (ul) ul[i] = _a5_2_get_output(r); } } /*! @} */ libosmocore-0.9.0/src/gsm/abis_nm.c000066400000000000000000000372211261607044000171730ustar00rootroot00000000000000/* GSM Network Management (OML) messages on the A-bis interface * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ /* (C) 2008-2011 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /*! \addtogroup oml * @{ */ /*! \file abis_nm.c */ #include #include #include #include #include #include #include #include const char abis_nm_ipa_magic[13] = "com.ipaccess"; const char abis_nm_osmo_magic[12] = "org.osmocom"; /*! \brief unidirectional messages from BTS to BSC */ const enum abis_nm_msgtype abis_nm_reports[4] = { NM_MT_SW_ACTIVATED_REP, NM_MT_TEST_REP, NM_MT_STATECHG_EVENT_REP, NM_MT_FAILURE_EVENT_REP, }; /*! \brief messages without ACK/NACK */ const enum abis_nm_msgtype abis_nm_no_ack_nack[3] = { NM_MT_MEAS_RES_REQ, NM_MT_STOP_MEAS, NM_MT_START_MEAS, }; /*! \brief messages related to software load */ const enum abis_nm_msgtype abis_nm_sw_load_msgs[9] = { NM_MT_LOAD_INIT_ACK, NM_MT_LOAD_INIT_NACK, NM_MT_LOAD_SEG_ACK, NM_MT_LOAD_ABORT, NM_MT_LOAD_END_ACK, NM_MT_LOAD_END_NACK, //NM_MT_SW_ACT_REQ, NM_MT_ACTIVATE_SW_ACK, NM_MT_ACTIVATE_SW_NACK, NM_MT_SW_ACTIVATED_REP, }; /*! \brief All NACKs (negative acknowledgements */ const enum abis_nm_msgtype abis_nm_nacks[33] = { NM_MT_LOAD_INIT_NACK, NM_MT_LOAD_END_NACK, NM_MT_SW_ACT_REQ_NACK, NM_MT_ACTIVATE_SW_NACK, NM_MT_ESTABLISH_TEI_NACK, NM_MT_CONN_TERR_SIGN_NACK, NM_MT_DISC_TERR_SIGN_NACK, NM_MT_CONN_TERR_TRAF_NACK, NM_MT_DISC_TERR_TRAF_NACK, NM_MT_CONN_MDROP_LINK_NACK, NM_MT_DISC_MDROP_LINK_NACK, NM_MT_SET_BTS_ATTR_NACK, NM_MT_SET_RADIO_ATTR_NACK, NM_MT_SET_CHAN_ATTR_NACK, NM_MT_PERF_TEST_NACK, NM_MT_SEND_TEST_REP_NACK, NM_MT_STOP_TEST_NACK, NM_MT_STOP_EVENT_REP_NACK, NM_MT_REST_EVENT_REP_NACK, NM_MT_CHG_ADM_STATE_NACK, NM_MT_CHG_ADM_STATE_REQ_NACK, NM_MT_REP_OUTST_ALARMS_NACK, NM_MT_CHANGEOVER_NACK, NM_MT_OPSTART_NACK, NM_MT_REINIT_NACK, NM_MT_SET_SITE_OUT_NACK, NM_MT_CHG_HW_CONF_NACK, NM_MT_GET_ATTR_NACK, NM_MT_SET_ALARM_THRES_NACK, NM_MT_BS11_BEGIN_DB_TX_NACK, NM_MT_BS11_END_DB_TX_NACK, NM_MT_BS11_CREATE_OBJ_NACK, NM_MT_BS11_DELETE_OBJ_NACK, }; static const struct value_string nack_names[] = { { NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" }, { NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" }, { NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" }, { NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" }, { NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" }, { NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" }, { NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" }, { NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" }, { NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" }, { NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" }, { NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" }, { NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" }, { NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" }, { NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" }, { NM_MT_PERF_TEST_NACK, "PERFORM TEST" }, { NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" }, { NM_MT_STOP_TEST_NACK, "STOP TEST" }, { NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" }, { NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" }, { NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" }, { NM_MT_CHG_ADM_STATE_REQ_NACK, "CHANGE ADMINISTRATIVE STATE REQUEST" }, { NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" }, { NM_MT_CHANGEOVER_NACK, "CHANGEOVER" }, { NM_MT_OPSTART_NACK, "OPSTART" }, { NM_MT_REINIT_NACK, "REINIT" }, { NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" }, { NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" }, { NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" }, { NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" }, { NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" }, { NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" }, { NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" }, { NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" }, { 0, NULL } }; /*! \brief Get human-readable string for OML NACK message type */ const char *abis_nm_nack_name(uint8_t nack) { return get_value_string(nack_names, nack); } /* Chapter 9.4.36 */ static const struct value_string nack_cause_names[] = { /* General Nack Causes */ { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, { NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" }, { NM_NACK_BTSNR_UNKN, "BTS no. unknown" }, { NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" }, { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, { NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" }, { NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" }, { NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" }, { NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" }, { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" }, { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, /* Specific Nack Causes */ { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, { NM_NACK_RES_NOTAVAIL, "Resource not available" }, { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, { NM_NACK_TEST_NOTSUPP, "Test not supported" }, { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, { NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" }, { NM_NACK_TEST_NOTINIT, "Test not initiated" }, { NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" }, { NM_NACK_TEST_NOSUCH, "No such test" }, { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" }, { NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" }, { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, { NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" }, { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, { NM_NACK_WAIT, "Wait" }, { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, { 0, NULL } }; /*! \brief Get human-readable string for NACK cause */ const char *abis_nm_nack_cause_name(uint8_t cause) { return get_value_string(nack_cause_names, cause); } /* Chapter 9.4.16: Event Type */ static const struct value_string event_type_names[] = { { NM_EVT_COMM_FAIL, "communication failure" }, { NM_EVT_QOS_FAIL, "quality of service failure" }, { NM_EVT_PROC_FAIL, "processing failure" }, { NM_EVT_EQUIP_FAIL, "equipment failure" }, { NM_EVT_ENV_FAIL, "environment failure" }, { 0, NULL } }; /*! \brief Get human-readable string for OML event type */ const char *abis_nm_event_type_name(uint8_t cause) { return get_value_string(event_type_names, cause); } /* Chapter 9.4.63: Perceived Severity */ static const struct value_string severity_names[] = { { NM_SEVER_CEASED, "failure ceased" }, { NM_SEVER_CRITICAL, "critical failure" }, { NM_SEVER_MAJOR, "major failure" }, { NM_SEVER_MINOR, "minor failure" }, { NM_SEVER_WARNING, "warning level failure" }, { NM_SEVER_INDETERMINATE, "indeterminate failure" }, { 0, NULL } }; /*! \brief Get human-readable string for perceived OML severity */ const char *abis_nm_severity_name(uint8_t cause) { return get_value_string(severity_names, cause); } /*! \brief Attributes that the BSC can set, not only get, according to Section 9.4 */ const enum abis_nm_attr abis_nm_att_settable[] = { NM_ATT_ADD_INFO, NM_ATT_ADD_TEXT, NM_ATT_DEST, NM_ATT_EVENT_TYPE, NM_ATT_FILE_DATA, NM_ATT_GET_ARI, NM_ATT_HW_CONF_CHG, NM_ATT_LIST_REQ_ATTR, NM_ATT_MDROP_LINK, NM_ATT_MDROP_NEXT, NM_ATT_NACK_CAUSES, NM_ATT_OUTST_ALARM, NM_ATT_PHYS_CONF, NM_ATT_PROB_CAUSE, NM_ATT_RAD_SUBC, NM_ATT_SOURCE, NM_ATT_SPEC_PROB, NM_ATT_START_TIME, NM_ATT_TEST_DUR, NM_ATT_TEST_NO, NM_ATT_TEST_REPORT, NM_ATT_WINDOW_SIZE, NM_ATT_SEVERITY, NM_ATT_MEAS_RES, NM_ATT_MEAS_TYPE, }; /*! \brief GSM A-bis OML TLV parser definition */ const struct tlv_definition abis_nm_att_tlvdef = { .def = { [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_BSIC] = { TLV_TYPE_TV }, [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, [NM_ATT_DEST] = { TLV_TYPE_TL16V }, [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_HSN] = { TLV_TYPE_TV }, [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, [NM_ATT_MAIO] = { TLV_TYPE_TV }, [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, [NM_ATT_NY1] = { TLV_TYPE_TV }, [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, [NM_ATT_TEI] = { TLV_TYPE_TV }, [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, [NM_ATT_TSC] = { TLV_TYPE_TV }, [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, }, }; /*! \brief org.osmocom GSM A-bis OML TLV parser definition */ const struct tlv_definition abis_nm_osmo_att_tlvdef = { .def = { [NM_ATT_OSMO_REDUCEPOWER] = { TLV_TYPE_TV }, }, }; /*! \brief Human-readable strings for A-bis OML Object Class */ const struct value_string abis_nm_msg_disc_names[] = { { ABIS_OM_MDISC_FOM, "FOM" }, { ABIS_OM_MDISC_MMI, "MMI" }, { ABIS_OM_MDISC_TRAU, "TRAU" }, { ABIS_OM_MDISC_MANUF, "MANUF" }, { 0, NULL } }; /*! \brief Human-readable strings for A-bis OML Object Class */ const struct value_string abis_nm_obj_class_names[] = { { NM_OC_SITE_MANAGER, "SITE-MANAGER" }, { NM_OC_BTS, "BTS" }, { NM_OC_RADIO_CARRIER, "RADIO-CARRIER" }, { NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" }, { NM_OC_CHANNEL, "CHANNEL" }, { NM_OC_BS11_ADJC, "ADJC" }, { NM_OC_BS11_HANDOVER, "HANDOVER" }, { NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" }, { NM_OC_BS11_BTSE, "BTSE" }, { NM_OC_BS11_RACK, "RACK" }, { NM_OC_BS11_TEST, "TEST" }, { NM_OC_BS11_ENVABTSE, "ENVABTSE" }, { NM_OC_BS11_BPORT, "BPORT" }, { NM_OC_GPRS_NSE, "GPRS-NSE" }, { NM_OC_GPRS_CELL, "GPRS-CELL" }, { NM_OC_GPRS_NSVC, "GPRS-NSVC" }, { NM_OC_BS11, "SIEMENSHW" }, { 0, NULL } }; /*! \brief Get human-readable string for OML Operational State */ const char *abis_nm_opstate_name(uint8_t os) { switch (os) { case NM_OPSTATE_DISABLED: return "Disabled"; case NM_OPSTATE_ENABLED: return "Enabled"; case NM_OPSTATE_NULL: return "NULL"; default: return "RFU"; } } /* Chapter 9.4.7 */ static const struct value_string avail_names[] = { { 0, "In test" }, { 1, "Failed" }, { 2, "Power off" }, { 3, "Off line" }, /* Not used */ { 5, "Dependency" }, { 6, "Degraded" }, { 7, "Not installed" }, { 0xff, "OK" }, { 0, NULL } }; /*! \brief Get human-readable string for OML Availability State */ const char *abis_nm_avail_name(uint8_t avail) { return get_value_string(avail_names, avail); } static const struct value_string test_names[] = { /* FIXME: standard test names */ { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" }, { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" }, { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" }, { 0, NULL } }; /*! \brief Get human-readable string for OML test */ const char *abis_nm_test_name(uint8_t test) { return get_value_string(test_names, test); } /*! \brief Human-readable names for OML administrative state */ const struct value_string abis_nm_adm_state_names[] = { { NM_STATE_LOCKED, "Locked" }, { NM_STATE_UNLOCKED, "Unlocked" }, { NM_STATE_SHUTDOWN, "Shutdown" }, { NM_STATE_NULL, "NULL" }, { 0, NULL } }; static const enum abis_nm_chan_comb chcomb4pchan[] = { [GSM_PCHAN_NONE] = 0xff, [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb, [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH, [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH, [GSM_PCHAN_UNKNOWN] = 0xff, [GSM_PCHAN_CCCH_SDCCH4_CBCH] = NM_CHANC_BCCH_CBCH, [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = NM_CHANC_SDCCH_CBCH, /* FIXME: bounds check */ }; /*! \brief Obtain OML Channel Combination for phnsical channel config */ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) { if (pchan < ARRAY_SIZE(chcomb4pchan)) return chcomb4pchan[pchan]; return -EINVAL; } /*! \brief Obtain physical channel config for OML Channel Combination */ enum gsm_phys_chan_config abis_nm_pchan4chcomb(uint8_t chcomb) { int i; for (i = 0; i < ARRAY_SIZE(chcomb4pchan); i++) { if (chcomb4pchan[i] == chcomb) return i; } return GSM_PCHAN_NONE; } /* this is just for compatibility reasons, it is now a macro */ #undef abis_nm_debugp_foh void abis_nm_debugp_foh(int ss, struct abis_om_fom_hdr *foh) { DEBUGP(ss, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", get_value_string(abis_nm_obj_class_names, foh->obj_class), foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); } /*! @} */ libosmocore-0.9.0/src/gsm/apn.c000066400000000000000000000014521261607044000163360ustar00rootroot00000000000000#include #include #include #include #include #define APN_OI_GPRS_FMT "mnc%03u.mcc%03u.gprs" #define APN_GPRS_FMT "%s.mnc%03u.mcc%03u.gprs" static char apn_strbuf[APN_MAXLEN+1]; char *osmo_apn_qualify(unsigned int mcc, unsigned int mnc, const char *ni) { snprintf(apn_strbuf, sizeof(apn_strbuf)-1, APN_GPRS_FMT, ni, mnc, mcc); apn_strbuf[sizeof(apn_strbuf)-1] = '\0'; return apn_strbuf; } char *osmo_apn_qualify_from_imsi(const char *imsi, const char *ni, int have_3dig_mnc) { char cbuf[3+1], nbuf[3+1]; strncpy(cbuf, imsi, 3); cbuf[3] = '\0'; if (have_3dig_mnc) { strncpy(nbuf, imsi+3, 3); nbuf[3] = '\0'; } else { strncpy(nbuf, imsi+3, 2); nbuf[2] = '\0'; } return osmo_apn_qualify(atoi(cbuf), atoi(nbuf), ni); } libosmocore-0.9.0/src/gsm/auth_comp128v1.c000066400000000000000000000026331261607044000202430ustar00rootroot00000000000000 /* GSM/GPRS/3G authentication core infrastructure */ /* (C) 2010-2011 by Harald Welte * * 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 #include static int c128v1_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand) { comp128(aud->u.gsm.ki, _rand, vec->sres, vec->kc); vec->auth_types = OSMO_AUTH_TYPE_GSM; return 0; } static struct osmo_auth_impl c128v1_alg = { .algo = OSMO_AUTH_ALG_COMP128v1, .name = "COMP128v1 (libosmogsm built-in)", .priority = 1000, .gen_vec = &c128v1_gen_vec, }; static __attribute__((constructor)) void on_dso_load_c128(void) { osmo_auth_register(&c128v1_alg); } libosmocore-0.9.0/src/gsm/auth_comp128v23.c000066400000000000000000000037231261607044000203300ustar00rootroot00000000000000/* registers COMP128 version 2 and 3 A3/A8 algorithms for the * GSM/GPRS/3G authentication core infrastructure * */ /* (C) 2010-2011 by Harald Welte * (C) 2013 by Kévin Redon * * 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 #include static int c128v2_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand) { comp128v2(aud->u.gsm.ki, _rand, vec->sres, vec->kc); vec->auth_types = OSMO_AUTH_TYPE_GSM; return 0; } static struct osmo_auth_impl c128v2_alg = { .algo = OSMO_AUTH_ALG_COMP128v2, .name = "COMP128v2 (libosmogsm built-in)", .priority = 1000, .gen_vec = &c128v2_gen_vec, }; static int c128v3_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand) { comp128v3(aud->u.gsm.ki, _rand, vec->sres, vec->kc); vec->auth_types = OSMO_AUTH_TYPE_GSM; return 0; } static struct osmo_auth_impl c128v3_alg = { .algo = OSMO_AUTH_ALG_COMP128v3, .name = "COMP128v3 (libosmogsm built-in)", .priority = 1000, .gen_vec = &c128v3_gen_vec, }; static __attribute__((constructor)) void on_dso_load_c128(void) { osmo_auth_register(&c128v2_alg); osmo_auth_register(&c128v3_alg); } libosmocore-0.9.0/src/gsm/auth_core.c000066400000000000000000000122151261607044000175300ustar00rootroot00000000000000/* GSM/GPRS/3G authentication core infrastructure */ /* (C) 2010-2012 by Harald Welte * * 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 #include #include #include #include #include #include /*! \addtogroup auth * @{ */ /* \file auth_core.c */ static LLIST_HEAD(osmo_auths); static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM]; /*! \brief Register an authentication algorithm implementation with the core * \param[in] impl Structure describing implementation and it's callbacks * * This function is called by an authentication implementation plugin to * register itself with the authentication core. */ int osmo_auth_register(struct osmo_auth_impl *impl) { if (impl->algo >= ARRAY_SIZE(selected_auths)) return -ERANGE; llist_add_tail(&impl->list, &osmo_auths); /* check if we want to select this implementation over others */ if (!selected_auths[impl->algo] || (selected_auths[impl->algo]->priority > impl->priority)) selected_auths[impl->algo] = impl; return 0; } /*! \brief Load all available authentication plugins from the given path * \param[in] path Path name of the directory containing the plugins * * This function will load all plugins contained in the specified path. */ int osmo_auth_load(const char *path) { /* load all plugins available from path */ return osmo_plugin_load_all(path); } /*! \brief Determine if a given authentication algorithm is supported * \param[in] algo Algorithm which should be checked * * This function is used by an application to determine at runtime if a * given authentication algorithm is supported or not. */ int osmo_auth_supported(enum osmo_auth_algo algo) { if (algo >= ARRAY_SIZE(selected_auths)) return -ERANGE; if (selected_auths[algo]) return 1; return 0; } /*! \brief Generate authentication vector * \param[out] vec Generated authentication vector * \param[in] aud Subscriber-specific key material * \param[in] _rand Random challenge to be used * * This function performs the core cryptographic function of the AUC, * computing authentication triples/quintuples based on the permanent * subscriber data and a random value. The result is what is forwarded * by the AUC via HLR and VLR to the MSC which will then be able to * invoke authentication with the MS */ int osmo_auth_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand) { struct osmo_auth_impl *impl = selected_auths[aud->algo]; int rc; if (!impl) return -ENOENT; rc = impl->gen_vec(vec, aud, _rand); if (rc < 0) return rc; memcpy(vec->rand, _rand, sizeof(vec->rand)); return 0; } /*! \brief Generate authentication vector and re-sync sequence * \param[out] vec Generated authentication vector * \param[in] aud Subscriber-specific key material * \param[in] rand_auts RAND value sent by the SIM/MS * \param[in] auts AUTS value sent by the SIM/MS * \param[in] _rand Random challenge to be used to generate vector * * This function performs a special variant of the core cryptographic * function of the AUC: computing authentication triples/quintuples * based on the permanent subscriber data, a random value as well as the * AUTS and RAND values returned by the SIM/MS. This special variant is * needed if the sequence numbers between MS and AUC have for some * reason become diffrent. */ int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *rand_auts, const uint8_t *auts, const uint8_t *_rand) { struct osmo_auth_impl *impl = selected_auths[aud->algo]; if (!impl || !impl->gen_vec_auts) return -ENOENT; return impl->gen_vec_auts(vec, aud, rand_auts, auts, _rand); } static const struct value_string auth_alg_vals[] = { { OSMO_AUTH_ALG_NONE, "None" }, { OSMO_AUTH_ALG_COMP128v1, "COMP128v1" }, { OSMO_AUTH_ALG_COMP128v2, "COMP128v2" }, { OSMO_AUTH_ALG_COMP128v3, "COMP128v3" }, { OSMO_AUTH_ALG_XOR, "XOR" }, { OSMO_AUTH_ALG_MILENAGE, "MILENAGE" }, { 0, NULL } }; /*! \brief Get human-readable name of authentication algorithm */ const char *osmo_auth_alg_name(enum osmo_auth_algo alg) { return get_value_string(auth_alg_vals, alg); } /*! \brief Parse human-readable name of authentication algorithm */ enum osmo_auth_algo osmo_auth_alg_parse(const char *name) { return get_string_value(auth_alg_vals, name); } /*! @} */ libosmocore-0.9.0/src/gsm/auth_milenage.c000066400000000000000000000060711261607044000203640ustar00rootroot00000000000000/* GSM/GPRS/3G authentication core infrastructure */ /* (C) 2011 by Harald Welte * * 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 #include "milenage/common.h" #include "milenage/milenage.h" static void sqn_u64_to_48bit(uint8_t *sqn, const uint64_t sqn64) { sqn[5] = (sqn64 >> 0) & 0xff; sqn[4] = (sqn64 >> 8) & 0xff; sqn[3] = (sqn64 >> 16) & 0xff; sqn[2] = (sqn64 >> 24) & 0xff; sqn[1] = (sqn64 >> 32) & 0xff; sqn[0] = (sqn64 >> 40) & 0xff; } static uint64_t sqn_48bit_to_u64(const uint8_t *sqn) { uint64_t sqn64; sqn64 = sqn[0]; sqn64 <<= 8; sqn64 |= sqn[1]; sqn64 <<= 8; sqn64 |= sqn[2]; sqn64 <<= 8; sqn64 |= sqn[3]; sqn64 <<= 8; sqn64 |= sqn[4]; sqn64 <<= 8; sqn64 |= sqn[5]; return sqn64; } static int milenage_gen_vec(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *_rand) { size_t res_len = sizeof(vec->res); uint8_t sqn[6]; int rc; sqn_u64_to_48bit(sqn, aud->u.umts.sqn); milenage_generate(aud->u.umts.opc, aud->u.umts.amf, aud->u.umts.k, sqn, _rand, vec->autn, vec->ik, vec->ck, vec->res, &res_len); vec->res_len = res_len; rc = gsm_milenage(aud->u.umts.opc, aud->u.umts.k, _rand, vec->sres, vec->kc); if (rc < 0) return rc; vec->auth_types = OSMO_AUTH_TYPE_UMTS | OSMO_AUTH_TYPE_GSM; aud->u.umts.sqn++; return 0; } static int milenage_gen_vec_auts(struct osmo_auth_vector *vec, struct osmo_sub_auth_data *aud, const uint8_t *auts, const uint8_t *rand_auts, const uint8_t *_rand) { uint8_t sqn_out[6]; uint8_t gen_opc[16]; uint8_t *opc; int rc; /* Check if we only know OP and compute OPC if required */ if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) { rc = milenage_opc_gen(gen_opc, aud->u.umts.k, aud->u.umts.opc); if (rc < 0) return rc; opc = gen_opc; } else opc = aud->u.umts.opc; rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out); if (rc < 0) return rc; aud->u.umts.sqn = sqn_48bit_to_u64(sqn_out) + 1; return milenage_gen_vec(vec, aud, _rand); } static struct osmo_auth_impl milenage_alg = { .algo = OSMO_AUTH_ALG_MILENAGE, .name = "MILENAGE (libosmogsm built-in)", .priority = 1000, .gen_vec = &milenage_gen_vec, .gen_vec_auts = &milenage_gen_vec_auts, }; static __attribute__((constructor)) void on_dso_load_milenage(void) { osmo_auth_register(&milenage_alg); } libosmocore-0.9.0/src/gsm/comp128.c000066400000000000000000000227121261607044000167530ustar00rootroot00000000000000/* * COMP128 implementation * * * This code is inspired by original code from : * Marc Briceno , Ian Goldberg , * and David Wagner * * 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 * * 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 #include /* 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<>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(const uint8_t *ki, const 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; } libosmocore-0.9.0/src/gsm/comp128v23.c000066400000000000000000000122761261607044000173120ustar00rootroot00000000000000/* COMP128 version 2 and 3 implementation * * This code is a C conversion of the original code from * http://www.hackingprojects.net/ * */ /* (C) 2013 by Kévin Redon * * 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 #include static const uint8_t table0[256] = { 197, 235, 60, 151, 98, 96, 3, 100, 248, 118, 42, 117, 172, 211, 181, 203, 61, 126, 156, 87, 149, 224, 55, 132, 186, 63, 238, 255, 85, 83, 152, 33, 160, 184, 210, 219, 159, 11, 180, 194, 130, 212, 147, 5, 215, 92, 27, 46, 113, 187, 52, 25, 185, 79, 221, 48, 70, 31, 101, 15, 195, 201, 50, 222, 137, 233, 229, 106, 122, 183, 178, 177, 144, 207, 234, 182, 37, 254, 227, 231, 54, 209, 133, 65, 202, 69, 237, 220, 189, 146, 120, 68, 21, 125, 38, 30, 2, 155, 53, 196, 174, 176, 51, 246, 167, 76, 110, 20, 82, 121, 103, 112, 56, 173, 49, 217, 252, 0, 114, 228, 123, 12, 93, 161, 253, 232, 240, 175, 67, 128, 22, 158, 89, 18, 77, 109, 190, 17, 62, 4, 153, 163, 59, 145, 138, 7, 74, 205, 10, 162, 80, 45, 104, 111, 150, 214, 154, 28, 191, 169, 213, 88, 193, 198, 200, 245, 39, 164, 124, 84, 78, 1, 188, 170, 23, 86, 226, 141, 32, 6, 131, 127, 199, 40, 135, 16, 57, 71, 91, 225, 168, 242, 206, 97, 166, 44, 14, 90, 236, 239, 230, 244, 223, 108, 102, 119, 148, 251, 29, 216, 8, 9, 249, 208, 24, 105, 94, 34, 64, 95, 115, 72, 134, 204, 43, 247, 243, 218, 47, 58, 73, 107, 241, 179, 116, 66, 36, 143, 81, 250, 139, 19, 13, 142, 140, 129, 192, 99, 171, 157, 136, 41, 75, 35, 165, 26 }, table1[256] = { 170, 42, 95, 141, 109, 30, 71, 89, 26, 147, 231, 205, 239, 212, 124, 129, 216, 79, 15, 185, 153, 14, 251, 162, 0, 241, 172, 197, 43, 10, 194, 235, 6, 20, 72, 45, 143, 104, 161, 119, 41, 136, 38, 189, 135, 25, 93, 18, 224, 171, 252, 195, 63, 19, 58, 165, 23, 55, 133, 254, 214, 144, 220, 178, 156, 52, 110, 225, 97, 183, 140, 39, 53, 88, 219, 167, 16, 198, 62, 222, 76, 139, 175, 94, 51, 134, 115, 22, 67, 1, 249, 217, 3, 5, 232, 138, 31, 56, 116, 163, 70, 128, 234, 132, 229, 184, 244, 13, 34, 73, 233, 154, 179, 131, 215, 236, 142, 223, 27, 57, 246, 108, 211, 8, 253, 85, 66, 245, 193, 78, 190, 4, 17, 7, 150, 127, 152, 213, 37, 186, 2, 243, 46, 169, 68, 101, 60, 174, 208, 158, 176, 69, 238, 191, 90, 83, 166, 125, 77, 59, 21, 92, 49, 151, 168, 99, 9, 50, 146, 113, 117, 228, 65, 230, 40, 82, 54, 237, 227, 102, 28, 36, 107, 24, 44, 126, 206, 201, 61, 114, 164, 207, 181, 29, 91, 64, 221, 255, 48, 155, 192, 111, 180, 210, 182, 247, 203, 148, 209, 98, 173, 11, 75, 123, 250, 118, 32, 47, 240, 202, 74, 177, 100, 80, 196, 33, 248, 86, 157, 137, 120, 130, 84, 204, 122, 81, 242, 188, 200, 149, 226, 218, 160, 187, 106, 35, 87, 105, 96, 145, 199, 159, 12, 121, 103, 112 }; static void _comp128v23_internal(uint8_t *output, const uint8_t *kxor, const uint8_t *rand) { uint8_t temp[16]; uint8_t km_rm[32]; uint8_t i,j,k,z; memset(temp, 0, sizeof(temp)); memcpy(km_rm, rand, 16); memcpy(km_rm + 16, kxor, 16); for (i=0; i<5; i++) { for (z=0; z<16; z++) { temp[z] = table0[table1[km_rm[16+z]]^km_rm[z]]; } j=0; while ((1<j) { k = 0; while ((1<<(4-i))>k) { km_rm[((2*k+1)<>(3*j+3)%8)&1)<< j); } } } int comp128v3(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc) { uint8_t k_mix[16]; uint8_t rand_mix[16]; uint8_t katyvasz[16]; uint8_t output[16]; uint8_t i; memset(k_mix, 0, sizeof(k_mix)); memset(rand_mix, 0, sizeof(rand_mix)); memset(katyvasz, 0, sizeof(katyvasz)); memset(output, 0, sizeof(output)); for (i=0; i<8; i++) { k_mix[i] = ki[15 - i]; k_mix[15 - i] = ki[i]; } for (i=0; i<8; i++) { rand_mix[i] = rand[15 - i]; rand_mix[15 - i] = rand[i]; } for (i=0; i<16; i++) { katyvasz[i] = k_mix[i]^rand_mix[i]; } for (i=0; i<8; i++) { _comp128v23_internal(rand_mix,katyvasz,rand_mix); } for (i=0; i<16; i++) { output[i] = rand_mix[15-i]; } memmove(output + 4, output + 8, 8); /* ignore bytes 4..7 */ /* the algorithm uses 16 bytes until this point, but only 12 bytes are effective * also 12 bytes coming out from the SIM card */ memcpy(sres, output, 4); memcpy(kc, output + 4, 8); return 0; } int comp128v2(const uint8_t *ki, const uint8_t *rand, uint8_t *sres, uint8_t *kc) { int r = comp128v3(ki, rand, sres, kc); kc[7] = 0; /* 10 last bits of Kc forced to 0 */ kc[6] &= 0xfc; return r; } libosmocore-0.9.0/src/gsm/gan.c000066400000000000000000000065041261607044000163300ustar00rootroot00000000000000/* (C) 2012 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include const struct value_string gan_msgt_vals[] = { { GA_MT_RC_DISCOVERY_REQUEST, "GA-RC DISCOVERY REQUEST" }, { GA_MT_RC_DISCOVERY_ACCEPT, "GA-RC DISCOVERY ACCEPT" }, { GA_MT_RC_DISCOVERY_REJECT, "GA-RC DISCOVERY REJECT" }, { GA_MT_RC_REGISTER_REQUEST, "GA-RC REGISTER REQUEST" }, { GA_MT_RC_REGISTER_ACCEPT, "GA-RC REGISTER ACCEPT" }, { GA_MT_RC_REGISTER_REDIRECT, "GA-RC REGISTER REDIRECT" }, { GA_MT_RC_REGISTER_REJECT, "GA-RC REGISTER REJECT" }, { GA_MT_RC_DEREGISTER, "GA-RC DEREGISTER" }, { GA_MT_RC_REGISTER_UPDATE_UL, "GA-RC REGISTER UPDATE UL" }, { GA_MT_RC_REGISTER_UPDATE_DL, "GA-RC REGISTER UPDATE DL" }, { GA_MT_RC_CELL_BCAST_INFO, "GA-RC CELL BROADCAST INFO" }, { GA_MT_CSR_CIPH_MODE_CMD, "GA-CSR CIPHER MDOE COMMAND" }, { GA_MT_CSR_CIPH_MODE_COMPL, "GA-CSR CIPHER MODE COMPLETE" }, { GA_MT_CSR_ACT_CHAN, "GA-CSR ACTIVATE CHANNEL" }, { GA_MT_CSR_ACT_CHAN_ACK, "GA-CSR ACTIVATE CHANNEL ACK" }, { GA_MT_CSR_ACT_CHAN_COMPL, "GA-CSR ACTIVATE CHANNEL COMPLETE" }, { GA_MT_CSR_ACT_CHAN_FAIL, "GA-CSR ACTIVATE CHANNEL FAIL" }, { GA_MT_CSR_CHAN_MODE_MOD, "GA-CSR CHANNEL MODE MODIFY" }, { GA_MT_CSR_CHAN_MODE_MOD_ACK, "GA-CSR CHANNEL MODE MODIFY ACK" }, { GA_MT_CSR_RELEASE, "GA-CSR RELEASE" }, { GA_MT_CSR_RELEASE_COMPL, "GA-CSR RELEASE COMPLETE" }, { GA_MT_CSR_CLEAR_REQ, "GA-CSR CLEAR REQUEST" }, { GA_MT_CSR_HO_ACCESS, "GA-CSR HANDOVER ACCESS" }, { GA_MT_CSR_HO_COMPL, "GA-CSR HANDOVER COMPLETE" }, { GA_MT_CSR_UL_QUAL_IND, "GA-CSR UL QUALITY INDICATION" }, { GA_MT_CSR_HO_INFO, "GA-CSR HANDOVER INFO" }, { GA_MT_CSR_HO_CMD, "GA-CSR HANDOVER COMMAND" }, { GA_MT_CSR_HO_FAIL, "GA-CSR HANDOVER FAILURE" }, { GA_MT_CSR_PAGING_REQ, "GA-CSR PAGING REQUEST" }, { GA_MT_CSR_PAGING_RESP, "GA-CSR PAGING RESPONSE" }, { GA_MT_CSR_UL_DIRECT_XFER, "GA-CSR UL DIRECT TRANSFER" }, { GA_MT_CSR_DL_DIRECT_XFER, "GA-CSR DL DIRECT TRANSFER" }, { GA_MT_CSR_STATUS, "GA-CSR STATUS" }, { GA_MT_RC_KEEPALIVE, "GA-CSR KEEPALIVE" }, { GA_MT_CSR_CM_ENQ, "GA-CSR CLASSMARK ENQUIRY" }, { GA_MT_CSR_CM_CHANGE, "GA-CSR CLASSMARK CHANGE" }, { GA_MT_PSR_GPRS_SUSPEND_REQ, "GA-PSR GPRS SUSPEND REQUEST" }, { GA_RC_SYNC_INFO, "GA-RC SYNCH INFORMATION" }, { GA_CSR_UTRAN_CM_CHG, "GA-CSR UTRAN CLASSMARK CHANGE" }, { GA_MT_CSR_REQUEST, "GA-CSR REQUEST" }, { GA_MT_CSR_REQUEST_ACCEPT, "GA-CSR REQUEST ACCEPT" }, { GA_MT_CSR_REQUEST_REJECT, "GA-CSR REQUEST REJECT" }, { 0, NULL } }; static const struct value_string gan_pdisc_vals[] = { { GA_PDISC_RC, "RC" }, { GA_PDISC_CSR, "CSR" }, { GA_PDISC_PSR, "PSR" }, { 0, NULL } }; libosmocore-0.9.0/src/gsm/gprs_cipher_core.c000066400000000000000000000053051261607044000210760ustar00rootroot00000000000000/* GPRS LLC cipher core infrastructure */ /* (C) 2010 by Harald Welte * * 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 #include #include #include #include #include static LLIST_HEAD(gprs_ciphers); static struct gprs_cipher_impl *selected_ciphers[_GPRS_ALGO_NUM]; /* register a cipher with the core */ int gprs_cipher_register(struct gprs_cipher_impl *ciph) { if (ciph->algo >= ARRAY_SIZE(selected_ciphers)) return -ERANGE; llist_add_tail(&ciph->list, &gprs_ciphers); /* check if we want to select this implementation over others */ if (!selected_ciphers[ciph->algo] || (selected_ciphers[ciph->algo]->priority > ciph->priority)) selected_ciphers[ciph->algo] = ciph; return 0; } /* load all available GPRS cipher plugins */ int gprs_cipher_load(const char *path) { /* load all plugins available from path */ return osmo_plugin_load_all(path); } /* function to be called by core code */ int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir) { if (algo >= ARRAY_SIZE(selected_ciphers)) return -ERANGE; if (!selected_ciphers[algo]) return -EINVAL; if (len > GSM0464_CIPH_MAX_BLOCK) return -ERANGE; /* run the actual cipher from the plugin */ return selected_ciphers[algo]->run(out, len, kc, iv, dir); } int gprs_cipher_supported(enum gprs_ciph_algo algo) { if (algo >= ARRAY_SIZE(selected_ciphers)) return -ERANGE; if (selected_ciphers[algo]) return 1; return 0; } /* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc) { uint32_t sx = ((1<<27) * sapi) + (1<<31); return (iov_ui ^ sx) + lfn + oc; } /* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc) { return iov_i + lfn + oc; } libosmocore-0.9.0/src/gsm/gsm0341.c000066400000000000000000000032671261607044000166640ustar00rootroot00000000000000/* * (C) 2014 by Harald Welte * 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 #include #include #include #include #include struct gsm341_ms_message * gsm0341_build_msg(void *ctx, uint8_t geo_scope, uint8_t msg_code, uint8_t update, uint16_t msg_id, uint8_t dcs, uint8_t page_total, uint8_t page_cur, uint8_t *data, uint8_t len) { struct gsm341_ms_message *cbmsg; msg_id = htons(msg_id); if (len > 88) return NULL; cbmsg = talloc_zero_size(ctx, sizeof(*cbmsg)+len); if (!cbmsg) return NULL; cbmsg->serial.code_hi = (msg_code >> 4) & 0xF; cbmsg->serial.gs = geo_scope; cbmsg->serial.update = update; cbmsg->serial.code_lo = msg_code & 0xF; cbmsg->msg_id = msg_id; cbmsg->dcs.group = dcs >> 4; cbmsg->dcs.language = dcs & 0xF; cbmsg->page.total = page_total; cbmsg->page.current = page_cur; memcpy(cbmsg->data, data, len); return cbmsg; } libosmocore-0.9.0/src/gsm/gsm0411_smc.c000066400000000000000000000364731261607044000175310ustar00rootroot00000000000000/* Point-to-Point (PP) Short Message Service (SMS) * Support on Mobile Radio Interface * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ /* (C) 2008 by Daniel Willmann * (C) 2009 by Harald Welte * (C) 2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * (C) 2011 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, see . * */ /* Notes on msg: * * Messages from lower layer are freed by lower layer. * * Messages to upper layer are freed after upper layer call returns, so upper * layer cannot use data after returning. Upper layer must not free the msg. * * This implies: Lower layer messages can be forwarded to upper layer. * * Upper layer messages are freed by lower layer, so they must not be freed * after calling lower layer. * * * Notes on release: * * Whenever the process returns to IDLE, the MM connection is released using * MMSMS-REL-REQ. It is allowed to destroy this process while processing * this message. * * There is an exception, if MMSMS-REL-IND is received from lower layer, the * process returns to IDLE without sending MMSMS-REL-REQ. * */ #include #include #include #include #include #include #include #include #include static void cp_timer_expired(void *data); #define MAX_SMS_RETRY 2 #define SMC_LOG_STR "SMC(%" PRIu64 ") " /* init a new instance */ void gsm411_smc_init(struct gsm411_smc_inst *inst, uint64_t id, int network, int (*mn_recv) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg), int (*mm_send) (struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type)) { memset(inst, 0, sizeof(*inst)); inst->id = id; inst->network = network; inst->cp_max_retr = MAX_SMS_RETRY; inst->cp_tc1 = GSM411_TMR_TC1A_SEC / (inst->cp_max_retr + 1); inst->cp_state = GSM411_CPS_IDLE; inst->mn_recv = mn_recv; inst->mm_send = mm_send; LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "instance created for %s\n", inst->id, inst->network ? "network" : "mobile"); } /* clear instance */ void gsm411_smc_clear(struct gsm411_smc_inst *inst) { LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "clearing instance\n", inst->id); osmo_timer_del(&inst->cp_timer); /* free stored msg */ if (inst->cp_msg) { LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "dropping pending message\n", inst->id); msgb_free(inst->cp_msg); inst->cp_msg = NULL; } } const char *smc_state_names[] = { "IDLE", "MM_CONN_PENDING", "WAIT_CP_ACK", "MM_ESTABLISHED", }; const struct value_string gsm411_cp_cause_strs[] = { { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, { GSM411_CP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, { 0, 0 } }; static void new_cp_state(struct gsm411_smc_inst *inst, enum gsm411_cp_state state) { LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "new CP state %s -> %s\n", inst->id, smc_state_names[inst->cp_state], smc_state_names[state]); inst->cp_state = state; } static int gsm411_tx_cp_error(struct gsm411_smc_inst *inst, uint8_t cause) { struct msgb *nmsg = gsm411_msgb_alloc(); uint8_t *causep; LOGP(DLSMS, LOGL_NOTICE, SMC_LOG_STR "TX CP-ERROR, cause %d (%s)\n", inst->id, cause, get_value_string(gsm411_cp_cause_strs, cause)); causep = msgb_put(nmsg, 1); *causep = cause; return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ERROR); } /* establish SMC connection */ static int gsm411_mnsms_est_req(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; if (inst->cp_msg) { LOGP(DLSMS, LOGL_FATAL, SMC_LOG_STR "EST REQ, but we already have an " "cp_msg. This should never happen, please fix!\n", inst->id); msgb_free(inst->cp_msg); } inst->cp_msg = msg; new_cp_state(inst, GSM411_CPS_MM_CONN_PENDING); /* clear stored release flag */ inst->cp_rel = 0; /* send MMSMS_EST_REQ */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_EST_REQ, nmsg, 0); } static int gsm411_mmsms_send_msg(struct gsm411_smc_inst *inst) { struct msgb *nmsg; LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "send CP data\n", inst->id); /* reset retry counter */ if (inst->cp_state != GSM411_CPS_WAIT_CP_ACK) inst->cp_retx = 0; /* 5.2.3.1.2: enter MO-wait for CP-ACK */ /* 5.2.3.2.3: enter MT-wait for CP-ACK */ new_cp_state(inst, GSM411_CPS_WAIT_CP_ACK); inst->cp_timer.data = inst; inst->cp_timer.cb = cp_timer_expired; /* 5.3.2.1: Set Timer TC1A */ osmo_timer_schedule(&inst->cp_timer, inst->cp_tc1, 0); /* clone cp_msg */ nmsg = gsm411_msgb_alloc(); memcpy(msgb_put(nmsg, inst->cp_msg->len), inst->cp_msg->data, inst->cp_msg->len); /* send MMSMS_DATA_REQ with CP-DATA */ return inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_DATA); } static int gsm411_mmsms_est_cnf(struct gsm411_smc_inst *inst, struct msgb *msg) { if (!inst->cp_msg) { LOGP(DLSMS, LOGL_FATAL, SMC_LOG_STR "EST CNF, but we have no cp_msg. This " "should never happen, please fix!\n", inst->id); return -EINVAL; } return gsm411_mmsms_send_msg(inst); } /* SMC TC1* is expired */ static void cp_timer_expired(void *data) { struct gsm411_smc_inst *inst = data; struct msgb *nmsg; if (inst->cp_retx == inst->cp_max_retr) { LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "TC1* timeout, no more retries.\n", inst->id); /* 5.3.2.1: enter idle state */ new_cp_state(inst, GSM411_CPS_IDLE); /* indicate error */ nmsg = gsm411_msgb_alloc(); inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); msgb_free(nmsg); /* free pending stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } /* release MM connection */ nmsg = gsm411_msgb_alloc(); inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); return; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "TC1* timeout, retrying...\n", inst->id); inst->cp_retx++; gsm411_mmsms_est_cnf(inst, NULL); } static int gsm411_mmsms_cp_ack(struct gsm411_smc_inst *inst, struct msgb *msg) { /* free stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "received CP-ACK\n", inst->id); /* 5.3.2.1 enter MM Connection established */ new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); /* 5.3.2.1: Reset Timer TC1* */ osmo_timer_del(&inst->cp_timer); /* pending release? */ if (inst->cp_rel) { struct msgb *nmsg; LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "we have pending release.\n", inst->id); new_cp_state(inst, GSM411_CPS_IDLE); /* release MM connection */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); } return 0; } static int gsm411_mmsms_cp_data(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; int mt = GSM411_MNSMS_DATA_IND; LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "received CP-DATA\n", inst->id); /* 5.3.1 enter MM Connection established (if idle) */ if (inst->cp_state == GSM411_CPS_IDLE) { new_cp_state(inst, GSM411_CPS_MM_ESTABLISHED); mt = GSM411_MNSMS_EST_IND; /* clear stored release flag */ inst->cp_rel = 0; } /* send MMSMS_DATA_REQ (CP ACK) */ nmsg = gsm411_msgb_alloc(); inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, nmsg, GSM411_MT_CP_ACK); /* indicate data */ inst->mn_recv(inst, mt, msg); return 0; } /* send CP DATA */ static int gsm411_mnsms_data_req(struct gsm411_smc_inst *inst, struct msgb *msg) { if (inst->cp_msg) { LOGP(DLSMS, LOGL_FATAL, SMC_LOG_STR "DATA REQ, but we already have an " "cp_msg. This should never happen, please fix!\n", inst->id); msgb_free(inst->cp_msg); } /* store and send */ inst->cp_msg = msg; return gsm411_mmsms_send_msg(inst); } /* release SMC connection */ static int gsm411_mnsms_rel_req(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; msgb_free(msg); /* discard silently */ if (inst->cp_state == GSM411_CPS_IDLE) return 0; /* store release, until established or released */ if (inst->cp_state != GSM411_CPS_MM_ESTABLISHED) { LOGP(DLSMS, LOGL_NOTICE, SMC_LOG_STR "cannot release yet current state: %s\n", inst->id, smc_state_names[inst->cp_state]); inst->cp_rel = 1; return 0; } /* free stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } new_cp_state(inst, GSM411_CPS_IDLE); /* release MM connection */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); } static int gsm411_mmsms_cp_error(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; /* free stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "received CP-ERROR\n", inst->id); /* 5.3.4 enter idle */ new_cp_state(inst, GSM411_CPS_IDLE); /* indicate error */ inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, msg); /* release MM connection */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); } static int gsm411_mmsms_rel_ind(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; /* free stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "MM layer is released\n", inst->id); /* 5.3.4 enter idle */ new_cp_state(inst, GSM411_CPS_IDLE); /* indicate error */ nmsg = gsm411_msgb_alloc(); inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); msgb_free(nmsg); return 0; } /* abort SMC connection */ static int gsm411_mnsms_abort_req(struct gsm411_smc_inst *inst, struct msgb *msg) { struct msgb *nmsg; /* free stored msg */ if (inst->cp_msg) { msgb_free(inst->cp_msg); inst->cp_msg = NULL; } /* 5.3.4 go idle */ new_cp_state(inst, GSM411_CPS_IDLE); /* send MMSMS_DATA_REQ with CP-ERROR */ inst->mm_send(inst, GSM411_MMSMS_DATA_REQ, msg, GSM411_MT_CP_ERROR); /* release MM connection */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); } /* statefull handling for MNSMS SAP messages */ static const struct smcdownstate { uint32_t states; int type; const char *name; int (*rout) (struct gsm411_smc_inst *inst, struct msgb *msg); } smcdownstatelist[] = { /* establish request */ {SBIT(GSM411_CPS_IDLE), GSM411_MNSMS_EST_REQ, "MNSMS-EST-REQ", gsm411_mnsms_est_req}, /* release request */ {ALL_STATES, GSM411_MNSMS_REL_REQ, "MNSMS-REL-REQ", gsm411_mnsms_rel_req}, /* data request */ {SBIT(GSM411_CPS_MM_ESTABLISHED), GSM411_MNSMS_DATA_REQ, "MNSMS-DATA-REQ", gsm411_mnsms_data_req}, /* abort request */ {ALL_STATES - SBIT(GSM411_CPS_IDLE), GSM411_MNSMS_ABORT_REQ, "MNSMS-ABORT-REQ", gsm411_mnsms_abort_req}, }; #define SMCDOWNSLLEN \ (sizeof(smcdownstatelist) / sizeof(struct smcdownstate)) /* message from upper layer */ int gsm411_smc_send(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg) { int i, rc; /* find function for current state and message */ for (i = 0; i < SMCDOWNSLLEN; i++) { if ((msg_type == smcdownstatelist[i].type) && (SBIT(inst->cp_state) & smcdownstatelist[i].states)) break; } if (i == SMCDOWNSLLEN) { LOGP(DLSMS, LOGL_NOTICE, SMC_LOG_STR "message %u unhandled at this state %s.\n", inst->id, msg_type, smc_state_names[inst->cp_state]); msgb_free(msg); return 0; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "message %s received in state %s\n", inst->id, smcdownstatelist[i].name, smc_state_names[inst->cp_state]); rc = smcdownstatelist[i].rout(inst, msg); return rc; } /* statefull handling for MMSMS SAP messages */ static const struct smcdatastate { uint32_t states; int type, cp_type; const char *name; int (*rout) (struct gsm411_smc_inst *inst, struct msgb *msg); } smcdatastatelist[] = { /* establish confirm */ {SBIT(GSM411_CPS_MM_CONN_PENDING), GSM411_MMSMS_EST_CNF, 0, "MMSMS-EST-CNF", gsm411_mmsms_est_cnf}, /* establish indication (CP DATA) */ {SBIT(GSM411_CPS_IDLE), GSM411_MMSMS_EST_IND, GSM411_MT_CP_DATA, "MMSMS-EST-IND (CP DATA)", gsm411_mmsms_cp_data}, /* data indication (CP DATA) */ {SBIT(GSM411_CPS_MM_ESTABLISHED), GSM411_MMSMS_DATA_IND, GSM411_MT_CP_DATA, "MMSMS-DATA-IND (CP DATA)", gsm411_mmsms_cp_data}, /* data indication (CP ACK) */ {SBIT(GSM411_CPS_WAIT_CP_ACK), GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ACK, "MMSMS-DATA-IND (CP ACK)", gsm411_mmsms_cp_ack}, /* data indication (CP ERROR) */ {ALL_STATES, GSM411_MMSMS_DATA_IND, GSM411_MT_CP_ERROR, "MMSMS-DATA-IND (CP_ERROR)", gsm411_mmsms_cp_error}, /* release indication */ {ALL_STATES - SBIT(GSM411_CPS_IDLE), GSM411_MMSMS_REL_IND, 0, "MMSMS-REL-IND", gsm411_mmsms_rel_ind}, }; #define SMCDATASLLEN \ (sizeof(smcdatastatelist) / sizeof(struct smcdatastate)) /* message from lower layer * WARNING: We must not free msg, since it will be performed by the * lower layer. */ int gsm411_smc_recv(struct gsm411_smc_inst *inst, int msg_type, struct msgb *msg, int cp_msg_type) { int i, rc; /* find function for current state and message */ for (i = 0; i < SMCDATASLLEN; i++) { /* state must match, MM message must match * CP msg must match only in case of MMSMS_DATA_IND */ if ((msg_type == smcdatastatelist[i].type) && (SBIT(inst->cp_state) & smcdatastatelist[i].states) && (msg_type != GSM411_MMSMS_DATA_IND || cp_msg_type == smcdatastatelist[i].cp_type)) break; } if (i == SMCDATASLLEN) { LOGP(DLSMS, LOGL_NOTICE, SMC_LOG_STR "message 0x%x/%u unhandled at this " "state %s.\n", inst->id, msg_type, cp_msg_type, smc_state_names[inst->cp_state]); if (msg_type == GSM411_MMSMS_EST_IND || msg_type == GSM411_MMSMS_DATA_IND) { struct msgb *nmsg; LOGP(DLSMS, LOGL_NOTICE, SMC_LOG_STR "RX Unimplemented CP " "msg_type: 0x%02x\n", inst->id, msg_type); /* 5.3.4 enter idle */ new_cp_state(inst, GSM411_CPS_IDLE); /* indicate error */ gsm411_tx_cp_error(inst, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); /* send error indication to upper layer */ nmsg = gsm411_msgb_alloc(); inst->mn_recv(inst, GSM411_MNSMS_ERROR_IND, nmsg); msgb_free(nmsg); /* release MM connection */ nmsg = gsm411_msgb_alloc(); return inst->mm_send(inst, GSM411_MMSMS_REL_REQ, nmsg, 0); } return 0; } LOGP(DLSMS, LOGL_INFO, SMC_LOG_STR "message %s received in state %s\n", inst->id, smcdatastatelist[i].name, smc_state_names[inst->cp_state]); rc = smcdatastatelist[i].rout(inst, msg); return rc; } libosmocore-0.9.0/src/gsm/gsm0411_smr.c000066400000000000000000000343241261607044000175410ustar00rootroot00000000000000/* Point-to-Point (PP) Short Message Service (SMS) * Support on Mobile Radio Interface * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ /* (C) 2008 by Daniel Willmann * (C) 2009 by Harald Welte * (C) 2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * (C) 2011 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, see . * */ /* Notes on msg: * * Messages from lower layer are freed by lower layer. * * Messages to upper layer are freed after upper layer call returns, so upper * layer cannot use data after returning. Upper layer must not free the msg. * * This implies: Lower layer messages can be forwarded to upper layer. * * Upper layer messages are freed by lower layer, so they must not be freed * after calling lower layer. * * * Notes on release: * * Sending Abort/Release (MNSMS-ABORT-REQ or MNSMS-REL-REQ) may cause the * lower layer to become IDLE. Then it is allowed to destroy this instance, * so sending this MUST be the last thing that is done. * */ #include #include #include #include #include #include #include #include #include #include #include #define SMR_LOG_STR "SMR(%" PRIu64 ") " static void rp_timer_expired(void *data); /* init a new instance */ void gsm411_smr_init(struct gsm411_smr_inst *inst, uint64_t id, int network, int (*rl_recv) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg), int (*mn_send) (struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg)) { memset(inst, 0, sizeof(*inst)); inst->id = id; inst->network = network; inst->rp_state = GSM411_RPS_IDLE; inst->rl_recv = rl_recv; inst->mn_send = mn_send; inst->rp_timer.data = inst; inst->rp_timer.cb = rp_timer_expired; LOGP(DLSMS, LOGL_INFO, SMR_LOG_STR "instance created for %s.\n", inst->id, inst->network ? "network" : "mobile"); } /* clear instance */ void gsm411_smr_clear(struct gsm411_smr_inst *inst) { LOGP(DLSMS, LOGL_INFO, SMR_LOG_STR "clearing SMR instance\n", inst->id); osmo_timer_del(&inst->rp_timer); } static const char *smr_state_names[] = { "IDLE", "WAIT_FOR_RP_ACK", "illegal state 2", "WAIT_TO_TX_RP_ACK", "WAIT_FOR_RETRANS_T", }; const struct value_string gsm411_rp_cause_strs[] = { { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, /* valid only for MT */ { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, /* valid for both directions */ { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, { 0, NULL } }; static void new_rp_state(struct gsm411_smr_inst *inst, enum gsm411_rp_state state) { LOGP(DLSMS, LOGL_INFO, SMR_LOG_STR "new RP state %s -> %s\n", inst->id, smr_state_names[inst->rp_state], smr_state_names[state]); inst->rp_state = state; /* stop timer when going idle */ if (state == GSM411_RPS_IDLE) osmo_timer_del(&inst->rp_timer); } /* Prefix msg with a RP-DATA header and send as CP-DATA */ static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, uint8_t rp_msg_type, uint8_t rp_msg_ref, int mnsms_msg_type) { struct gsm411_rp_hdr *rp; uint8_t len = msg->len; /* GSM 04.11 RP-DATA header */ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); rp->len = len + 2; rp->msg_type = rp_msg_type; rp->msg_ref = rp_msg_ref; return inst->mn_send(inst, mnsms_msg_type, msg); } static int gsm411_send_rp_error(struct gsm411_smr_inst *inst, uint8_t msg_ref, uint8_t cause) { struct msgb *msg = gsm411_msgb_alloc(); msgb_tv_put(msg, 1, cause); LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "TX: SMS RP ERROR, cause %d (%s)\n", inst->id, cause, get_value_string(gsm411_rp_cause_strs, cause)); return gsm411_rp_sendmsg(inst, msg, (inst->network) ? GSM411_MT_RP_ERROR_MT : GSM411_MT_RP_ERROR_MO, msg_ref, GSM411_MNSMS_DATA_REQ); } static int gsm411_send_release(struct gsm411_smr_inst *inst) { struct msgb *msg = gsm411_msgb_alloc(); LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "TX: MNSMS-REL-REQ\n", inst->id); return inst->mn_send(inst, GSM411_MNSMS_REL_REQ, msg); } static int gsm411_send_abort(struct gsm411_smr_inst *inst) { struct msgb *msg = gsm411_msgb_alloc(); LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "TX: MNSMS-ABORT-REQ\n", inst->id); msgb_tv_put(msg, 1, 111); //FIXME: better idea ? */ return inst->mn_send(inst, GSM411_MNSMS_ABORT_REQ, msg); } static int gsm411_send_report(struct gsm411_smr_inst *inst) { struct msgb *msg = gsm411_msgb_alloc(); LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "Sending empty SM_RL_REPORT_IND\n", inst->id); return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); } static int gsm411_rl_data_req(struct gsm411_smr_inst *inst, struct msgb *msg) { LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "TX SMS RP-DATA\n", inst->id); /* start TR1N and enter 'wait for RP-ACK state' */ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR1M); new_rp_state(inst, GSM411_RPS_WAIT_FOR_RP_ACK); return inst->mn_send(inst, GSM411_MNSMS_EST_REQ, msg); } static int gsm411_rl_report_req(struct gsm411_smr_inst *inst, struct msgb *msg) { LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "TX SMS REPORT\n", inst->id); new_rp_state(inst, GSM411_RPS_IDLE); inst->mn_send(inst, GSM411_MNSMS_DATA_REQ, msg); gsm411_send_release(inst); return 0; } static int gsm411_mnsms_est_ind(struct gsm411_smr_inst *inst, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; uint8_t msg_type = rp_data->msg_type & 0x07; int rc; /* check direction */ if (inst->network == (msg_type & 1)) { LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "Invalid RP type 0x%02x\n", inst->id, msg_type); gsm411_send_rp_error(inst, rp_data->msg_ref, GSM411_RP_CAUSE_MSG_INCOMP_STATE); new_rp_state(inst, GSM411_RPS_IDLE); gsm411_send_release(inst); return -EINVAL; } switch (msg_type) { case GSM411_MT_RP_DATA_MT: case GSM411_MT_RP_DATA_MO: LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "RX SMS RP-DATA\n", inst->id); /* start TR2N and enter 'wait to send RP-ACK state' */ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); break; case GSM411_MT_RP_SMMA_MO: LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "RX SMS RP-SMMA\n", inst->id); /* start TR2N and enter 'wait to send RP-ACK state' */ osmo_timer_schedule(&inst->rp_timer, GSM411_TMR_TR2M); new_rp_state(inst, GSM411_RPS_WAIT_TO_TX_RP_ACK); rc = inst->rl_recv(inst, GSM411_SM_RL_DATA_IND, msg); break; default: LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "invalid RP type 0x%02x\n", inst->id, msg_type); gsm411_send_rp_error(inst, rp_data->msg_ref, GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); new_rp_state(inst, GSM411_RPS_IDLE); rc = -EINVAL; break; } return rc; } static int gsm411_mnsms_data_ind_tx(struct gsm411_smr_inst *inst, struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr*)msg->l3h; struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; uint8_t msg_type = rp_data->msg_type & 0x07; int rc; /* check direction */ if (inst->network == (msg_type & 1)) { LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "invalid RP type 0x%02x\n", inst->id, msg_type); gsm411_send_rp_error(inst, rp_data->msg_ref, GSM411_RP_CAUSE_MSG_INCOMP_STATE); new_rp_state(inst, GSM411_RPS_IDLE); gsm411_send_release(inst); return -EINVAL; } switch (msg_type) { case GSM411_MT_RP_ACK_MO: case GSM411_MT_RP_ACK_MT: LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "RX SMS RP-ACK\n", inst->id); new_rp_state(inst, GSM411_RPS_IDLE); inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); gsm411_send_release(inst); return 0; case GSM411_MT_RP_ERROR_MO: case GSM411_MT_RP_ERROR_MT: LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "RX SMS RP-ERROR\n", inst->id); new_rp_state(inst, GSM411_RPS_IDLE); inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); gsm411_send_release(inst); return 0; default: LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "Invalid RP type 0x%02x\n", inst->id, msg_type); gsm411_send_rp_error(inst, rp_data->msg_ref, GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); new_rp_state(inst, GSM411_RPS_IDLE); gsm411_send_release(inst); return -EINVAL; } return rc; } static int gsm411_mnsms_error_ind_tx(struct gsm411_smr_inst *inst, struct msgb *msg) { LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "TX SMS MNSMS-ERROR-IND\n", inst->id); new_rp_state(inst, GSM411_RPS_IDLE); inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); gsm411_send_release(inst); return 0; } static int gsm411_mnsms_error_ind_rx(struct gsm411_smr_inst *inst, struct msgb *msg) { LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "RX SMS MNSMS-ERROR-IND\n", inst->id); new_rp_state(inst, GSM411_RPS_IDLE); return inst->rl_recv(inst, GSM411_SM_RL_REPORT_IND, msg); } /* SMR TR1* is expired */ static void rp_timer_expired(void *data) { struct gsm411_smr_inst *inst = data; const char *str; str = inst->rp_state == GSM411_RPS_WAIT_TO_TX_RP_ACK ? "TR2N" : "TR1N"; LOGP(DLSMS, LOGL_DEBUG, SMR_LOG_STR "%s expired\n", inst->id, str); gsm411_send_report(inst); gsm411_send_abort(inst); } /* statefull handling for SM-RL SAP messages */ static const struct smrdownstate { uint32_t states; int type; const char *name; int (*rout) (struct gsm411_smr_inst *inst, struct msgb *msg); } smrdownstatelist[] = { /* data request */ {SBIT(GSM411_RPS_IDLE), GSM411_SM_RL_DATA_REQ, "SM-RL-DATA_REQ", gsm411_rl_data_req}, /* report request */ {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), GSM411_SM_RL_REPORT_REQ, "SM-RL-REPORT_REQ", gsm411_rl_report_req}, }; #define SMRDOWNSLLEN \ (sizeof(smrdownstatelist) / sizeof(struct smrdownstate)) /* message from upper layer */ int gsm411_smr_send(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg) { int i, rc; /* find function for current state and message */ for (i = 0; i < SMRDOWNSLLEN; i++) { if ((msg_type == smrdownstatelist[i].type) && (SBIT(inst->rp_state) & smrdownstatelist[i].states)) break; } if (i == SMRDOWNSLLEN) { LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "message %u unhandled at this state " "%s.\n", inst->id, msg_type, smr_state_names[inst->rp_state]); msgb_free(msg); return 0; } LOGP(DLSMS, LOGL_INFO, SMR_LOG_STR "message %s received in state %s\n", inst->id, smrdownstatelist[i].name, smr_state_names[inst->rp_state]); rc = smrdownstatelist[i].rout(inst, msg); return rc; } /* statefull handling for MMSMS SAP messages */ static const struct smrdatastate { uint32_t states; int type; const char *name; int (*rout) (struct gsm411_smr_inst *inst, struct msgb *msg); } smrdatastatelist[] = { /* establish indication */ {SBIT(GSM411_RPS_IDLE), GSM411_MNSMS_EST_IND, "MNSMS-EST-IND", gsm411_mnsms_est_ind}, /* data indication */ {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), GSM411_MNSMS_DATA_IND, "MNSMS-DATA-IND", gsm411_mnsms_data_ind_tx}, /* error indication */ {SBIT(GSM411_RPS_WAIT_FOR_RP_ACK), GSM411_MNSMS_ERROR_IND, "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_tx}, /* error indication */ {SBIT(GSM411_RPS_WAIT_TO_TX_RP_ACK), GSM411_MNSMS_ERROR_IND, "MNSMS-ERROR-IND", gsm411_mnsms_error_ind_rx}, }; #define SMRDATASLLEN \ (sizeof(smrdatastatelist) / sizeof(struct smrdatastate)) /* message from lower layer * WARNING: We must not free msg, since it will be performed by the * lower layer. */ int gsm411_smr_recv(struct gsm411_smr_inst *inst, int msg_type, struct msgb *msg) { int i, rc; /* find function for current state and message */ for (i = 0; i < SMRDATASLLEN; i++) { /* state must match, MM message must match * CP msg must match only in case of MMSMS_DATA_IND */ if ((msg_type == smrdatastatelist[i].type) && (SBIT(inst->rp_state) & smrdatastatelist[i].states)) break; } if (i == SMRDATASLLEN) { LOGP(DLSMS, LOGL_NOTICE, SMR_LOG_STR "message %u unhandled at this state " "%s.\n", inst->id, msg_type, smr_state_names[inst->rp_state]); return 0; } LOGP(DLSMS, LOGL_INFO, SMR_LOG_STR "message %s received in state %s\n", inst->id, smrdatastatelist[i].name, smr_state_names[inst->rp_state]); rc = smrdatastatelist[i].rout(inst, msg); return rc; } libosmocore-0.9.0/src/gsm/gsm0411_utils.c000066400000000000000000000202571261607044000201000ustar00rootroot00000000000000/* Point-to-Point (PP) Short Message Service (SMS) * Support on Mobile Radio Interface * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ /* (C) 2008 by Daniel Willmann * (C) 2009 by Harald Welte * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * (C) 2011 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, see . * */ #include "../../config.h" #include #include #include #include #include #include #include #include #define GSM411_ALLOC_SIZE 1024 #define GSM411_ALLOC_HEADROOM 128 struct msgb *gsm411_msgb_alloc(void) { return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, "GSM 04.11"); } /* Turn int into semi-octet representation: 98 => 0x89 */ uint8_t gsm411_bcdify(uint8_t value) { uint8_t ret; ret = value / 10; ret |= (value % 10) << 4; return ret; } /* Turn semi-octet representation into int: 0x89 => 98 */ uint8_t gsm411_unbcdify(uint8_t value) { uint8_t ret; if ((value & 0x0F) > 9 || (value >> 4) > 9) LOGP(DLSMS, LOGL_ERROR, "gsm411_unbcdify got too big nibble: 0x%02X\n", value); ret = (value&0x0F)*10; ret += value>>4; return ret; } /* Generate 03.40 TP-SCTS */ void gsm340_gen_scts(uint8_t *scts, time_t time) { struct tm *tm = gmtime(&time); *scts++ = gsm411_bcdify(tm->tm_year % 100); *scts++ = gsm411_bcdify(tm->tm_mon + 1); *scts++ = gsm411_bcdify(tm->tm_mday); *scts++ = gsm411_bcdify(tm->tm_hour); *scts++ = gsm411_bcdify(tm->tm_min); *scts++ = gsm411_bcdify(tm->tm_sec); #ifdef HAVE_TM_GMTOFF_IN_TM *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15)); #else #warning find a portable way to obtain timezone offset *scts++ = 0; #endif } /* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ time_t gsm340_scts(uint8_t *scts) { struct tm tm; uint8_t yr = gsm411_unbcdify(*scts++); int ofs; memset(&tm, 0x00, sizeof(struct tm)); if (yr <= 80) tm.tm_year = 100 + yr; else tm.tm_year = yr; tm.tm_mon = gsm411_unbcdify(*scts++) - 1; tm.tm_mday = gsm411_unbcdify(*scts++); tm.tm_hour = gsm411_unbcdify(*scts++); tm.tm_min = gsm411_unbcdify(*scts++); tm.tm_sec = gsm411_unbcdify(*scts++); #ifdef HAVE_TM_GMTOFF_IN_TM tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60; #endif /* according to gsm 03.40 time zone is "expressed in quarters of an hour" */ ofs = gsm411_unbcdify(*scts++) * 15*60; return mktime(&tm) - ofs; } /* Return the default validity period in minutes */ static unsigned long gsm340_vp_default(void) { unsigned long minutes; /* Default validity: two days */ minutes = 24 * 60 * 2; return minutes; } /* Decode validity period format 'relative' */ static unsigned long gsm340_vp_relative(uint8_t *sms_vp) { /* Chapter 9.2.3.12.1 */ uint8_t vp; unsigned long minutes; vp = *(sms_vp); if (vp <= 143) minutes = (vp + 1) * 5; else if (vp <= 167) minutes = 12*60 + (vp-143) * 30; else if (vp <= 196) minutes = (vp-166) * 60 * 24; else minutes = (vp-192) * 60 * 24 * 7; return minutes; } /* Decode validity period format 'absolute' */ static unsigned long gsm340_vp_absolute(uint8_t *sms_vp) { /* Chapter 9.2.3.12.2 */ time_t expires, now; unsigned long minutes; expires = gsm340_scts(sms_vp); now = time(NULL); if (expires <= now) minutes = 0; else minutes = (expires-now)/60; return minutes; } /* Decode validity period format 'relative in integer representation' */ static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp) { uint8_t vp; unsigned long minutes; vp = *(sms_vp); if (vp == 0) { LOGP(DLSMS, LOGL_ERROR, "reserved relative_integer validity period\n"); return gsm340_vp_default(); } minutes = vp/60; return minutes; } /* Decode validity period format 'relative in semi-octet representation' */ static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp) { unsigned long minutes; minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */ minutes += gsm411_unbcdify(*sms_vp++); /* minutes */ minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */ return minutes; } /* decode validity period. return minutes */ unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp) { uint8_t fi; /* functionality indicator */ switch (sms_vpf) { case GSM340_TP_VPF_RELATIVE: return gsm340_vp_relative(sms_vp); case GSM340_TP_VPF_ABSOLUTE: return gsm340_vp_absolute(sms_vp); case GSM340_TP_VPF_ENHANCED: /* Chapter 9.2.3.12.3 */ fi = *sms_vp++; /* ignore additional fi */ if (fi & (1<<7)) sms_vp++; /* read validity period format */ switch (fi & 0x7) { case 0x0: return gsm340_vp_default(); /* no vpf specified */ case 0x1: return gsm340_vp_relative(sms_vp); case 0x2: return gsm340_vp_relative_integer(sms_vp); case 0x3: return gsm340_vp_relative_semioctet(sms_vp); default: /* The GSM spec says that the SC should reject any unsupported and/or undefined values. FIXME */ LOGP(DLSMS, LOGL_ERROR, "Reserved enhanced validity period format\n"); return gsm340_vp_default(); } case GSM340_TP_VPF_NONE: default: return gsm340_vp_default(); } } /* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs) { uint8_t cgbits = dcs >> 4; enum sms_alphabet alpha = DCS_NONE; if ((cgbits & 0xc) == 0) { if (cgbits & 2) { LOGP(DLSMS, LOGL_NOTICE, "Compressed SMS not supported yet\n"); return 0xffffffff; } switch ((dcs >> 2)&0x03) { case 0: alpha = DCS_7BIT_DEFAULT; break; case 1: alpha = DCS_8BIT_DATA; break; case 2: alpha = DCS_UCS2; break; } } else if (cgbits == 0xc || cgbits == 0xd) alpha = DCS_7BIT_DEFAULT; else if (cgbits == 0xe) alpha = DCS_UCS2; else if (cgbits == 0xf) { if (dcs & 4) alpha = DCS_8BIT_DATA; else alpha = DCS_7BIT_DEFAULT; } return alpha; } /* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type, uint8_t plan, const char *number) { int len_in_bytes; oa[1] = 0x80 | (type << 4) | plan; if (type == GSM340_TYPE_ALPHA_NUMERIC) { /* * TODO/FIXME: what is the 'useful semi-octets' excluding any * semi octet containing only fill bits. * The current code picks the number of bytes written by the * 7bit encoding routines and multiplies it by two. */ gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes); oa[0] = len_in_bytes * 2; len_in_bytes += 2; } else { /* prevent buffer overflows */ if (strlen(number) > 20) number = ""; len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number); /* GSM 03.40 tells us the length is in 'useful semi-octets' */ oa[0] = strlen(number) & 0xff; } return len_in_bytes; } /* Prefix msg with a RP header */ int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type, uint8_t rp_msg_ref) { struct gsm411_rp_hdr *rp; uint8_t len = msg->len; /* GSM 04.11 RP-DATA header */ rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); rp->len = len + 2; rp->msg_type = rp_msg_type; rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */ return 0; } /* Prefix msg with a 04.08/04.11 CP header */ int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans, uint8_t msg_type) { struct gsm48_hdr *gh; gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); /* Outgoing needs the highest bit set */ gh->proto_discr = proto | (trans << 4); gh->msg_type = msg_type; return 0; } libosmocore-0.9.0/src/gsm/gsm0480.c000066400000000000000000000315741261607044000166720ustar00rootroot00000000000000/* Format functions for GSM 04.80 */ /* * (C) 2010 by Holger Hans Peter Freyther * (C) 2009 by Mike Haben * * 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 #include #include #include #include #include static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) { uint8_t *data = msgb_push(msgb, 2); data[0] = tag; data[1] = msgb->len - 2; return data; } static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, uint8_t value) { uint8_t *data = msgb_push(msgb, 3); data[0] = tag; data[1] = 1; data[2] = value; return data; } /* wrap an invoke around it... the other way around * * 1.) Invoke Component tag * 2.) Invoke ID Tag * 3.) Operation * 4.) Data */ int gsm0480_wrap_invoke(struct msgb *msg, int op, int link_id) { /* 3. operation */ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, op); /* 2. invoke id tag */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, link_id); /* 1. component tag */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_INVOKE); return 0; } /* wrap the GSM 04.08 Facility IE around it */ int gsm0480_wrap_facility(struct msgb *msg) { msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); return 0; } struct msgb *gsm0480_create_unstructuredSS_Notify(int alertPattern, const char *text) { struct msgb *msg; uint8_t *seq_len_ptr, *ussd_len_ptr, *data; int len; msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); if (!msg) return NULL; /* SEQUENCE { */ msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); seq_len_ptr = msgb_put(msg, 1); /* DCS { */ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); msgb_put_u8(msg, 1); msgb_put_u8(msg, 0x0F); /* } DCS */ /* USSD-String { */ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); ussd_len_ptr = msgb_put(msg, 1); data = msgb_put(msg, 0); gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len); msgb_put(msg, len); ussd_len_ptr[0] = len; /* USSD-String } */ /* alertingPattern { */ msgb_put_u8(msg, ASN1_OCTET_STRING_TAG); msgb_put_u8(msg, 1); msgb_put_u8(msg, alertPattern); /* } alertingPattern */ seq_len_ptr[0] = 3 + 2 + ussd_len_ptr[0] + 3; /* } SEQUENCE */ return msg; } struct msgb *gsm0480_create_notifySS(const char *text) { struct msgb *msg; uint8_t *data, *tmp_len; uint8_t *seq_len_ptr, *cal_len_ptr, *opt_len_ptr, *nam_len_ptr; int len; len = strlen(text); if (len < 1 || len > 160) return NULL; msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); if (!msg) return NULL; msgb_put_u8(msg, GSM_0480_SEQUENCE_TAG); seq_len_ptr = msgb_put(msg, 1); /* ss_code for CNAP { */ msgb_put_u8(msg, 0x81); msgb_put_u8(msg, 1); msgb_put_u8(msg, 0x19); /* } ss_code */ /* nameIndicator { */ msgb_put_u8(msg, 0xB4); nam_len_ptr = msgb_put(msg, 1); /* callingName { */ msgb_put_u8(msg, 0xA0); opt_len_ptr = msgb_put(msg, 1); msgb_put_u8(msg, 0xA0); cal_len_ptr = msgb_put(msg, 1); /* namePresentationAllowed { */ /* add the DCS value */ msgb_put_u8(msg, 0x80); msgb_put_u8(msg, 1); msgb_put_u8(msg, 0x0F); /* add the lengthInCharacters */ msgb_put_u8(msg, 0x81); msgb_put_u8(msg, 1); msgb_put_u8(msg, strlen(text)); /* add the actual string */ msgb_put_u8(msg, 0x82); tmp_len = msgb_put(msg, 1); data = msgb_put(msg, 0); gsm_7bit_encode_n_ussd(data, msgb_tailroom(msg), text, &len); tmp_len[0] = len; msgb_put(msg, len); /* }; namePresentationAllowed */ cal_len_ptr[0] = 3 + 3 + 2 + len; opt_len_ptr[0] = cal_len_ptr[0] + 2; /* }; callingName */ nam_len_ptr[0] = opt_len_ptr[0] + 2; /* ); nameIndicator */ /* write the lengths... */ seq_len_ptr[0] = 3 + nam_len_ptr[0] + 2; return msg; } /* Forward declarations */ static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req); static int parse_ss_info_elements(const uint8_t *ussd_ie, uint16_t len, struct ss_request *req); static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, struct ss_request *req); static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, struct ss_request *req); static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, struct ss_request *req); static int parse_ss_for_bs_req(const uint8_t *ss_req_data, uint16_t length, struct ss_request *req); /* Decode a mobile-originated USSD-request message */ int gsm0480_decode_ussd_request(const struct gsm48_hdr *hdr, uint16_t len, struct ussd_request *req) { struct ss_request ss; int rc = 0; memset(&ss, 0, sizeof(ss)); if (len < sizeof(*hdr) + 2) { LOGP(0, LOGL_DEBUG, "USSD Request is too short.\n"); return 0; } if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) { req->transaction_id = hdr->proto_discr & 0x70; ss.transaction_id = req->transaction_id; rc = parse_ss(hdr, len, &ss); /* convert from ss_request to legacy ussd_request */ req->transaction_id = ss.transaction_id; req->invoke_id = ss.invoke_id; if (ss.ussd_text[0] == 0xFF) req->text[0] = '\0'; else { memcpy(req->text, ss.ussd_text, sizeof(req->text)); req->text[sizeof(req->text)-1] = '\0'; } } if (!rc) LOGP(0, LOGL_DEBUG, "Error occurred while parsing received USSD!\n"); return rc; } /* Decode a mobile-originated SS request message */ int gsm0480_decode_ss_request(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req) { int rc = 0; if (len < sizeof(*hdr) + 2) { LOGP(0, LOGL_DEBUG, "SS Request is too short.\n"); return 0; } if ((hdr->proto_discr & 0x0f) == GSM48_PDISC_NC_SS) { req->transaction_id = hdr->proto_discr & 0x70; rc = parse_ss(hdr, len, req); } if (!rc) LOGP(0, LOGL_DEBUG, "Error occurred while parsing received SS!\n"); return rc; } static int parse_ss(const struct gsm48_hdr *hdr, uint16_t len, struct ss_request *req) { int rc = 1; uint8_t msg_type = hdr->msg_type & 0xBF; /* message-type - section 3.4 */ switch (msg_type) { case GSM0480_MTYPE_RELEASE_COMPLETE: LOGP(0, LOGL_DEBUG, "SS Release Complete\n"); /* could also parse out the optional Cause/Facility data */ req->ussd_text[0] = 0xFF; break; case GSM0480_MTYPE_REGISTER: case GSM0480_MTYPE_FACILITY: rc &= parse_ss_info_elements(&hdr->data[0], len - sizeof(*hdr), req); break; default: LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 message-type field 0x%02x\n", hdr->msg_type); rc = 0; break; } return rc; } static int parse_ss_info_elements(const uint8_t *ss_ie, uint16_t len, struct ss_request *req) { int rc = -1; /* Information Element Identifier - table 3.2 & GSM 04.08 section 10.5 */ uint8_t iei; uint8_t iei_length; iei = ss_ie[0]; iei_length = ss_ie[1]; /* If the data does not fit, report an error */ if (len - 2 < iei_length) return 0; switch (iei) { case GSM48_IE_CAUSE: break; case GSM0480_IE_FACILITY: rc = parse_facility_ie(ss_ie + 2, iei_length, req); break; case GSM0480_IE_SS_VERSION: break; default: LOGP(0, LOGL_DEBUG, "Unhandled GSM 04.08 or 04.80 IEI 0x%02x\n", iei); rc = 0; break; } return rc; } static int parse_facility_ie(const uint8_t *facility_ie, uint16_t length, struct ss_request *req) { int rc = 1; uint8_t offset = 0; while (offset + 2 <= length) { /* Component Type tag - table 3.7 */ uint8_t component_type = facility_ie[offset]; uint8_t component_length = facility_ie[offset+1]; /* size check */ if (offset + 2 + component_length > length) { LOGP(0, LOGL_ERROR, "Component does not fit.\n"); return 0; } switch (component_type) { case GSM0480_CTYPE_INVOKE: rc &= parse_ss_invoke(facility_ie+2, component_length, req); break; case GSM0480_CTYPE_RETURN_RESULT: break; case GSM0480_CTYPE_RETURN_ERROR: break; case GSM0480_CTYPE_REJECT: break; default: LOGP(0, LOGL_DEBUG, "Unknown GSM 04.80 Facility " "Component Type 0x%02x\n", component_type); rc = 0; break; } offset += (component_length+2); }; return rc; } /* Parse an Invoke component - see table 3.3 */ static int parse_ss_invoke(const uint8_t *invoke_data, uint16_t length, struct ss_request *req) { int rc = 1; uint8_t offset; if (length < 3) return 0; /* mandatory part */ if (invoke_data[0] != GSM0480_COMPIDTAG_INVOKE_ID) { LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag " "0x%02x (expecting Invoke ID tag)\n", invoke_data[0]); } offset = invoke_data[1] + 2; req->invoke_id = invoke_data[2]; /* look ahead once */ if (offset + 1 > length) return 0; /* optional part */ if (invoke_data[offset] == GSM0480_COMPIDTAG_LINKED_ID) offset += invoke_data[offset+1] + 2; /* skip over it */ /* mandatory part */ if (invoke_data[offset] == GSM0480_OPERATION_CODE) { if (offset + 2 > length) return 0; uint8_t operation_code = invoke_data[offset+2]; req->opcode = operation_code; switch (operation_code) { case GSM0480_OP_CODE_PROCESS_USS_REQ: rc = parse_process_uss_req(invoke_data + offset + 3, length - offset - 3, req); break; case GSM0480_OP_CODE_ACTIVATE_SS: case GSM0480_OP_CODE_DEACTIVATE_SS: case GSM0480_OP_CODE_INTERROGATE_SS: rc = parse_ss_for_bs_req(invoke_data + offset + 3, length - offset - 3, req); break; default: LOGP(0, LOGL_DEBUG, "GSM 04.80 operation code 0x%02x " "is not yet handled\n", operation_code); rc = 0; break; } } else { LOGP(0, LOGL_DEBUG, "Unexpected GSM 04.80 Component-ID tag 0x%02x " "(expecting Operation Code tag)\n", invoke_data[0]); rc = 0; } return rc; } /* Parse the parameters of a Process UnstructuredSS Request */ static int parse_process_uss_req(const uint8_t *uss_req_data, uint16_t length, struct ss_request *req) { int rc = 0; int num_chars; uint8_t dcs; /* we need at least that much */ if (length < 8) return 0; if (uss_req_data[0] == GSM_0480_SEQUENCE_TAG) { if (uss_req_data[2] == ASN1_OCTET_STRING_TAG) { dcs = uss_req_data[4]; if ((dcs == 0x0F) && (uss_req_data[5] == ASN1_OCTET_STRING_TAG)) { num_chars = (uss_req_data[6] * 8) / 7; /* Prevent a mobile-originated buffer-overrun! */ if (num_chars > MAX_LEN_USSD_STRING) num_chars = MAX_LEN_USSD_STRING; gsm_7bit_decode_n_ussd((char *)req->ussd_text, sizeof(req->ussd_text), &(uss_req_data[7]), num_chars); rc = 1; } } } return rc; } /* Parse the parameters of a Interrogate/Activate/DeactivateSS Request */ static int parse_ss_for_bs_req(const uint8_t *ss_req_data, uint16_t length, struct ss_request *req) { int rc = 0; /* we need at least that much */ if (length < 5) return 0; if (ss_req_data[0] == GSM_0480_SEQUENCE_TAG) { if ((ss_req_data[2] == ASN1_OCTET_STRING_TAG) && ss_req_data[3] == 1) { req->ss_code = ss_req_data[4]; rc = 1; } } return rc; } struct msgb *gsm0480_create_ussd_resp(uint8_t invoke_id, uint8_t trans_id, const char *text) { struct msgb *msg; struct gsm48_hdr *gh; uint8_t *ptr8; int response_len; msg = msgb_alloc_headroom(1024, 128, "GSM 04.80"); if (!msg) return NULL; /* First put the payload text into the message */ ptr8 = msgb_put(msg, 0); gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), text, &response_len); msgb_put(msg, response_len); /* Then wrap it as an Octet String */ msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); /* Pre-pend the DCS octet string */ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); /* Then wrap these as a Sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the operation code */ msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, GSM0480_OP_CODE_PROCESS_USS_REQ); /* Wrap the operation code and IA5 string as a sequence */ msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); /* Pre-pend the invoke ID */ msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id); /* Wrap this up as a Return Result component */ msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); /* Wrap the component in a Facility message */ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); /* And finally pre-pend the L3 header */ gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh->proto_discr = GSM48_PDISC_NC_SS | trans_id | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; return msg; } libosmocore-0.9.0/src/gsm/gsm0502.c000066400000000000000000000025361261607044000166610ustar00rootroot00000000000000/* Paging helper code */ /* (C) 2009 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, see . * */ #include #include #include #include #include unsigned int gsm0502_calc_paging_group(struct gsm48_control_channel_descr *chan_desc, uint64_t imsi) { int ccch_conf; int bs_cc_chans; int blocks; unsigned int group; ccch_conf = chan_desc->ccch_conf; bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf); /* code word + 2, as 2 channels equals 0x0 */ blocks = gsm48_number_of_paging_subchannels(chan_desc); group = gsm0502_get_paging_group(imsi, bs_cc_chans, blocks); return group; } libosmocore-0.9.0/src/gsm/gsm0808.c000066400000000000000000000501071261607044000166670ustar00rootroot00000000000000/* (C) 2009,2010 by Holger Hans Peter Freyther * (C) 2009,2010 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include #include #include #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, uint16_t _ci) { struct msgb* msg; struct { uint8_t ident; struct gsm48_loc_area_id lai; uint16_t ci; } __attribute__ ((packed)) lai_ci; msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap cmpl l3"); if (!msg) return NULL; /* create layer 3 header */ msgb_v_put(msg, BSS_MAP_MSG_COMPLETE_LAYER_3); /* create the cell header */ lai_ci.ident = CELL_IDENT_WHOLE_GLOBAL; gsm48_generate_lai(&lai_ci.lai, cc, nc, lac); lai_ci.ci = htons(_ci); msgb_tlv_put(msg, GSM0808_IE_CELL_IDENTIFIER, sizeof(lai_ci), (uint8_t *) &lai_ci); /* copy the layer3 data */ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, msgb_l3len(msg_l3), msg_l3->l3h); /* push the bssmap header */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_reset(void) { uint8_t cause = GSM0808_CAUSE_EQUIPMENT_FAILURE; struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: reset"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_RESET); msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_reset_ack(void) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: reset ack"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_RESET_ACKNOWLEDGE); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_clear_complete(void) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: clear complete"); uint8_t val = BSS_MAP_MSG_CLEAR_COMPLETE; if (!msg) return NULL; msg->l3h = msg->data; msgb_tlv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 1, &val); return msg; } struct msgb *gsm0808_create_clear_command(uint8_t reason) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: clear command"); if (!msg) return NULL; msg->l3h = msgb_tv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 4); msgb_v_put(msg, BSS_MAP_MSG_CLEAR_CMD); msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &reason); return msg; } struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "cipher-complete"); if (!msg) return NULL; /* send response with BSS override for A5/1... cheating */ msgb_v_put(msg, 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_tlv_put(msg, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS, msgb_l3len(layer3), layer3->l3h); } /* and the optional BSS message */ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id); /* pre-pend the header */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); 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; msgb_tv_put(msg, BSS_MAP_MSG_CIPHER_MODE_REJECT, cause); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "classmark-update"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_CLASSMARK_UPDATE); msgb_tlv_put(msg, GSM0808_IE_CLASSMARK_INFORMATION_T2, cm2_len, cm2); if (cm3) msgb_tlv_put(msg, GSM0808_IE_CLASSMARK_INFORMATION_T3, cm3_len, cm3); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); 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; msgb_v_put(msg, BSS_MAP_MSG_SAPI_N_REJECT); msgb_v_put(msg, link_id); msgb_v_put(msg, GSM0808_CAUSE_BSS_NOT_EQUIPPED); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_assignment_completed(uint8_t rr_cause, uint8_t chosen_channel, uint8_t encr_alg_id, uint8_t speech_mode) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: ass compl"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_ASSIGMENT_COMPLETE); /* write 3.2.2.22 */ msgb_tv_put(msg, GSM0808_IE_RR_CAUSE, 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 */ msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, chosen_channel); /* write chosen encryption algorithm 3.2.2.44 */ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, 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) msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, speech_mode); /* write LSA identifier 3.2.2.15 */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause) { struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: ass fail"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_ASSIGMENT_FAILURE); msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause); /* RR cause 3.2.2.22 */ if (rr_cause) msgb_tv_put(msg, GSM0808_IE_RR_CAUSE, *rr_cause); /* Circuit pool 3.22.45 */ /* Circuit pool list 3.2.2.46 */ /* update the size */ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } struct msgb *gsm0808_create_clear_rqst(uint8_t cause) { struct msgb *msg; msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "bssmap: clear rqst"); if (!msg) return NULL; msgb_v_put(msg, BSS_MAP_MSG_CLEAR_RQST); msgb_tlv_put(msg, GSM0808_IE_CAUSE, 1, &cause); msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg)); return msg; } void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id) { uint8_t *hh = msgb_push(msg, 3); hh[0] = BSSAP_MSG_DTAP; hh[1] = link_id; hh[2] = msg->len - 3; } struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id) { struct dtap_header *header; uint8_t *data; struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "dtap"); if (!msg) return NULL; /* DTAP header */ msg->l3h = msgb_put(msg, sizeof(*header)); header = (struct dtap_header *) &msg->l3h[0]; header->type = BSSAP_MSG_DTAP; header->link_id = link_id; header->length = msgb_l3len(msg_l3); /* Payload */ data = msgb_put(msg, header->length); memcpy(data, msg_l3->l3h, header->length); return msg; } /* As per 3GPP TS 48.008 version 11.7.0 Release 11 */ static const struct tlv_definition bss_att_tlvdef = { .def = { [GSM0808_IE_CIRCUIT_IDENTITY_CODE] = { TLV_TYPE_FIXED, 2 }, [GSM0808_IE_CONNECTION_RELEASE_RQSTED] = { TLV_TYPE_TV }, [GSM0808_IE_RESOURCE_AVAILABLE] = { TLV_TYPE_FIXED, 21 }, [GSM0808_IE_CAUSE] = { TLV_TYPE_TLV }, [GSM0808_IE_IMSI] = { TLV_TYPE_TLV }, [GSM0808_IE_TMSI] = { TLV_TYPE_TLV }, [GSM0808_IE_NUMBER_OF_MSS] = { TLV_TYPE_TV }, [GSM0808_IE_LAYER_3_HEADER_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_CHANNEL_TYPE] = { TLV_TYPE_TLV }, [GSM0808_IE_PERIODICITY] = { TLV_TYPE_TV }, [GSM0808_IE_EXTENDED_RESOURCE_INDICATOR]= { TLV_TYPE_TV }, [GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE] = { TLV_TYPE_FIXED, 4 }, [GSM0808_IE_LSA_IDENTIFIER] = { TLV_TYPE_TLV }, [GSM0808_IE_LSA_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_LSA_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_IDENTIFIER] = { TLV_TYPE_TLV }, [GSM0808_IE_PRIORITY] = { TLV_TYPE_TLV }, [GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV }, [GSM0808_IE_CLASSMARK_INFORMATION_T3] = { TLV_TYPE_TLV }, [GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV }, [GSM0808_IE_RR_CAUSE] = { TLV_TYPE_TV }, [GSM0808_IE_LAYER_3_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_DLCI] = { TLV_TYPE_TV }, [GSM0808_IE_DOWNLINK_DTX_FLAG] = { TLV_TYPE_TV }, [GSM0808_IE_CELL_IDENTIFIER_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_ID_LIST_SEGMENT] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS] = { TLV_TYPE_TLV }, [GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS] = { TLV_TYPE_TLV }, [GSM0808_IE_RESPONSE_RQST] = { TLV_TYPE_T }, [GSM0808_IE_RESOURCE_INDICATION_METHOD] = { TLV_TYPE_TV }, [GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1] = { TLV_TYPE_TV }, [GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_DIAGNOSTIC] = { TLV_TYPE_TLV }, [GSM0808_IE_CHOSEN_CHANNEL] = { TLV_TYPE_TV }, [GSM0808_IE_CIPHER_RESPONSE_MODE] = { TLV_TYPE_TV }, [GSM0808_IE_LAYER_3_MESSAGE_CONTENTS] = { TLV_TYPE_TLV }, [GSM0808_IE_CHANNEL_NEEDED] = { TLV_TYPE_TV }, [GSM0808_IE_TRACE_TYPE] = { TLV_TYPE_TV }, [GSM0808_IE_TRIGGERID] = { TLV_TYPE_TLV }, [GSM0808_IE_TRACE_REFERENCE] = { TLV_TYPE_TV }, [GSM0808_IE_TRANSACTIONID] = { TLV_TYPE_TLV }, [GSM0808_IE_MOBILE_IDENTITY] = { TLV_TYPE_TLV }, [GSM0808_IE_OMCID] = { TLV_TYPE_TLV }, [GSM0808_IE_FORWARD_INDICATOR] = { TLV_TYPE_TV }, [GSM0808_IE_CHOSEN_ENCR_ALG] = { TLV_TYPE_TV }, [GSM0808_IE_CIRCUIT_POOL] = { TLV_TYPE_TV }, [GSM0808_IE_CIRCUIT_POOL_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_TIME_INDICATION] = { TLV_TYPE_TV }, [GSM0808_IE_RESOURCE_SITUATION] = { TLV_TYPE_TLV }, [GSM0808_IE_CURRENT_CHANNEL_TYPE_1] = { TLV_TYPE_TV }, [GSM0808_IE_QUEUEING_INDICATOR] = { TLV_TYPE_TV }, [GSM0808_IE_SPEECH_VERSION] = { TLV_TYPE_TV }, [GSM0808_IE_ASSIGNMENT_REQUIREMENT] = { TLV_TYPE_TV }, [GSM0808_IE_TALKER_FLAG] = { TLV_TYPE_T }, [GSM0808_IE_GROUP_CALL_REFERENCE] = { TLV_TYPE_TLV }, [GSM0808_IE_EMLPP_PRIORITY] = { TLV_TYPE_TV }, [GSM0808_IE_CONFIG_EVO_INDI] = { TLV_TYPE_TV }, [GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION] = { TLV_TYPE_TLV }, [GSM0808_IE_LCS_QOS] = { TLV_TYPE_TLV }, [GSM0808_IE_LSA_ACCESS_CTRL_SUPPR] = { TLV_TYPE_TV }, [GSM0808_IE_LCS_PRIORITY] = { TLV_TYPE_TLV }, [GSM0808_IE_LOCATION_TYPE] = { TLV_TYPE_TLV }, [GSM0808_IE_LOCATION_ESTIMATE] = { TLV_TYPE_TLV }, [GSM0808_IE_POSITIONING_DATA] = { TLV_TYPE_TLV }, [GSM0808_IE_LCS_CAUSE] = { TLV_TYPE_TLV }, [GSM0808_IE_APDU] = { TLV_TYPE_TLV }, [GSM0808_IE_NETWORK_ELEMENT_IDENTITY] = { TLV_TYPE_TLV }, [GSM0808_IE_GPS_ASSISTANCE_DATA] = { TLV_TYPE_TLV }, [GSM0808_IE_DECIPHERING_KEYS] = { TLV_TYPE_TLV }, [GSM0808_IE_RETURN_ERROR_RQST] = { TLV_TYPE_TLV }, [GSM0808_IE_RETURN_ERROR_CAUSE] = { TLV_TYPE_TLV }, [GSM0808_IE_SEGMENTATION] = { TLV_TYPE_TLV }, [GSM0808_IE_SERVICE_HANDOVER] = { TLV_TYPE_TLV }, [GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS] = { TLV_TYPE_TLV }, [GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000] = { TLV_TYPE_TLV }, [GSM0808_IE_GERAN_CLASSMARK] = { TLV_TYPE_TLV }, [GSM0808_IE_GERAN_BSC_CONTAINER] = { TLV_TYPE_TLV }, [GSM0808_IE_NEW_BSS_TO_OLD_BSS_INFO] = { TLV_TYPE_TLV }, [GSM0800_IE_INTER_SYSTEM_INFO] = { TLV_TYPE_TLV }, [GSM0808_IE_SNA_ACCESS_INFO] = { TLV_TYPE_TLV }, [GSM0808_IE_VSTK_RAND_INFO] = { TLV_TYPE_TLV }, [GSM0808_IE_PAGING_INFO] = { TLV_TYPE_TV }, [GSM0808_IE_IMEI] = { TLV_TYPE_TLV }, [GSM0808_IE_VELOCITY_ESTIMATE] = { TLV_TYPE_TLV }, [GSM0808_IE_VGCS_FEATURE_FLAGS] = { TLV_TYPE_TLV }, [GSM0808_IE_TALKER_PRIORITY] = { TLV_TYPE_TV }, [GSM0808_IE_EMERGENCY_SET_INDICATION] = { TLV_TYPE_T }, [GSM0808_IE_TALKER_IDENTITY] = { TLV_TYPE_TLV }, [GSM0808_IE_SMS_TO_VGCS] = { TLV_TYPE_TLV }, [GSM0808_IE_VGCS_TALKER_MODE] = { TLV_TYPE_TLV }, [GSM0808_IE_VGCS_VBS_CELL_STATUS] = { TLV_TYPE_TLV }, [GSM0808_IE_GANSS_ASSISTANCE_DATA] = { TLV_TYPE_TLV }, [GSM0808_IE_GANSS_POSITIONING_DATA] = { TLV_TYPE_TLV }, [GSM0808_IE_GANSS_LOCATION_TYPE] = { TLV_TYPE_TLV }, [GSM0808_IE_APP_DATA] = { TLV_TYPE_TLV }, [GSM0808_IE_DATA_IDENTITY] = { TLV_TYPE_TLV }, [GSM0808_IE_APP_DATA_INFO] = { TLV_TYPE_TLV }, [GSM0808_IE_MSISDN] = { TLV_TYPE_TLV }, [GSM0808_IE_AOIP_TRASP_ADDR] = { TLV_TYPE_TLV }, [GSM0808_IE_SPEECH_CODEC_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_SPEECH_CODEC] = { TLV_TYPE_TLV }, [GSM0808_IE_CALL_ID] = { TLV_TYPE_FIXED, 4 }, [GSM0808_IE_CALL_ID_LIST] = { TLV_TYPE_TLV }, [GSM0808_IE_A_IF_SEL_FOR_RESET] = { TLV_TYPE_TV }, [GSM0808_IE_KC_128] = { TLV_TYPE_FIXED, 16 }, [GSM0808_IE_CSG_IDENTIFIER] = { TLV_TYPE_TLV }, [GSM0808_IE_REDIR_ATTEMPT_FLAG] = { TLV_TYPE_T }, [GSM0808_IE_REROUTE_REJ_CAUSE] = { TLV_TYPE_TV }, [GSM0808_IE_SEND_SEQ_NUM] = { TLV_TYPE_TV }, [GSM0808_IE_REROUTE_COMPL_OUTCOME] = { TLV_TYPE_TV }, [GSM0808_IE_GLOBAL_CALL_REF] = { TLV_TYPE_TLV }, [GSM0808_IE_LCLS_CONFIG] = { TLV_TYPE_TV }, [GSM0808_IE_LCLS_CONN_STATUS_CTRL] = { TLV_TYPE_TV }, [GSM0808_IE_LCLS_CORR_NOT_NEEDED] = { TLV_TYPE_TV }, [GSM0808_IE_LCLS_BSS_STATUS] = { TLV_TYPE_TV }, [GSM0808_IE_LCLS_BREAK_REQ] = { TLV_TYPE_TV }, [GSM0808_IE_CSFB_INDICATION] = { TLV_TYPE_T }, [GSM0808_IE_CS_TO_PS_SRVCC] = { TLV_TYPE_T }, [GSM0808_IE_SRC_ENB_TO_TGT_ENB_TRANSP] = { TLV_TYPE_TLV }, [GSM0808_IE_CS_TO_PS_SRVCC_IND] = { TLV_TYPE_T }, [GSM0808_IE_CN_TO_MS_TRANSP_INFO] = { TLV_TYPE_TLV }, [GSM0808_IE_SELECTED_PLMN_ID] = { TLV_TYPE_FIXED, 3 }, [GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 }, }, }; const struct tlv_definition *gsm0808_att_tlvdef(void) { return &bss_att_tlvdef; } static const struct value_string gsm0808_msgt_names[] = { { BSS_MAP_MSG_ASSIGMENT_RQST, "ASSIGNMENT REQ" }, { BSS_MAP_MSG_ASSIGMENT_COMPLETE, "ASSIGNMENT COMPL" }, { BSS_MAP_MSG_ASSIGMENT_FAILURE, "ASSIGNMENT FAIL" }, { BSS_MAP_MSG_CHAN_MOD_RQST, "CHANNEL MODIFY REQUEST" }, { BSS_MAP_MSG_HANDOVER_RQST, "HANDOVER REQ" }, { BSS_MAP_MSG_HANDOVER_REQUIRED, "HANDOVER REQUIRED" }, { BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE,"HANDOVER REQ ACK" }, { BSS_MAP_MSG_HANDOVER_CMD, "HANDOVER CMD" }, { BSS_MAP_MSG_HANDOVER_COMPLETE, "HANDOVER COMPLETE" }, { BSS_MAP_MSG_HANDOVER_SUCCEEDED, "HANDOVER SUCCESS" }, { BSS_MAP_MSG_HANDOVER_FAILURE, "HANDOVER FAILURE" }, { BSS_MAP_MSG_HANDOVER_PERFORMED, "HANDOVER PERFORMED" }, { BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE, "HANDOVER CAND ENQ" }, { BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE, "HANDOVER CAND RESP" }, { BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT, "HANDOVER REQ REJ" }, { BSS_MAP_MSG_HANDOVER_DETECT, "HANDOVER DETECT" }, { BSS_MAP_MSG_INT_HANDOVER_REQUIRED, "INT HANDOVER REQ" }, { BSS_MAP_MSG_INT_HANDOVER_REQUIRED_REJ,"INT HANDOVER REQ REJ" }, { BSS_MAP_MSG_INT_HANDOVER_CMD, "INT HANDOVER CMD" }, { BSS_MAP_MSG_INT_HANDOVER_ENQUIRY, "INT HANDOVER ENQ" }, { BSS_MAP_MSG_CLEAR_CMD, "CLEAR COMMAND" }, { BSS_MAP_MSG_CLEAR_COMPLETE, "CLEAR COMPLETE" }, { BSS_MAP_MSG_CLEAR_RQST, "CLEAR REQUEST" }, { BSS_MAP_MSG_SAPI_N_REJECT, "SAPI N REJECT" }, { BSS_MAP_MSG_CONFUSION, "CONFUSION" }, { BSS_MAP_MSG_SUSPEND, "SUSPEND" }, { BSS_MAP_MSG_RESUME, "RESUME" }, { BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION, "CONN ORIENT INFO" }, { BSS_MAP_MSG_PERFORM_LOCATION_RQST, "PERFORM LOC REQ" }, { BSS_MAP_MSG_LSA_INFORMATION, "LSA INFORMATION" }, { BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE, "PERFORM LOC RESP" }, { BSS_MAP_MSG_PERFORM_LOCATION_ABORT, "PERFORM LOC ABORT" }, { BSS_MAP_MSG_COMMON_ID, "COMMON ID" }, { BSS_MAP_MSG_REROUTE_CMD, "REROUTE COMMAND" }, { BSS_MAP_MSG_REROUTE_COMPLETE, "REROUTE COMPLETE" }, { BSS_MAP_MSG_RESET, "RESET" }, { BSS_MAP_MSG_RESET_ACKNOWLEDGE, "RESET ACK" }, { BSS_MAP_MSG_OVERLOAD, "OVERLOAD" }, { BSS_MAP_MSG_RESET_CIRCUIT, "RESET CIRCUIT" }, { BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE, "RESET CIRCUIT ACK" }, { BSS_MAP_MSG_MSC_INVOKE_TRACE, "MSC INVOKE TRACE" }, { BSS_MAP_MSG_BSS_INVOKE_TRACE, "BSS INVOKE TRACE" }, { BSS_MAP_MSG_CONNECTIONLESS_INFORMATION, "CONNLESS INFO" }, { BSS_MAP_MSG_RESET_IP_RSRC, "RESET IP RESOURCE" }, { BSS_MAP_MSG_RESET_IP_RSRC_ACK, "RESET IP RESOURCE ACK" }, { BSS_MAP_MSG_BLOCK, "BLOCK" }, { BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE, "BLOCK ACK" }, { BSS_MAP_MSG_UNBLOCK, "UNBLOCK" }, { BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE, "UNBLOCK ACK" }, { BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK, "CIRC GROUP BLOCK" }, { BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE, "CIRC GORUP BLOCK ACK" }, { BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK, "CIRC GROUP UNBLOCK" }, { BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE, "CIRC GROUP UNBLOCK ACK" }, { BSS_MAP_MSG_UNEQUIPPED_CIRCUIT, "UNEQUIPPED CIRCUIT" }, { BSS_MAP_MSG_CHANGE_CIRCUIT, "CHANGE CIRCUIT" }, { BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE, "CHANGE CIRCUIT ACK" }, { BSS_MAP_MSG_RESOURCE_RQST, "RESOURCE REQ" }, { BSS_MAP_MSG_RESOURCE_INDICATION, "RESOURCE IND" }, { BSS_MAP_MSG_PAGING, "PAGING" }, { BSS_MAP_MSG_CIPHER_MODE_CMD, "CIPHER MODE CMD" }, { BSS_MAP_MSG_CLASSMARK_UPDATE, "CLASSMARK UPDATE" }, { BSS_MAP_MSG_CIPHER_MODE_COMPLETE, "CIPHER MODE COMPLETE" }, { BSS_MAP_MSG_QUEUING_INDICATION, "QUEUING INDICATION" }, { BSS_MAP_MSG_COMPLETE_LAYER_3, "COMPLETE LAYER 3" }, { BSS_MAP_MSG_CLASSMARK_RQST, "CLASSMARK REQ" }, { BSS_MAP_MSG_CIPHER_MODE_REJECT, "CIPHER MODE REJECT" }, { BSS_MAP_MSG_LOAD_INDICATION, "LOAD IND" }, { BSS_MAP_MSG_VGCS_VBS_SETUP, "VGCS/VBS SETUP" }, { BSS_MAP_MSG_VGCS_VBS_SETUP_ACK, "VGCS/VBS SETUP ACK" }, { BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE, "VGCS/VBS SETUP REFUSE" }, { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST, "VGCS/VBS ASSIGN REQ" }, { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT, "VGCS/VBS ASSIGN RES" }, { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE, "VGCS/VBS ASSIGN FAIL" }, { BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION, "VGCS/VBS QUEUING IND" }, { BSS_MAP_MSG_UPLINK_RQST, "UPLINK REQ" }, { BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE, "UPLINK REQ ACK" }, { BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION, "UPLINK REQ CONF" }, { BSS_MAP_MSG_UPLINK_RELEASE_INDICATION,"UPLINK REL IND" }, { BSS_MAP_MSG_UPLINK_REJECT_CMD, "UPLINK REJ CMD" }, { BSS_MAP_MSG_UPLINK_RELEASE_CMD, "UPLINK REL CMD" }, { BSS_MAP_MSG_UPLINK_SEIZED_CMD, "UPLINK SEIZED CMD" }, { BSS_MAP_MSG_VGCS_ADDL_INFO, "VGCS ADDL INFO" }, { BSS_MAP_MSG_NOTIFICATION_DATA, "NOTIF DATA" }, { BSS_MAP_MSG_UPLINK_APP_DATA, "UPLINK APP DATA" }, { BSS_MAP_MSG_LCLS_CONNECT_CTRL, "LCLS-CONNECT-CONTROL" }, { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "CLS-CONNECT-CONTROL-ACK" }, { BSS_MAP_MSG_LCLS_NOTIFICATION, "LCLS-NOTIFICATION" }, { 0, NULL } }; const char *gsm0808_bssmap_name(uint8_t msg_type) { return get_value_string(gsm0808_msgt_names, msg_type); } static const struct value_string gsm0808_bssap_names[] = { { BSSAP_MSG_BSS_MANAGEMENT, "MANAGEMENT" }, { BSSAP_MSG_DTAP, "DTAP" }, }; const char *gsm0808_bssap_name(uint8_t msg_type) { return get_value_string(gsm0808_bssap_names, msg_type); } libosmocore-0.9.0/src/gsm/gsm48.c000066400000000000000000000340461261607044000165270ustar00rootroot00000000000000/* 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 * (C) 2008, 2009 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 #include #include #include #include #include #include #include #include #include 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_NET_DST] = { 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 const struct value_string mi_type_names[] = { { GSM_MI_TYPE_NONE, "NONE" }, { GSM_MI_TYPE_IMSI, "IMSI" }, { GSM_MI_TYPE_IMEI, "IMEI" }, { GSM_MI_TYPE_IMEISV, "IMEI-SV" }, { GSM_MI_TYPE_TMSI, "TMSI" }, { 0, NULL } }; const char *gsm48_mi_type_name(uint8_t mi) { return get_value_string(mi_type_names, mi); } 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); } /* Attention: this function retunrs true integers, not hex! */ int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, uint16_t *mnc, uint16_t *lac) { *mcc = (lai->digits[0] & 0x0f) * 100 + (lai->digits[0] >> 4) * 10 + (lai->digits[1] & 0x0f); if ((lai->digits[1] & 0xf0) == 0xf0) { *mnc = (lai->digits[2] & 0x0f) * 10 + (lai->digits[2] >> 4); } else { *mnc = (lai->digits[2] & 0x0f) * 100 + (lai->digits[2] >> 4) * 10 + (lai->digits[1] >> 4); } *lac = ntohs(lai->lac); return 0; } int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi) { uint32_t tmsi_be = htonl(tmsi); buf[0] = GSM48_IE_MOBILE_ID; buf[1] = GSM48_TMSI_LEN; buf[2] = 0xf0 | GSM_MI_TYPE_TMSI; memcpy(&buf[3], &tmsi_be, sizeof(tmsi_be)); return 7; } int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi) { unsigned int length = strlen(imsi), i, off = 0; uint8_t odd = (length & 0x1) == 1; buf[0] = GSM48_IE_MOBILE_ID; buf[2] = osmo_char2bcd(imsi[0]) << 4 | GSM_MI_TYPE_IMSI | (odd << 3); /* if the length is even we will fill half of the last octet */ if (odd) buf[1] = (length + 1) >> 1; else buf[1] = (length + 2) >> 1; for (i = 1; i < buf[1]; ++i) { uint8_t lower, upper; lower = osmo_char2bcd(imsi[++off]); if (!odd && off + 1 == length) upper = 0x0f; else upper = osmo_char2bcd(imsi[++off]) & 0x0f; buf[2 + i] = (upper << 4) | lower; } return 2 + buf[1]; } /* Convert Mobile Identity (10.5.1.4) to string */ int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi, const int mi_len) { int i; uint8_t mi_type; char *str_cur = string; uint32_t tmsi; mi_type = mi[0] & GSM_MI_TYPE_MASK; switch (mi_type) { case GSM_MI_TYPE_NONE: break; case GSM_MI_TYPE_TMSI: /* Table 10.5.4.3, reverse generate_mid_from_tmsi */ if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) { memcpy(&tmsi, &mi[1], 4); tmsi = ntohl(tmsi); return snprintf(string, str_len, "%u", tmsi); } break; case GSM_MI_TYPE_IMSI: case GSM_MI_TYPE_IMEI: case GSM_MI_TYPE_IMEISV: *str_cur++ = osmo_bcd2char(mi[0] >> 4); for (i = 1; i < mi_len; i++) { if (str_cur + 2 >= string + str_len) return str_cur - string; *str_cur++ = osmo_bcd2char(mi[i] & 0xf); /* skip last nibble in last input byte when GSM_EVEN */ if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD)) *str_cur++ = osmo_bcd2char(mi[i] >> 4); } break; default: break; } *str_cur++ = '\0'; return str_cur - string; } void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf) { raid->mcc = (buf[0] & 0xf) * 100; raid->mcc += (buf[0] >> 4) * 10; raid->mcc += (buf[1] & 0xf) * 1; /* I wonder who came up with the stupidity of encoding the MNC * differently depending on how many digits its decimal number has! */ if ((buf[1] >> 4) == 0xf) { raid->mnc = (buf[2] & 0xf) * 10; raid->mnc += (buf[2] >> 4) * 1; } else { raid->mnc = (buf[2] & 0xf) * 100; raid->mnc += (buf[2] >> 4) * 10; raid->mnc += (buf[1] >> 4) * 1; } raid->lac = ntohs(*(uint16_t *)(buf + 3)); raid->rac = buf[5]; } int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid) { uint16_t mcc = raid->mcc; uint16_t mnc = raid->mnc; uint16_t _lac; 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) | (((mnc / 10) % 10) << 4); } _lac = htons(raid->lac); memcpy(buf + 3, &_lac, 2); buf[5] = raid->rac; return 6; } /* From Table 10.5.33 of GSM 04.08 */ int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc) { unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc); if (chan_desc->ccch_conf == RSL_BCCH_CCCH_CONF_1_C) return OSMO_MAX(1, n_pag_blocks) * (chan_desc->bs_pa_mfrms + 2); else return n_pag_blocks * (chan_desc->bs_pa_mfrms + 2); } libosmocore-0.9.0/src/gsm/gsm48_ie.c000066400000000000000000000762331261607044000172100ustar00rootroot00000000000000/* 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 * (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 #include #include #include #include #include #include #include #include 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); } /* TS 04.08 10.5.4.5: 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; switch (bcap->transfer) { case 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; } break; case GSM_MNCC_BCAP_UNR_DIG: case GSM_MNCC_BCAP_FAX_G3: i = 1; while(!(lv[i] & 0x80)) { i++; /* octet 3a etc */ if (in_len < i) return 0; /* ignore them */ } /* octet 4: skip */ i++; /* octet 5 */ i++; if (in_len < i) return 0; bcap->data.rate_adaption = (lv[i] >> 3) & 3; bcap->data.sig_access = lv[i] & 7; while(!(lv[i] & 0x80)) { i++; /* octet 5a etc */ if (in_len < i) return 0; /* ignore them */ } /* octet 6 */ i++; if (in_len < i) return 0; bcap->data.async = lv[i] & 1; if (!(lv[i] & 0x80)) { i++; if (in_len < i) return 0; /* octet 6a */ bcap->data.nr_stop_bits = ((lv[i] >> 7) & 1) + 1; if (lv[i] & 0x10) bcap->data.nr_data_bits = 8; else bcap->data.nr_data_bits = 7; bcap->data.user_rate = lv[i] & 0xf; if (!(lv[i] & 0x80)) { i++; if (in_len < i) return 0; /* octet 6b */ bcap->data.parity = lv[i] & 7; bcap->data.interm_rate = (lv[i] >> 5) & 3; /* octet 6c */ if (!(lv[i] & 0x80)) { i++; if (in_len < i) return 0; bcap->data.transp = (lv[i] >> 5) & 3; bcap->data.modem_type = lv[i] & 0x1F; } } } break; default: i = 1; while (!(lv[i] & 0x80)) { i++; /* octet 3a etc */ if (in_len < i) return 0; /* ignore them */ } /* FIXME: implement OCTET 4+ parsing */ break; } return 0; } /* TS 04.08 10.5.4.5: 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; switch (bcap->transfer) { case 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 */ break; case GSM48_BCAP_ITCAP_UNR_DIG_INF: case GSM48_BCAP_ITCAP_FAX_G3: lv[i++] |= 0x80; /* last IE of octet 3 etc */ /* octet 4 */ lv[i++] = 0xb8; /* octet 5 */ lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3) | (bcap->data.sig_access & 7); /* octet 6 */ lv[i++] = 0x20 | (bcap->data.async & 1); /* octet 6a */ lv[i++] = (bcap->data.user_rate & 0xf) | (bcap->data.nr_data_bits == 8 ? 0x10 : 0x00) | (bcap->data.nr_stop_bits == 2 ? 0x40 : 0x00); /* octet 6b */ lv[i++] = (bcap->data.parity & 7) | ((bcap->data.interm_rate & 3) << 5); /* octet 6c */ lv[i] = 0x80 | (bcap->data.modem_type & 0x1f); break; default: return -EINVAL; } 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; } /* TS 04.08 10.5.4.5a: 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; } /* TS 04.08 10.5.4.5a: 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; } /* TS 04.08 10.5.4.7: 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; } /* TS 04.08 10.5.4.7: encode 'called party BCD number' */ int gsm48_encode_called(struct msgb *msg, const struct gsm_mncc_number *called) { uint8_t lv[18]; int ret; /* octet 3 */ lv[1] = 0x80; /* no extension */ lv[1] |= called->plan; lv[1] |= called->type << 4; /* octet 4..N, octet 2 */ ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number); if (ret < 0) return ret; msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1); return 0; } /* decode callerid of various IEs */ int gsm48_decode_callerid(struct gsm_mncc_number *callerid, const uint8_t *lv) { uint8_t in_len = lv[0]; int i = 1; if (in_len < 1) return -EINVAL; /* octet 3 */ callerid->plan = lv[1] & 0x0f; callerid->type = (lv[1] & 0x70) >> 4; /* octet 3a */ if (!(lv[1] & 0x80)) { callerid->screen = lv[2] & 0x03; callerid->present = (lv[2] & 0x60) >> 5; i = 2; } /* octet 4..N */ gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i); return 0; } /* encode callerid of various IEs */ int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len, const struct gsm_mncc_number *callerid) { uint8_t lv[max_len - 1]; int h_len = 1; int ret; /* octet 3 */ lv[1] = callerid->plan; lv[1] |= callerid->type << 4; if (callerid->present || callerid->screen) { /* octet 3a */ lv[2] = callerid->screen; lv[2] |= callerid->present << 5; lv[2] |= 0x80; h_len++; } else lv[1] |= 0x80; /* octet 4..N, octet 2 */ ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number); if (ret < 0) return ret; msgb_tlv_put(msg, ie, lv[0], lv+1); return 0; } /* TS 04.08 10.5.4.11: 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; } /* TS 04.08 10.5.4.11: 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; } /* TS 04.08 10.5.4.9: decode 'calling number' */ int gsm48_decode_calling(struct gsm_mncc_number *calling, const uint8_t *lv) { return gsm48_decode_callerid(calling, lv); } /* TS 04.08 10.5.4.9: 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); } /* TS 04.08 10.5.4.13: decode 'connected number' */ int gsm48_decode_connected(struct gsm_mncc_number *connected, const uint8_t *lv) { return gsm48_decode_callerid(connected, lv); } /* TS 04.08 10.5.4.13: 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); } /* TS 04.08 10.5.4.21b: decode 'redirecting number' */ int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting, const uint8_t *lv) { return gsm48_decode_callerid(redirecting, lv); } /* TS 04.08 10.5.4.21b: 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); } /* TS 04.08 10.5.4.15: 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; } /* TS 04.08 10.5.4.15: 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; } /* TS 04.08 10.5.4.20: decode 'notify' */ int gsm48_decode_notify(int *notify, const uint8_t *v) { *notify = v[0] & 0x7f; return 0; } /* TS 04.08 10.5.4.20: encode 'notify' */ int gsm48_encode_notify(struct msgb *msg, int notify) { msgb_v_put(msg, notify | 0x80); return 0; } /* TS 04.08 10.5.4.23: decode 'signal' */ int gsm48_decode_signal(int *signal, const uint8_t *v) { *signal = v[0]; return 0; } /* TS 04.08 10.5.4.23: encode 'signal' */ int gsm48_encode_signal(struct msgb *msg, int signal) { msgb_tv_put(msg, GSM48_IE_SIGNAL, signal); return 0; } /* TS 04.08 10.5.4.17: 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; } /* TS 04.08 10.5.4.17: encode 'keypad' */ int gsm48_encode_keypad(struct msgb *msg, int keypad) { msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad); return 0; } /* TS 04.08 10.5.4.21: 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; } /* TS 04.08 10.5.4.21: 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; } /* TS 04.08 10.5.4.25: 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; } /* TS 04.08 10.5.4.25: 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; } /* TS 04.08 10.5.4.24: 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; } /* TS 04.08 10.5.4.24: 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 */ /* TS 04.08 10.5.4.19: 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; } static int32_t smod(int32_t n, int32_t m) { int32_t res; res = n % m; if (res <= 0) res += m; return res; } /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask, uint8_t frqt) { int i; /* NOTES: * * The Range format uses "SMOD" computation. * e.g. "n SMOD m" equals "((n - 1) % m) + 1" * A cascade of multiple SMOD computations is simpified: * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1" * * The Range format uses 16 octets of data in SYSTEM INFORMATION. * When used in dedicated messages, the length can be less. * In this case the ranges are decoded for all frequencies that * fit in the block of given length. */ /* tabula rasa */ for (i = 0; i < 1024; i++) f[i].mask &= ~frqt; /* 00..XXX. */ if ((cd[0] & 0xc0 & mask) == 0x00) { /* Bit map 0 format */ if (len < 16) return -EINVAL; for (i = 1; i <= 124; i++) if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7)))) f[i].mask |= frqt; return 0; } /* 10..0XX. */ if ((cd[0] & 0xc8 & mask) == 0x80) { /* Range 1024 format */ uint16_t w[17]; /* 1..16 */ struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd; if (len < 2) return -EINVAL; memset(w, 0, sizeof(w)); if (r->f0) f[0].mask |= frqt; w[1] = (r->w1_hi << 8) | r->w1_lo; if (len >= 4) w[2] = (r->w2_hi << 1) | r->w2_lo; if (len >= 5) w[3] = (r->w3_hi << 2) | r->w3_lo; if (len >= 6) w[4] = (r->w4_hi << 2) | r->w4_lo; if (len >= 7) w[5] = (r->w5_hi << 2) | r->w5_lo; if (len >= 8) w[6] = (r->w6_hi << 2) | r->w6_lo; if (len >= 9) w[7] = (r->w7_hi << 2) | r->w7_lo; if (len >= 10) w[8] = (r->w8_hi << 1) | r->w8_lo; if (len >= 10) w[9] = r->w9; if (len >= 11) w[10] = r->w10; if (len >= 12) w[11] = (r->w11_hi << 6) | r->w11_lo; if (len >= 13) w[12] = (r->w12_hi << 5) | r->w12_lo; if (len >= 14) w[13] = (r->w13_hi << 4) | r->w13_lo; if (len >= 15) w[14] = (r->w14_hi << 3) | r->w14_lo; if (len >= 16) w[15] = (r->w15_hi << 2) | r->w15_lo; if (len >= 16) w[16] = r->w16; if (w[1]) f[w[1]].mask |= frqt; if (w[2]) f[smod(w[1] - 512 + w[2], 1023)].mask |= frqt; if (w[3]) f[smod(w[1] + w[3], 1023)].mask |= frqt; if (w[4]) f[smod(w[1] - 512 + smod(w[2] - 256 + w[4], 511), 1023)].mask |= frqt; if (w[5]) f[smod(w[1] + smod(w[3] - 256 + w[5], 511), 1023)].mask |= frqt; if (w[6]) f[smod(w[1] - 512 + smod(w[2] + w[6], 511), 1023)].mask |= frqt; if (w[7]) f[smod(w[1] + smod(w[3] + w[7], 511), 1023)].mask |= frqt; if (w[8]) f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + w[8] , 255), 511), 1023)].mask |= frqt; if (w[9]) f[smod(w[1] + smod(w[3] - 256 + smod(w[5] - 128 + w[9] , 255), 511), 1023)].mask |= frqt; if (w[10]) f[smod(w[1] - 512 + smod(w[2] + smod(w[6] - 128 + w[10], 255), 511), 1023)].mask |= frqt; if (w[11]) f[smod(w[1] + smod(w[3] + smod(w[7] - 128 + w[11], 255), 511), 1023)].mask |= frqt; if (w[12]) f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] + w[12], 255), 511), 1023)].mask |= frqt; if (w[13]) f[smod(w[1] + smod(w[3] - 256 + smod(w[5] + w[13], 255), 511), 1023)].mask |= frqt; if (w[14]) f[smod(w[1] - 512 + smod(w[2] + smod(w[6] + w[14], 255), 511), 1023)].mask |= frqt; if (w[15]) f[smod(w[1] + smod(w[3] + smod(w[7] + w[15], 255), 511), 1023)].mask |= frqt; if (w[16]) f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + smod(w[8] - 64 + w[16], 127), 255), 511), 1023)].mask |= frqt; return 0; } /* 10..100. */ if ((cd[0] & 0xce & mask) == 0x88) { /* Range 512 format */ uint16_t w[18]; /* 1..17 */ struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd; if (len < 4) return -EINVAL; memset(w, 0, sizeof(w)); w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; w[1] = (r->w1_hi << 2) | r->w1_lo; if (len >= 5) w[2] = (r->w2_hi << 2) | r->w2_lo; if (len >= 6) w[3] = (r->w3_hi << 2) | r->w3_lo; if (len >= 7) w[4] = (r->w4_hi << 1) | r->w4_lo; if (len >= 7) w[5] = r->w5; if (len >= 8) w[6] = r->w6; if (len >= 9) w[7] = (r->w7_hi << 6) | r->w7_lo; if (len >= 10) w[8] = (r->w8_hi << 4) | r->w8_lo; if (len >= 11) w[9] = (r->w9_hi << 2) | r->w9_lo; if (len >= 11) w[10] = r->w10; if (len >= 12) w[11] = r->w11; if (len >= 13) w[12] = (r->w12_hi << 4) | r->w12_lo; if (len >= 14) w[13] = (r->w13_hi << 2) | r->w13_lo; if (len >= 14) w[14] = r->w14; if (len >= 15) w[15] = r->w15; if (len >= 16) w[16] = (r->w16_hi << 3) | r->w16_lo; if (len >= 16) w[17] = r->w17; f[w[0]].mask |= frqt; if (w[1]) f[(w[0] + w[1]) % 1024].mask |= frqt; if (w[2]) f[(w[0] + smod(w[1] - 256 + w[2], 511)) % 1024].mask |= frqt; if (w[3]) f[(w[0] + smod(w[1] + w[3], 511)) % 1024].mask |= frqt; if (w[4]) f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + w[4], 255), 511)) % 1024].mask |= frqt; if (w[5]) f[(w[0] + smod(w[1] + smod(w[3] - 128 + w[5], 255), 511)) % 1024].mask |= frqt; if (w[6]) f[(w[0] + smod(w[1] - 256 + smod(w[2] + w[6], 255), 511)) % 1024].mask |= frqt; if (w[7]) f[(w[0] + smod(w[1] + smod(w[3] + w[7], 255), 511)) % 1024].mask |= frqt; if (w[8]) f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + w[8] , 127), 255), 511)) % 1024].mask |= frqt; if (w[9]) f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + w[9] , 127), 255), 511)) % 1024].mask |= frqt; if (w[10]) f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] - 64 + w[10], 127), 255), 511)) % 1024].mask |= frqt; if (w[11]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 64 + w[11], 127), 255), 511)) % 1024].mask |= frqt; if (w[12]) f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] + w[12], 127), 255), 511)) % 1024].mask |= frqt; if (w[13]) f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] + w[13], 127), 255), 511)) % 1024].mask |= frqt; if (w[14]) f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] + w[14], 127), 255), 511)) % 1024].mask |= frqt; if (w[15]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 127), 255), 511)) % 1024].mask |= frqt; if (w[16]) f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + smod(w[8] - 32 + w[16], 63), 127), 255), 511)) % 1024].mask |= frqt; if (w[17]) f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + smod(w[9] - 32 + w[17], 63), 127), 255), 511)) % 1024].mask |= frqt; return 0; } /* 10..101. */ if ((cd[0] & 0xce & mask) == 0x8a) { /* Range 256 format */ uint16_t w[22]; /* 1..21 */ struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd; if (len < 4) return -EINVAL; memset(w, 0, sizeof(w)); w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; w[1] = (r->w1_hi << 1) | r->w1_lo; if (len >= 4) w[2] = r->w2; if (len >= 5) w[3] = r->w3; if (len >= 6) w[4] = (r->w4_hi << 5) | r->w4_lo; if (len >= 7) w[5] = (r->w5_hi << 3) | r->w5_lo; if (len >= 8) w[6] = (r->w6_hi << 1) | r->w6_lo; if (len >= 8) w[7] = r->w7; if (len >= 9) w[8] = (r->w8_hi << 4) | r->w8_lo; if (len >= 10) w[9] = (r->w9_hi << 1) | r->w9_lo; if (len >= 10) w[10] = r->w10; if (len >= 11) w[11] = (r->w11_hi << 3) | r->w11_lo; if (len >= 11) w[12] = r->w12; if (len >= 12) w[13] = r->w13; if (len >= 13) w[14] = (r->w14_hi << 2) | r->w14_lo; if (len >= 13) w[15] = r->w15; if (len >= 14) w[16] = (r->w16_hi << 3) | r->w16_lo; if (len >= 14) w[17] = r->w17; if (len >= 15) w[18] = (r->w18_hi << 3) | r->w18_lo; if (len >= 15) w[19] = r->w19; if (len >= 16) w[20] = (r->w20_hi << 3) | r->w20_lo; if (len >= 16) w[21] = r->w21; f[w[0]].mask |= frqt; if (w[1]) f[(w[0] + w[1]) % 1024].mask |= frqt; if (w[2]) f[(w[0] + smod(w[1] - 128 + w[2], 255)) % 1024].mask |= frqt; if (w[3]) f[(w[0] + smod(w[1] + w[3], 255)) % 1024].mask |= frqt; if (w[4]) f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + w[4], 127), 255)) % 1024].mask |= frqt; if (w[5]) f[(w[0] + smod(w[1] + smod(w[3] - 64 + w[5], 127), 255)) % 1024].mask |= frqt; if (w[6]) f[(w[0] + smod(w[1] - 128 + smod(w[2] + w[6], 127), 255)) % 1024].mask |= frqt; if (w[7]) f[(w[0] + smod(w[1] + smod(w[3] + w[7], 127), 255)) % 1024].mask |= frqt; if (w[8]) f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + w[8] , 63), 127), 255)) % 1024].mask |= frqt; if (w[9]) f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + w[9] , 63), 127), 255)) % 1024].mask |= frqt; if (w[10]) f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + w[10], 63), 127), 255)) % 1024].mask |= frqt; if (w[11]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + w[11], 63), 127), 255)) % 1024].mask |= frqt; if (w[12]) f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + w[12], 63), 127), 255)) % 1024].mask |= frqt; if (w[13]) f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + w[13], 63), 127), 255)) % 1024].mask |= frqt; if (w[14]) f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] + w[14], 63), 127), 255)) % 1024].mask |= frqt; if (w[15]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 63), 127), 255)) % 1024].mask |= frqt; if (w[16]) f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + smod(w[8] - 16 + w[16], 31), 63), 127), 255)) % 1024].mask |= frqt; if (w[17]) f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + smod(w[9] - 16 + w[17], 31), 63), 127), 255)) % 1024].mask |= frqt; if (w[18]) f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + smod(w[10] - 16 + w[18], 31), 63), 127), 255)) % 1024].mask |= frqt; if (w[19]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + smod(w[11] - 16 + w[19], 31), 63), 127), 255)) % 1024].mask |= frqt; if (w[20]) f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + smod(w[12] - 16 + w[20], 31), 63), 127), 255)) % 1024].mask |= frqt; if (w[21]) f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + smod(w[13] - 16 + w[21], 31), 63), 127), 255)) % 1024].mask |= frqt; return 0; } /* 10..110. */ if ((cd[0] & 0xce & mask) == 0x8c) { /* Range 128 format */ uint16_t w[29]; /* 1..28 */ struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd; if (len < 3) return -EINVAL; memset(w, 0, sizeof(w)); w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; w[1] = r->w1; if (len >= 4) w[2] = r->w2; if (len >= 5) w[3] = (r->w3_hi << 4) | r->w3_lo; if (len >= 6) w[4] = (r->w4_hi << 1) | r->w4_lo; if (len >= 6) w[5] = r->w5; if (len >= 7) w[6] = (r->w6_hi << 3) | r->w6_lo; if (len >= 7) w[7] = r->w7; if (len >= 8) w[8] = r->w8; if (len >= 8) w[9] = r->w9; if (len >= 9) w[10] = r->w10; if (len >= 9) w[11] = r->w11; if (len >= 10) w[12] = r->w12; if (len >= 10) w[13] = r->w13; if (len >= 11) w[14] = r->w14; if (len >= 11) w[15] = r->w15; if (len >= 12) w[16] = r->w16; if (len >= 12) w[17] = r->w17; if (len >= 13) w[18] = (r->w18_hi << 1) | r->w18_lo; if (len >= 13) w[19] = r->w19; if (len >= 13) w[20] = r->w20; if (len >= 14) w[21] = (r->w21_hi << 2) | r->w21_lo; if (len >= 14) w[22] = r->w22; if (len >= 14) w[23] = r->w23; if (len >= 15) w[24] = r->w24; if (len >= 15) w[25] = r->w25; if (len >= 16) w[26] = (r->w26_hi << 1) | r->w26_lo; if (len >= 16) w[27] = r->w27; if (len >= 16) w[28] = r->w28; f[w[0]].mask |= frqt; if (w[1]) f[(w[0] + w[1]) % 1024].mask |= frqt; if (w[2]) f[(w[0] + smod(w[1] - 64 + w[2], 127)) % 1024].mask |= frqt; if (w[3]) f[(w[0] + smod(w[1] + w[3], 127)) % 1024].mask |= frqt; if (w[4]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + w[4], 63), 127)) % 1024].mask |= frqt; if (w[5]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + w[5], 63), 127)) % 1024].mask |= frqt; if (w[6]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + w[6], 63), 127)) % 1024].mask |= frqt; if (w[7]) f[(w[0] + smod(w[1] + smod(w[3] + w[7], 63), 127)) % 1024].mask |= frqt; if (w[8]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + w[8] , 31), 63), 127)) % 1024].mask |= frqt; if (w[9]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + w[9] , 31), 63), 127)) % 1024].mask |= frqt; if (w[10]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + w[10], 31), 63), 127)) % 1024].mask |= frqt; if (w[11]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + w[11], 31), 63), 127)) % 1024].mask |= frqt; if (w[12]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + w[12], 31), 63), 127)) % 1024].mask |= frqt; if (w[13]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + w[13], 31), 63), 127)) % 1024].mask |= frqt; if (w[14]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + w[14], 31), 63), 127)) % 1024].mask |= frqt; if (w[15]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 31), 63), 127)) % 1024].mask |= frqt; if (w[16]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] - 8 + w[16], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[17]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] - 8 + w[17], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[18]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] - 8 + w[18], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[19]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] - 8 + w[19], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[20]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] - 8 + w[20], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[21]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + smod(w[13] - 8 + w[21], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[22]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + smod(w[14] - 8 + w[22], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[23]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + smod(w[15] - 8 + w[23], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[24]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] + w[24], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[25]) f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] + w[25], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[26]) f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] + w[26], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[27]) f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] + w[27], 15), 31), 63), 127)) % 1024].mask |= frqt; if (w[28]) f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] + w[28], 15), 31), 63), 127)) % 1024].mask |= frqt; return 0; } /* 10..111. */ if ((cd[0] & 0xce & mask) == 0x8e) { /* Variable bitmap format (can be any length >= 3) */ uint16_t orig = 0; struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd; if (len < 3) return -EINVAL; orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; f[orig].mask |= frqt; for (i = 1; 2 + (i >> 3) < len; i++) if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7)))) f[(orig + i) % 1024].mask |= frqt; return 0; } return 0; } libosmocore-0.9.0/src/gsm/gsm_utils.c000066400000000000000000000440631261607044000175730ustar00rootroot00000000000000/* * (C) 2008 by Daniel Willmann * (C) 2009,2013 by Holger Hans Peter Freyther * (C) 2009-2010 by Harald Welte * (C) 2010-2012 by Nico Golde * * 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. * */ /*! \mainpage libosmogsm Documentation * * \section sec_intro Introduction * This library is a collection of common code used in various * GSM related sub-projects inside the Osmocom family of projects. It * includes A5/1 and A5/2 ciphers, COMP128v1, a LAPDm implementation, * a GSM TLV parser, SMS utility routines as well as * protocol definitions for a series of protocols: * * Um L2 (04.06) * * Um L3 (04.08) * * A-bis RSL (08.58) * * A-bis OML (08.59, 12.21) * * A (08.08) * \n\n * Please note that C language projects inside Osmocom are typically * single-threaded event-loop state machine designs. As such, * routines in libosmogsm are not thread-safe. If you must use them in * a multi-threaded context, you have to add your own locking. * * \section sec_copyright Copyright and License * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n * All rights reserved. \n\n * The source code of libosmogsm is licensed 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.\n * See or COPYING included in the source * code package istelf.\n * The information detailed here is provided AS IS with NO WARRANTY OF * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. * \n\n * * \section sec_contact Contact and Support * Community-based support is available at the OpenBSC mailing list * \n * Commercial support options available upon request from * */ //#include #include #include #include #include #include #include #include #include #include "../../config.h" /* ETSI GSM 03.38 6.2.1 and 6.2.1.1 default alphabet * Greek symbols at hex positions 0x10 and 0x12-0x1a * left out as they can't be handled with a char and * since most phones don't display or write these * characters this would only needlessly make the code * more complex. * * Note that this table contains the latin1->7bit mapping _and_ has * been merged with the reverse mapping (7bit->latin1) for the * extended characters at offset 0x7f. */ static unsigned char gsm_7bit_alphabet[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0xff, 0x0d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x3c, 0x2f, 0x3e, 0x14, 0x11, 0xff, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x28, 0x40, 0x29, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0xff, 0x01, 0xff, 0x03, 0xff, 0x7b, 0x7d, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x7e, 0x5d, 0xff, 0x7c, 0xff, 0xff, 0xff, 0xff, 0x5b, 0x0e, 0x1c, 0x09, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5d, 0xff, 0xff, 0xff, 0xff, 0x5c, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x5e, 0xff, 0xff, 0x1e, 0x7f, 0xff, 0xff, 0xff, 0x7b, 0x0f, 0x1d, 0xff, 0x04, 0x05, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0x7d, 0x08, 0xff, 0xff, 0xff, 0x7c, 0xff, 0x0c, 0x06, 0xff, 0xff, 0x7e, 0xff, 0xff }; /* GSM 03.38 6.2.1 Character lookup for decoding */ static int gsm_septet_lookup(uint8_t ch) { int i = 0; for (; i < sizeof(gsm_7bit_alphabet); i++) { if (gsm_7bit_alphabet[i] == ch) return i; } return -1; } /* Compute the number of octets from the number of septets, for instance: 47 septets needs 41,125 = 42 octets */ uint8_t gsm_get_octet_len(const uint8_t sept_len){ int octet_len = (sept_len * 7) / 8; if ((sept_len * 7) % 8 != 0) octet_len++; return octet_len; } /* GSM 03.38 6.2.1 Character unpacking */ int gsm_7bit_decode_n_hdr(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) { int i = 0; int shift = 0; uint8_t c7, c8; uint8_t next_is_ext = 0; const char *text_buf_begin = text; const char *text_buf_end = text + n; int nchars; OSMO_ASSERT (n > 0); /* skip the user data header */ if (ud_hdr_ind) { /* get user data header length + 1 (for the 'user data header length'-field) */ shift = ((user_data[0] + 1) * 8) / 7; if ((((user_data[0] + 1) * 8) % 7) != 0) shift++; septet_l = septet_l - shift; } for (i = 0; i < septet_l && text != text_buf_end - 1; i++) { c7 = ((user_data[((i + shift) * 7 + 7) >> 3] << (7 - (((i + shift) * 7 + 7) & 7))) | (user_data[((i + shift) * 7) >> 3] >> (((i + shift) * 7) & 7))) & 0x7f; if (next_is_ext) { /* this is an extension character */ next_is_ext = 0; c8 = gsm_7bit_alphabet[0x7f + c7]; } else if (c7 == 0x1b && i + 1 < septet_l) { next_is_ext = 1; continue; } else { c8 = gsm_septet_lookup(c7); } *(text++) = c8; } nchars = text - text_buf_begin; *text = '\0'; return nchars; } int gsm_7bit_decode_n(char *text, size_t n, const uint8_t *user_data, uint8_t septet_l) { return gsm_7bit_decode_n_hdr(text, n, user_data, septet_l, 0); } int gsm_7bit_decode_n_ussd(char *text, size_t n, const uint8_t *user_data, uint8_t length) { int nchars; nchars = gsm_7bit_decode_n_hdr(text, n, user_data, length, 0); /* remove last , if it fits up to the end of last octet */ if (nchars && (user_data[gsm_get_octet_len(length) - 1] >> 1) == '\r') text[--nchars] = '\0'; return nchars; } /* GSM 03.38 6.2.1 Prepare character packing */ int gsm_septet_encode(uint8_t *result, const char *data) { int i, y = 0; uint8_t ch; for (i = 0; i < strlen(data); i++) { ch = data[i]; switch(ch){ /* fall-through for extension characters */ case 0x0c: case 0x5e: case 0x7b: case 0x7d: case 0x5c: case 0x5b: case 0x7e: case 0x5d: case 0x7c: result[y++] = 0x1b; default: result[y] = gsm_7bit_alphabet[ch]; break; } y++; } return y; } /* 7bit to octet packing */ int gsm_septets2octets(uint8_t *result, const uint8_t *rdata, uint8_t septet_len, uint8_t padding) { int i = 0, z = 0; uint8_t cb, nb; int shift = 0; uint8_t *data = calloc(septet_len + 1, sizeof(uint8_t)); if (padding) { shift = 7 - padding; /* the first zero is needed for padding */ memcpy(data + 1, rdata, septet_len); septet_len++; } else memcpy(data, rdata, septet_len); for (i = 0; i < septet_len; i++) { if (shift == 7) { /* * special end case with the. This is necessary if the * last septet fits into the previous octet. E.g. 48 * non-extension characters: * ....ag ( a = 1100001, g = 1100111) * result[40] = 100001 XX, result[41] = 1100111 1 */ if (i + 1 < septet_len) { shift = 0; continue; } else if (i + 1 == septet_len) break; } cb = (data[i] & 0x7f) >> shift; if (i + 1 < septet_len) { nb = (data[i + 1] & 0x7f) << (7 - shift); cb = cb | nb; } result[z++] = cb; shift++; } free(data); return z; } /* GSM 03.38 6.2.1 Character packing */ int gsm_7bit_encode_n(uint8_t *result, size_t n, const char *data, int *octets) { int y = 0; int o; size_t max_septets = n * 8 / 7; /* prepare for the worst case, every character expanding to two bytes */ uint8_t *rdata = calloc(strlen(data) * 2, sizeof(uint8_t)); y = gsm_septet_encode(rdata, data); if (y > max_septets) { /* * Limit the number of septets to avoid the generation * of more than n octets. */ y = max_septets; } o = gsm_septets2octets(result, rdata, y, 0); if (octets) *octets = o; free(rdata); /* * We don't care about the number of octets, because they are not * unique. E.g.: * 1.) 46 non-extension characters + 1 extension character * => (46 * 7 bit + (1 * (2 * 7 bit))) / 8 bit = 42 octets * 2.) 47 non-extension characters * => (47 * 7 bit) / 8 bit = 41,125 = 42 octets * 3.) 48 non-extension characters * => (48 * 7 bit) / 8 bit = 42 octects */ return y; } int gsm_7bit_encode_n_ussd(uint8_t *result, size_t n, const char *data, int *octets) { int y; y = gsm_7bit_encode_n(result, n, data, octets); /* if last octet contains only one bit, add */ if (((y * 7) & 7) == 1) result[(*octets) - 1] |= ('\r' << 1); /* if last character is and completely fills last octet, add * another . */ if (y && ((y * 7) & 7) == 0 && (result[(*octets) - 1] >> 1) == '\r' && *octets < n - 1) { result[(*octets)++] = '\r'; y++; } return y; } /* convert power class to dBm according to GSM TS 05.05 */ unsigned int ms_class_gmsk_dbm(enum gsm_band band, int class) { 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 (class == 1) return 43; /* 20W */ if (class == 2) return 39; /* 8W */ if (class == 3) return 37; /* 5W */ if (class == 4) return 33; /* 2W */ if (class == 5) return 29; /* 0.8W */ break; case GSM_BAND_1800: if (class == 1) return 30; /* 1W */ if (class == 2) return 24; /* 0.25W */ if (class == 3) return 36; /* 4W */ break; case GSM_BAND_1900: if (class == 1) return 30; /* 1W */ if (class == 2) return 24; /* 0.25W */ if (class == 3) return 33; /* 2W */ break; } return -EINVAL; } /* 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 05.08 Chapter 8.1.4 */ int rxlev2dbm(uint8_t rxlev) { if (rxlev > 63) rxlev = 63; return -110 + rxlev; } /* According to TS 05.08 Chapter 8.1.4 */ uint8_t dbm2rxlev(int dbm) { int rxlev = dbm + 110; if (rxlev > 63) rxlev = 63; else if (rxlev < 0) rxlev = 0; return rxlev; } const char *gsm_band_name(enum gsm_band band) { switch (band) { case GSM_BAND_450: return "GSM450"; case GSM_BAND_480: return "GSM480"; case GSM_BAND_750: return "GSM750"; case GSM_BAND_810: return "GSM810"; case GSM_BAND_850: return "GSM850"; case GSM_BAND_900: return "GSM900"; case GSM_BAND_1800: return "DCS1800"; case GSM_BAND_1900: return "PCS1900"; } return "invalid"; } enum gsm_band gsm_band_parse(const char* mhz) { while (*mhz && !isdigit(*mhz)) mhz++; if (*mhz == '\0') return -EINVAL; switch (strtol(mhz, NULL, 10)) { case 450: return GSM_BAND_450; case 480: return GSM_BAND_480; case 750: return GSM_BAND_750; case 810: return GSM_BAND_810; case 850: return GSM_BAND_850; case 900: return GSM_BAND_900; case 1800: return GSM_BAND_1800; case 1900: return GSM_BAND_1900; default: return -EINVAL; } } enum gsm_band gsm_arfcn2band(uint16_t arfcn) { int is_pcs = arfcn & ARFCN_PCS; arfcn &= ~ARFCN_FLAG_MASK; if (is_pcs) return GSM_BAND_1900; else if (arfcn <= 124) return GSM_BAND_900; else if (arfcn >= 955 && arfcn <= 1023) return GSM_BAND_900; else if (arfcn >= 128 && arfcn <= 251) return GSM_BAND_850; else if (arfcn >= 512 && arfcn <= 885) return GSM_BAND_1800; else if (arfcn >= 259 && arfcn <= 293) return GSM_BAND_450; else if (arfcn >= 306 && arfcn <= 340) return GSM_BAND_480; else if (arfcn >= 350 && arfcn <= 425) return GSM_BAND_810; else if (arfcn >= 438 && arfcn <= 511) return GSM_BAND_750; else return GSM_BAND_1800; } struct gsm_freq_range { uint16_t arfcn_first; uint16_t arfcn_last; uint16_t freq_ul_first; uint16_t freq_dl_offset; uint16_t flags; }; static struct gsm_freq_range gsm_ranges[] = { { 512, 810, 18502, 800, ARFCN_PCS }, /* PCS 1900 */ { 0, 124, 8900, 450, 0 }, /* P-GSM + E-GSM ARFCN 0 */ { 955, 1023, 8762, 450, 0 }, /* E-GSM + R-GSM */ { 128, 251, 8242, 450, 0 }, /* GSM 850 */ { 512, 885, 17102, 950, 0 }, /* DCS 1800 */ { 259, 293, 4506, 100, 0 }, /* GSM 450 */ { 306, 340, 4790, 100, 0 }, /* GSM 480 */ { 350, 425, 8060, 450, 0 }, /* GSM 810 */ { 438, 511, 7472, 300, 0 }, /* GSM 750 */ { /* Guard */ } }; /* Convert an ARFCN to the frequency in MHz * 10 */ uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink) { struct gsm_freq_range *r; uint16_t flags = arfcn & ARFCN_FLAG_MASK; uint16_t freq10_ul = 0xffff; uint16_t freq10_dl = 0xffff; arfcn &= ~ARFCN_FLAG_MASK; for (r=gsm_ranges; r->freq_ul_first>0; r++) { if ((flags == r->flags) && (arfcn >= r->arfcn_first) && (arfcn <= r->arfcn_last)) { freq10_ul = r->freq_ul_first + 2 * (arfcn - r->arfcn_first); freq10_dl = freq10_ul + r->freq_dl_offset; break; } } return uplink ? freq10_ul : freq10_dl; } /* Convert a Frequency in MHz * 10 to ARFCN */ uint16_t gsm_freq102arfcn(uint16_t freq10, int uplink) { struct gsm_freq_range *r; uint16_t freq10_lo, freq10_hi; uint16_t arfcn = 0xffff; for (r=gsm_ranges; r->freq_ul_first>0; r++) { /* Generate frequency limits */ freq10_lo = r->freq_ul_first; freq10_hi = freq10_lo + 2 * (r->arfcn_last - r->arfcn_first); if (!uplink) { freq10_lo += r->freq_dl_offset; freq10_hi += r->freq_dl_offset; } /* Check if this fits */ if (freq10 >= freq10_lo && freq10 <= freq10_hi) { arfcn = r->arfcn_first + ((freq10 - freq10_lo) >> 1); arfcn |= r->flags; break; } } if (uplink) arfcn |= ARFCN_UPLINK; return arfcn; } 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 23.003 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; else if ((tlli & 0xf0000000) == 0x00000000) return TLLI_G_RNTI; else if ((tlli & 0xf0000000) == 0x10000000) return TLLI_RAND_G_RNTI; 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; } /* Wrappers for deprecated functions: */ int gsm_7bit_decode(char *text, const uint8_t *user_data, uint8_t septet_l) { gsm_7bit_decode_n(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE, user_data, septet_l); /* Mimic the original behaviour. */ return septet_l; } int gsm_7bit_decode_ussd(char *text, const uint8_t *user_data, uint8_t length) { return gsm_7bit_decode_n_ussd(text, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE, user_data, length); } int gsm_7bit_encode(uint8_t *result, const char *data) { int out; return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE, data, &out); } int gsm_7bit_encode_ussd(uint8_t *result, const char *data, int *octets) { return gsm_7bit_encode_n_ussd(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE, data, octets); } int gsm_7bit_encode_oct(uint8_t *result, const char *data, int *octets) { return gsm_7bit_encode_n(result, GSM_7BIT_LEGACY_MAX_BUFFER_SIZE, data, octets); } libosmocore-0.9.0/src/gsm/ipa.c000066400000000000000000000233511261607044000163330ustar00rootroot00000000000000/* OpenBSC Abis input driver for ip.access */ /* (C) 2009 by Harald Welte * (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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define IPA_ALLOC_SIZE 1200 /* * Common propietary IPA messages: * - PONG: in reply to PING. * - ID_REQUEST: first messages once OML has been established. * - ID_ACK: in reply to ID_ACK. */ static const uint8_t ipa_pong_msg[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; static const uint8_t ipa_id_ack_msg[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; static const uint8_t ipa_id_req_msg[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET, 0x01, IPAC_IDTAG_UNIT, 0x01, IPAC_IDTAG_MACADDR, 0x01, IPAC_IDTAG_LOCATION1, 0x01, IPAC_IDTAG_LOCATION2, 0x01, IPAC_IDTAG_EQUIPVERS, 0x01, IPAC_IDTAG_SWVERSION, 0x01, IPAC_IDTAG_UNITNAME, 0x01, IPAC_IDTAG_SERNR, }; static const char *idtag_names[] = { [IPAC_IDTAG_SERNR] = "Serial_Number", [IPAC_IDTAG_UNITNAME] = "Unit_Name", [IPAC_IDTAG_LOCATION1] = "Location_1", [IPAC_IDTAG_LOCATION2] = "Location_2", [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", [IPAC_IDTAG_SWVERSION] = "Software_Version", [IPAC_IDTAG_IPADDR] = "IP_Address", [IPAC_IDTAG_MACADDR] = "MAC_Address", [IPAC_IDTAG_UNIT] = "Unit_ID", }; const char *ipa_ccm_idtag_name(uint8_t tag) { if (tag >= ARRAY_SIZE(idtag_names)) return "unknown"; return idtag_names[tag]; } int ipa_ccm_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) { return ipa_ccm_idtag_parse_off(dec, buf, len, 0); } int ipa_ccm_idtag_parse_off(struct tlv_parsed *dec, unsigned char *buf, int len, const int len_offset) { uint8_t t_len; uint8_t t_tag; uint8_t *cur = buf; memset(dec, 0, sizeof(*dec)); while (len >= 2) { len -= 2; t_len = *cur++; t_tag = *cur++; if (t_len < len_offset) { LOGP(DLMI, LOGL_ERROR, "minimal offset not included: %d\n", t_len); return -EINVAL; } if (t_len > len + 1) { LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len); return -EINVAL; } DEBUGPC(DLMI, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); dec->lv[t_tag].len = t_len - len_offset; dec->lv[t_tag].val = cur; cur += t_len - len_offset; len -= t_len - len_offset; } return 0; } int ipa_parse_unitid(const char *str, struct ipaccess_unit *unit_data) { unsigned long ul; char *endptr; const char *nptr; nptr = str; ul = strtoul(nptr, &endptr, 10); if (endptr <= nptr) return -EINVAL; unit_data->site_id = ul & 0xffff; if (*endptr++ != '/') return -EINVAL; nptr = endptr; ul = strtoul(nptr, &endptr, 10); if (endptr <= nptr) return -EINVAL; unit_data->bts_id = ul & 0xffff; if (*endptr++ != '/') return -EINVAL; nptr = endptr; ul = strtoul(nptr, &endptr, 10); if (endptr <= nptr) return -EINVAL; unit_data->trx_id = ul & 0xffff; return 0; } int ipa_ccm_tlv_to_unitdata(struct ipaccess_unit *ud, const struct tlv_parsed *tp) { int rc = 0; if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1)) ud->serno = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_SERNR)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1)) ud->unit_name = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_UNITNAME)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1)) ud->location1 = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_LOCATION1)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1)) ud->location2 = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_LOCATION2)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1)) ud->equipvers = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1)) ud->swversion = talloc_strdup(ud, (char *) TLVP_VAL(tp, IPAC_IDTAG_SWVERSION)); if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) { rc = osmo_macaddr_parse(ud->mac_addr, (char *) TLVP_VAL(tp, IPAC_IDTAG_MACADDR)); if (rc < 0) goto out; } if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1)) rc = ipa_parse_unitid((char *) TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud); out: return rc; } int ipa_send(int fd, const void *msg, size_t msglen) { int ret; ret = write(fd, msg, msglen); if (ret < 0) return -errno; if (ret < msglen) { LOGP(DLINP, LOGL_ERROR, "ipa_send: short write\n"); return -EIO; } return ret; } int ipa_ccm_send_pong(int fd) { return ipa_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg)); } int ipa_ccm_send_id_ack(int fd) { return ipa_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg)); } int ipa_ccm_send_id_req(int fd) { return ipa_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg)); } /* base handling of the ip.access protocol */ int ipa_ccm_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd) { uint8_t msg_type = *(msg->l2h); int ret; switch (msg_type) { case IPAC_MSGT_PING: ret = ipa_ccm_send_pong(bfd->fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Cannot send PING " "message. Reason: %s\n", strerror(errno)); break; } ret = 1; break; case IPAC_MSGT_PONG: DEBUGP(DLMI, "PONG!\n"); ret = 1; break; case IPAC_MSGT_ID_ACK: DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); ret = ipa_ccm_send_id_ack(bfd->fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK " "message. Reason: %s\n", strerror(errno)); break; } ret = 1; break; default: /* This is not an IPA PING, PONG or ID_ACK message */ ret = 0; break; } return ret; } /* base handling of the ip.access protocol */ int ipa_ccm_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd) { uint8_t msg_type = *(msg->l2h); int ret = 0; switch (msg_type) { case IPAC_MSGT_PING: ret = ipa_ccm_send_pong(bfd->fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Cannot send PONG " "message. Reason: %s\n", strerror(errno)); } break; case IPAC_MSGT_PONG: DEBUGP(DLMI, "PONG!\n"); break; case IPAC_MSGT_ID_ACK: DEBUGP(DLMI, "ID_ACK\n"); break; } return ret; } void ipa_prepend_header_ext(struct msgb *msg, int proto) { struct ipaccess_head_ext *hh_ext; /* prepend the osmo ip.access header extension */ hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext)); hh_ext->proto = proto; } void ipa_prepend_header(struct msgb *msg, int proto) { struct ipaccess_head *hh; /* prepend the ip.access header */ hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); hh->len = htons(msg->len - sizeof(*hh)); hh->proto = proto; } int ipa_msg_recv(int fd, struct msgb **rmsg) { int rc = ipa_msg_recv_buffered(fd, rmsg, NULL); if (rc < 0) { errno = -rc; rc = -1; } return rc; } int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg) { struct msgb *msg = tmp_msg ? *tmp_msg : NULL; struct ipaccess_head *hh; int len, ret; int needed; if (msg == NULL) { msg = ipa_msg_alloc(0); if (msg == NULL) { ret = -ENOMEM; goto discard_msg; } msg->l1h = msg->tail; } if (msg->l2h == NULL) { /* first read our 3-byte header */ needed = sizeof(*hh) - msg->len; ret = recv(fd, msg->tail, needed, 0); if (ret == 0) goto discard_msg; if (ret < 0) { if (errno == EAGAIN || errno == EINTR) ret = 0; else { ret = -errno; goto discard_msg; } } msgb_put(msg, ret); if (ret < needed) { if (msg->len == 0) { ret = -EAGAIN; goto discard_msg; } LOGP(DLINP, LOGL_INFO, "Received part of IPA message header (%d/%zu)\n", msg->len, sizeof(*hh)); if (!tmp_msg) { ret = -EIO; goto discard_msg; } *tmp_msg = msg; return -EAGAIN; } msg->l2h = msg->tail; } hh = (struct ipaccess_head *) msg->data; /* then read the length as specified in header */ len = ntohs(hh->len); if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) { LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, " "received %d bytes\n", len, msg->len); ret = -EIO; goto discard_msg; } needed = len - msgb_l2len(msg); if (needed > 0) { ret = recv(fd, msg->tail, needed, 0); if (ret == 0) goto discard_msg; if (ret < 0) { if (errno == EAGAIN || errno == EINTR) ret = 0; else { ret = -errno; goto discard_msg; } } msgb_put(msg, ret); if (ret < needed) { LOGP(DLINP, LOGL_INFO, "Received part of IPA message L2 data (%d/%d)\n", msgb_l2len(msg), len); if (!tmp_msg) { ret = -EIO; goto discard_msg; } *tmp_msg = msg; return -EAGAIN; } } ret = msgb_l2len(msg); if (ret == 0) { LOGP(DLINP, LOGL_INFO, "Discarding IPA message without payload\n"); ret = -EAGAIN; goto discard_msg; } if (tmp_msg) *tmp_msg = NULL; *rmsg = msg; return ret; discard_msg: if (tmp_msg) *tmp_msg = NULL; msgb_free(msg); return ret; } struct msgb *ipa_msg_alloc(int headroom) { struct msgb *nmsg; headroom += sizeof(struct ipaccess_head); nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP"); if (!nmsg) return NULL; return nmsg; } libosmocore-0.9.0/src/gsm/kasumi.c000066400000000000000000000172051261607044000170540ustar00rootroot00000000000000/* Kasumi cipher and KGcore functions */ /* (C) 2013 by Max * * 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 #include #include /* See TS 135 202 for constants and full Kasumi spec. */ inline static uint16_t kasumi_FI(uint16_t I, uint16_t skey) { static const uint16_t S7[] = { 54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18, 123, 33, 55, 113, 39, 114, 21, 67, 65, 12, 47, 73, 46, 27, 25, 111, 124, 81, 53, 9, 121, 79, 52, 60, 58, 48, 101, 127, 40, 120, 104, 70, 71, 43, 20, 122, 72, 61, 23, 109, 13, 100, 77, 1, 16, 7, 82, 10, 105, 98, 117, 116, 76, 11, 89, 106, 0,125,118, 99, 86, 69, 30, 57, 126, 87, 112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66, 102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29, 115, 44, 64, 107, 108, 24, 110, 83, 36, 78, 42, 19, 15, 41, 88, 119, 59, 3 }; static const uint16_t S9[] = { 167, 239, 161, 379, 391, 334, 9, 338, 38, 226, 48, 358, 452, 385, 90, 397, 183, 253, 147, 331, 415, 340, 51, 362, 306, 500, 262, 82, 216, 159, 356, 177, 175, 241, 489, 37, 206, 17, 0, 333, 44, 254, 378, 58, 143, 220, 81, 400, 95, 3, 315, 245, 54, 235, 218, 405, 472, 264, 172, 494, 371, 290, 399, 76, 165, 197, 395, 121, 257, 480, 423, 212, 240, 28, 462, 176, 406, 507, 288, 223, 501, 407, 249, 265, 89, 186, 221, 428,164, 74, 440, 196, 458, 421, 350, 163, 232, 158, 134, 354, 13, 250, 491, 142,191, 69, 193, 425, 152, 227, 366, 135, 344, 300, 276, 242, 437, 320, 113, 278, 11, 243, 87, 317, 36, 93, 496, 27, 487, 446, 482, 41, 68, 156, 457, 131, 326, 403, 339, 20, 39, 115, 442, 124, 475, 384, 508, 53, 112, 170, 479, 151, 126, 169, 73, 268, 279, 321, 168, 364, 363, 292, 46, 499, 393, 327, 324, 24, 456, 267, 157, 460, 488, 426, 309, 229, 439, 506, 208, 271, 349, 401, 434, 236, 16, 209, 359, 52, 56, 120, 199, 277, 465, 416, 252, 287, 246, 6, 83, 305, 420, 345, 153,502, 65, 61, 244, 282, 173, 222, 418, 67, 386, 368, 261, 101, 476, 291, 195,430, 49, 79, 166, 330, 280, 383, 373, 128, 382, 408, 155, 495, 367, 388, 274, 107, 459, 417, 62, 454, 132, 225, 203, 316, 234, 14, 301, 91, 503, 286, 424, 211, 347, 307, 140, 374, 35, 103, 125, 427, 19, 214, 453, 146, 498, 314, 444, 230, 256, 329, 198, 285, 50, 116, 78, 410, 10, 205, 510, 171, 231, 45, 139, 467, 29, 86, 505, 32, 72, 26, 342, 150, 313, 490, 431, 238, 411, 325, 149, 473, 40, 119, 174, 355, 185, 233, 389, 71, 448, 273, 372, 55, 110, 178, 322, 12, 469, 392, 369, 190, 1, 109, 375, 137, 181, 88, 75, 308, 260, 484, 98, 272, 370, 275, 412, 111, 336, 318, 4, 504, 492, 259, 304, 77, 337, 435, 21, 357, 303, 332, 483, 18, 47, 85, 25, 497, 474, 289, 100, 269, 296, 478, 270, 106, 31, 104, 433, 84, 414, 486, 394, 96, 99, 154, 511, 148, 413, 361, 409, 255, 162, 215, 302, 201, 266, 351, 343, 144, 441, 365, 108, 298, 251, 34, 182, 509, 138, 210, 335, 133, 311, 352, 328, 141, 396, 346, 123, 319, 450, 281, 429, 228, 443, 481, 92, 404, 485, 422, 248, 297, 23, 213, 130, 466, 22, 217, 283, 70, 294, 360, 419, 127, 312, 377, 7, 468, 194, 2, 117, 295, 463, 258, 224, 447, 247, 187, 80, 398, 284, 353, 105, 390, 299, 471, 470, 184, 57, 200, 348, 63, 204, 188, 33, 451, 97, 30, 310, 219, 94, 160, 129, 493, 64, 179, 263, 102, 189, 207, 114, 402, 438, 477, 387, 122, 192, 42, 381, 5, 145, 118, 180, 449, 293, 323, 136, 380, 43, 66, 60, 455, 341, 445, 202, 432, 8, 237, 15, 376, 436, 464, 59, 461 }; uint16_t L, R; /* Split 16 bit input into two unequal halves: 9 and 7 bits, same for subkey */ L = I >> 7; /* take 9 bits */ R = I & 0x7F; /* take 7 bits */ L = S9[L] ^ R; R = S7[R] ^ (L & 0x7F); L ^= (skey & 0x1FF); R ^= (skey >> 9); L = S9[L] ^ R; R = S7[R] ^ (L & 0x7F); return (R << 9) + L; } inline static uint32_t kasumi_FO(uint32_t I, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3, unsigned i) { uint16_t L = I >> 16, R = I; /* Split 32 bit input into Left and Right parts */ L ^= KOi1[i]; L = kasumi_FI(L, KIi1[i]); L ^= R; R ^= KOi2[i]; R = kasumi_FI(R, KIi2[i]); R ^= L; L ^= KOi3[i]; L = kasumi_FI(L, KIi3[i]); L ^= R; return (((uint32_t)R) << 16) + L; } inline static uint32_t kasumi_FL(uint32_t I, const uint16_t *KLi1, const uint16_t *KLi2, unsigned i) { uint16_t L = I >> 16, R = I, tmp; /* Split 32 bit input into Left and Right parts */ tmp = L & KLi1[i]; R ^= osmo_rol16(tmp, 1); tmp = R | KLi2[i]; L ^= osmo_rol16(tmp, 1); return (((uint32_t)L) << 16) + R; } uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3) { uint32_t i, L = P >> 32, R = P; /* Split 64 bit input into Left and Right parts */ for (i = 0; i < 8; i++) { R ^= kasumi_FO(kasumi_FL(L, KLi1, KLi2, i), KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i); /* odd round */ i++; L ^= kasumi_FL(kasumi_FO(R, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i), KLi1, KLi2, i); /* even round */ } return (((uint64_t)L) << 32) + R; /* Concatenate Left and Right 32 bits into 64 bit ciphertext */ } void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3) { uint16_t i, C[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xFEDC, 0xBA98, 0x7654, 0x3210 }; /* Work with 16 bit subkeys and create prime subkeys */ for (i = 0; i < 8; i++) C[i] ^= osmo_load16be(key + i * 2); /* C[] now stores K-prime[] */ /* Create round-specific subkeys */ for (i = 0; i < 8; i++) { KLi1[i] = osmo_rol16(osmo_load16be(key + i * 2), 1); KLi2[i] = C[(i + 2) & 0x7]; KOi1[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 1)) & 0xE)), 5); KOi2[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 5)) & 0xE)), 8); KOi3[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 6)) & 0xE)), 13); KIi1[i] = C[(i + 4) & 0x7]; KIi2[i] = C[(i + 3) & 0x7]; KIi3[i] = C[(i + 7) & 0x7]; } } void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl) { uint16_t KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8], i; uint64_t A = ((uint64_t)cc) << 32, BLK = 0, _ca = ((uint64_t)CA << 16) ; A |= _ca; _ca = (uint64_t)((cb << 3) | (cd << 2)) << 24; A |= _ca; /* Register loading complete: see TR 55.919 8.2 and TS 55.216 3.2 */ uint8_t ck_km[16]; for (i = 0; i < 16; i++) ck_km[i] = ck[i] ^ 0x55; /* Modified key established */ /* preliminary round with modified key */ _kasumi_key_expand(ck_km, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3); A = _kasumi(A, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3); /* Run Kasumi in OFB to obtain enough data for gamma. */ _kasumi_key_expand(ck, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3); /* i is a block counter */ for (i = 0; i < cl / 64 + 1; i++) { BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3); osmo_store64be(BLK, co + (i * 8)); } } libosmocore-0.9.0/src/gsm/lapd_core.c000066400000000000000000001707271261607044000175240ustar00rootroot00000000000000/* LAPD core implementation */ /* (C) 2010-2011 by Harald Welte * (C) 2010-2011 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. * */ /*! \addtogroup lapd * @{ */ /*! \file lapd_core.c */ /*! * Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue * * RX data is stored in the rcv_buffer (pointer). If the message is complete, it * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is * received while there is an incomplete rcv_buffer, it is appended to it. * * TX data is stored in the send_queue first. When transmitting a frame, * the first message in the send_queue is moved to the send_buffer. There it * resides until all fragments are acknowledged. Fragments to be sent by I * frames are stored in the tx_hist buffer for resend, if required. Also the * current fragment is copied into the tx_queue. There it resides until it is * forwarded to layer 1. * * In case we have SAPI 0, we only have a window size of 1, so the unack- * nowledged message resides always in the send_buffer. In case of a suspend, * it can be written back to the first position of the send_queue. * * The layer 1 normally sends a PH-READY-TO-SEND. But because we use * asynchronous transfer between layer 1 and layer 2 (serial link), we must * send a frame before layer 1 reaches the right timeslot to send it. So we * move the tx_queue to layer 1 when there is not already a pending frame, and * wait until acknowledge after the frame has been sent. If we receive an * acknowledge, we can send the next frame from the buffer, if any. * * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it * will trigger next I frame, if possible. * * T203 is optional. It will be stated when entering MF EST state. It will also * be started when I or S frame is received in that state . It will be * restarted in the lapd_acknowledge() function, in case outstanding frames * will not trigger T200. It will be stoped, when T200 is started in MF EST * state. It will also be stoped when leaving MF EST state. * */ /* Enable this to test content resolution on network side: * - The first SABM is received, UA is dropped. * - The phone repeats SABM, but it's content is wrong, so it is ignored * - The phone repeats SABM again, content is right, so UA is sent. */ //#define TEST_CONTENT_RESOLUTION_NETWORK #include #include #include #include #include #include #include #include #include #include #include /* TS 04.06 Table 4 / Section 3.8.1 */ #define LAPD_U_SABM 0x7 #define LAPD_U_SABME 0xf #define LAPD_U_DM 0x3 #define LAPD_U_UI 0x0 #define LAPD_U_DISC 0x8 #define LAPD_U_UA 0xC #define LAPD_U_FRMR 0x11 #define LAPD_S_RR 0x0 #define LAPD_S_RNR 0x1 #define LAPD_S_REJ 0x2 #define CR_USER2NET_CMD 0 #define CR_USER2NET_RESP 1 #define CR_NET2USER_CMD 1 #define CR_NET2USER_RESP 0 #define LAPD_HEADROOM 56 #define SBIT(a) (1 << a) #define ALL_STATES 0xffffffff static void lapd_t200_cb(void *data); static void lapd_t203_cb(void *data); static int lapd_send_i(struct lapd_msg_ctx *lctx, int line); static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); /* UTILITY FUNCTIONS */ struct msgb *lapd_msgb_alloc(int length, const char *name) { /* adding space for padding, FIXME: add as an option */ if (length < 21) length = 21; return msgb_alloc_headroom(length + LAPD_HEADROOM, LAPD_HEADROOM, name); } static inline uint8_t do_mod(uint8_t x, uint8_t m) { return x & (m - 1); } static inline uint8_t inc_mod(uint8_t x, uint8_t m) { return (x + 1) & (m - 1); } static inline uint8_t add_mod(uint8_t x, uint8_t y, uint8_t m) { return (x + y) & (m - 1); } static inline uint8_t sub_mod(uint8_t x, uint8_t y, uint8_t m) { return (x - y) & (m - 1); /* handle negative results correctly */ } static void lapd_dl_flush_send(struct lapd_datalink *dl) { struct msgb *msg; /* Flush send-queue */ while ((msg = msgb_dequeue(&dl->send_queue))) msgb_free(msg); /* Clear send-buffer */ msgb_free(dl->send_buffer); dl->send_buffer = NULL; } static void lapd_dl_flush_hist(struct lapd_datalink *dl) { unsigned int i; for (i = 0; i < dl->range_hist; i++) { if (dl->tx_hist[i].msg) { msgb_free(dl->tx_hist[i].msg); dl->tx_hist[i].msg = NULL; } } } static void lapd_dl_flush_tx(struct lapd_datalink *dl) { struct msgb *msg; while ((msg = msgb_dequeue(&dl->tx_queue))) msgb_free(msg); lapd_dl_flush_hist(dl); } /* Figure B.2/Q.921 */ const char *lapd_state_names[] = { "LAPD_STATE_NULL", "LAPD_STATE_TEI_UNASS", "LAPD_STATE_ASS_TEI_WAIT", "LAPD_STATE_EST_TEI_WAIT", "LAPD_STATE_IDLE", "LAPD_STATE_SABM_SENT", "LAPD_STATE_DISC_SENT", "LAPD_STATE_MF_EST", "LAPD_STATE_TIMER_RECOV", }; static void lapd_start_t200(struct lapd_datalink *dl) { if (osmo_timer_pending(&dl->t200)) return; LOGP(DLLAPD, LOGL_INFO, "start T200\n"); osmo_timer_schedule(&dl->t200, dl->t200_sec, dl->t200_usec); } static void lapd_start_t203(struct lapd_datalink *dl) { if (osmo_timer_pending(&dl->t203)) return; LOGP(DLLAPD, LOGL_INFO, "start T203\n"); osmo_timer_schedule(&dl->t203, dl->t203_sec, dl->t203_usec); } static void lapd_stop_t200(struct lapd_datalink *dl) { if (!osmo_timer_pending(&dl->t200)) return; LOGP(DLLAPD, LOGL_INFO, "stop T200\n"); osmo_timer_del(&dl->t200); } static void lapd_stop_t203(struct lapd_datalink *dl) { if (!osmo_timer_pending(&dl->t203)) return; LOGP(DLLAPD, LOGL_INFO, "stop T203\n"); osmo_timer_del(&dl->t203); } static void lapd_dl_newstate(struct lapd_datalink *dl, uint32_t state) { LOGP(DLLAPD, LOGL_INFO, "new state %s -> %s\n", lapd_state_names[dl->state], lapd_state_names[state]); if (state != LAPD_STATE_MF_EST && dl->state == LAPD_STATE_MF_EST) { /* stop T203 on leaving MF EST state, if running */ lapd_stop_t203(dl); /* remove content res. (network side) on leaving MF EST state */ msgb_free(dl->cont_res); dl->cont_res = NULL; } /* start T203 on entering MF EST state, if enabled */ if ((dl->t203_sec || dl->t203_usec) && state == LAPD_STATE_MF_EST && dl->state != LAPD_STATE_MF_EST) lapd_start_t203(dl); dl->state = state; } static void *tall_lapd_ctx = NULL; /* init datalink instance and allocate history */ void lapd_dl_init(struct lapd_datalink *dl, uint8_t k, uint8_t v_range, int maxf) { int m; memset(dl, 0, sizeof(*dl)); INIT_LLIST_HEAD(&dl->send_queue); INIT_LLIST_HEAD(&dl->tx_queue); dl->reestablish = 1; dl->n200_est_rel = 3; dl->n200 = 3; dl->t200_sec = 1; dl->t200_usec = 0; dl->t200.data = dl; dl->t200.cb = &lapd_t200_cb; dl->t203_sec = 10; dl->t203_usec = 0; dl->t203.data = dl; dl->t203.cb = &lapd_t203_cb; dl->maxf = maxf; if (k > v_range - 1) k = v_range - 1; dl->k = k; dl->v_range = v_range; /* Calculate modulo for history array: * - The history range must be at least k+1. * - The history range must be 2^x, where x is as low as possible. */ k++; for (m = 0x80; m; m >>= 1) { if ((m & k)) { if (k > m) m <<= 1; dl->range_hist = m; break; } } LOGP(DLLAPD, LOGL_INFO, "Init DL layer: sequence range = %d, k = %d, " "history range = %d\n", dl->v_range, dl->k, dl->range_hist); lapd_dl_newstate(dl, LAPD_STATE_IDLE); if (!tall_lapd_ctx) tall_lapd_ctx = talloc_named_const(NULL, 1, "lapd context"); dl->tx_hist = talloc_zero_array(tall_lapd_ctx, struct lapd_history, dl->range_hist); } /* reset to IDLE state */ void lapd_dl_reset(struct lapd_datalink *dl) { if (dl->state == LAPD_STATE_IDLE) return; LOGP(DLLAPD, LOGL_INFO, "Resetting LAPDm instance\n"); /* enter idle state (and remove eventual cont_res) */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); /* flush buffer */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); /* Discard partly received L3 message */ msgb_free(dl->rcv_buffer); dl->rcv_buffer = NULL; /* stop Timers */ lapd_stop_t200(dl); lapd_stop_t203(dl); } /* reset and de-allocate history buffer */ void lapd_dl_exit(struct lapd_datalink *dl) { /* free all ressources except history buffer */ lapd_dl_reset(dl); /* free history buffer list */ talloc_free(dl->tx_hist); dl->tx_hist = NULL; } /*! \brief Set the \ref lapdm_mode of a LAPDm entity */ int lapd_set_mode(struct lapd_datalink *dl, enum lapd_mode mode) { switch (mode) { case LAPD_MODE_USER: dl->cr.loc2rem.cmd = CR_USER2NET_CMD; dl->cr.loc2rem.resp = CR_USER2NET_RESP; dl->cr.rem2loc.cmd = CR_NET2USER_CMD; dl->cr.rem2loc.resp = CR_NET2USER_RESP; break; case LAPD_MODE_NETWORK: dl->cr.loc2rem.cmd = CR_NET2USER_CMD; dl->cr.loc2rem.resp = CR_NET2USER_RESP; dl->cr.rem2loc.cmd = CR_USER2NET_CMD; dl->cr.rem2loc.resp = CR_USER2NET_RESP; break; default: return -EINVAL; } dl->mode = mode; return 0; } /* send DL message with optional msgb */ static int send_dl_l3(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx, struct msgb *msg) { struct lapd_datalink *dl = lctx->dl; struct osmo_dlsap_prim dp; osmo_prim_init(&dp.oph, 0, prim, op, msg); return dl->send_dlsap(&dp, lctx); } /* send simple DL message */ static inline int send_dl_simple(uint8_t prim, uint8_t op, struct lapd_msg_ctx *lctx) { struct msgb *msg = lapd_msgb_alloc(0, "DUMMY"); return send_dl_l3(prim, op, lctx, msg); } /* send MDL-ERROR INDICATION */ static int mdl_error(uint8_t cause, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct osmo_dlsap_prim dp; LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND cause %d\n", cause); osmo_prim_init(&dp.oph, 0, PRIM_MDL_ERROR, PRIM_OP_INDICATION, NULL); dp.u.error_ind.cause = cause; return dl->send_dlsap(&dp, lctx); } /* send UA response */ static int lapd_send_ua(struct lapd_msg_ctx *lctx, uint8_t len, uint8_t *data) { struct msgb *msg = lapd_msgb_alloc(len, "LAPD UA"); struct lapd_msg_ctx nctx; struct lapd_datalink *dl = lctx->dl; memcpy(&nctx, lctx, sizeof(nctx)); msg->l3h = msgb_put(msg, len); if (len) memcpy(msg->l3h, data, len); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.resp; nctx.format = LAPD_FORM_U; nctx.s_u = LAPD_U_UA; /* keep nctx.p_f */ nctx.length = len; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* send DM response */ static int lapd_send_dm(struct lapd_msg_ctx *lctx) { struct msgb *msg = lapd_msgb_alloc(0, "LAPD DM"); struct lapd_msg_ctx nctx; struct lapd_datalink *dl = lctx->dl; memcpy(&nctx, lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.resp; nctx.format = LAPD_FORM_U; nctx.s_u = LAPD_U_DM; /* keep nctx.p_f */ nctx.length = 0; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* send RR response / command */ static int lapd_send_rr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) { struct msgb *msg = lapd_msgb_alloc(0, "LAPD RR"); struct lapd_msg_ctx nctx; struct lapd_datalink *dl = lctx->dl; memcpy(&nctx, lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; nctx.format = LAPD_FORM_S; nctx.s_u = LAPD_S_RR; nctx.p_f = f_bit; nctx.n_recv = dl->v_recv; nctx.length = 0; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* send RNR response / command */ static int lapd_send_rnr(struct lapd_msg_ctx *lctx, uint8_t f_bit, uint8_t cmd) { struct msgb *msg = lapd_msgb_alloc(0, "LAPD RNR"); struct lapd_msg_ctx nctx; struct lapd_datalink *dl = lctx->dl; memcpy(&nctx, lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = (cmd) ? dl->cr.loc2rem.cmd : dl->cr.loc2rem.resp; nctx.format = LAPD_FORM_S; nctx.s_u = LAPD_S_RNR; nctx.p_f = f_bit; nctx.n_recv = dl->v_recv; nctx.length = 0; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* send REJ response */ static int lapd_send_rej(struct lapd_msg_ctx *lctx, uint8_t f_bit) { struct msgb *msg = lapd_msgb_alloc(0, "LAPD REJ"); struct lapd_msg_ctx nctx; struct lapd_datalink *dl = lctx->dl; memcpy(&nctx, lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.resp; nctx.format = LAPD_FORM_S; nctx.s_u = LAPD_S_REJ; nctx.p_f = f_bit; nctx.n_recv = dl->v_recv; nctx.length = 0; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* resend SABM or DISC message */ static int lapd_send_resend(struct lapd_datalink *dl) { struct msgb *msg; uint8_t h = do_mod(dl->v_send, dl->range_hist); int length = dl->tx_hist[h].msg->len; struct lapd_msg_ctx nctx; /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_U; if (dl->state == LAPD_STATE_SABM_SENT) nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; else nctx.s_u = LAPD_U_DISC; nctx.p_f = 1; nctx.length = length; nctx.more = 0; /* Resend SABM/DISC from tx_hist */ msg = lapd_msgb_alloc(length, "LAPD resend"); msg->l3h = msgb_put(msg, length); if (length) memcpy(msg->l3h, dl->tx_hist[h].msg->data, length); return dl->send_ph_data_req(&nctx, msg); } /* reestablish link */ static int lapd_reestablish(struct lapd_datalink *dl) { struct osmo_dlsap_prim dp; struct msgb *msg; msg = lapd_msgb_alloc(0, "DUMMY"); osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg); return lapd_est_req(&dp, &dl->lctx); } /* Timer callback on T200 expiry */ static void lapd_t200_cb(void *data) { struct lapd_datalink *dl = data; LOGP(DLLAPD, LOGL_INFO, "Timeout T200 (%p) state=%d\n", dl, (int) dl->state); switch (dl->state) { case LAPD_STATE_SABM_SENT: /* 5.4.1.3 */ if (dl->retrans_ctr + 1 >= dl->n200_est_rel + 1) { /* send RELEASE INDICATION to L3 */ send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, &dl->lctx); /* send MDL ERROR INIDCATION to L3 */ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); /* flush tx and send buffers */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); /* go back to idle state */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); /* NOTE: we must not change any other states or buffers * and queues, since we may reconnect after handover * failure. the buffered messages is replaced there */ break; } /* retransmit SABM command */ lapd_send_resend(dl); /* increment re-transmission counter */ dl->retrans_ctr++; /* restart T200 (PH-READY-TO-SEND) */ lapd_start_t200(dl); break; case LAPD_STATE_DISC_SENT: /* 5.4.4.3 */ if (dl->retrans_ctr + 1 >= dl->n200_est_rel + 1) { /* send RELEASE INDICATION to L3 */ send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); /* send MDL ERROR INIDCATION to L3 */ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); /* flush tx and send buffers */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); /* go back to idle state */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); /* NOTE: we must not change any other states or buffers * and queues, since we may reconnect after handover * failure. the buffered messages is replaced there */ break; } /* retransmit DISC command */ lapd_send_resend(dl); /* increment re-transmission counter */ dl->retrans_ctr++; /* restart T200 (PH-READY-TO-SEND) */ lapd_start_t200(dl); break; case LAPD_STATE_MF_EST: /* 5.5.7 */ dl->retrans_ctr = 0; lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); /* fall through */ case LAPD_STATE_TIMER_RECOV: dl->retrans_ctr++; if (dl->retrans_ctr < dl->n200) { uint8_t vs = sub_mod(dl->v_send, 1, dl->v_range); uint8_t h = do_mod(vs, dl->range_hist); /* retransmit I frame (V_s-1) with P=1, if any */ if (dl->tx_hist[h].msg) { struct msgb *msg; int length = dl->tx_hist[h].msg->len; struct lapd_msg_ctx nctx; LOGP(DLLAPD, LOGL_INFO, "retransmit last frame" " V(S)=%d\n", vs); /* Create I frame (segment) from tx_hist */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_I; nctx.p_f = 1; nctx.n_send = vs; nctx.n_recv = dl->v_recv; nctx.length = length; nctx.more = dl->tx_hist[h].more; msg = lapd_msgb_alloc(length, "LAPD I resend"); msg->l3h = msgb_put(msg, length); memcpy(msg->l3h, dl->tx_hist[h].msg->data, length); dl->send_ph_data_req(&nctx, msg); } else { /* OR send appropriate supervision frame with P=1 */ if (!dl->own_busy && !dl->seq_err_cond) { lapd_send_rr(&dl->lctx, 1, 1); /* NOTE: In case of sequence error * condition, the REJ frame has been * transmitted when entering the * condition, so it has not be done * here */ } else if (dl->own_busy) { lapd_send_rnr(&dl->lctx, 1, 1); } else { LOGP(DLLAPD, LOGL_INFO, "unhandled, " "pls. fix\n"); } } /* restart T200 (PH-READY-TO-SEND) */ lapd_start_t200(dl); } else { /* send MDL ERROR INIDCATION to L3 */ mdl_error(MDL_CAUSE_T200_EXPIRED, &dl->lctx); /* reestablish */ if (!dl->reestablish) break; LOGP(DLLAPD, LOGL_NOTICE, "N200 reached, performing " "reestablishment.\n"); lapd_reestablish(dl); } break; default: LOGP(DLLAPD, LOGL_INFO, "T200 expired in unexpected " "dl->state %d\n", (int) dl->state); } } /* Timer callback on T203 expiry */ static void lapd_t203_cb(void *data) { struct lapd_datalink *dl = data; LOGP(DLLAPD, LOGL_INFO, "Timeout T203 (%p) state=%d\n", dl, (int) dl->state); if (dl->state != LAPD_STATE_MF_EST) { LOGP(DLLAPD, LOGL_ERROR, "T203 fired outside MF EST state, " "please fix!\n"); return; } /* set retransmission counter to 0 */ dl->retrans_ctr = 0; /* enter timer recovery state */ lapd_dl_newstate(dl, LAPD_STATE_TIMER_RECOV); /* transmit a supervisory command with P bit set to 1 as follows: */ if (!dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "transmit an RR poll command\n"); /* Send RR with P=1 */ lapd_send_rr(&dl->lctx, 1, 1); } else { LOGP(DLLAPD, LOGL_INFO, "transmit an RNR poll command\n"); /* Send RNR with P=1 */ lapd_send_rnr(&dl->lctx, 1, 1); } /* start T200 */ lapd_start_t200(dl); } /* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */ static void lapd_acknowledge(struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; uint8_t nr = lctx->n_recv; int s = 0, rej = 0, t200_reset = 0; int i, h; /* supervisory frame ? */ if (lctx->format == LAPD_FORM_S) s = 1; /* REJ frame ? */ if (s && lctx->s_u == LAPD_S_REJ) rej = 1; /* Flush all transmit buffers of acknowledged frames */ for (i = dl->v_ack; i != nr; i = inc_mod(i, dl->v_range)) { h = do_mod(i, dl->range_hist); if (dl->tx_hist[h].msg) { msgb_free(dl->tx_hist[h].msg); dl->tx_hist[h].msg = NULL; LOGP(DLLAPD, LOGL_INFO, "ack frame %d\n", i); } } if (dl->state != LAPD_STATE_TIMER_RECOV) { /* When not in the timer recovery condition, the data * link layer entity shall reset the timer T200 on * receipt of a valid I frame with N(R) higher than V(A), * or an REJ with an N(R) equal to V(A). */ if ((!rej && nr != dl->v_ack) || (rej && nr == dl->v_ack)) { t200_reset = 1; lapd_stop_t200(dl); /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */ } /* 5.7.4: N(R) sequence error * N(R) is called valid, if and only if * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8. */ if (sub_mod(nr, dl->v_ack, dl->v_range) > sub_mod(dl->v_send, dl->v_ack, dl->v_range)) { LOGP(DLLAPD, LOGL_NOTICE, "N(R) sequence error\n"); mdl_error(MDL_CAUSE_SEQ_ERR, lctx); } } /* V(A) shall be set to the value of N(R) */ dl->v_ack = nr; /* If T200 has been stopped by the receipt of an I, RR or RNR frame, * and if there are outstanding I frames, restart T200 */ if (t200_reset && !rej) { if (dl->tx_hist[sub_mod(dl->v_send, 1, dl->range_hist)].msg) { LOGP(DLLAPD, LOGL_INFO, "start T200, due to unacked I " "frame(s)\n"); lapd_start_t200(dl); } } /* This also does a restart, when I or S frame is received */ /* Stop T203, if running */ lapd_stop_t203(dl); /* Start T203, if T200 is not running in MF EST state, if enabled */ if (!osmo_timer_pending(&dl->t200) && (dl->t203_sec || dl->t203_usec) && (dl->state == LAPD_STATE_MF_EST)) { lapd_start_t203(dl); } } /* L1 -> L2 */ /* Receive a LAPD U (Unnumbered) message from L1 */ static int lapd_rx_u(struct msgb *msg, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; int length = lctx->length; int rc = 0; uint8_t prim, op; switch (lctx->s_u) { case LAPD_U_SABM: case LAPD_U_SABME: prim = PRIM_DL_EST; op = PRIM_OP_INDICATION; LOGP(DLLAPD, LOGL_INFO, "SABM(E) received in state %s\n", lapd_state_names[dl->state]); /* 5.7.1 */ dl->seq_err_cond = 0; /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.resp) { LOGP(DLLAPD, LOGL_ERROR, "SABM response error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } /* G.4.5 If SABM is received with L>N201 or with M bit * set, AN MDL-ERROR-INDICATION is sent to MM. */ if (lctx->more || length > lctx->n201) { LOGP(DLLAPD, LOGL_ERROR, "SABM too large error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); return -EIO; } switch (dl->state) { case LAPD_STATE_IDLE: break; case LAPD_STATE_MF_EST: LOGP(DLLAPD, LOGL_INFO, "SABM command, multiple " "frame established state\n"); /* If link is lost on the remote side, we start over * and send DL-ESTABLISH indication again. */ /* Additionally, continue in case of content resoltion * (GSM network). This happens, if the mobile has not * yet received UA or another mobile (collision) tries * to establish connection. The mobile must receive * UA again. */ /* 5.4.2.1 */ if (!length) { /* If no content resolution, this is a * re-establishment. */ LOGP(DLLAPD, LOGL_INFO, "Remote reestablish\n"); break; } if (!dl->cont_res) { LOGP(DLLAPD, LOGL_INFO, "SABM command not " "allowed in this state\n"); mdl_error(MDL_CAUSE_SABM_MF, lctx); msgb_free(msg); return 0; } /* Ignore SABM if content differs from first SABM. */ if (dl->mode == LAPD_MODE_NETWORK && length) { #ifdef TEST_CONTENT_RESOLUTION_NETWORK dl->cont_res->data[0] ^= 0x01; #endif if (memcmp(dl->cont_res->data, msg->data, length)) { LOGP(DLLAPD, LOGL_INFO, "Another SABM " "with diffrent content - " "ignoring!\n"); msgb_free(msg); return 0; } } /* send UA again */ lapd_send_ua(lctx, length, msg->l3h); msgb_free(msg); return 0; case LAPD_STATE_DISC_SENT: /* 5.4.6.2 send DM with F=P */ lapd_send_dm(lctx); /* stop Timer T200 */ lapd_stop_t200(dl); msgb_free(msg); return send_dl_simple(prim, op, lctx); default: /* collision: Send UA, but still wait for rx UA, then * change to MF_EST state. */ /* check for contention resoultion */ if (dl->tx_hist[0].msg && dl->tx_hist[0].msg->len) { LOGP(DLLAPD, LOGL_NOTICE, "SABM not allowed " "during contention resolution\n"); mdl_error(MDL_CAUSE_SABM_INFO_NOTALL, lctx); } lapd_send_ua(lctx, length, msg->l3h); msgb_free(msg); return 0; } /* save message context for further use */ memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); #ifndef TEST_CONTENT_RESOLUTION_NETWORK /* send UA response */ lapd_send_ua(lctx, length, msg->l3h); #endif /* set Vs, Vr and Va to 0 */ dl->v_send = dl->v_recv = dl->v_ack = 0; /* clear tx_hist */ lapd_dl_flush_hist(dl); /* enter multiple-frame-established state */ lapd_dl_newstate(dl, LAPD_STATE_MF_EST); /* store content resolution data on network side * Note: cont_res will be removed when changing state again, * so it must be allocated AFTER lapd_dl_newstate(). */ if (dl->mode == LAPD_MODE_NETWORK && length) { dl->cont_res = lapd_msgb_alloc(length, "CONT RES"); memcpy(msgb_put(dl->cont_res, length), msg->l3h, length); LOGP(DLLAPD, LOGL_NOTICE, "Store content res.\n"); } /* send notification to L3 */ if (length == 0) { /* 5.4.1.2 Normal establishment procedures */ rc = send_dl_simple(prim, op, lctx); msgb_free(msg); } else { /* 5.4.1.4 Contention resolution establishment */ msgb_trim(msg, length); rc = send_dl_l3(prim, op, lctx, msg); } break; case LAPD_U_DM: LOGP(DLLAPD, LOGL_INFO, "DM received in state %s\n", lapd_state_names[dl->state]); /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.cmd) { LOGP(DLLAPD, LOGL_ERROR, "DM command error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } if (!lctx->p_f) { /* 5.4.1.2 DM responses with the F bit set to "0" * shall be ignored. */ msgb_free(msg); return 0; } switch (dl->state) { case LAPD_STATE_SABM_SENT: break; case LAPD_STATE_MF_EST: if (lctx->p_f) { LOGP(DLLAPD, LOGL_INFO, "unsolicited DM " "response\n"); mdl_error(MDL_CAUSE_UNSOL_DM_RESP, lctx); } else { LOGP(DLLAPD, LOGL_INFO, "unsolicited DM " "response, multiple frame established " "state\n"); mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); /* reestablish */ if (!dl->reestablish) { msgb_free(msg); return 0; } LOGP(DLLAPD, LOGL_NOTICE, "Performing " "reestablishment.\n"); lapd_reestablish(dl); } msgb_free(msg); return 0; case LAPD_STATE_TIMER_RECOV: /* FP = 0 (DM is normal in case PF = 1) */ if (!lctx->p_f) { LOGP(DLLAPD, LOGL_INFO, "unsolicited DM " "response, multiple frame established " "state\n"); mdl_error(MDL_CAUSE_UNSOL_DM_RESP_MF, lctx); msgb_free(msg); /* reestablish */ if (!dl->reestablish) return 0; LOGP(DLLAPD, LOGL_NOTICE, "Performing " "reestablishment.\n"); return lapd_reestablish(dl); } break; case LAPD_STATE_DISC_SENT: /* stop Timer T200 */ lapd_stop_t200(dl); /* go to idle state */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); lapd_dl_newstate(dl, LAPD_STATE_IDLE); rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); msgb_free(msg); return 0; case LAPD_STATE_IDLE: /* 5.4.5 all other frame types shall be discarded */ default: LOGP(DLLAPD, LOGL_INFO, "unsolicited DM response! " "(discarding)\n"); msgb_free(msg); return 0; } /* stop timer T200 */ lapd_stop_t200(dl); /* go to idle state */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); msgb_free(msg); break; case LAPD_U_UI: LOGP(DLLAPD, LOGL_INFO, "UI received\n"); /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.resp) { LOGP(DLLAPD, LOGL_ERROR, "UI indicates response " "error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } /* G.4.5 If UI is received with L>N201 or with M bit * set, AN MDL-ERROR-INDICATION is sent to MM. */ if (length > lctx->n201 || lctx->more) { LOGP(DLLAPD, LOGL_ERROR, "UI too large error " "(%d > N201(%d) or M=%d)\n", length, lctx->n201, lctx->more); msgb_free(msg); mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); return -EIO; } /* do some length checks */ if (length == 0) { /* 5.3.3 UI frames received with the length indicator * set to "0" shall be ignored */ LOGP(DLLAPD, LOGL_INFO, "length=0 (discarding)\n"); msgb_free(msg); return 0; } msgb_trim(msg, length); rc = send_dl_l3(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION, lctx, msg); break; case LAPD_U_DISC: prim = PRIM_DL_REL; op = PRIM_OP_INDICATION; LOGP(DLLAPD, LOGL_INFO, "DISC received in state %s\n", lapd_state_names[dl->state]); /* flush tx and send buffers */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); /* 5.7.1 */ dl->seq_err_cond = 0; /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.resp) { LOGP(DLLAPD, LOGL_ERROR, "DISC response error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } if (length > 0 || lctx->more) { /* G.4.4 If a DISC or DM frame is received with L>0 or * with the M bit set to "1", an MDL-ERROR-INDICATION * primitive with cause "U frame with incorrect * parameters" is sent to the mobile management entity. */ LOGP(DLLAPD, LOGL_ERROR, "U frame iwth incorrect parameters "); msgb_free(msg); mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); return -EIO; } switch (dl->state) { case LAPD_STATE_IDLE: LOGP(DLLAPD, LOGL_INFO, "DISC in idle state\n"); /* send DM with F=P */ msgb_free(msg); return lapd_send_dm(lctx); case LAPD_STATE_SABM_SENT: LOGP(DLLAPD, LOGL_INFO, "DISC in SABM state\n"); /* 5.4.6.2 send DM with F=P */ lapd_send_dm(lctx); /* stop Timer T200 */ lapd_stop_t200(dl); /* go to idle state */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); msgb_free(msg); return send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); case LAPD_STATE_MF_EST: case LAPD_STATE_TIMER_RECOV: LOGP(DLLAPD, LOGL_INFO, "DISC in est state\n"); break; case LAPD_STATE_DISC_SENT: LOGP(DLLAPD, LOGL_INFO, "DISC in disc state\n"); prim = PRIM_DL_REL; op = PRIM_OP_CONFIRM; break; default: lapd_send_ua(lctx, length, msg->l3h); msgb_free(msg); return 0; } /* send UA response */ lapd_send_ua(lctx, length, msg->l3h); /* stop Timer T200 */ lapd_stop_t200(dl); /* enter idle state, keep tx-buffer with UA response */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); /* send notification to L3 */ rc = send_dl_simple(prim, op, lctx); msgb_free(msg); break; case LAPD_U_UA: LOGP(DLLAPD, LOGL_INFO, "UA received in state %s\n", lapd_state_names[dl->state]); /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.cmd) { LOGP(DLLAPD, LOGL_ERROR, "UA indicates command " "error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } /* G.4.5 If UA is received with L>N201 or with M bit * set, AN MDL-ERROR-INDICATION is sent to MM. */ if (lctx->more || length > lctx->n201) { LOGP(DLLAPD, LOGL_ERROR, "UA too large error\n"); msgb_free(msg); mdl_error(MDL_CAUSE_UFRM_INC_PARAM, lctx); return -EIO; } if (!lctx->p_f) { /* 5.4.1.2 A UA response with the F bit set to "0" * shall be ignored. */ LOGP(DLLAPD, LOGL_INFO, "F=0 (discarding)\n"); msgb_free(msg); return 0; } switch (dl->state) { case LAPD_STATE_SABM_SENT: break; case LAPD_STATE_MF_EST: case LAPD_STATE_TIMER_RECOV: LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! " "(discarding)\n"); mdl_error(MDL_CAUSE_UNSOL_UA_RESP, lctx); msgb_free(msg); return 0; case LAPD_STATE_DISC_SENT: LOGP(DLLAPD, LOGL_INFO, "UA in disconnect state\n"); /* stop Timer T200 */ lapd_stop_t200(dl); /* go to idle state */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); lapd_dl_newstate(dl, LAPD_STATE_IDLE); rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, lctx); msgb_free(msg); return 0; case LAPD_STATE_IDLE: /* 5.4.5 all other frame types shall be discarded */ default: LOGP(DLLAPD, LOGL_INFO, "unsolicited UA response! " "(discarding)\n"); msgb_free(msg); return 0; } LOGP(DLLAPD, LOGL_INFO, "UA in SABM state\n"); /* stop Timer T200 */ lapd_stop_t200(dl); /* compare UA with SABME if contention resolution is applied */ if (dl->tx_hist[0].msg->len) { if (length != (dl->tx_hist[0].msg->len) || !!memcmp(dl->tx_hist[0].msg->data, msg->l3h, length)) { LOGP(DLLAPD, LOGL_INFO, "**** UA response " "mismatches ****\n"); rc = send_dl_simple(PRIM_DL_REL, PRIM_OP_INDICATION, lctx); msgb_free(msg); /* go to idle state */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); lapd_dl_newstate(dl, LAPD_STATE_IDLE); return 0; } } /* set Vs, Vr and Va to 0 */ dl->v_send = dl->v_recv = dl->v_ack = 0; /* clear tx_hist */ lapd_dl_flush_hist(dl); /* enter multiple-frame-established state */ lapd_dl_newstate(dl, LAPD_STATE_MF_EST); /* send outstanding frames, if any (resume / reconnect) */ lapd_send_i(lctx, __LINE__); /* send notification to L3 */ rc = send_dl_simple(PRIM_DL_EST, PRIM_OP_CONFIRM, lctx); msgb_free(msg); break; case LAPD_U_FRMR: LOGP(DLLAPD, LOGL_NOTICE, "Frame reject received\n"); /* send MDL ERROR INIDCATION to L3 */ mdl_error(MDL_CAUSE_FRMR, lctx); msgb_free(msg); /* reestablish */ if (!dl->reestablish) break; LOGP(DLLAPD, LOGL_NOTICE, "Performing reestablishment.\n"); rc = lapd_reestablish(dl); break; default: /* G.3.1 */ LOGP(DLLAPD, LOGL_NOTICE, "Unnumbered frame not allowed.\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } return rc; } /* Receive a LAPD S (Supervisory) message from L1 */ static int lapd_rx_s(struct msgb *msg, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; int length = lctx->length; if (length > 0 || lctx->more) { /* G.4.3 If a supervisory frame is received with L>0 or * with the M bit set to "1", an MDL-ERROR-INDICATION * primitive with cause "S frame with incorrect * parameters" is sent to the mobile management entity. */ LOGP(DLLAPD, LOGL_ERROR, "S frame with incorrect parameters\n"); msgb_free(msg); mdl_error(MDL_CAUSE_SFRM_INC_PARAM, lctx); return -EIO; } if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f && dl->state != LAPD_STATE_TIMER_RECOV) { /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */ LOGP(DLLAPD, LOGL_NOTICE, "S frame response with F=1 error\n"); mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); } switch (dl->state) { case LAPD_STATE_IDLE: /* if P=1, respond DM with F=1 (5.2.2) */ /* 5.4.5 all other frame types shall be discarded */ if (lctx->p_f) lapd_send_dm(lctx); /* F=P */ /* fall though */ case LAPD_STATE_SABM_SENT: case LAPD_STATE_DISC_SENT: LOGP(DLLAPD, LOGL_NOTICE, "S frame ignored in this state\n"); msgb_free(msg); return 0; } switch (lctx->s_u) { case LAPD_S_RR: LOGP(DLLAPD, LOGL_INFO, "RR received in state %s\n", lapd_state_names[dl->state]); /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ lapd_acknowledge(lctx); /* 5.5.3.2 */ if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { if (!dl->own_busy && !dl->seq_err_cond) { LOGP(DLLAPD, LOGL_INFO, "RR frame command " "with polling bit set and we are not " "busy, so we reply with RR frame " "response\n"); lapd_send_rr(lctx, 1, 0); /* NOTE: In case of sequence error condition, * the REJ frame has been transmitted when * entering the condition, so it has not be * done here */ } else if (dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "RR frame command " "with polling bit set and we are busy, " "so we reply with RR frame response\n"); lapd_send_rnr(lctx, 1, 0); } } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f && dl->state == LAPD_STATE_TIMER_RECOV) { LOGP(DLLAPD, LOGL_INFO, "RR response with F==1, " "and we are in timer recovery state, so " "we leave that state\n"); /* V(S) to the N(R) in the RR frame */ dl->v_send = lctx->n_recv; /* stop Timer T200 */ lapd_stop_t200(dl); /* 5.5.7 Clear timer recovery condition */ lapd_dl_newstate(dl, LAPD_STATE_MF_EST); } /* Send message, if possible due to acknowledged data */ lapd_send_i(lctx, __LINE__); break; case LAPD_S_RNR: LOGP(DLLAPD, LOGL_INFO, "RNR received in state %s\n", lapd_state_names[dl->state]); /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ lapd_acknowledge(lctx); /* 5.5.5 */ /* Set peer receiver busy condition */ dl->peer_busy = 1; if (lctx->p_f) { if (lctx->cr == dl->cr.rem2loc.cmd) { if (!dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "RNR poll " "command and we are not busy, " "so we reply with RR final " "response\n"); /* Send RR with F=1 */ lapd_send_rr(lctx, 1, 0); } else { LOGP(DLLAPD, LOGL_INFO, "RNR poll " "command and we are busy, so " "we reply with RNR final " "response\n"); /* Send RNR with F=1 */ lapd_send_rnr(lctx, 1, 0); } } else if (dl->state == LAPD_STATE_TIMER_RECOV) { LOGP(DLLAPD, LOGL_INFO, "RNR poll response " "and we in timer recovery state, so " "we leave that state\n"); /* 5.5.7 Clear timer recovery condition */ lapd_dl_newstate(dl, LAPD_STATE_MF_EST); /* V(S) to the N(R) in the RNR frame */ dl->v_send = lctx->n_recv; } } else LOGP(DLLAPD, LOGL_INFO, "RNR not polling/final state " "received\n"); /* Send message, if possible due to acknowledged data */ lapd_send_i(lctx, __LINE__); break; case LAPD_S_REJ: LOGP(DLLAPD, LOGL_INFO, "REJ received in state %s\n", lapd_state_names[dl->state]); /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ lapd_acknowledge(lctx); /* 5.5.4.1 */ if (dl->state != LAPD_STATE_TIMER_RECOV) { /* Clear an existing peer receiver busy condition */ dl->peer_busy = 0; /* V(S) and V(A) to the N(R) in the REJ frame */ dl->v_send = dl->v_ack = lctx->n_recv; /* stop Timer T200 */ lapd_stop_t200(dl); /* 5.5.3.2 */ if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { if (!dl->own_busy && !dl->seq_err_cond) { LOGP(DLLAPD, LOGL_INFO, "REJ poll " "command not in timer recovery " "state and not in own busy " "condition received, so we " "respond with RR final " "response\n"); lapd_send_rr(lctx, 1, 0); /* NOTE: In case of sequence error * condition, the REJ frame has been * transmitted when entering the * condition, so it has not be done * here */ } else if (dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "REJ poll " "command not in timer recovery " "state and in own busy " "condition received, so we " "respond with RNR final " "response\n"); lapd_send_rnr(lctx, 1, 0); } } else LOGP(DLLAPD, LOGL_INFO, "REJ response or not " "polling command not in timer recovery " "state received\n"); /* send MDL ERROR INIDCATION to L3 */ if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { mdl_error(MDL_CAUSE_UNSOL_SPRV_RESP, lctx); } } else if (lctx->cr == dl->cr.rem2loc.resp && lctx->p_f) { LOGP(DLLAPD, LOGL_INFO, "REJ poll response in timer " "recovery state received\n"); /* Clear an existing peer receiver busy condition */ dl->peer_busy = 0; /* V(S) and V(A) to the N(R) in the REJ frame */ dl->v_send = dl->v_ack = lctx->n_recv; /* stop Timer T200 */ lapd_stop_t200(dl); /* 5.5.7 Clear timer recovery condition */ lapd_dl_newstate(dl, LAPD_STATE_MF_EST); } else { /* Clear an existing peer receiver busy condition */ dl->peer_busy = 0; /* V(S) and V(A) to the N(R) in the REJ frame */ dl->v_send = dl->v_ack = lctx->n_recv; /* 5.5.3.2 */ if (lctx->cr == dl->cr.rem2loc.cmd && lctx->p_f) { if (!dl->own_busy && !dl->seq_err_cond) { LOGP(DLLAPD, LOGL_INFO, "REJ poll " "command in timer recovery " "state and not in own busy " "condition received, so we " "respond with RR final " "response\n"); lapd_send_rr(lctx, 1, 0); /* NOTE: In case of sequence error * condition, the REJ frame has been * transmitted when entering the * condition, so it has not be done * here */ } else if (dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "REJ poll " "command in timer recovery " "state and in own busy " "condition received, so we " "respond with RNR final " "response\n"); lapd_send_rnr(lctx, 1, 0); } } else LOGP(DLLAPD, LOGL_INFO, "REJ response or not " "polling command in timer recovery " "state received\n"); } /* FIXME: 5.5.4.2 2) */ /* Send message, if possible due to acknowledged data */ lapd_send_i(lctx, __LINE__); break; default: /* G.3.1 */ LOGP(DLLAPD, LOGL_ERROR, "Supervisory frame not allowed.\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } msgb_free(msg); return 0; } /* Receive a LAPD I (Information) message from L1 */ static int lapd_rx_i(struct msgb *msg, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; //uint8_t nr = lctx->n_recv; uint8_t ns = lctx->n_send; int length = lctx->length; int rc; LOGP(DLLAPD, LOGL_INFO, "I received in state %s on SAPI(%u)\n", lapd_state_names[dl->state], lctx->sapi); /* G.2.2 Wrong value of the C/R bit */ if (lctx->cr == dl->cr.rem2loc.resp) { LOGP(DLLAPD, LOGL_ERROR, "I frame response not allowed\n"); msgb_free(msg); mdl_error(MDL_CAUSE_FRM_UNIMPL, lctx); return -EINVAL; } if (length == 0 || length > lctx->n201) { /* G.4.2 If the length indicator of an I frame is set * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION * primitive with cause "I frame with incorrect length" * is sent to the mobile management entity. */ LOGP(DLLAPD, LOGL_ERROR, "I frame length not allowed\n"); msgb_free(msg); mdl_error(MDL_CAUSE_IFRM_INC_LEN, lctx); return -EIO; } /* G.4.2 If the numerical value of L is Lmore && length < lctx->n201) { LOGP(DLLAPD, LOGL_ERROR, "I frame with M bit too short\n"); msgb_free(msg); mdl_error(MDL_CAUSE_IFRM_INC_MBITS, lctx); return -EIO; } switch (dl->state) { case LAPD_STATE_IDLE: /* if P=1, respond DM with F=1 (5.2.2) */ /* 5.4.5 all other frame types shall be discarded */ if (lctx->p_f) lapd_send_dm(lctx); /* F=P */ /* fall though */ case LAPD_STATE_SABM_SENT: case LAPD_STATE_DISC_SENT: LOGP(DLLAPD, LOGL_NOTICE, "I frame ignored in this state\n"); msgb_free(msg); return 0; } /* 5.7.1: N(s) sequence error */ if (ns != dl->v_recv) { LOGP(DLLAPD, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, " "V(R)=%u\n", ns, dl->v_recv); /* discard data */ msgb_free(msg); if (dl->seq_err_cond != 1) { /* FIXME: help me understand what exactly todo here */ dl->seq_err_cond = 1; lapd_send_rej(lctx, lctx->p_f); } else { /* If there are two subsequent sequence errors received, * ignore it. (Ignore every second subsequent error.) * This happens if our reply with the REJ is too slow, * so the remote gets a T200 timeout and sends another * frame with a sequence error. * Test showed that replying with two subsequent REJ * messages could the remote L2 process to abort. * Replying too slow shouldn't happen, but may happen * over serial link between BB and LAPD. */ dl->seq_err_cond = 2; } /* Even if N(s) sequence error, acknowledge to N(R)-1 */ /* 5.5.3.1: Acknowlege all transmitted frames up the N(R)-1 */ lapd_acknowledge(lctx); /* V(A) is also set here */ /* Send message, if possible due to acknowledged data */ lapd_send_i(lctx, __LINE__); return 0; } dl->seq_err_cond = 0; /* Increment receiver state */ dl->v_recv = inc_mod(dl->v_recv, dl->v_range); LOGP(DLLAPD, LOGL_INFO, "incrementing V(R) to %u\n", dl->v_recv); /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */ lapd_acknowledge(lctx); /* V(A) is also set here */ /* Only if we are not in own receiver busy condition */ if (!dl->own_busy) { /* if the frame carries a complete segment */ if (!lctx->more && !dl->rcv_buffer) { LOGP(DLLAPD, LOGL_INFO, "message in single I frame\n"); /* send a DATA INDICATION to L3 */ msgb_trim(msg, length); rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx, msg); } else { /* create rcv_buffer */ if (!dl->rcv_buffer) { LOGP(DLLAPD, LOGL_INFO, "message in multiple " "I frames (first message)\n"); dl->rcv_buffer = lapd_msgb_alloc(dl->maxf, "LAPD RX"); dl->rcv_buffer->l3h = dl->rcv_buffer->data; } /* concat. rcv_buffer */ if (msgb_l3len(dl->rcv_buffer) + length > dl->maxf) { LOGP(DLLAPD, LOGL_NOTICE, "Received frame " "overflow!\n"); } else { memcpy(msgb_put(dl->rcv_buffer, length), msg->l3h, length); } /* if the last segment was received */ if (!lctx->more) { LOGP(DLLAPD, LOGL_INFO, "message in multiple " "I frames (last message)\n"); rc = send_dl_l3(PRIM_DL_DATA, PRIM_OP_INDICATION, lctx, dl->rcv_buffer); dl->rcv_buffer = NULL; } else LOGP(DLLAPD, LOGL_INFO, "message in multiple " "I frames (next message)\n"); msgb_free(msg); } } else LOGP(DLLAPD, LOGL_INFO, "I frame ignored during own receiver " "busy condition\n"); /* Check for P bit */ if (lctx->p_f) { /* 5.5.2.1 */ /* check if we are not in own receiver busy */ if (!dl->own_busy) { LOGP(DLLAPD, LOGL_INFO, "we are not busy, send RR\n"); /* Send RR with F=1 */ rc = lapd_send_rr(lctx, 1, 0); } else { LOGP(DLLAPD, LOGL_INFO, "we are busy, send RNR\n"); /* Send RNR with F=1 */ rc = lapd_send_rnr(lctx, 1, 0); } } else { /* 5.5.2.2 */ /* check if we are not in own receiver busy */ if (!dl->own_busy) { /* NOTE: V(R) is already set above */ rc = lapd_send_i(lctx, __LINE__); /* if update_pending_iframe returns 0 it updated * the lapd header of an iframe in the tx queue */ if (rc && dl->update_pending_frames) rc = dl->update_pending_frames(lctx); if (rc) { LOGP(DLLAPD, LOGL_INFO, "we are not busy and " "have no pending data, send RR\n"); /* Send RR with F=0 */ return lapd_send_rr(lctx, 0, 0); } /* all I or one RR is sent, we are done */ return 0; } else { LOGP(DLLAPD, LOGL_INFO, "we are busy, send RNR\n"); /* Send RNR with F=0 */ rc = lapd_send_rnr(lctx, 0, 0); } } /* Send message, if possible due to acknowledged data */ lapd_send_i(lctx, __LINE__); return rc; } /* Receive a LAPD message from L1 */ int lapd_ph_data_ind(struct msgb *msg, struct lapd_msg_ctx *lctx) { int rc; switch (lctx->format) { case LAPD_FORM_U: rc = lapd_rx_u(msg, lctx); break; case LAPD_FORM_S: rc = lapd_rx_s(msg, lctx); break; case LAPD_FORM_I: rc = lapd_rx_i(msg, lctx); break; default: LOGP(DLLAPD, LOGL_NOTICE, "unknown LAPD format\n"); msgb_free(msg); rc = -EINVAL; } return rc; } /* L3 -> L2 */ /* send unit data */ static int lapd_udata_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; struct lapd_msg_ctx nctx; memcpy(&nctx, lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_U; nctx.s_u = LAPD_U_UI; /* keep nctx.p_f */ nctx.length = msg->len; nctx.more = 0; return dl->send_ph_data_req(&nctx, msg); } /* request link establishment */ static int lapd_est_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; struct lapd_msg_ctx nctx; if (msg->len) LOGP(DLLAPD, LOGL_INFO, "perform establishment with content " "(SABM)\n"); else LOGP(DLLAPD, LOGL_INFO, "perform normal establishm. (SABM)\n"); /* Flush send-queue */ /* Clear send-buffer */ lapd_dl_flush_send(dl); /* be sure that history is empty */ lapd_dl_flush_hist(dl); /* save message context for further use */ memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); /* Discard partly received L3 message */ msgb_free(dl->rcv_buffer); dl->rcv_buffer = NULL; /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_U; nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; nctx.p_f = 1; nctx.length = msg->len; nctx.more = 0; /* Transmit-buffer carries exactly one segment */ dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST"); msgb_put(dl->tx_hist[0].msg, msg->len); if (msg->len) memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len); dl->tx_hist[0].more = 0; /* set Vs to 0, because it is used as index when resending SABM */ dl->v_send = 0; /* Set states */ dl->own_busy = dl->peer_busy = 0; dl->retrans_ctr = 0; lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); /* Tramsmit and start T200 */ dl->send_ph_data_req(&nctx, msg); lapd_start_t200(dl); return 0; } /* send data */ static int lapd_data_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; if (msgb_l3len(msg) == 0) { LOGP(DLLAPD, LOGL_ERROR, "writing an empty message is not possible.\n"); msgb_free(msg); return -1; } LOGP(DLLAPD, LOGL_INFO, "writing message to send-queue: l3len: %d\n", msgb_l3len(msg)); /* Write data into the send queue */ msgb_enqueue(&dl->send_queue, msg); /* Send message, if possible */ lapd_send_i(&dl->lctx, __LINE__); return 0; } /* Send next I frame from queued/buffered data */ static int lapd_send_i(struct lapd_msg_ctx *lctx, int line) { struct lapd_datalink *dl = lctx->dl; uint8_t k = dl->k; uint8_t h; struct msgb *msg; int length, left; int rc = - 1; /* we sent nothing */ struct lapd_msg_ctx nctx; LOGP(DLLAPD, LOGL_INFO, "%s() called from line %d\n", __func__, line); next_frame: if (dl->peer_busy) { LOGP(DLLAPD, LOGL_INFO, "peer busy, not sending\n"); return rc; } if (dl->state == LAPD_STATE_TIMER_RECOV) { LOGP(DLLAPD, LOGL_INFO, "timer recovery, not sending\n"); return rc; } /* If the send state variable V(S) is equal to V(A) plus k * (where k is the maximum number of outstanding I frames - see * subclause 5.8.4), the data link layer entity shall not transmit any * new I frames, but shall retransmit an I frame as a result * of the error recovery procedures as described in subclauses 5.5.4 and * 5.5.7. */ if (dl->v_send == add_mod(dl->v_ack, k, dl->v_range)) { LOGP(DLLAPD, LOGL_INFO, "k frames outstanding, not sending " "more (k=%u V(S)=%u V(A)=%u)\n", k, dl->v_send, dl->v_ack); return rc; } h = do_mod(dl->v_send, dl->range_hist); /* if we have no tx_hist yet, we create it */ if (!dl->tx_hist[h].msg) { /* Get next message into send-buffer, if any */ if (!dl->send_buffer) { next_message: dl->send_out = 0; dl->send_buffer = msgb_dequeue(&dl->send_queue); /* No more data to be sent */ if (!dl->send_buffer) return rc; LOGP(DLLAPD, LOGL_INFO, "get message from " "send-queue\n"); } /* How much is left in the send-buffer? */ left = msgb_l3len(dl->send_buffer) - dl->send_out; /* Segment, if data exceeds N201 */ length = left; if (length > lctx->n201) length = lctx->n201; LOGP(DLLAPD, LOGL_INFO, "msg-len %d sent %d left %d N201 %d " "length %d first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, lctx->n201, length, dl->send_buffer->l3h[0]); /* If message in send-buffer is completely sent */ if (left == 0) { msgb_free(dl->send_buffer); dl->send_buffer = NULL; goto next_message; } LOGP(DLLAPD, LOGL_INFO, "send I frame %sV(S)=%d\n", (left > length) ? "segment " : "", dl->v_send); /* Create I frame (segment) and transmit-buffer content */ msg = lapd_msgb_alloc(length, "LAPD I"); msg->l3h = msgb_put(msg, length); /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_I; nctx.p_f = 0; nctx.n_send = dl->v_send; nctx.n_recv = dl->v_recv; nctx.length = length; if (left > length) nctx.more = 1; else nctx.more = 0; if (length) memcpy(msg->l3h, dl->send_buffer->l3h + dl->send_out, length); /* store in tx_hist */ dl->tx_hist[h].msg = lapd_msgb_alloc(msg->len, "HIST"); msgb_put(dl->tx_hist[h].msg, msg->len); if (length) memcpy(dl->tx_hist[h].msg->data, msg->l3h, msg->len); dl->tx_hist[h].more = nctx.more; /* Add length to track how much is already in the tx buffer */ dl->send_out += length; } else { LOGP(DLLAPD, LOGL_INFO, "resend I frame from tx buffer " "V(S)=%d\n", dl->v_send); /* Create I frame (segment) from tx_hist */ length = dl->tx_hist[h].msg->len; msg = lapd_msgb_alloc(length, "LAPD I resend"); msg->l3h = msgb_put(msg, length); /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_I; nctx.p_f = 0; nctx.n_send = dl->v_send; nctx.n_recv = dl->v_recv; nctx.length = length; nctx.more = dl->tx_hist[h].more; if (length) memcpy(msg->l3h, dl->tx_hist[h].msg->data, length); } /* The value of the send state variable V(S) shall be incremented by 1 * at the end of the transmission of the I frame */ dl->v_send = inc_mod(dl->v_send, dl->v_range); /* If timer T200 is not running at the time right before transmitting a * frame, when the PH-READY-TO-SEND primitive is received from the * physical layer., it shall be set. */ if (!osmo_timer_pending(&dl->t200)) { /* stop Timer T203, if running */ lapd_stop_t203(dl); /* start Timer T200 */ lapd_start_t200(dl); } dl->send_ph_data_req(&nctx, msg); rc = 0; /* we sent something */ goto next_frame; } /* request link suspension */ static int lapd_susp_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; LOGP(DLLAPD, LOGL_INFO, "perform suspension\n"); /* put back the send-buffer to the send-queue (first position) */ if (dl->send_buffer) { LOGP(DLLAPD, LOGL_INFO, "put frame in sendbuffer back to " "queue\n"); llist_add(&dl->send_buffer->list, &dl->send_queue); dl->send_buffer = NULL; } else LOGP(DLLAPD, LOGL_INFO, "no frame in sendbuffer\n"); /* Clear transmit buffer, but keep send buffer */ lapd_dl_flush_tx(dl); /* Stop timers (there is no state change, so we must stop all timers */ lapd_stop_t200(dl); lapd_stop_t203(dl); msgb_free(msg); return send_dl_simple(PRIM_DL_SUSP, PRIM_OP_CONFIRM, &dl->lctx); } /* requesst resume or reconnect of link */ static int lapd_res_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; struct lapd_msg_ctx nctx; LOGP(DLLAPD, LOGL_INFO, "perform re-establishment (SABM) length=%d\n", msg->len); /* be sure that history is empty */ lapd_dl_flush_hist(dl); /* save message context for further use */ memcpy(&dl->lctx, lctx, sizeof(dl->lctx)); /* Replace message in the send-buffer (reconnect) */ msgb_free(dl->send_buffer); dl->send_buffer = NULL; dl->send_out = 0; if (msg->len) { /* Write data into the send buffer, to be sent first */ dl->send_buffer = msg; } else { msgb_free(msg); msg = NULL; dl->send_buffer = NULL; } /* Discard partly received L3 message */ msgb_free(dl->rcv_buffer); dl->rcv_buffer = NULL; /* Create new msgb (old one is now free) */ msg = lapd_msgb_alloc(0, "LAPD SABM"); msg->l3h = msg->data; /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_U; nctx.s_u = (dl->use_sabme) ? LAPD_U_SABME : LAPD_U_SABM; nctx.p_f = 1; nctx.length = 0; nctx.more = 0; dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST"); msgb_put(dl->tx_hist[0].msg, msg->len); if (msg->len) memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len); dl->tx_hist[0].more = 0; /* set Vs to 0, because it is used as index when resending SABM */ dl->v_send = 0; /* Set states */ dl->own_busy = dl->peer_busy = 0; dl->retrans_ctr = 0; lapd_dl_newstate(dl, LAPD_STATE_SABM_SENT); /* Tramsmit and start T200 */ dl->send_ph_data_req(&nctx, msg); lapd_start_t200(dl); return 0; } /* requesst release of link */ static int lapd_rel_req(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; struct lapd_msg_ctx nctx; /* local release */ if (dp->u.rel_req.mode) { LOGP(DLLAPD, LOGL_INFO, "perform local release\n"); msgb_free(msg); /* stop Timer T200 */ lapd_stop_t200(dl); /* enter idle state, T203 is stopped here, if running */ lapd_dl_newstate(dl, LAPD_STATE_IDLE); /* flush buffers */ lapd_dl_flush_tx(dl); lapd_dl_flush_send(dl); /* send notification to L3 */ return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); } /* in case we are already disconnecting */ if (dl->state == LAPD_STATE_DISC_SENT) return -EBUSY; /* flush tx_hist */ lapd_dl_flush_hist(dl); LOGP(DLLAPD, LOGL_INFO, "perform normal release (DISC)\n"); /* Push LAPD header on msgb */ /* assemble message */ memcpy(&nctx, &dl->lctx, sizeof(nctx)); /* keep nctx.ldp */ /* keep nctx.sapi */ /* keep nctx.tei */ nctx.cr = dl->cr.loc2rem.cmd; nctx.format = LAPD_FORM_U; nctx.s_u = LAPD_U_DISC; nctx.p_f = 1; nctx.length = 0; nctx.more = 0; dl->tx_hist[0].msg = lapd_msgb_alloc(msg->len, "HIST"); msgb_put(dl->tx_hist[0].msg, msg->len); if (msg->len) memcpy(dl->tx_hist[0].msg->data, msg->l3h, msg->len); dl->tx_hist[0].more = 0; /* set Vs to 0, because it is used as index when resending DISC */ dl->v_send = 0; /* Set states */ dl->own_busy = dl->peer_busy = 0; dl->retrans_ctr = 0; lapd_dl_newstate(dl, LAPD_STATE_DISC_SENT); /* Tramsmit and start T200 */ dl->send_ph_data_req(&nctx, msg); lapd_start_t200(dl); return 0; } /* request release of link in idle state */ static int lapd_rel_req_idle(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg = dp->oph.msg; msgb_free(msg); /* send notification to L3 */ return send_dl_simple(PRIM_DL_REL, PRIM_OP_CONFIRM, &dl->lctx); } /* statefull handling for DL SAP messages from L3 */ static const struct l2downstate { uint32_t states; int prim, op; const char *name; int (*rout) (struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); } l2downstatelist[] = { /* create and send UI command */ {ALL_STATES, PRIM_DL_UNIT_DATA, PRIM_OP_REQUEST, "DL-UNIT-DATA-REQUEST", lapd_udata_req}, /* create and send SABM command */ {SBIT(LAPD_STATE_IDLE), PRIM_DL_EST, PRIM_OP_REQUEST, "DL-ESTABLISH-REQUEST", lapd_est_req}, /* create and send I command */ {SBIT(LAPD_STATE_MF_EST) | SBIT(LAPD_STATE_TIMER_RECOV), PRIM_DL_DATA, PRIM_OP_REQUEST, "DL-DATA-REQUEST", lapd_data_req}, /* suspend datalink */ {SBIT(LAPD_STATE_MF_EST) | SBIT(LAPD_STATE_TIMER_RECOV), PRIM_DL_SUSP, PRIM_OP_REQUEST, "DL-SUSPEND-REQUEST", lapd_susp_req}, /* create and send SABM command (resume) */ {SBIT(LAPD_STATE_MF_EST) | SBIT(LAPD_STATE_TIMER_RECOV), PRIM_DL_RES, PRIM_OP_REQUEST, "DL-RESUME-REQUEST", lapd_res_req}, /* create and send SABM command (reconnect) */ {SBIT(LAPD_STATE_IDLE) | SBIT(LAPD_STATE_MF_EST) | SBIT(LAPD_STATE_TIMER_RECOV), PRIM_DL_RECON, PRIM_OP_REQUEST, "DL-RECONNECT-REQUEST", lapd_res_req}, /* create and send DISC command */ {SBIT(LAPD_STATE_SABM_SENT) | SBIT(LAPD_STATE_MF_EST) | SBIT(LAPD_STATE_TIMER_RECOV) | SBIT(LAPD_STATE_DISC_SENT), PRIM_DL_REL, PRIM_OP_REQUEST, "DL-RELEASE-REQUEST", lapd_rel_req}, /* release in idle state */ {SBIT(LAPD_STATE_IDLE), PRIM_DL_REL, PRIM_OP_REQUEST, "DL-RELEASE-REQUEST", lapd_rel_req_idle}, }; #define L2DOWNSLLEN \ (sizeof(l2downstatelist) / sizeof(struct l2downstate)) int lapd_recv_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; int i, supported = 0; struct msgb *msg = dp->oph.msg; int rc; /* find function for current state and message */ for (i = 0; i < L2DOWNSLLEN; i++) { if (dp->oph.primitive == l2downstatelist[i].prim && dp->oph.operation == l2downstatelist[i].op) { supported = 1; if ((SBIT(dl->state) & l2downstatelist[i].states)) break; } } if (!supported) { LOGP(DLLAPD, LOGL_NOTICE, "Message %u/%u unsupported.\n", dp->oph.primitive, dp->oph.operation); msgb_free(msg); return 0; } if (i == L2DOWNSLLEN) { LOGP(DLLAPD, LOGL_NOTICE, "Message %u/%u unhandled at this " "state %s.\n", dp->oph.primitive, dp->oph.operation, lapd_state_names[dl->state]); msgb_free(msg); return 0; } LOGP(DLLAPD, LOGL_INFO, "Message %s received in state %s\n", l2downstatelist[i].name, lapd_state_names[dl->state]); rc = l2downstatelist[i].rout(dp, lctx); return rc; } /*! @} */ libosmocore-0.9.0/src/gsm/lapdm.c000066400000000000000000001047751261607044000166710ustar00rootroot00000000000000/* GSM LAPDm (TS 04.06) implementation */ /* (C) 2010-2011 by Harald Welte * (C) 2010-2011 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. * */ /*! \addtogroup lapdm * @{ */ /*! \file lapdm.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* TS 04.06 Figure 4 / Section 3.2 */ #define LAPDm_LPD_NORMAL 0 #define LAPDm_LPD_SMSCB 1 #define LAPDm_SAPI_NORMAL 0 #define LAPDm_SAPI_SMS 3 #define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1) #define LAPDm_ADDR_LPD(addr) (((addr) >> 5) & 0x3) #define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7) #define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1) #define LAPDm_ADDR_EA(addr) ((addr) & 0x1) /* TS 04.06 Table 3 / Section 3.4.3 */ #define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1)) #define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1) #define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3) #define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0) #define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1) #define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3) #define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3) #define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1) #define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2) #define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1) #define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5) #define LAPDm_LEN(len) ((len << 2) | 0x1) #define LAPDm_MORE 0x2 #define LAPDm_EL 0x1 #define LAPDm_U_UI 0x0 /* TS 04.06 Section 5.8.3 */ #define N201_AB_SACCH 18 #define N201_AB_SDCCH 20 #define N201_AB_FACCH 20 #define N201_Bbis 23 #define N201_Bter_SACCH 21 #define N201_Bter_SDCCH 23 #define N201_Bter_FACCH 23 #define N201_B4 19 /* 5.8.2.1 N200 during establish and release */ #define N200_EST_REL 5 /* 5.8.2.1 N200 during timer recovery state */ #define N200_TR_SACCH 5 #define N200_TR_SDCCH 23 #define N200_TR_FACCH_FR 34 #define N200_TR_EFACCH_FR 48 #define N200_TR_FACCH_HR 29 /* FIXME: set N200 depending on chan_nr */ #define N200 N200_TR_SDCCH enum lapdm_format { LAPDm_FMT_A, LAPDm_FMT_B, LAPDm_FMT_Bbis, LAPDm_FMT_Bter, LAPDm_FMT_B4, }; static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg); static int send_rslms_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); static int update_pending_frames(struct lapd_msg_ctx *lctx); static void lapdm_dl_init(struct lapdm_datalink *dl, struct lapdm_entity *entity, int t200) { memset(dl, 0, sizeof(*dl)); dl->entity = entity; lapd_dl_init(&dl->dl, 1, 8, 200); dl->dl.reestablish = 0; /* GSM uses no reestablish */ dl->dl.send_ph_data_req = lapdm_send_ph_data_req; dl->dl.send_dlsap = send_rslms_dlsap; dl->dl.update_pending_frames = update_pending_frames; dl->dl.n200_est_rel = N200_EST_REL; dl->dl.n200 = N200; dl->dl.t203_sec = 0; dl->dl.t203_usec = 0; dl->dl.t200_sec = t200; dl->dl.t200_usec = 0; } /*! \brief initialize a LAPDm entity and all datalinks inside * \param[in] le LAPDm entity * \param[in] mode \ref lapdm_mode (BTS/MS) */ void lapdm_entity_init(struct lapdm_entity *le, enum lapdm_mode mode, int t200) { unsigned int i; for (i = 0; i < ARRAY_SIZE(le->datalink); i++) lapdm_dl_init(&le->datalink[i], le, t200); lapdm_entity_set_mode(le, mode); } /*! \brief initialize a LAPDm channel and all its channels * \param[in] lc \ref lapdm_channel to be initialized * \param[in] mode \ref lapdm_mode (BTS/MS) * * This really is a convenience wrapper around calling \ref * lapdm_entity_init twice. */ void lapdm_channel_init(struct lapdm_channel *lc, enum lapdm_mode mode) { lapdm_entity_init(&lc->lapdm_acch, mode, 2); /* FIXME: this depends on chan type */ lapdm_entity_init(&lc->lapdm_dcch, mode, 1); } /*! \brief flush and release all resoures in LAPDm entity */ void lapdm_entity_exit(struct lapdm_entity *le) { unsigned int i; struct lapdm_datalink *dl; for (i = 0; i < ARRAY_SIZE(le->datalink); i++) { dl = &le->datalink[i]; lapd_dl_exit(&dl->dl); } } /* \brief lfush and release all resources in LAPDm channel * * A convenience wrapper calling \ref lapdm_entity_exit on both * entities inside the \ref lapdm_channel */ void lapdm_channel_exit(struct lapdm_channel *lc) { lapdm_entity_exit(&lc->lapdm_acch); lapdm_entity_exit(&lc->lapdm_dcch); } struct lapdm_datalink *lapdm_datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi) { switch (sapi) { case LAPDm_SAPI_NORMAL: return &le->datalink[0]; case LAPDm_SAPI_SMS: return &le->datalink[1]; default: return NULL; } } /* Append padding (if required) */ static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201) { int pad_len = n201 - msgb_l2len(msg); uint8_t *data; if (pad_len < 0) { LOGP(DLLAPD, LOGL_ERROR, "cannot pad message that is already too big!\n"); return; } data = msgb_put(msg, pad_len); memset(data, 0x2B, pad_len); } /* input function that L2 calls when sending messages up to L3 */ static int rslms_sendmsg(struct msgb *msg, struct lapdm_entity *le) { if (!le->l3_cb) { msgb_free(msg); return -EIO; } /* call the layer2 message handler that is registered */ return le->l3_cb(msg, le, le->l3_ctx); } /* write a frame into the tx queue */ static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, uint8_t chan_nr, uint8_t link_id, uint8_t pad) { struct lapdm_entity *le = dl->entity; struct osmo_phsap_prim pp; /* if there is a pending message, queue it */ if (le->tx_pending || le->flags & LAPDM_ENT_F_POLLING_ONLY) { *msgb_push(msg, 1) = pad; *msgb_push(msg, 1) = link_id; *msgb_push(msg, 1) = chan_nr; msgb_enqueue(&dl->dl.tx_queue, msg); return -EBUSY; } osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST, msg); pp.u.data.chan_nr = chan_nr; pp.u.data.link_id = link_id; /* send the frame now */ le->tx_pending = 0; /* disabled flow control */ lapdm_pad_msgb(msg, pad); return le->l1_prim_cb(&pp.oph, le->l1_ctx); } static struct msgb *tx_dequeue_msgb(struct lapdm_entity *le) { struct lapdm_datalink *dl; int last = le->last_tx_dequeue; int i = last, n = ARRAY_SIZE(le->datalink); struct msgb *msg = NULL; /* round-robin dequeue */ do { /* next */ i = (i + 1) % n; dl = &le->datalink[i]; if ((msg = msgb_dequeue(&dl->dl.tx_queue))) break; } while (i != last); if (msg) { /* Set last dequeue position */ le->last_tx_dequeue = i; } return msg; } /*! \brief dequeue a msg that's pending transmission via L1 and wrap it into * a osmo_phsap_prim */ int lapdm_phsap_dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp) { struct msgb *msg; uint8_t pad; msg = tx_dequeue_msgb(le); if (!msg) return -ENODEV; /* if we have a message, send PH-DATA.req */ osmo_prim_init(&pp->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_REQUEST, msg); /* Pull chan_nr and link_id */ pp->u.data.chan_nr = *msg->data; msgb_pull(msg, 1); pp->u.data.link_id = *msg->data; msgb_pull(msg, 1); pad = *msg->data; msgb_pull(msg, 1); /* Pad the frame, we can transmit now */ lapdm_pad_msgb(msg, pad); return 0; } /* get next frame from the tx queue. because the ms has multiple datalinks, * each datalink's queue is read round-robin. */ static int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le) { struct osmo_phsap_prim pp; /* we may send again */ le->tx_pending = 0; /* free confirm message */ if (msg) msgb_free(msg); if (lapdm_phsap_dequeue_prim(le, &pp) < 0) { /* no message in all queues */ /* If user didn't request PH-EMPTY_FRAME.req, abort */ if (!(le->flags & LAPDM_ENT_F_EMPTY_FRAME)) return 0; /* otherwise, send PH-EMPTY_FRAME.req */ osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_EMPTY_FRAME, PRIM_OP_REQUEST, NULL); } else { le->tx_pending = 1; } return le->l1_prim_cb(&pp.oph, le->l1_ctx); } /* Create RSLms various RSLms messages */ static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx, struct msgb *msg) { /* Add the RSL + RLL header */ rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1); /* send off the RSLms message to L3 */ return rslms_sendmsg(msg, mctx->dl->entity); } /* Take a B4 format message from L1 and create RSLms UNIT DATA IND */ static int send_rslms_rll_l3_ui(struct lapdm_msg_ctx *mctx, struct msgb *msg) { uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg); struct abis_rsl_rll_hdr *rllh; /* Add the RSL + RLL header */ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); msgb_push(msg, 2 + 2); rsl_rll_push_hdr(msg, RSL_MT_UNIT_DATA_IND, mctx->chan_nr, mctx->link_id, 1); rllh = (struct abis_rsl_rll_hdr *)msgb_l2(msg); rllh->data[0] = RSL_IE_TIMING_ADVANCE; rllh->data[1] = mctx->ta_ind; rllh->data[2] = RSL_IE_MS_POWER; rllh->data[3] = mctx->tx_power_ind; return rslms_sendmsg(msg, mctx->dl->entity); } static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx) { struct msgb *msg; msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1); /* send off the RSLms message to L3 */ return rslms_sendmsg(msg, mctx->dl->entity); } static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx) { struct msgb *msg; LOGP(DLLAPD, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause); msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1); msgb_tlv_put(msg, RSL_IE_RLM_CAUSE, 1, &cause); return rslms_sendmsg(msg, mctx->dl->entity); } /* DLSAP L2 -> L3 (RSLms) */ static int send_rslms_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct lapdm_datalink *mdl = container_of(dl, struct lapdm_datalink, dl); struct lapdm_msg_ctx *mctx = &mdl->mctx; uint8_t rll_msg = 0; switch (OSMO_PRIM_HDR(&dp->oph)) { case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_INDICATION): if (dp->oph.msg && dp->oph.msg->len == 0) { /* omit L3 info by freeing message */ msgb_free(dp->oph.msg); dp->oph.msg = NULL; } rll_msg = RSL_MT_EST_IND; break; case OSMO_PRIM(PRIM_DL_EST, PRIM_OP_CONFIRM): rll_msg = RSL_MT_EST_CONF; break; case OSMO_PRIM(PRIM_DL_DATA, PRIM_OP_INDICATION): rll_msg = RSL_MT_DATA_IND; break; case OSMO_PRIM(PRIM_DL_UNIT_DATA, PRIM_OP_INDICATION): return send_rslms_rll_l3_ui(mctx, dp->oph.msg); case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_INDICATION): rll_msg = RSL_MT_REL_IND; break; case OSMO_PRIM(PRIM_DL_REL, PRIM_OP_CONFIRM): rll_msg = RSL_MT_REL_CONF; break; case OSMO_PRIM(PRIM_DL_SUSP, PRIM_OP_CONFIRM): rll_msg = RSL_MT_SUSP_CONF; break; case OSMO_PRIM(PRIM_MDL_ERROR, PRIM_OP_INDICATION): rsl_rll_error(dp->u.error_ind.cause, mctx); if (dp->oph.msg) msgb_free(dp->oph.msg); return 0; } if (!rll_msg) { LOGP(DLLAPD, LOGL_ERROR, "Unsupported op %d, prim %d. Please " "fix!\n", dp->oph.primitive, dp->oph.operation); return -EINVAL; } if (!dp->oph.msg) return send_rll_simple(rll_msg, mctx); return send_rslms_rll_l3(rll_msg, mctx, dp->oph.msg); } /* send a data frame to layer 1 */ static int lapdm_send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg) { uint8_t l3_len = msg->tail - msg->data; struct lapd_datalink *dl = lctx->dl; struct lapdm_datalink *mdl = container_of(dl, struct lapdm_datalink, dl); struct lapdm_msg_ctx *mctx = &mdl->mctx; int format = lctx->format; /* prepend l2 header */ msg->l2h = msgb_push(msg, 3); msg->l2h[0] = LAPDm_ADDR(lctx->lpd, lctx->sapi, lctx->cr); /* EA is set here too */ switch (format) { case LAPD_FORM_I: msg->l2h[1] = LAPDm_CTRL_I(lctx->n_recv, lctx->n_send, lctx->p_f); break; case LAPD_FORM_S: msg->l2h[1] = LAPDm_CTRL_S(lctx->n_recv, lctx->s_u, lctx->p_f); break; case LAPD_FORM_U: msg->l2h[1] = LAPDm_CTRL_U(lctx->s_u, lctx->p_f); break; default: msgb_free(msg); return -EINVAL; } msg->l2h[2] = LAPDm_LEN(l3_len); /* EL is set here too */ if (lctx->more) msg->l2h[2] |= LAPDm_MORE; /* add ACCH header with last indicated tx-power and TA */ if ((mctx->link_id & 0x40)) { struct lapdm_entity *le = mdl->entity; msg->l2h = msgb_push(msg, 2); msg->l2h[0] = le->tx_power; msg->l2h[1] = le->ta; } return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, 23); } static int update_pending_frames(struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct msgb *msg; int rc = -1; llist_for_each_entry(msg, &dl->tx_queue, list) { if (LAPDm_CTRL_is_I(msg->l2h[1])) { msg->l2h[1] = LAPDm_CTRL_I(dl->v_recv, LAPDm_CTRL_I_Ns(msg->l2h[1]), LAPDm_CTRL_PF_BIT(msg->l2h[1])); rc = 0; } else if (LAPDm_CTRL_is_S(msg->l2h[1])) { LOGP(DLLAPD, LOGL_ERROR, "Supervisory frame in queue, this shouldn't happen\n"); } } return rc; } /* input into layer2 (from layer 1) */ static int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, uint8_t chan_nr, uint8_t link_id) { uint8_t cbits = chan_nr >> 3; uint8_t sapi; /* we cannot take SAPI from link_id, as L1 has no clue */ struct lapdm_msg_ctx mctx; struct lapd_msg_ctx lctx; int rc = 0; int n201; /* when we reach here, we have a msgb with l2h pointing to the raw * 23byte mac block. The l1h has already been purged. */ memset(&mctx, 0, sizeof(mctx)); mctx.chan_nr = chan_nr; mctx.link_id = link_id; /* check for L1 chan_nr/link_id and determine LAPDm hdr format */ if (cbits == 0x10 || cbits == 0x12) { /* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */ mctx.lapdm_fmt = LAPDm_FMT_Bbis; n201 = N201_Bbis; sapi = 0; } else { if (mctx.link_id & 0x40) { /* It was received from network on SACCH */ /* If UI on SACCH sent by BTS, lapdm_fmt must be B4 */ if (le->mode == LAPDM_MODE_MS && LAPDm_CTRL_is_U(msg->l2h[3]) && LAPDm_CTRL_U_BITS(msg->l2h[3]) == 0) { mctx.lapdm_fmt = LAPDm_FMT_B4; n201 = N201_B4; LOGP(DLLAPD, LOGL_INFO, "fmt=B4\n"); } else { mctx.lapdm_fmt = LAPDm_FMT_B; n201 = N201_AB_SACCH; LOGP(DLLAPD, LOGL_INFO, "fmt=B\n"); } /* SACCH frames have a two-byte L1 header that * OsmocomBB L1 doesn't strip */ mctx.tx_power_ind = msg->l2h[0] & 0x1f; mctx.ta_ind = msg->l2h[1]; msgb_pull(msg, 2); msg->l2h += 2; sapi = (msg->l2h[0] >> 2) & 7; } else { mctx.lapdm_fmt = LAPDm_FMT_B; LOGP(DLLAPD, LOGL_INFO, "fmt=B\n"); n201 = N201_AB_SDCCH; sapi = (msg->l2h[0] >> 2) & 7; } } mctx.dl = lapdm_datalink_for_sapi(le, sapi); /* G.2.1 No action on frames containing an unallocated SAPI. */ if (!mctx.dl) { LOGP(DLLAPD, LOGL_NOTICE, "Received frame for unsupported " "SAPI %d!\n", sapi); msgb_free(msg); return -EIO; } switch (mctx.lapdm_fmt) { case LAPDm_FMT_A: case LAPDm_FMT_B: case LAPDm_FMT_B4: lctx.dl = &mctx.dl->dl; /* obtain SAPI from address field */ mctx.link_id |= LAPDm_ADDR_SAPI(msg->l2h[0]); /* G.2.3 EA bit set to "0" is not allowed in GSM */ if (!LAPDm_ADDR_EA(msg->l2h[0])) { LOGP(DLLAPD, LOGL_NOTICE, "EA bit 0 is not allowed in " "GSM\n"); msgb_free(msg); rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx); return -EINVAL; } /* adress field */ lctx.lpd = LAPDm_ADDR_LPD(msg->l2h[0]); lctx.sapi = LAPDm_ADDR_SAPI(msg->l2h[0]); lctx.cr = LAPDm_ADDR_CR(msg->l2h[0]); /* command field */ if (LAPDm_CTRL_is_I(msg->l2h[1])) { lctx.format = LAPD_FORM_I; lctx.n_send = LAPDm_CTRL_I_Ns(msg->l2h[1]); lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]); } else if (LAPDm_CTRL_is_S(msg->l2h[1])) { lctx.format = LAPD_FORM_S; lctx.n_recv = LAPDm_CTRL_Nr(msg->l2h[1]); lctx.s_u = LAPDm_CTRL_S_BITS(msg->l2h[1]); } else if (LAPDm_CTRL_is_U(msg->l2h[1])) { lctx.format = LAPD_FORM_U; lctx.s_u = LAPDm_CTRL_U_BITS(msg->l2h[1]); } else lctx.format = LAPD_FORM_UKN; lctx.p_f = LAPDm_CTRL_PF_BIT(msg->l2h[1]); if (lctx.sapi != LAPDm_SAPI_NORMAL && lctx.sapi != LAPDm_SAPI_SMS && lctx.format == LAPD_FORM_U && lctx.s_u == LAPDm_U_UI) { /* 5.3.3 UI frames with invalid SAPI values shall be * discarded */ LOGP(DLLAPD, LOGL_INFO, "sapi=%u (discarding)\n", lctx.sapi); msgb_free(msg); return 0; } if (mctx.lapdm_fmt == LAPDm_FMT_B4) { lctx.n201 = n201; lctx.length = n201; lctx.more = 0; msg->l3h = msg->l2h + 2; msgb_pull_to_l3(msg); } else { /* length field */ if (!(msg->l2h[2] & LAPDm_EL)) { /* G.4.1 If the EL bit is set to "0", an * MDL-ERROR-INDICATION primitive with cause * "frame not implemented" is sent to the * mobile management entity. */ LOGP(DLLAPD, LOGL_NOTICE, "we don't support " "multi-octet length\n"); msgb_free(msg); rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, &mctx); return -EINVAL; } lctx.n201 = n201; lctx.length = msg->l2h[2] >> 2; lctx.more = !!(msg->l2h[2] & LAPDm_MORE); msg->l3h = msg->l2h + 3; msgb_pull_to_l3(msg); } /* store context for messages from lapd */ memcpy(&mctx.dl->mctx, &mctx, sizeof(mctx.dl->mctx)); /* send to LAPD */ rc = lapd_ph_data_ind(msg, &lctx); break; case LAPDm_FMT_Bter: /* FIXME */ msgb_free(msg); break; case LAPDm_FMT_Bbis: /* directly pass up to layer3 */ LOGP(DLLAPD, LOGL_INFO, "fmt=Bbis UI\n"); msg->l3h = msg->l2h; msgb_pull_to_l3(msg); rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg); break; default: msgb_free(msg); } return rc; } /* input into layer2 (from layer 1) */ static int l2_ph_rach_ind(struct lapdm_entity *le, uint8_t ra, uint32_t fn, uint8_t acc_delay) { struct abis_rsl_cchan_hdr *ch; struct gsm48_req_ref req_ref; struct gsm_time gt; struct msgb *msg = msgb_alloc_headroom(512, 64, "RSL CHAN RQD"); if (!msg) return -ENOMEM; msg->l2h = msgb_push(msg, sizeof(*ch)); ch = (struct abis_rsl_cchan_hdr *)msg->l2h; rsl_init_cchan_hdr(ch, RSL_MT_CHAN_RQD); ch->chan_nr = RSL_CHAN_RACH; /* generate a RSL CHANNEL REQUIRED message */ gsm_fn2gsmtime(>, fn); req_ref.ra = ra; req_ref.t1 = gt.t1; /* FIXME: modulo? */ req_ref.t2 = gt.t2; req_ref.t3_low = gt.t3 & 7; req_ref.t3_high = gt.t3 >> 3; msgb_tv_fixed_put(msg, RSL_IE_REQ_REFERENCE, 3, (uint8_t *) &req_ref); msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, acc_delay); return rslms_sendmsg(msg, le); } static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr); /*! \brief Receive a PH-SAP primitive from L1 */ int lapdm_phsap_up(struct osmo_prim_hdr *oph, struct lapdm_entity *le) { struct osmo_phsap_prim *pp = (struct osmo_phsap_prim *) oph; int rc = 0; if (oph->sap != SAP_GSM_PH) { LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n", oph->sap); rc = -ENODEV; goto free; } switch (oph->primitive) { case PRIM_PH_DATA: if (oph->operation != PRIM_OP_INDICATION) { LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n", oph->operation); rc = -ENODEV; goto free; } rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr, pp->u.data.link_id); break; case PRIM_PH_RTS: if (oph->operation != PRIM_OP_INDICATION) { LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n", oph->operation); rc = -ENODEV; goto free; } rc = l2_ph_data_conf(oph->msg, le); break; case PRIM_PH_RACH: switch (oph->operation) { case PRIM_OP_INDICATION: rc = l2_ph_rach_ind(le, pp->u.rach_ind.ra, pp->u.rach_ind.fn, pp->u.rach_ind.acc_delay); break; case PRIM_OP_CONFIRM: rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn); break; default: rc = -EIO; goto free; } break; default: LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n", oph->primitive); rc = -EINVAL; goto free; } return rc; free: msgb_free(oph->msg); return rc; } /* L3 -> L2 / RSLMS -> LAPDm */ /* Set LAPDm context for established connection */ static int set_lapdm_context(struct lapdm_datalink *dl, uint8_t chan_nr, uint8_t link_id, int n201, uint8_t sapi) { memset(&dl->mctx, 0, sizeof(dl->mctx)); dl->mctx.dl = dl; dl->mctx.chan_nr = chan_nr; dl->mctx.link_id = link_id; dl->dl.lctx.dl = &dl->dl; dl->dl.lctx.n201 = n201; dl->dl.lctx.sapi = sapi; return 0; } /* L3 requests establishment of data link */ static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t chan_nr = rllh->chan_nr; uint8_t link_id = rllh->link_id; uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ set_lapdm_context(dl, chan_nr, link_id, n201, sapi); rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); /* contention resolution establishment procedure */ if (sapi != 0) { /* According to clause 6, the contention resolution * procedure is only permitted with SAPI value 0 */ LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 but contention" "resolution (discarding)\n"); msgb_free(msg); return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); } /* transmit a SABM command with the P bit set to "1". The SABM * command shall contain the layer 3 message unit */ length = TLVP_LEN(&tv, RSL_IE_L3_INFO); } else { /* normal establishment procedure */ msg->l3h = msg->l2h + sizeof(*rllh); length = 0; } /* check if the layer3 message length exceeds N201 */ if (length > n201) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " "(discarding)\n", length, n201); msgb_free(msg); return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); } /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_to_l3(msg); msgb_trim(msg, length); /* prepare prim */ osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &dl->dl.lctx); } /* L3 requests transfer of unnumbered information */ static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) { struct lapdm_entity *le = dl->entity; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t chan_nr = rllh->chan_nr; uint8_t link_id = rllh->link_id; int ui_bts = (le->mode == LAPDM_MODE_BTS && (link_id & 0x40)); uint8_t sapi = link_id & 7; struct tlv_parsed tv; int length; /* check if the layer3 message length exceeds N201 */ rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); if (TLVP_PRESENT(&tv, RSL_IE_TIMING_ADVANCE)) { le->ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); } if (TLVP_PRESENT(&tv, RSL_IE_MS_POWER)) { le->tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); } if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { LOGP(DLLAPD, LOGL_ERROR, "unit data request without message " "error\n"); msgb_free(msg); return -EINVAL; } msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); length = TLVP_LEN(&tv, RSL_IE_L3_INFO); /* check if the layer3 message length exceeds N201 */ if (length + ((link_id & 0x40) ? 4 : 2) + !ui_bts > 23) { LOGP(DLLAPD, LOGL_ERROR, "frame too large: %d > N201(%d) " "(discarding)\n", length, ((link_id & 0x40) ? 18 : 20) + ui_bts); msgb_free(msg); return -EIO; } LOGP(DLLAPD, LOGL_INFO, "sending unit data (tx_power=%d, ta=%d)\n", le->tx_power, le->ta); /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_to_l3(msg); msgb_trim(msg, length); /* Push L1 + LAPDm header on msgb */ msg->l2h = msgb_push(msg, 2 + !ui_bts); msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, dl->dl.cr.loc2rem.cmd); msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0); if (!ui_bts) msg->l2h[2] = LAPDm_LEN(length); if (link_id & 0x40) { msg->l2h = msgb_push(msg, 2); msg->l2h[0] = le->tx_power; msg->l2h[1] = le->ta; } /* Tramsmit */ return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, 23); } /* L3 requests transfer of acknowledged information */ static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct tlv_parsed tv; int length; struct osmo_dlsap_prim dp; rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { LOGP(DLLAPD, LOGL_ERROR, "data request without message " "error\n"); msgb_free(msg); return -EINVAL; } msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); length = TLVP_LEN(&tv, RSL_IE_L3_INFO); /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_to_l3(msg); msgb_trim(msg, length); /* prepare prim */ osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &dl->dl.lctx); } /* L3 requests suspension of data link */ static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t sapi = rllh->link_id & 7; struct osmo_dlsap_prim dp; if (sapi != 0) { LOGP(DLLAPD, LOGL_ERROR, "SAPI != 0 while suspending\n"); msgb_free(msg); return -EINVAL; } /* prepare prim */ osmo_prim_init(&dp.oph, 0, PRIM_DL_SUSP, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &dl->dl.lctx); } /* L3 requests resume of data link */ static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); int msg_type = rllh->c.msg_type; uint8_t chan_nr = rllh->chan_nr; uint8_t link_id = rllh->link_id; uint8_t sapi = rllh->link_id & 7; struct tlv_parsed tv; uint8_t length; uint8_t n201 = (rllh->link_id & 0x40) ? N201_AB_SACCH : N201_AB_SDCCH; struct osmo_dlsap_prim dp; /* Set LAPDm context for established connection */ set_lapdm_context(dl, chan_nr, link_id, n201, sapi); rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { LOGP(DLLAPD, LOGL_ERROR, "resume without message error\n"); msgb_free(msg); return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); } msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); length = TLVP_LEN(&tv, RSL_IE_L3_INFO); /* Remove RLL header from msgb and set length to L3-info */ msgb_pull_to_l3(msg); msgb_trim(msg, length); /* prepare prim */ osmo_prim_init(&dp.oph, 0, (msg_type == RSL_MT_RES_REQ) ? PRIM_DL_RES : PRIM_DL_RECON, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &dl->dl.lctx); } /* L3 requests release of data link */ static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t mode = 0; struct osmo_dlsap_prim dp; /* get release mode */ if (rllh->data[0] == RSL_IE_RELEASE_MODE) mode = rllh->data[1] & 1; /* Pull rllh */ msgb_pull_to_l3(msg); /* 04.06 3.8.3: No information field is permitted with the DISC * command. */ msgb_trim(msg, 0); /* prepare prim */ osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg); dp.u.rel_req.mode = mode; /* send to L2 */ return lapd_recv_dlsap(&dp, &dl->dl.lctx); } /* L3 requests channel in idle state */ static int rslms_rx_chan_rqd(struct lapdm_channel *lc, struct msgb *msg) { struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); void *l1ctx = lc->lapdm_dcch.l1_ctx; struct osmo_phsap_prim pp; osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_REQUEST, NULL); if (msgb_l2len(msg) < sizeof(*cch) + 4 + 2 + 2) { LOGP(DLLAPD, LOGL_ERROR, "Message too short for CHAN RQD!\n"); return -EINVAL; } if (cch->data[0] != RSL_IE_REQ_REFERENCE) { LOGP(DLLAPD, LOGL_ERROR, "Missing REQ REFERENCE IE\n"); return -EINVAL; } pp.u.rach_req.ra = cch->data[1]; pp.u.rach_req.offset = ((cch->data[2] & 0x7f) << 8) | cch->data[3]; pp.u.rach_req.is_combined_ccch = cch->data[2] >> 7; if (cch->data[4] != RSL_IE_ACCESS_DELAY) { LOGP(DLLAPD, LOGL_ERROR, "Missing ACCESS_DELAY IE\n"); return -EINVAL; } /* TA = 0 - delay */ pp.u.rach_req.ta = 0 - cch->data[5]; if (cch->data[6] != RSL_IE_MS_POWER) { LOGP(DLLAPD, LOGL_ERROR, "Missing MS POWER IE\n"); return -EINVAL; } pp.u.rach_req.tx_power = cch->data[7]; msgb_free(msg); return lc->lapdm_dcch.l1_prim_cb(&pp.oph, l1ctx); } /* L1 confirms channel request */ static int l2_ph_chan_conf(struct msgb *msg, struct lapdm_entity *le, uint32_t frame_nr) { struct abis_rsl_cchan_hdr *ch; struct gsm_time tm; struct gsm48_req_ref *ref; gsm_fn2gsmtime(&tm, frame_nr); msgb_pull_to_l3(msg); msg->l2h = msgb_push(msg, sizeof(*ch) + sizeof(*ref)); ch = (struct abis_rsl_cchan_hdr *)msg->l2h; rsl_init_cchan_hdr(ch, RSL_MT_CHAN_CONF); ch->chan_nr = RSL_CHAN_RACH; ch->data[0] = RSL_IE_REQ_REFERENCE; ref = (struct gsm48_req_ref *) (ch->data + 1); ref->t1 = tm.t1; ref->t2 = tm.t2; ref->t3_low = tm.t3 & 0x7; ref->t3_high = tm.t3 >> 3; return rslms_sendmsg(msg, le); } /* incoming RSLms RLL message from L3 */ static int rslms_rx_rll(struct msgb *msg, struct lapdm_channel *lc) { struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); int msg_type = rllh->c.msg_type; uint8_t sapi = rllh->link_id & 7; struct lapdm_entity *le; struct lapdm_datalink *dl; int rc = 0; if (msgb_l2len(msg) < sizeof(*rllh)) { LOGP(DLLAPD, LOGL_ERROR, "Message too short for RLL hdr!\n"); msgb_free(msg); return -EINVAL; } if (rllh->link_id & 0x40) le = &lc->lapdm_acch; else le = &lc->lapdm_dcch; /* G.2.1 No action shall be taken on frames containing an unallocated * SAPI. */ dl = lapdm_datalink_for_sapi(le, sapi); if (!dl) { LOGP(DLLAPD, LOGL_ERROR, "No instance for SAPI %d!\n", sapi); msgb_free(msg); return -EINVAL; } switch (msg_type) { case RSL_MT_DATA_REQ: case RSL_MT_SUSP_REQ: case RSL_MT_REL_REQ: /* This is triggered in abnormal error conditions where * set_lapdm_context() was not called for the channel earlier. */ if (!dl->dl.lctx.dl) { LOGP(DLLAPD, LOGL_NOTICE, "(%p) RLL Message '%s' received without LAPDm context. (sapi %d)\n", lc->name, rsl_msg_name(msg_type), sapi); msgb_free(msg); return -EINVAL; } break; default: LOGP(DLLAPD, LOGL_INFO, "(%p) RLL Message '%s' received. (sapi %d)\n", lc->name, rsl_msg_name(msg_type), sapi); } switch (msg_type) { case RSL_MT_UNIT_DATA_REQ: rc = rslms_rx_rll_udata_req(msg, dl); break; case RSL_MT_EST_REQ: rc = rslms_rx_rll_est_req(msg, dl); break; case RSL_MT_DATA_REQ: rc = rslms_rx_rll_data_req(msg, dl); break; case RSL_MT_SUSP_REQ: rc = rslms_rx_rll_susp_req(msg, dl); break; case RSL_MT_RES_REQ: rc = rslms_rx_rll_res_req(msg, dl); break; case RSL_MT_RECON_REQ: rc = rslms_rx_rll_res_req(msg, dl); break; case RSL_MT_REL_REQ: rc = rslms_rx_rll_rel_req(msg, dl); break; default: LOGP(DLLAPD, LOGL_NOTICE, "Message unsupported.\n"); msgb_free(msg); rc = -EINVAL; } return rc; } /* incoming RSLms COMMON CHANNEL message from L3 */ static int rslms_rx_com_chan(struct msgb *msg, struct lapdm_channel *lc) { struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); int msg_type = cch->c.msg_type; int rc = 0; if (msgb_l2len(msg) < sizeof(*cch)) { LOGP(DLLAPD, LOGL_ERROR, "Message too short for COM CHAN hdr!\n"); return -EINVAL; } switch (msg_type) { case RSL_MT_CHAN_RQD: /* create and send RACH request */ rc = rslms_rx_chan_rqd(lc, msg); break; default: LOGP(DLLAPD, LOGL_NOTICE, "Unknown COMMON CHANNEL msg %d!\n", msg_type); msgb_free(msg); return 0; } return rc; } /*! \brief Receive a RSLms \ref msgb from Layer 3 */ int lapdm_rslms_recvmsg(struct msgb *msg, struct lapdm_channel *lc) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); int rc = 0; if (msgb_l2len(msg) < sizeof(*rslh)) { LOGP(DLLAPD, LOGL_ERROR, "Message too short RSL hdr!\n"); return -EINVAL; } switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = rslms_rx_rll(msg, lc); break; case ABIS_RSL_MDISC_COM_CHAN: rc = rslms_rx_com_chan(msg, lc); break; default: LOGP(DLLAPD, LOGL_ERROR, "unknown RSLms message " "discriminator 0x%02x", rslh->msg_discr); msgb_free(msg); return -EINVAL; } return rc; } /*! \brief Set the \ref lapdm_mode of a LAPDm entity */ int lapdm_entity_set_mode(struct lapdm_entity *le, enum lapdm_mode mode) { int i; enum lapd_mode lm; switch (mode) { case LAPDM_MODE_MS: lm = LAPD_MODE_USER; break; case LAPDM_MODE_BTS: lm = LAPD_MODE_NETWORK; break; default: return -EINVAL; } for (i = 0; i < ARRAY_SIZE(le->datalink); i++) { lapd_set_mode(&le->datalink[i].dl, lm); } le->mode = mode; return 0; } /*! \brief Set the \ref lapdm_mode of a LAPDm channel*/ int lapdm_channel_set_mode(struct lapdm_channel *lc, enum lapdm_mode mode) { int rc; rc = lapdm_entity_set_mode(&lc->lapdm_dcch, mode); if (rc < 0) return rc; return lapdm_entity_set_mode(&lc->lapdm_acch, mode); } /*! \brief Set the L1 callback and context of a LAPDm channel */ void lapdm_channel_set_l1(struct lapdm_channel *lc, osmo_prim_cb cb, void *ctx) { lc->lapdm_dcch.l1_prim_cb = cb; lc->lapdm_acch.l1_prim_cb = cb; lc->lapdm_dcch.l1_ctx = ctx; lc->lapdm_acch.l1_ctx = ctx; } /*! \brief Set the L3 callback and context of a LAPDm channel */ void lapdm_channel_set_l3(struct lapdm_channel *lc, lapdm_cb_t cb, void *ctx) { lc->lapdm_dcch.l3_cb = cb; lc->lapdm_acch.l3_cb = cb; lc->lapdm_dcch.l3_ctx = ctx; lc->lapdm_acch.l3_ctx = ctx; } /*! \brief Reset an entire LAPDm entity and all its datalinks */ void lapdm_entity_reset(struct lapdm_entity *le) { struct lapdm_datalink *dl; int i; for (i = 0; i < ARRAY_SIZE(le->datalink); i++) { dl = &le->datalink[i]; lapd_dl_reset(&dl->dl); } } /*! \brief Reset a LAPDm channel with all its entities */ void lapdm_channel_reset(struct lapdm_channel *lc) { lapdm_entity_reset(&lc->lapdm_dcch); lapdm_entity_reset(&lc->lapdm_acch); } /*! \brief Set the flags of a LAPDm entity */ void lapdm_entity_set_flags(struct lapdm_entity *le, unsigned int flags) { le->flags = flags; } /*! \brief Set the flags of all LAPDm entities in a LAPDm channel */ void lapdm_channel_set_flags(struct lapdm_channel *lc, unsigned int flags) { lapdm_entity_set_flags(&lc->lapdm_dcch, flags); lapdm_entity_set_flags(&lc->lapdm_acch, flags); } /*! @} */ libosmocore-0.9.0/src/gsm/libosmogsm.map000066400000000000000000000115371261607044000202730ustar00rootroot00000000000000LIBOSMOGSM_1.0 { global: abis_nm_adm_state_names; abis_nm_att_settable; abis_nm_avail_name; abis_nm_chcomb4pchan; abis_nm_debugp_foh; abis_nm_event_type_name; abis_nm_nack_cause_name; abis_nm_nack_name; abis_nm_att_tlvdef; abis_nm_osmo_att_tlvdef; abis_nm_msg_disc_names; abis_nm_obj_class_names; abis_nm_opstate_name; abis_nm_nacks; abis_nm_no_ack_nack; abis_nm_pchan4chcomb; abis_nm_reports; abis_nm_severity_name; abis_nm_sw_load_msgs; abis_nm_test_name; abis_nm_osmo_magic; abis_nm_ipa_magic; osmo_sitype_strs; comp128; dbm2rxlev; gprs_cipher_gen_input_i; gprs_cipher_gen_input_ui; gprs_cipher_load; gprs_cipher_register; gprs_cipher_run; gprs_cipher_supported; gprs_tlli_type; gprs_tmsi2tlli; gsm0341_build_msg; gsm0480_create_notifySS; gsm0480_create_unstructuredSS_Notify; gsm0480_create_ussd_resp; gsm0480_decode_ussd_request; gsm0480_decode_ss_request; gsm0480_wrap_facility; gsm0480_wrap_invoke; gsm0502_calc_paging_group; gsm0808_att_tlvdef; gsm0808_bssap_name; gsm0808_bssmap_name; gsm0808_create_assignment_completed; gsm0808_create_assignment_failure; gsm0808_create_cipher_complete; gsm0808_create_cipher_reject; gsm0808_create_classmark_update; gsm0808_create_clear_command; gsm0808_create_clear_complete; gsm0808_create_clear_rqst; gsm0808_create_dtap; gsm0808_create_layer3; gsm0808_create_reset; gsm0808_create_sapi_reject; gsm0808_prepend_dtap_header; gsm338_get_sms_alphabet; gsm340_gen_oa; gsm340_gen_scts; gsm340_scts; gsm340_validity_period; gsm411_bcdify; gsm411_msgb_alloc; gsm411_push_cp_header; gsm411_push_rp_header; gsm411_smc_clear; gsm411_smc_init; gsm411_smc_recv; gsm411_smc_send; gsm411_smr_clear; gsm411_smr_init; gsm411_smr_recv; gsm411_smr_send; gsm411_unbcdify; gsm411_cp_cause_strs; gsm411_rp_cause_strs; gsm48_att_tlvdef; gsm48_cc_msg_name; gsm48_cc_state_name; gsm48_construct_ra; gsm48_decode_bcd_number; gsm48_decode_bearer_cap; gsm48_decode_called; gsm48_decode_callerid; gsm48_decode_calling; gsm48_decode_cause; gsm48_decode_cccap; gsm48_decode_connected; gsm48_decode_facility; gsm48_decode_freq_list; gsm48_decode_keypad; gsm48_decode_lai; gsm48_decode_notify; gsm48_decode_progress; gsm48_decode_redirecting; gsm48_decode_signal; gsm48_decode_ssversion; gsm48_decode_useruser; gsm48_encode_bcd_number; gsm48_encode_bearer_cap; gsm48_encode_called; gsm48_encode_callerid; gsm48_encode_calling; gsm48_encode_cause; gsm48_encode_cccap; gsm48_encode_connected; gsm48_encode_facility; gsm48_encode_keypad; gsm48_encode_more; gsm48_encode_notify; gsm48_encode_progress; gsm48_encode_redirecting; gsm48_encode_signal; gsm48_encode_ssversion; gsm48_encode_useruser; gsm48_generate_lai; gsm48_generate_mid_from_imsi; gsm48_generate_mid_from_tmsi; gsm48_mi_to_string; gsm48_mm_att_tlvdef; gsm48_number_of_paging_subchannels; gsm48_parse_ra; gsm48_rr_att_tlvdef; gsm48_mi_type_name; gsm_7bit_decode; gsm_7bit_decode_ussd; gsm_7bit_encode; gsm_7bit_encode_ussd; gsm_7bit_encode_oct; gsm_7bit_decode_n; gsm_7bit_decode_n_ussd; gsm_7bit_decode_n_hdr; gsm_7bit_encode_n; gsm_7bit_encode_n_ussd; gsm_arfcn2band; gsm_arfcn2freq10; gsm_freq102arfcn; gsm_band_name; gsm_band_parse; gsm_fn2gsmtime; gsm_get_octet_len; gsm_gsmtime2fn; gsm_milenage; gsm_septet_encode; gsm_septets2octets; lapd_dl_exit; lapd_dl_init; lapd_dl_reset; lapd_msgb_alloc; lapd_ph_data_ind; lapd_recv_dlsap; lapd_set_mode; lapd_state_names; lapdm_channel_exit; lapdm_channel_init; lapdm_channel_reset; lapdm_channel_set_flags; lapdm_channel_set_l1; lapdm_channel_set_l3; lapdm_channel_set_mode; lapdm_datalink_for_sapi; lapdm_entity_exit; lapdm_entity_init; lapdm_entity_reset; lapdm_entity_set_flags; lapdm_entity_set_mode; lapdm_phsap_dequeue_prim; lapdm_phsap_up; lapdm_rslms_recvmsg; milenage_auts; milenage_check; milenage_f1; milenage_f2345; milenage_generate; milenage_opc_gen; ms_class_gmsk_dbm; ms_pwr_ctl_lvl; ms_pwr_dbm; osmo_a5; osmo_a5_1; osmo_a5_2; osmo_auth_alg_name; osmo_auth_alg_parse; osmo_auth_gen_vec; osmo_auth_gen_vec_auts; osmo_auth_load; osmo_auth_register; osmo_auth_supported; osmo_rsl2sitype; osmo_sitype2rsl; rr_cause_name; rsl_att_tlvdef; rsl_ccch_conf_to_bs_cc_chans; rsl_ccch_conf_to_bs_ccch_sdcch_comb; rsl_chan_nr_str; rsl_dec_chan_nr; rsl_enc_chan_nr; rsl_err_name; rsl_init_cchan_hdr; rsl_init_rll_hdr; rsl_ipac_msg_name; rsl_msg_name; rsl_rll_push_hdr; rsl_rll_push_l3; rsl_rll_simple; rsl_rlm_cause_name; rxlev2dbm; rxlev_stat_dump; rxlev_stat_get_next; rxlev_stat_input; rxlev_stat_reset; tlv_def_patch; tlv_dump; tlv_parse; tlv_parse_one; tvlv_att_def; vtvlv_gan_att_def; gan_msgt_vals; gan_pdisc_vals; ipa_ccm_rcvmsg_base; ipa_ccm_rcvmsg_bts_base; ipa_ccm_send_id_ack; ipa_ccm_send_id_req; ipa_ccm_send_pong; ipa_ccm_tlv_to_unitdata; ipa_ccm_idtag_name; ipa_ccm_idtag_parse; ipa_ccm_idtag_parse_off; ipa_msg_alloc; ipa_msg_recv; ipa_msg_recv_buffered; ipa_parse_unitid; ipa_prepend_header; ipa_prepend_header_ext; ipa_send; osmo_apn_qualify; osmo_apn_qualify_from_imsi; local: *; }; libosmocore-0.9.0/src/gsm/milenage/000077500000000000000000000000001261607044000171735ustar00rootroot00000000000000libosmocore-0.9.0/src/gsm/milenage/aes-encblock.c000066400000000000000000000016331261607044000216700ustar00rootroot00000000000000/* * AES encrypt_block * * Copyright (c) 2003-2007, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include "common.h" #include "aes.h" #include "aes_wrap.h" /** * aes_128_encrypt_block - Perform one AES 128-bit block operation * @key: Key for AES * @in: Input data (16 bytes) * @out: Output of the AES block operation (16 bytes) * Returns: 0 on success, -1 on failure */ int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) { void *ctx; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; aes_encrypt(ctx, in, out); aes_encrypt_deinit(ctx); return 0; } libosmocore-0.9.0/src/gsm/milenage/aes-internal-enc.c000066400000000000000000000052461261607044000224730ustar00rootroot00000000000000/* * AES (Rijndael) cipher - encrypt * * Modifications to public domain implementation: * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2005, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" #include "aes_i.h" static void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(pt ) ^ rk[0]; s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] #ifdef FULL_UNROLL ROUND(1,t,s); ROUND(2,s,t); ROUND(3,t,s); ROUND(4,s,t); ROUND(5,t,s); ROUND(6,s,t); ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); rk += Nr << 2; #else /* !FULL_UNROLL */ /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1,t,s); rk += 8; if (--r == 0) break; ROUND(0,s,t); } #endif /* ?FULL_UNROLL */ #undef ROUND /* * apply last round and * map cipher state to byte array block: */ s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUTU32(ct , s0); s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUTU32(ct + 4, s1); s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUTU32(ct + 8, s2); s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; if (len != 16) return NULL; rk = os_malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; rijndaelKeySetupEnc(rk, key); return rk; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { rijndaelEncrypt(ctx, plain, crypt); } void aes_encrypt_deinit(void *ctx) { os_memset(ctx, 0, AES_PRIV_SIZE); os_free(ctx); } libosmocore-0.9.0/src/gsm/milenage/aes-internal.c000066400000000000000000001207661261607044000217350ustar00rootroot00000000000000/* * AES (Rijndael) cipher * * Modifications to public domain implementation: * - support only 128-bit keys * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2005, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" #include "aes_i.h" /* * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto * * This code is hereby placed in the public domain. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Te0[x] = S [x].[02, 01, 01, 03]; Te1[x] = S [x].[03, 02, 01, 01]; Te2[x] = S [x].[01, 03, 02, 01]; Te3[x] = S [x].[01, 01, 03, 02]; Te4[x] = S [x].[01, 01, 01, 01]; Td0[x] = Si[x].[0e, 09, 0d, 0b]; Td1[x] = Si[x].[0b, 0e, 09, 0d]; Td2[x] = Si[x].[0d, 0b, 0e, 09]; Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; #ifndef AES_SMALL_TABLES const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; #endif /* AES_SMALL_TABLES */ const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; #ifndef AES_SMALL_TABLES const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #else /* AES_SMALL_TABLES */ const u8 Td4s[256] = { 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, }; const u8 rcons[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #endif /* AES_SMALL_TABLES */ /** * Expand the cipher key into the encryption key schedule. * * @return the number of rounds for the given cipher key size. */ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) { int i; u32 temp; rk[0] = GETU32(cipherKey ); rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; rk += 4; } } libosmocore-0.9.0/src/gsm/milenage/aes.h000066400000000000000000000013361261607044000201170ustar00rootroot00000000000000/* * AES functions * Copyright (c) 2003-2006, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #pragma once #define AES_BLOCK_SIZE 16 void * aes_encrypt_init(const u8 *key, size_t len); void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); void * aes_decrypt_init(const u8 *key, size_t len); void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); libosmocore-0.9.0/src/gsm/milenage/aes_i.h000066400000000000000000000075131261607044000204320ustar00rootroot00000000000000/* * AES (Rijndael) cipher * Copyright (c) 2003-2005, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #pragma once #include "aes.h" /* #define FULL_UNROLL */ #define AES_SMALL_TABLES extern const u32 Te0[256]; extern const u32 Te1[256]; extern const u32 Te2[256]; extern const u32 Te3[256]; extern const u32 Te4[256]; extern const u32 Td0[256]; extern const u32 Td1[256]; extern const u32 Td2[256]; extern const u32 Td3[256]; extern const u32 Td4[256]; extern const u32 rcon[10]; extern const u8 Td4s[256]; extern const u8 rcons[10]; #ifndef AES_SMALL_TABLES #define RCON(i) rcon[(i)] #define TE0(i) Te0[((i) >> 24) & 0xff] #define TE1(i) Te1[((i) >> 16) & 0xff] #define TE2(i) Te2[((i) >> 8) & 0xff] #define TE3(i) Te3[(i) & 0xff] #define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) #define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) #define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) #define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) #define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) #define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) #define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) #define TE4(i) (Te4[(i)] & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] #define TD1(i) Td1[((i) >> 16) & 0xff] #define TD2(i) Td2[((i) >> 8) & 0xff] #define TD3(i) Td3[(i) & 0xff] #define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) #define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) #define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) #define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) Td1[(i) & 0xff] #define TD2_(i) Td2[(i) & 0xff] #define TD3_(i) Td3[(i) & 0xff] #else /* AES_SMALL_TABLES */ #define RCON(i) (rcons[(i)] << 24) static inline u32 rotr(u32 val, int bits) { return (val >> bits) | (val << (32 - bits)); } #define TE0(i) Te0[((i) >> 24) & 0xff] #define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) #define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) #define TE3(i) rotr(Te0[(i) & 0xff], 24) #define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) #define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) #define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) #define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] #define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) #define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) #define TD3(i) rotr(Td0[(i) & 0xff], 24) #define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) #define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) #define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) #define TD44(i) (Td4s[(i) & 0xff]) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) rotr(Td0[(i) & 0xff], 8) #define TD2_(i) rotr(Td0[(i) & 0xff], 16) #define TD3_(i) rotr(Td0[(i) & 0xff], 24) #endif /* AES_SMALL_TABLES */ #ifdef _MSC_VER #define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) #define GETU32(p) SWAP(*((u32 *)(p))) #define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } #else #define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) #define PUTU32(ct, st) { \ (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } #endif #define AES_PRIV_SIZE (4 * 44) void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]); libosmocore-0.9.0/src/gsm/milenage/aes_wrap.h000066400000000000000000000033601261607044000211470ustar00rootroot00000000000000/* * AES-based functions * * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * * Copyright (c) 2003-2007, Jouni Malinen * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #pragma once int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len); int __must_check aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag); int __must_check aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag); int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len); libosmocore-0.9.0/src/gsm/milenage/common.h000066400000000000000000000006651261607044000206430ustar00rootroot00000000000000 #include #include #include #define MSG_DEBUG #define wpa_hexdump(x, args...) #define wpa_hexdump_key(x, args...) #define wpa_printf(x, args...) #define os_memcpy(x, y, z) memcpy(x, y, z) #define os_memcmp(x, y, z) memcmp(x, y, z) #define os_memset(x, y, z) memset(x, y, z) #define os_malloc(x) malloc(x) #define os_free(x) free(x) typedef uint8_t u8; typedef uint32_t u32; #define __must_check libosmocore-0.9.0/src/gsm/milenage/crypto.h000066400000000000000000000000001261607044000206520ustar00rootroot00000000000000libosmocore-0.9.0/src/gsm/milenage/includes.h000066400000000000000000000000001261607044000211400ustar00rootroot00000000000000libosmocore-0.9.0/src/gsm/milenage/milenage.c000066400000000000000000000237001261607044000211220ustar00rootroot00000000000000/* * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * * This file implements an example authentication algorithm defined for 3GPP * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow * EAP-AKA to be tested properly with real USIM cards. * * This implementations assumes that the r1..r5 and c1..c5 constants defined in * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to * be AES (Rijndael). */ #include "includes.h" #include "common.h" #include "aes_wrap.h" #include "milenage.h" /** * milenage_f1 - Milenage f1 and f1* algorithms * @opc: OPc = 128-bit value derived from OP and K * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @sqn: SQN = 48-bit sequence number * @amf: AMF = 16-bit authentication management field * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL * Returns: 0 on success, -1 on failure */ int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; if (aes_128_encrypt_block(k, tmp1, tmp1)) return -1; /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ os_memcpy(tmp2, sqn, 6); os_memcpy(tmp2 + 6, amf, 2); os_memcpy(tmp2 + 8, tmp2, 8); /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ for (i = 0; i < 16; i++) tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; /* XOR with TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp3[i] ^= tmp1[i]; /* XOR with c1 (= ..00, i.e., NOP) */ /* f1 || f1* = E_K(tmp3) XOR OP_c */ if (aes_128_encrypt_block(k, tmp3, tmp1)) return -1; for (i = 0; i < 16; i++) tmp1[i] ^= opc[i]; if (mac_a) os_memcpy(mac_a, tmp1, 8); /* f1 */ if (mac_s) os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ return 0; } /** * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms * @opc: OPc = 128-bit value derived from OP and K * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL * Returns: 0 on success, -1 on failure */ int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) { u8 tmp1[16], tmp2[16], tmp3[16]; int i; /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ for (i = 0; i < 16; i++) tmp1[i] = _rand[i] ^ opc[i]; if (aes_128_encrypt_block(k, tmp1, tmp2)) return -1; /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ /* f2 and f5 */ /* rotate by r2 (= 0, i.e., NOP) */ for (i = 0; i < 16; i++) tmp1[i] = tmp2[i] ^ opc[i]; tmp1[15] ^= 1; /* XOR c2 (= ..01) */ /* f5 || f2 = E_K(tmp1) XOR OP_c */ if (aes_128_encrypt_block(k, tmp1, tmp3)) return -1; for (i = 0; i < 16; i++) tmp3[i] ^= opc[i]; if (res) os_memcpy(res, tmp3 + 8, 8); /* f2 */ if (ak) os_memcpy(ak, tmp3, 6); /* f5 */ /* f3 */ if (ck) { /* rotate by r3 = 0x20 = 4 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 2; /* XOR c3 (= ..02) */ if (aes_128_encrypt_block(k, tmp1, ck)) return -1; for (i = 0; i < 16; i++) ck[i] ^= opc[i]; } /* f4 */ if (ik) { /* rotate by r4 = 0x40 = 8 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 4; /* XOR c4 (= ..04) */ if (aes_128_encrypt_block(k, tmp1, ik)) return -1; for (i = 0; i < 16; i++) ik[i] ^= opc[i]; } /* f5* */ if (akstar) { /* rotate by r5 = 0x60 = 12 bytes */ for (i = 0; i < 16; i++) tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; tmp1[15] ^= 8; /* XOR c5 (= ..08) */ if (aes_128_encrypt_block(k, tmp1, tmp1)) return -1; for (i = 0; i < 6; i++) akstar[i] = tmp1[i] ^ opc[i]; } return 0; } /** * milenage_generate - Generate AKA AUTN,IK,CK,RES * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @amf: AMF = 16-bit authentication management field * @k: K = 128-bit subscriber key * @sqn: SQN = 48-bit sequence number * @_rand: RAND = 128-bit random challenge * @autn: Buffer for AUTN = 128-bit authentication token * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @res_len: Max length for res; set to used length or 0 on failure */ void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len) { int i; u8 mac_a[8], ak[6]; if (*res_len < 8) { *res_len = 0; return; } if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { *res_len = 0; return; } *res_len = 8; /* AUTN = (SQN ^ AK) || AMF || MAC */ for (i = 0; i < 6; i++) autn[i] = sqn[i] ^ ak[i]; os_memcpy(autn + 6, amf, 2); os_memcpy(autn + 8, mac_a, 8); } /** * milenage_auts - Milenage AUTS validation * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @auts: AUTS = 112-bit authentication token from client * @sqn: Buffer for SQN = 48-bit sequence number * Returns: 0 = success (sqn filled), -1 on failure */ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 *sqn) { u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ u8 ak[6], mac_s[8]; int i; if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) return -1; for (i = 0; i < 6; i++) sqn[i] = auts[i] ^ ak[i]; if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || memcmp(mac_s, auts + 6, 8) != 0) return -1; return 0; } /** * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @_rand: RAND = 128-bit random challenge * @sres: Buffer for SRES = 32-bit SRES * @kc: Buffer for Kc = 64-bit Kc * Returns: 0 on success, -1 on failure */ int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) { u8 res[8], ck[16], ik[16]; int i; if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) return -1; for (i = 0; i < 8; i++) kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; #ifdef GSM_MILENAGE_ALT_SRES os_memcpy(sres, res, 4); #else /* GSM_MILENAGE_ALT_SRES */ for (i = 0; i < 4; i++) sres[i] = res[i] ^ res[i + 4]; #endif /* GSM_MILENAGE_ALT_SRES */ return 0; } /** * milenage_generate - Generate AKA AUTN,IK,CK,RES * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) * @k: K = 128-bit subscriber key * @sqn: SQN = 48-bit sequence number * @_rand: RAND = 128-bit random challenge * @autn: AUTN = 128-bit authentication token * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL * @res: Buffer for RES = 64-bit signed response (f2), or %NULL * @res_len: Variable that will be set to RES length * @auts: 112-bit buffer for AUTS * Returns: 0 on success, -1 on failure, or -2 on synchronization failure */ int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, u8 *auts) { int i; u8 mac_a[8], ak[6], rx_sqn[6]; const u8 *amf; wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) return -1; *res_len = 8; wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); /* AUTN = (SQN ^ AK) || AMF || MAC */ for (i = 0; i < 6; i++) rx_sqn[i] = autn[i] ^ ak[i]; wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); if (os_memcmp(rx_sqn, sqn, 6) <= 0) { u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) return -1; wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); for (i = 0; i < 6; i++) auts[i] = sqn[i] ^ ak[i]; if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) return -1; wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); return -2; } amf = autn + 6; wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) return -1; wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); if (os_memcmp(mac_a, autn + 8, 8) != 0) { wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", autn + 8, 8); return -1; } return 0; } int milenage_opc_gen(u8 *opc, const u8 *k, const u8 *op) { int i; /* Encrypt OP using K */ if (aes_128_encrypt_block(k, op, opc)) return -1; /* XOR the resulting Ek(OP) with OP */ for (i = 0; i < 16; i++) opc[i] = opc[i] ^ op[i]; return 0; } libosmocore-0.9.0/src/gsm/milenage/milenage.h000066400000000000000000000023371261607044000211320ustar00rootroot00000000000000/* * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * * 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. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #pragma once void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len); int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, u8 *sqn); int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc); int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, u8 *auts); int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s); int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar); int milenage_opc_gen(u8 *opc, const u8 *k, const u8 *op); libosmocore-0.9.0/src/gsm/rsl.c000066400000000000000000000416671261607044000163740ustar00rootroot00000000000000/* 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 * * 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 #include #include #include #include /*! \addtogroup rsl * @{ */ /*! \file rsl.c */ /*! \brief Size for RSL \ref msgb_alloc */ #define RSL_ALLOC_SIZE 200 /*! \brief Headroom size for RSL \ref msgb_alloc */ #define RSL_ALLOC_HEADROOM 56 /*! \brief Initialize a RSL RLL header */ 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; } /*! \brief Initialize a RSL Common Channel header */ void rsl_init_cchan_hdr(struct abis_rsl_cchan_hdr *ch, uint8_t msg_type) { ch->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; ch->c.msg_type = msg_type; ch->ie_chan = RSL_IE_CHAN_NR; } /* \brief TLV parser definition for RSL */ 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 }, }, }; /*! \brief 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; } /*! \brief Decode RSL channel number * \param[in] chan_nr Channel Number * \param[out] type Channel Type * \param[out] subch Sub-channel Number * \param[out] timeslot Timeslot */ 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; } /*! \brief Get human-readable string for RSL channel number */ const char *rsl_chan_nr_str(uint8_t chan_nr) { static char str[20]; int ts = chan_nr & 7; uint8_t cbits = chan_nr >> 3; if (cbits == 0x01) sprintf(str, "TCH/F on TS%d", ts); else if ((cbits & 0x1e) == 0x02) sprintf(str, "TCH/H(%u) on TS%d", cbits & 0x01, ts); else if ((cbits & 0x1c) == 0x04) sprintf(str, "SDCCH/4(%u) on TS%d", cbits & 0x03, ts); else if ((cbits & 0x18) == 0x08) sprintf(str, "SDCCH/8(%u) on TS%d", cbits & 0x07, ts); else if (cbits == 0x10) sprintf(str, "BCCH on TS%d", ts); else if (cbits == 0x11) sprintf(str, "RACH on TS%d", ts); else if (cbits == 0x12) sprintf(str, "PCH/AGCH on TS%d", ts); else sprintf(str, "UNKNOWN on TS%d", ts); return str; } static const struct value_string rsl_err_vals[] = { { RSL_ERR_RADIO_IF_FAIL, "Radio Interface Failure" }, { RSL_ERR_RADIO_LINK_FAIL, "Radio Link Failure" }, { RSL_ERR_HANDOVER_ACC_FAIL, "Handover Access Failure" }, { RSL_ERR_TALKER_ACC_FAIL, "Talker Access Failure" }, { RSL_ERR_OM_INTERVENTION, "O&M Intervention" }, { RSL_ERR_NORMAL_UNSPEC, "Normal event, unspecified" }, { RSL_ERR_T_MSRFPCI_EXP, "Siemens: T_MSRFPCI Expired" }, { RSL_ERR_EQUIPMENT_FAIL, "Equipment Failure" }, { RSL_ERR_RR_UNAVAIL, "Radio Resource not available" }, { RSL_ERR_TERR_CH_FAIL, "Terrestrial Channel Failure" }, { RSL_ERR_CCCH_OVERLOAD, "CCCH Overload" }, { RSL_ERR_ACCH_OVERLOAD, "ACCH Overload" }, { RSL_ERR_PROCESSOR_OVERLOAD, "Processor Overload" }, { RSL_ERR_RES_UNAVAIL, "Resource not available, unspecified" }, { RSL_ERR_TRANSC_UNAVAIL, "Transcoding not available" }, { RSL_ERR_SERV_OPT_UNAVAIL, "Service or Option not available" }, { RSL_ERR_ENCR_UNIMPL, "Encryption algorithm not implemented" }, { RSL_ERR_SERV_OPT_UNIMPL, "Service or Option not implemented" }, { RSL_ERR_RCH_ALR_ACTV_ALLOC, "Radio channel already activated" }, { RSL_ERR_INVALID_MESSAGE, "Invalid Message, unspecified" }, { RSL_ERR_MSG_DISCR, "Message Discriminator Error" }, { RSL_ERR_MSG_TYPE, "Message Type Error" }, { RSL_ERR_MSG_SEQ, "Message Sequence Error" }, { RSL_ERR_IE_ERROR, "General IE error" }, { RSL_ERR_MAND_IE_ERROR, "Mandatory IE error" }, { RSL_ERR_OPT_IE_ERROR, "Optional IE error" }, { RSL_ERR_IE_NONEXIST, "IE non-existent" }, { RSL_ERR_IE_LENGTH, "IE length error" }, { RSL_ERR_IE_CONTENT, "IE content error" }, { RSL_ERR_PROTO, "Protocol error, unspecified" }, { RSL_ERR_INTERWORKING, "Interworking error, unspecified" }, { 0, NULL } }; /*! \brief Get human-readable name for RSL Error */ const char *rsl_err_name(uint8_t err) { return get_value_string(rsl_err_vals, err); } /* Names for Radio Link Layer Management */ static const struct value_string rsl_msgt_names[] = { { RSL_MT_DATA_REQ, "DATA_REQ" }, { RSL_MT_DATA_IND, "DATA_IND" }, { RSL_MT_ERROR_IND, "ERROR_IND" }, { RSL_MT_EST_REQ, "EST_REQ" }, { RSL_MT_EST_CONF, "EST_CONF" }, { RSL_MT_EST_IND, "EST_IND" }, { RSL_MT_REL_REQ, "REL_REQ" }, { RSL_MT_REL_CONF, "REL_CONF" }, { RSL_MT_REL_IND, "REL_IND" }, { RSL_MT_UNIT_DATA_REQ, "UNIT_DATA_REQ" }, { RSL_MT_UNIT_DATA_IND, "UNIT_DATA_IND" }, { RSL_MT_SUSP_REQ, "SUSP_REQ" }, { RSL_MT_SUSP_CONF, "SUSP_CONF" }, { RSL_MT_RES_REQ, "RES_REQ" }, { RSL_MT_RECON_REQ, "RECON_REQ" }, { RSL_MT_BCCH_INFO, "BCCH_INFO" }, { RSL_MT_CCCH_LOAD_IND, "CCCH_LOAD_IND" }, { RSL_MT_CHAN_RQD, "CHAN_RQD" }, { RSL_MT_DELETE_IND, "DELETE_IND" }, { RSL_MT_PAGING_CMD, "PAGING_CMD" }, { RSL_MT_IMMEDIATE_ASSIGN_CMD, "IMM_ASS_CMD" }, { RSL_MT_SMS_BC_REQ, "SMS_BC_REQ" }, { RSL_MT_CHAN_CONF, "CHAN_CONF" }, { RSL_MT_RF_RES_IND, "RF_RES_IND" }, { RSL_MT_SACCH_FILL, "SACCH_FILL" }, { RSL_MT_OVERLOAD, "OVERLOAD" }, { RSL_MT_ERROR_REPORT, "ERROR_REPORT" }, { RSL_MT_SMS_BC_CMD, "SMS_BC_CMD" }, { RSL_MT_CBCH_LOAD_IND, "CBCH_LOAD_IND" }, { RSL_MT_NOT_CMD, "NOTIFY_CMD" }, { RSL_MT_CHAN_ACTIV, "CHAN_ACTIV" }, { RSL_MT_CHAN_ACTIV_ACK, "CHAN_ACTIV_ACK" }, { RSL_MT_CHAN_ACTIV_NACK, "CHAN_ACTIV_NACK" }, { RSL_MT_CONN_FAIL, "CONN_FAIL" }, { RSL_MT_DEACTIVATE_SACCH, "DEACTIVATE_SACCH" }, { RSL_MT_ENCR_CMD, "ENCR_CMD" }, { RSL_MT_HANDO_DET, "HANDOVER_DETECT" }, { RSL_MT_MEAS_RES, "MEAS_RES" }, { RSL_MT_MODE_MODIFY_REQ, "MODE_MODIFY_REQ" }, { RSL_MT_MODE_MODIFY_ACK, "MODE_MODIFY_ACK" }, { RSL_MT_MODE_MODIFY_NACK, "MODE_MODIFY_NACK" }, { RSL_MT_PHY_CONTEXT_REQ, "PHY_CONTEXT_REQ" }, { RSL_MT_PHY_CONTEXT_CONF, "PHY_CONTEXT_CONF" }, { RSL_MT_RF_CHAN_REL, "RF_CHAN_REL" }, { RSL_MT_MS_POWER_CONTROL, "MS_POWER_CONTROL" }, { RSL_MT_BS_POWER_CONTROL, "BS_POWER_CONTROL" }, { RSL_MT_PREPROC_CONFIG, "PREPROC_CONFIG" }, { RSL_MT_PREPROC_MEAS_RES, "PREPROC_MEAS_RES" }, { RSL_MT_RF_CHAN_REL_ACK, "RF_CHAN_REL_ACK" }, { RSL_MT_SACCH_INFO_MODIFY, "SACCH_INFO_MODIFY" }, { RSL_MT_TALKER_DET, "TALKER_DETECT" }, { RSL_MT_LISTENER_DET, "LISTENER_DETECT" }, { RSL_MT_REMOTE_CODEC_CONF_REP, "REM_CODEC_CONF_REP" }, { RSL_MT_RTD_REP, "RTD_REQ" }, { RSL_MT_PRE_HANDO_NOTIF, "HANDO_NOTIF" }, { RSL_MT_MR_CODEC_MOD_REQ, "CODEC_MOD_REQ" }, { RSL_MT_MR_CODEC_MOD_ACK, "CODEC_MOD_ACK" }, { RSL_MT_MR_CODEC_MOD_NACK, "CODEC_MOD_NACK" }, { RSL_MT_MR_CODEC_MOD_PER, "CODEC_MODE_PER" }, { RSL_MT_TFO_REP, "TFO_REP" }, { RSL_MT_TFO_MOD_REQ, "TFO_MOD_REQ" }, { RSL_MT_LOCATION_INFO, "LOCATION_INFO" }, { 0, NULL } }; /*! \brief Get human-readable string for RSL Message Type */ const char *rsl_msg_name(uint8_t msg_type) { return get_value_string(rsl_msgt_names, msg_type); } /*! \brief ip.access specific */ static const struct value_string rsl_ipac_msgt_names[] = { { RSL_MT_IPAC_PDCH_ACT, "IPAC_PDCH_ACT" }, { RSL_MT_IPAC_PDCH_ACT_ACK, "IPAC_PDCH_ACT_ACK" }, { RSL_MT_IPAC_PDCH_ACT_NACK, "IPAC_PDCH_ACT_NACK" }, { RSL_MT_IPAC_PDCH_DEACT, "IPAC_PDCH_DEACT" }, { RSL_MT_IPAC_PDCH_DEACT_ACK, "IPAC_PDCH_DEACT_ACK" }, { RSL_MT_IPAC_PDCH_DEACT_NACK, "IPAC_PDCH_DEACT_NACK" }, { RSL_MT_IPAC_CONNECT_MUX, "IPAC_CONNECT_MUX" }, { RSL_MT_IPAC_CONNECT_MUX_ACK, "IPAC_CONNECT_MUX_ACK" }, { RSL_MT_IPAC_CONNECT_MUX_NACK, "IPAC_CONNECT_MUX_NACK" }, { RSL_MT_IPAC_BIND_MUX, "IPAC_BIND_MUX" }, { RSL_MT_IPAC_BIND_MUX_ACK, "IPAC_BIND_MUX_ACK" }, { RSL_MT_IPAC_BIND_MUX_NACK, "IPAC_BIND_MUX_NACK" }, { RSL_MT_IPAC_DISC_MUX, "IPAC_DISC_MUX" }, { RSL_MT_IPAC_DISC_MUX_ACK, "IPAC_DISC_MUX_ACK" }, { RSL_MT_IPAC_DISC_MUX_NACK, "IPAC_DISC_MUX_NACK" }, { RSL_MT_IPAC_CRCX, "IPAC_CRCX" }, { RSL_MT_IPAC_CRCX_ACK, "IPAC_CRCX_ACK" }, { RSL_MT_IPAC_CRCX_NACK, "IPAC_CRCX_NACK" }, { RSL_MT_IPAC_MDCX, "IPAC_MDCX" }, { RSL_MT_IPAC_MDCX_ACK, "IPAC_MDCX_ACK" }, { RSL_MT_IPAC_MDCX_NACK, "IPAC_MDCX_NACK" }, { RSL_MT_IPAC_DLCX_IND, "IPAC_DLCX_IND" }, { RSL_MT_IPAC_DLCX, "IPAC_DLCX" }, { RSL_MT_IPAC_DLCX_ACK, "IPAC_DLCX_ACK" }, { RSL_MT_IPAC_DLCX_NACK, "IPAC_DLCX_NACK" }, { 0, NULL } }; /*! \brief Get human-readable name of ip.access RSL msg type */ const char *rsl_ipac_msg_name(uint8_t msg_type) { return get_value_string(rsl_ipac_msgt_names, msg_type); } 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, "Frame 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 }, }; /*! \brief Get human-readable string for RLM cause */ 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; } } /*! \brief Push a RSL RLL header onto an existing msgb */ void rsl_rll_push_hdr(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, uint8_t link_id, int transparent) { struct abis_rsl_rll_hdr *rh; rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh)); rsl_init_rll_hdr(rh, msg_type); if (transparent) rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP; rh->chan_nr = chan_nr; rh->link_id = link_id; /* set the l2 header pointer */ msg->l2h = (uint8_t *)rh; } /*! \brief Push a RSL RLL header with L3_INFO IE */ void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, uint8_t link_id, int transparent) { uint8_t l3_len = msg->tail - (uint8_t *)msgb_l3(msg); /* construct a RSLms RLL message (DATA INDICATION, UNIT DATA * INDICATION) and send it off via RSLms */ /* Push the L3 IE tag and lengh */ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); /* Then push the RSL header */ rsl_rll_push_hdr(msg, msg_type, chan_nr, link_id, transparent); } /*! \brief Create msgb with RSL RLL header */ 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; } /*! @} */ libosmocore-0.9.0/src/gsm/rxlev_stat.c000066400000000000000000000037311261607044000177550ustar00rootroot00000000000000/* Rx Level statistics */ /* (C) 2010 by Harald Welte * * 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 #include #include #include #include #include #include #include void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev) { struct bitvec bv; if (rxlev >= NUM_RXLEVS) rxlev = NUM_RXLEVS-1; bv.data_len = NUM_ARFCNS/8; bv.data = st->rxlev_buckets[rxlev]; bitvec_set_bit_pos(&bv, arfcn, ONE); } /* get the next ARFCN that has the specified Rxlev */ int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn) { struct bitvec bv; if (rxlev >= NUM_RXLEVS) rxlev = NUM_RXLEVS-1; bv.data_len = NUM_ARFCNS/8; if (arfcn < 0) arfcn = -1; bv.data = (uint8_t *) st->rxlev_buckets[rxlev]; return bitvec_find_bit_pos(&bv, arfcn+1, ONE); } void rxlev_stat_reset(struct rxlev_stats *st) { memset(st, 0, sizeof(*st)); } void rxlev_stat_dump(const struct rxlev_stats *st) { int i; for (i = NUM_RXLEVS-1; i >= 0; i--) { int16_t arfcn = -1; printf("ARFCN with RxLev %u: ", i); while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { printf("%u ", arfcn); } printf("\n"); } } libosmocore-0.9.0/src/gsm/sysinfo.c000066400000000000000000000115611261607044000172540ustar00rootroot00000000000000/* GSM 04.08 System Information (SI) encoding and decoding * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include /* verify the sizes of the system information type structs */ /* rest octets are not part of the struct */ osmo_static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); osmo_static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); osmo_static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); /* bs11 forgot the l2 len, 0-6 rest octets */ osmo_static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); osmo_static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = { [SYSINFO_TYPE_1] = RSL_SYSTEM_INFO_1, [SYSINFO_TYPE_2] = RSL_SYSTEM_INFO_2, [SYSINFO_TYPE_3] = RSL_SYSTEM_INFO_3, [SYSINFO_TYPE_4] = RSL_SYSTEM_INFO_4, [SYSINFO_TYPE_5] = RSL_SYSTEM_INFO_5, [SYSINFO_TYPE_6] = RSL_SYSTEM_INFO_6, [SYSINFO_TYPE_7] = RSL_SYSTEM_INFO_7, [SYSINFO_TYPE_8] = RSL_SYSTEM_INFO_8, [SYSINFO_TYPE_9] = RSL_SYSTEM_INFO_9, [SYSINFO_TYPE_10] = RSL_SYSTEM_INFO_10, [SYSINFO_TYPE_13] = RSL_SYSTEM_INFO_13, [SYSINFO_TYPE_16] = RSL_SYSTEM_INFO_16, [SYSINFO_TYPE_17] = RSL_SYSTEM_INFO_17, [SYSINFO_TYPE_18] = RSL_SYSTEM_INFO_18, [SYSINFO_TYPE_19] = RSL_SYSTEM_INFO_19, [SYSINFO_TYPE_20] = RSL_SYSTEM_INFO_20, [SYSINFO_TYPE_2bis] = RSL_SYSTEM_INFO_2bis, [SYSINFO_TYPE_2ter] = RSL_SYSTEM_INFO_2ter, [SYSINFO_TYPE_2quater] = RSL_SYSTEM_INFO_2quater, [SYSINFO_TYPE_5bis] = RSL_SYSTEM_INFO_5bis, [SYSINFO_TYPE_5ter] = RSL_SYSTEM_INFO_5ter, [SYSINFO_TYPE_EMO] = RSL_EXT_MEAS_ORDER, [SYSINFO_TYPE_MEAS_INFO]= RSL_MEAS_INFO, }; static const uint8_t rsl2sitype[256] = { [RSL_SYSTEM_INFO_1] = SYSINFO_TYPE_1, [RSL_SYSTEM_INFO_2] = SYSINFO_TYPE_2, [RSL_SYSTEM_INFO_3] = SYSINFO_TYPE_3, [RSL_SYSTEM_INFO_4] = SYSINFO_TYPE_4, [RSL_SYSTEM_INFO_5] = SYSINFO_TYPE_5, [RSL_SYSTEM_INFO_6] = SYSINFO_TYPE_6, [RSL_SYSTEM_INFO_7] = SYSINFO_TYPE_7, [RSL_SYSTEM_INFO_8] = SYSINFO_TYPE_8, [RSL_SYSTEM_INFO_9] = SYSINFO_TYPE_9, [RSL_SYSTEM_INFO_10] = SYSINFO_TYPE_10, [RSL_SYSTEM_INFO_13] = SYSINFO_TYPE_13, [RSL_SYSTEM_INFO_16] = SYSINFO_TYPE_16, [RSL_SYSTEM_INFO_17] = SYSINFO_TYPE_17, [RSL_SYSTEM_INFO_18] = SYSINFO_TYPE_18, [RSL_SYSTEM_INFO_19] = SYSINFO_TYPE_19, [RSL_SYSTEM_INFO_20] = SYSINFO_TYPE_20, [RSL_SYSTEM_INFO_2bis] = SYSINFO_TYPE_2bis, [RSL_SYSTEM_INFO_2ter] = SYSINFO_TYPE_2ter, [RSL_SYSTEM_INFO_2quater] = SYSINFO_TYPE_2quater, [RSL_SYSTEM_INFO_5bis] = SYSINFO_TYPE_5bis, [RSL_SYSTEM_INFO_5ter] = SYSINFO_TYPE_5ter, [RSL_EXT_MEAS_ORDER] = SYSINFO_TYPE_EMO, [RSL_MEAS_INFO] = SYSINFO_TYPE_MEAS_INFO, }; const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = { { SYSINFO_TYPE_1, "1" }, { SYSINFO_TYPE_2, "2" }, { SYSINFO_TYPE_3, "3" }, { SYSINFO_TYPE_4, "4" }, { SYSINFO_TYPE_5, "5" }, { SYSINFO_TYPE_6, "6" }, { SYSINFO_TYPE_7, "7" }, { SYSINFO_TYPE_8, "8" }, { SYSINFO_TYPE_9, "9" }, { SYSINFO_TYPE_10, "10" }, { SYSINFO_TYPE_13, "13" }, { SYSINFO_TYPE_16, "16" }, { SYSINFO_TYPE_17, "17" }, { SYSINFO_TYPE_18, "18" }, { SYSINFO_TYPE_19, "19" }, { SYSINFO_TYPE_20, "20" }, { SYSINFO_TYPE_2bis, "2bis" }, { SYSINFO_TYPE_2ter, "2ter" }, { SYSINFO_TYPE_2quater, "2quater" }, { SYSINFO_TYPE_5bis, "5bis" }, { SYSINFO_TYPE_5ter, "5ter" }, { SYSINFO_TYPE_EMO, "EMO" }, { SYSINFO_TYPE_MEAS_INFO, "MI" }, { 0, NULL } }; uint8_t osmo_sitype2rsl(enum osmo_sysinfo_type si_type) { return sitype2rsl[si_type]; } enum osmo_sysinfo_type osmo_rsl2sitype(uint8_t rsl_si) { return rsl2sitype[rsl_si]; } libosmocore-0.9.0/src/gsm/tlv_parser.c000066400000000000000000000130661261607044000177450ustar00rootroot00000000000000/* (C) 2008-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include /*! \addtogroup tlv * @{ */ /*! \file tlv_parser.c */ struct tlv_definition tvlv_att_def; struct tlv_definition vtvlv_gan_att_def; /*! \brief Dump pasred TLV structure to stdout */ 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; } /*! \brief Parse a single TLV encoded IE * \param[out] o_tag the tag of the IE that was found * \param[out] o_len length of the IE that was found * \param[out] o_val pointer to the data of the IE that was found * \param[in] def structure defining the valid TLV tags / configurations * \param[in] buf the input data buffer to be parsed * \param[in] buf_len length of the input data buffer * \returns number of bytes consumed by the TLV entry / IE parsed */ 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 known 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: 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_vTvLV_GAN: /* 44.318 / 11.1.4 */ /* FIXME: variable-length TAG! */ if (*(buf+1) & 0x80) { /* like TL16Vbut without highest bit of len */ if (2 > buf_len) return -1; *o_val = buf+3; *o_len = (*(buf+1) & 0x7F) << 8 | *(buf+2); len = *o_len + 3; if (len > buf_len) return -2; } else { /* like TLV */ goto tlv; } 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; } /*! \brief Parse an entire buffer of TLV encoded Information Elements * \param[out] dec caller-allocated pointer to \ref tlv_parsed * \param[in] def structure defining the valid TLV tags / configurations * \param[in] buf the input data buffer to be parsed * \param[in] buf_len length of the input data buffer * \param[in] lv_tag an initial LV tag at the start of the buffer * \param[in] lv_tag2 a second initial LV tag following the \a lv_tag * \returns number of bytes consumed by the TLV entry / IE parsed */ 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; } /*! \brief 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; for (i = 0; i < ARRAY_SIZE(vtvlv_gan_att_def.def); i++) vtvlv_gan_att_def.def[i].type = TLV_TYPE_vTvLV_GAN; } /*! @} */ libosmocore-0.9.0/src/gsmtap_util.c000066400000000000000000000230561261607044000173260ustar00rootroot00000000000000/* GSMTAP support code in libmsomcore */ /* * (C) 2010-2011 by Harald Welte * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \addtogroup gsmtap * @{ */ /*! \file gsmtap_util.c */ /*! \brief convert RSL channel number to GSMTAP channel type * \param[in] rsl_chantype RSL channel type * \param[in] link_id RSL link identifier * \returns GSMTAP channel type */ 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; } /*! \brief create an arbitrary type GSMTAP message * \param[in] type The GSMTAP_TYPE_xxx constant of the message to create * \param[in] arfcn GSM ARFCN (Channel Number) * \param[in] ts GSM time slot * \param[in] chan_type Channel Type * \param[in] ss Sub-slot * \param[in] fn GSM Frame Number * \param[in] signal_dbm Signal Strength (dBm) * \param[in] snr Signal/Noise Ratio (SNR) * \param[in] data Pointer to data buffer * \param[in] len Length of \ref data * * This function will allocate a new msgb and fill it with a GSMTAP * header containing the information */ struct msgb *gsmtap_makemsg_ex(uint8_t type, 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 = type; 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; } /*! \brief create L1/L2 data and put it into GSMTAP * \param[in] arfcn GSM ARFCN (Channel Number) * \param[in] ts GSM time slot * \param[in] chan_type Channel Type * \param[in] ss Sub-slot * \param[in] fn GSM Frame Number * \param[in] signal_dbm Signal Strength (dBm) * \param[in] snr Signal/Noise Ratio (SNR) * \param[in] data Pointer to data buffer * \param[in] len Length of \ref data * * This function will allocate a new msgb and fill it with a GSMTAP * header containing the information */ 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) { return gsmtap_makemsg_ex(GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn, signal_dbm, snr, data, len); } #ifdef HAVE_SYS_SOCKET_H #include #include /*! \brief Create a new (sending) GSMTAP source socket * \param[in] host host name or IP address in string format * \param[in] port UDP port number in host byte order * * Opens a GSMTAP source (sending) socket, conncet it to host/port and * return resulting fd. If \a host is NULL, the destination address * will be localhost. If \a port is 0, the default \ref * GSMTAP_UDP_PORT will be used. * */ int gsmtap_source_init_fd(const char *host, uint16_t port) { if (port == 0) port = GSMTAP_UDP_PORT; if (host == NULL) host = "localhost"; return osmo_sock_init(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, host, port, OSMO_SOCK_F_CONNECT); } /*! \brief Add a local sink to an existing GSMTAP source and return fd * \param[in] gsmtap_fd file descriptor of the gsmtap socket * \returns file descriptor of locally bound receive socket * * In case the GSMTAP socket is connected to a local destination * IP/port, this function creates a corresponding receiving socket * bound to that destination IP + port. * * In case the gsmtap socket is not connected to a local IP/port, or * creation of the receiving socket fails, a negative error code is * returned. */ int gsmtap_source_add_sink_fd(int gsmtap_fd) { struct sockaddr_storage ss; socklen_t ss_len = sizeof(ss); int rc; rc = getpeername(gsmtap_fd, (struct sockaddr *)&ss, &ss_len); if (rc < 0) return rc; if (osmo_sockaddr_is_local((struct sockaddr *)&ss, ss_len) == 1) { rc = osmo_sock_init_sa((struct sockaddr *)&ss, SOCK_DGRAM, IPPROTO_UDP, OSMO_SOCK_F_BIND); if (rc >= 0) return rc; } return -ENODEV; } /*! \brief Send a \ref msgb through a GSMTAP source * \param[in] gti GSMTAP instance * \param[in] msg message buffer */ int gsmtap_sendmsg(struct gsmtap_inst *gti, struct msgb *msg) { if (!gti) return -ENODEV; if (gti->ofd_wq_mode) return osmo_wqueue_enqueue(>i->wq, msg); else { /* try immediate send and return error if any */ int rc; rc = write(gsmtap_inst_fd(gti), msg->data, msg->len); if (rc <= 0) { return rc; } else if (rc >= msg->len) { msgb_free(msg); return 0; } else { /* short write */ return -EIO; } } } /*! \brief send an arbitrary type through GSMTAP. * See \ref gsmtap_makemsg_ex for arguments */ int gsmtap_send_ex(struct gsmtap_inst *gti, uint8_t type, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, uint8_t snr, const uint8_t *data, unsigned int len) { struct msgb *msg; if (!gti) return -ENODEV; msg = gsmtap_makemsg_ex(type, arfcn, ts, chan_type, ss, fn, signal_dbm, snr, data, len); if (!msg) return -ENOMEM; return gsmtap_sendmsg(gti, msg); } /*! \brief send a message from L1/L2 through GSMTAP. * See \ref gsmtap_makemsg for arguments */ int gsmtap_send(struct gsmtap_inst *gti, uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, uint32_t fn, int8_t signal_dbm, uint8_t snr, const uint8_t *data, unsigned int len) { return gsmtap_send_ex(gti, GSMTAP_TYPE_UM, arfcn, ts, chan_type, ss, fn, signal_dbm, snr, data, len); } /* Callback from select layer if we can write to the socket */ static int gsmtap_wq_w_cb(struct osmo_fd *ofd, struct msgb *msg) { int rc; rc = write(ofd->fd, msg->data, msg->len); if (rc < 0) { perror("writing msgb to gsmtap fd"); return rc; } if (rc != msg->len) { perror("short write to gsmtap fd"); return -EIO; } return 0; } /* Callback from select layer if we can read from the sink socket */ static int gsmtap_sink_fd_cb(struct osmo_fd *fd, unsigned int flags) { int rc; uint8_t buf[4096]; if (!(flags & BSC_FD_READ)) return 0; rc = read(fd->fd, buf, sizeof(buf)); if (rc < 0) { perror("reading from gsmtap sink fd"); return rc; } /* simply discard any data arriving on the socket */ return 0; } /*! \brief Add a local sink to an existing GSMTAP source instance */ int gsmtap_source_add_sink(struct gsmtap_inst *gti) { int fd; fd = gsmtap_source_add_sink_fd(gsmtap_inst_fd(gti)); if (fd < 0) return fd; if (gti->ofd_wq_mode) { struct osmo_fd *sink_ofd; sink_ofd = >i->sink_ofd; sink_ofd->fd = fd; sink_ofd->when = BSC_FD_READ; sink_ofd->cb = gsmtap_sink_fd_cb; osmo_fd_register(sink_ofd); } return fd; } /*! \brief Open GSMTAP source socket, connect and register osmo_fd * \param[in] host host name or IP address in string format * \param[in] port UDP port number in host byte order * \param[in] ofd_wq_mode Register \ref osmo_wqueue (1) or not (0) * * Open GSMTAP source (sending) socket, connect it to host/port, * allocate 'struct gsmtap_inst' and optionally osmo_fd/osmo_wqueue * registration. This means it is like \ref gsmtap_init2 but integrated * with libosmocore \ref select */ struct gsmtap_inst *gsmtap_source_init(const char *host, uint16_t port, int ofd_wq_mode) { struct gsmtap_inst *gti; int fd; fd = gsmtap_source_init_fd(host, port); if (fd < 0) return NULL; gti = talloc_zero(NULL, struct gsmtap_inst); gti->ofd_wq_mode = ofd_wq_mode; gti->wq.bfd.fd = fd; gti->sink_ofd.fd = -1; if (ofd_wq_mode) { osmo_wqueue_init(>i->wq, 64); gti->wq.write_cb = &gsmtap_wq_w_cb; osmo_fd_register(>i->wq.bfd); } return gti; } #endif /* HAVE_SYS_SOCKET_H */ /*! @} */ libosmocore-0.9.0/src/logging.c000066400000000000000000000544441261607044000164310ustar00rootroot00000000000000/* Debugging/Logging support code */ /* (C) 2008-2010 by Harald Welte * (C) 2008 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. * */ /* \addtogroup logging * @{ */ /* \file logging.c */ #include "../config.h" #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #include #include #include /* for LOGGING_STR. */ struct log_info *osmo_log_info; static struct log_context log_context; static void *tall_log_ctx = NULL; LLIST_HEAD(osmo_log_target_list); #define LOGLEVEL_DEFS 6 /* Number of loglevels.*/ static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = { { 0, "EVERYTHING" }, { LOGL_DEBUG, "DEBUG" }, { LOGL_INFO, "INFO" }, { LOGL_NOTICE, "NOTICE" }, { LOGL_ERROR, "ERROR" }, { LOGL_FATAL, "FATAL" }, { 0, NULL }, }; #define INT2IDX(x) (-1*(x)-1) static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = { [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */ .name = "DLGLOBAL", .description = "Library-internal global log family", .loglevel = LOGL_NOTICE, .enabled = 1, }, [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */ .name = "DLLAPD", .description = "LAPD in libosmogsm", .loglevel = LOGL_NOTICE, .enabled = 1, }, [INT2IDX(DLINP)] = { .name = "DLINP", .description = "A-bis Intput Subsystem", .loglevel = LOGL_NOTICE, .enabled = 1, }, [INT2IDX(DLMUX)] = { .name = "DLMUX", .description = "A-bis B-Subchannel TRAU Frame Multiplex", .loglevel = LOGL_NOTICE, .enabled = 1, }, [INT2IDX(DLMI)] = { .name = "DLMI", .description = "A-bis Input Driver for Signalling", .enabled = 0, .loglevel = LOGL_NOTICE, }, [INT2IDX(DLMIB)] = { .name = "DLMIB", .description = "A-bis Input Driver for B-Channels (voice)", .enabled = 0, .loglevel = LOGL_NOTICE, }, [INT2IDX(DLSMS)] = { .name = "DLSMS", .description = "Layer3 Short Message Service (SMS)", .enabled = 1, .loglevel = LOGL_NOTICE, .color = "\033[1;38m", }, [INT2IDX(DLCTRL)] = { .name = "DLCTRL", .description = "Control Interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [INT2IDX(DLGTP)] = { .name = "DLGTP", .description = "GPRS GTP library", .enabled = 1, .loglevel = LOGL_NOTICE, }, [INT2IDX(DLSTATS)] = { .name = "DLSTATS", .description = "Statistics messages and logging", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; /*! \brief descriptive string for each log level */ /* You have to keep this in sync with the structure loglevel_strs. */ const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = { "Don't use. It doesn't log anything", "Log debug messages and higher levels", "Log informational messages and higher levels", "Log noticable messages and higher levels", "Log error messages and higher levels", "Log only fatal messages", NULL, }; /* special magic for negative (library-internal) log subsystem numbers */ static int subsys_lib2index(int subsys) { return (subsys * -1) + (osmo_log_info->num_cat_user-1); } /*! \brief Parse a human-readable log level into a numeric value */ int log_parse_level(const char *lvl) { return get_string_value(loglevel_strs, lvl); } /*! \brief convert a numeric log level into human-readable string */ const char *log_level_str(unsigned int lvl) { return get_value_string(loglevel_strs, lvl); } /*! \brief parse a human-readable log category into numeric form * \param[in] category human-readable log category name * \returns numeric category value, or -EINVAL otherwise */ int log_parse_category(const char *category) { int i; for (i = 0; i < osmo_log_info->num_cat; ++i) { if (osmo_log_info->cat[i].name == NULL) continue; if (!strcasecmp(osmo_log_info->cat[i].name+1, category)) return i; } return -EINVAL; } /*! \brief parse the log category mask * \param[in] target log target to be configured * \param[in] _mask log category mask string * * 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 < osmo_log_info->num_cat; ++i) target->categories[i].enabled = 0; category_token = strtok(mask, ":"); do { for (i = 0; i < osmo_log_info->num_cat; ++i) { size_t length, cat_length; char* colon = strstr(category_token, ","); if (!osmo_log_info->cat[i].name) continue; length = strlen(category_token); cat_length = strlen(osmo_log_info->cat[i].name); /* Use longest length not to match subocurrences. */ if (cat_length > length) length = cat_length; 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 const char* category_name(int subsys) { if (subsys < osmo_log_info->num_cat) return osmo_log_info->cat[subsys].name; return NULL; } static void _output(struct log_target *target, unsigned int subsys, unsigned int level, const char *file, int line, int cont, const char *format, va_list ap) { char buf[4096]; int ret, len = 0, offset = 0, rem = sizeof(buf); /* are we using color */ if (target->use_color) { const char *c = color(subsys); if (c) { ret = snprintf(buf + offset, rem, "%s", c); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } } if (!cont) { if (target->print_ext_timestamp) { struct tm tm; struct timeval tv; gettimeofday(&tv, NULL); localtime_r(&tv.tv_sec, &tm); ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(tv.tv_usec / 1000)); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } else if (target->print_timestamp) { char *timestr; time_t tm; tm = time(NULL); timestr = ctime(&tm); timestr[strlen(timestr)-1] = '\0'; ret = snprintf(buf + offset, rem, "%s ", timestr); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } if (target->print_category) { ret = snprintf(buf + offset, rem, "%s ", category_name(subsys)); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } if (target->print_filename) { ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ", subsys, file, line); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } } ret = vsnprintf(buf + offset, rem, format, ap); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); ret = snprintf(buf + offset, rem, "%s", target->use_color ? "\033[0;m" : ""); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); err: buf[sizeof(buf)-1] = '\0'; target->output(target, level, buf); } /*! \brief vararg version of logging function */ void osmo_vlogp(int subsys, int level, const char *file, int line, int cont, const char *format, va_list ap) { struct log_target *tar; if (subsys < 0) subsys = subsys_lib2index(subsys); if (subsys > osmo_log_info->num_cat) subsys = DLGLOBAL; llist_for_each_entry(tar, &osmo_log_target_list, entry) { struct log_category *category; int output = 0; va_list bp; category = &tar->categories[subsys]; /* subsystem is not supposed to be logged */ if (!category->enabled) continue; /* Check the global log level */ if (tar->loglevel != 0 && level < tar->loglevel) continue; /* Check the category log level */ if (tar->loglevel == 0 && category->loglevel != 0 && level < category->loglevel) continue; /* Apply filters here... if that becomes messy we will * need to put filters in a list and each filter will * say stop, continue, output */ if ((tar->filter_map & LOG_FILTER_ALL) != 0) output = 1; else if (osmo_log_info->filter_fn) output = osmo_log_info->filter_fn(&log_context, tar); if (!output) continue; /* According to the manpage, vsnprintf leaves the value of ap * in undefined state. Since _output uses vsnprintf and it may * be called several times, we have to pass a copy of ap. */ va_copy(bp, ap); _output(tar, subsys, level, file, line, cont, format, bp); va_end(bp); } } /*! \brief logging function used by DEBUGP() macro */ void logp(int subsys, const char *file, int line, int cont, const char *format, ...) { va_list ap; va_start(ap, format); osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap); va_end(ap); } /*! \brief logging function used by LOGP() macro */ void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...) { va_list ap; va_start(ap, format); osmo_vlogp(subsys, level, file, line, cont, format, ap); va_end(ap); } /*! \brief Register a new log target with the logging core * \param[in] target Log target to be registered */ void log_add_target(struct log_target *target) { llist_add_tail(&target->entry, &osmo_log_target_list); } /*! \brief Unregister a log target from the logging core * \param[in] target Log target to be unregistered */ void log_del_target(struct log_target *target) { llist_del(&target->entry); } /*! \brief Reset (clear) the logging context */ void log_reset_context(void) { memset(&log_context, 0, sizeof(log_context)); } /*! \brief Set the logging context * \param[in] ctx_nr logging context number * \param[in] value value to which the context is to be set * * A logging context is something like the subscriber identity to which * the currently processed message relates, or the BTS through which it * was received. As soon as this data is known, it can be set using * this function. The main use of context information is for logging * filters. */ 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; } /*! \brief Enable the \ref LOG_FILTER_ALL log filter * \param[in] target Log target to be affected * \param[in] all enable (1) or disable (0) the ALL filter * * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will * be printed. It acts as a wildcard. Setting it to \a 1 means there * is no filtering. */ 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; } /*! \brief Enable or disable the use of colored output * \param[in] target Log target to be affected * \param[in] use_color Use color (1) or don't use color (0) */ void log_set_use_color(struct log_target *target, int use_color) { target->use_color = use_color; } /*! \brief Enable or disable printing of timestamps while logging * \param[in] target Log target to be affected * \param[in] print_timestamp Enable (1) or disable (0) timestamps */ void log_set_print_timestamp(struct log_target *target, int print_timestamp) { target->print_timestamp = print_timestamp; } /*! \brief Enable or disable printing of extended timestamps while logging * \param[in] target Log target to be affected * \param[in] print_timestamp Enable (1) or disable (0) timestamps * * When both timestamp and extended timestamp is enabled then only * the extended timestamp will be used. The format of the timestamp * is YYYYMMDDhhmmssnnn. */ void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp) { target->print_ext_timestamp = print_timestamp; } /*! \brief Enable or disable printing of the filename while logging * \param[in] target Log target to be affected * \param[in] print_filename Enable (1) or disable (0) filenames */ void log_set_print_filename(struct log_target *target, int print_filename) { target->print_filename = print_filename; } /*! \brief Enable or disable printing of the category name * \param[in] target Log target to be affected * \param[in] print_catname Enable (1) or disable (0) filenames * * Print the category/subsys name in front of every log message. */ void log_set_print_category(struct log_target *target, int print_category) { target->print_category = print_category; } /*! \brief Set the global log level for a given log target * \param[in] target Log target to be affected * \param[in] log_level New global log level */ void log_set_log_level(struct log_target *target, int log_level) { target->loglevel = log_level; } /*! \brief Set a category filter on a given log target * \param[in] target Log target to be affected * \param[in] category Log category to be affected * \param[in] enable whether to enable or disable the filter * \param[in] level Log level of the filter */ void log_set_category_filter(struct log_target *target, int category, int enable, int level) { if (category >= osmo_log_info->num_cat) return; target->categories[category].enabled = !!enable; target->categories[category].loglevel = level; } static void _file_output(struct log_target *target, unsigned int level, const char *log) { fprintf(target->tgt_file.out, "%s", log); fflush(target->tgt_file.out); } /*! \brief Create a new log target skeleton */ 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; target->categories = talloc_zero_array(target, struct log_category, osmo_log_info->num_cat); if (!target->categories) { talloc_free(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; target->print_filename = 1; /* global log level */ target->loglevel = 0; return target; } /*! \brief Create the STDERR log target */ struct log_target *log_target_create_stderr(void) { /* since C89/C99 says stderr is a macro, we can safely do this! */ #ifdef stderr struct log_target *target; target = log_target_create(); if (!target) return NULL; target->type = LOG_TGT_TYPE_STDERR; target->tgt_file.out = stderr; target->output = _file_output; return target; #else return NULL; #endif /* stderr */ } /*! \brief Create a new file-based log target * \param[in] fname File name of the new log file * \returns Log target in case of success, NULL otherwise */ struct log_target *log_target_create_file(const char *fname) { struct log_target *target; target = log_target_create(); if (!target) return NULL; target->type = LOG_TGT_TYPE_FILE; target->tgt_file.out = fopen(fname, "a"); if (!target->tgt_file.out) return NULL; target->output = _file_output; target->tgt_file.fname = talloc_strdup(target, fname); return target; } /*! \brief Find a registered log target * \param[in] type Log target type * \param[in] fname File name * \returns Log target (if found), NULL otherwise */ struct log_target *log_target_find(int type, const char *fname) { struct log_target *tgt; llist_for_each_entry(tgt, &osmo_log_target_list, entry) { if (tgt->type != type) continue; if (tgt->type == LOG_TGT_TYPE_FILE) { if (!strcmp(fname, tgt->tgt_file.fname)) return tgt; } else return tgt; } return NULL; } /*! \brief Unregister, close and delete a log target */ void log_target_destroy(struct log_target *target) { /* just in case, to make sure we don't have any references */ log_del_target(target); if (target->output == &_file_output) { /* since C89/C99 says stderr is a macro, we can safely do this! */ #ifdef stderr /* don't close stderr */ if (target->tgt_file.out != stderr) #endif { fclose(target->tgt_file.out); target->tgt_file.out = NULL; } } talloc_free(target); } /*! \brief close and re-open a log file (for log file rotation) */ int log_target_file_reopen(struct log_target *target) { fclose(target->tgt_file.out); target->tgt_file.out = fopen(target->tgt_file.fname, "a"); if (!target->tgt_file.out) return -errno; /* we assume target->output already to be set */ return 0; } /*! \brief close and re-open a log file (for log file rotation) */ int log_targets_reopen(void) { struct log_target *tar; int rc = 0; llist_for_each_entry(tar, &osmo_log_target_list, entry) { switch (tar->type) { case LOG_TGT_TYPE_FILE: if (log_target_file_reopen(tar) < 0) rc = -1; break; default: break; } } return rc; } /*! \brief Generates the logging command string for VTY * \param[in] unused_info Deprecated parameter, no longer used! */ const char *log_vty_command_string(const struct log_info *unused_info) { struct log_info *info = osmo_log_info; int len = 0, offset = 0, ret, i, rem; int size = strlen("logging level () ()") + 1; char *str; for (i = 0; i < info->num_cat; i++) { if (info->cat[i].name == NULL) continue; size += strlen(info->cat[i].name) + 1; } for (i = 0; i < LOGLEVEL_DEFS; i++) size += strlen(loglevel_strs[i].str) + 1; rem = size; str = talloc_zero_size(tall_log_ctx, size); if (!str) return NULL; ret = snprintf(str + offset, rem, "logging level (all|"); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); for (i = 0; i < info->num_cat; i++) { if (info->cat[i].name) { int j, name_len = strlen(info->cat[i].name)+1; char name[name_len]; for (j = 0; j < name_len; j++) name[j] = tolower(info->cat[i].name[j]); name[name_len-1] = '\0'; ret = snprintf(str + offset, rem, "%s|", name+1); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } } offset--; /* to remove the trailing | */ rem++; ret = snprintf(str + offset, rem, ") ("); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); for (i = 0; i < LOGLEVEL_DEFS; i++) { int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1; char loglevel_str[loglevel_str_len]; for (j = 0; j < loglevel_str_len; j++) loglevel_str[j] = tolower(loglevel_strs[i].str[j]); loglevel_str[loglevel_str_len-1] = '\0'; ret = snprintf(str + offset, rem, "%s|", loglevel_str); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } offset--; /* to remove the trailing | */ rem++; ret = snprintf(str + offset, rem, ")"); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); err: str[size-1] = '\0'; return str; } /*! \brief Generates the logging command description for VTY * \param[in] unused_info Deprecated parameter, no longer used! */ const char *log_vty_command_description(const struct log_info *unused_info) { struct log_info *info = osmo_log_info; char *str; int i, ret, len = 0, offset = 0, rem; unsigned int size = strlen(LOGGING_STR "Set the log level for a specified category\n") + 1; for (i = 0; i < info->num_cat; i++) { if (info->cat[i].name == NULL) continue; size += strlen(info->cat[i].description) + 1; } for (i = 0; i < LOGLEVEL_DEFS; i++) size += strlen(loglevel_descriptions[i]) + 1; size += strlen("Global setting for all subsystems") + 1; rem = size; str = talloc_zero_size(tall_log_ctx, size); if (!str) return NULL; ret = snprintf(str + offset, rem, LOGGING_STR "Set the log level for a specified category\n"); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); ret = snprintf(str + offset, rem, "Global setting for all subsystems\n"); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); for (i = 0; i < info->num_cat; i++) { if (info->cat[i].name == NULL) continue; ret = snprintf(str + offset, rem, "%s\n", info->cat[i].description); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } for (i = 0; i < LOGLEVEL_DEFS; i++) { ret = snprintf(str + offset, rem, "%s\n", loglevel_descriptions[i]); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } err: str[size-1] = '\0'; return str; } /*! \brief Initialize the Osmocom logging core * \param[in] inf Information regarding logging categories * \param[in] ctx \ref talloc context for logging allocations * \returns 0 in case of success, negative in case of error */ int log_init(const struct log_info *inf, void *ctx) { int i; tall_log_ctx = talloc_named_const(ctx, 1, "logging"); if (!tall_log_ctx) return -ENOMEM; osmo_log_info = talloc_zero(tall_log_ctx, struct log_info); if (!osmo_log_info) return -ENOMEM; osmo_log_info->filter_fn = inf->filter_fn; osmo_log_info->num_cat_user = inf->num_cat; /* total number = number of user cat + library cat */ osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat); osmo_log_info->cat = talloc_zero_array(osmo_log_info, struct log_info_cat, osmo_log_info->num_cat); if (!osmo_log_info->cat) { talloc_free(osmo_log_info); osmo_log_info = NULL; return -ENOMEM; } /* copy over the user part */ for (i = 0; i < inf->num_cat; i++) { memcpy((struct log_info_cat *) &osmo_log_info->cat[i], &inf->cat[i], sizeof(struct log_info_cat)); } /* copy over the library part */ for (i = 0; i < ARRAY_SIZE(internal_cat); i++) { unsigned int cn = osmo_log_info->num_cat_user + i; memcpy((struct log_info_cat *) &osmo_log_info->cat[cn], &internal_cat[i], sizeof(struct log_info_cat)); } return 0; } /*! @} */ libosmocore-0.9.0/src/logging_syslog.c000066400000000000000000000043371261607044000200250ustar00rootroot00000000000000/* Syslog logging support code */ /* (C) 2011 by Harald Welte * 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. * */ /*! \addtogroup logging * @{ */ /*! \file logging_syslog.c */ #include "../config.h" #ifdef HAVE_SYSLOG_H #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include static int logp2syslog_level(unsigned int level) { if (level >= LOGL_FATAL) return LOG_CRIT; else if (level >= LOGL_ERROR) return LOG_ERR; else if (level >= LOGL_NOTICE) return LOG_NOTICE; else if (level >= LOGL_INFO) return LOG_INFO; else return LOG_DEBUG; } static void _syslog_output(struct log_target *target, unsigned int level, const char *log) { syslog(logp2syslog_level(level), "%s", log); } /*! \brief Create a new logging target for syslog logging * \param[in] ident syslog string identifier * \param[in] option syslog options * \param[in] facility syslog facility * \returns Log target in case of success, NULL in case of error */ struct log_target *log_target_create_syslog(const char *ident, int option, int facility) { struct log_target *target; target = log_target_create(); if (!target) return NULL; target->tgt_syslog.facility = facility; target->type = LOG_TGT_TYPE_SYSLOG; target->output = _syslog_output; openlog(ident, option, facility); return target; } #endif /* HAVE_SYSLOG_H */ /* @} */ libosmocore-0.9.0/src/loggingrb.c000066400000000000000000000055101261607044000167430ustar00rootroot00000000000000/* Ringbuffer-backed logging support code */ /* (C) 2012-2013 by Katerina Barone-Adesi * 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. * */ /*! \addtogroup logging * @{ */ /*! \file loggingrb.c */ #include #include #include static void _rb_output(struct log_target *target, unsigned int level, const char *log) { osmo_strrb_add(target->tgt_rb.rb, log); } /*! \brief Return the number of log strings in the osmo_strrb-backed target. * \param[in] target The target to search. * * \return The number of log strings in the osmo_strrb-backed target. */ size_t log_target_rb_used_size(struct log_target const *target) { return osmo_strrb_elements(target->tgt_rb.rb); } /*! \brief Return the capacity of the osmo_strrb-backed target. * \param[in] target The target to search. * * Note that this is the capacity (aka max number of messages). * It is not the number of unused message slots. * \return The number of log strings in the osmo_strrb-backed target. */ size_t log_target_rb_avail_size(struct log_target const *target) { struct osmo_strrb *rb = target->tgt_rb.rb; return rb->size - 1; } /*! \brief Return the nth log entry in a target. * \param[in] target The target to search. * \param[in] logindex The index of the log entry/error message. * * \return A pointer to the nth message, or NULL if logindex is invalid. */ const char *log_target_rb_get(struct log_target const *target, size_t logindex) { return osmo_strrb_get_nth(target->tgt_rb.rb, logindex); } /*! \brief Create a new logging target for ringbuffer-backed logging. * \param[in] size The capacity (number of messages) of the logging target. * \returns A log target in case of success, NULL in case of error. */ struct log_target *log_target_create_rb(size_t size) { struct log_target *target; struct osmo_strrb *rb; target = log_target_create(); if (!target) return NULL; rb = osmo_strrb_create(target, size + 1); if (!rb) { log_target_destroy(target); return NULL; } target->tgt_rb.rb = rb; target->type = LOG_TGT_TYPE_STRRB; target->output = _rb_output; return target; } /* @} */ libosmocore-0.9.0/src/macaddr.c000066400000000000000000000047621261607044000163740ustar00rootroot00000000000000/* * (C) 2013-2014 by Harald Welte * (C) 2014 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 #include #include #include int osmo_macaddr_parse(uint8_t *out, const char *in) { /* 00:00:00:00:00:00 */ char tmp[18]; char *tok; unsigned int i = 0; if (strlen(in) < 17) return -1; strncpy(tmp, in, sizeof(tmp)-1); tmp[sizeof(tmp)-1] = '\0'; for (tok = strtok(tmp, ":"); tok && (i < 6); tok = strtok(NULL, ":")) { unsigned long ul = strtoul(tok, NULL, 16); out[i++] = ul & 0xff; } return 0; } #if defined(__FreeBSD__) || defined(__APPLE__) #include #include #include #include #include int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name) { int rc = -1; struct ifaddrs *ifa, *ifaddr; if (getifaddrs(&ifaddr) != 0) return -1; for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { struct sockaddr_dl *sdl; sdl = (struct sockaddr_dl *) ifa->ifa_addr; if (!sdl) continue; if (sdl->sdl_family != AF_LINK) continue; if (sdl->sdl_type != IFT_ETHER) continue; if (strcmp(ifa->ifa_name, dev_name) != 0) continue; memcpy(mac_out, LLADDR(sdl), 6); rc = 0; break; } freeifaddrs(ifaddr); return 0; } #else #include #include #include #include int osmo_get_macaddr(uint8_t *mac_out, const char *dev_name) { int fd, rc; struct ifreq ifr; fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd < 0) return fd; memset(&ifr, 0, sizeof(ifr)); memcpy(&ifr.ifr_name, dev_name, sizeof(ifr.ifr_name)); rc = ioctl(fd, SIOCGIFHWADDR, &ifr); close(fd); if (rc < 0) return rc; memcpy(mac_out, ifr.ifr_hwaddr.sa_data, 6); return 0; } #endif libosmocore-0.9.0/src/msgb.c000066400000000000000000000122011261607044000157140ustar00rootroot00000000000000/* (C) 2008 by Harald Welte * (C) 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. * */ /*! \addtogroup msgb * @{ */ /*! \file msgb.c */ #include #include #include #include //#include #include //#include void *tall_msgb_ctx; /*! \brief Allocate a new message buffer * \param[in] size Length in octets, including headroom * \param[in] name Human-readable name to be associated with msgb * * This function allocates a 'struct msgb' as well as the underlying * memory buffer for the actual message data (size specified by \a size) * using the talloc memory context previously set by \ref msgb_set_talloc_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; } /*! \brief Release given message buffer * \param[in] m Message buffer to be free'd */ void msgb_free(struct msgb *m) { talloc_free(m); } /*! \brief Enqueue message buffer to tail of a queue * \param[in] queue linked list header of queue * \param[in] msg message buffer to be added to the queue * * The function will append the specified message buffer \a msg to the * queue implemented by \ref llist_head \a queue */ void msgb_enqueue(struct llist_head *queue, struct msgb *msg) { llist_add_tail(&msg->list, queue); } /*! \brief Dequeue message buffer from head of queue * \param[in] queue linked list header of queue * \returns message buffer (if any) or NULL if queue empty * * The function will remove the first message buffer from the queue * implemented by \ref llist_head \a 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); } /*! \brief Re-set all message buffer pointers * \param[in] msg message buffer that is to be resetted * * This will re-set the various internal pointers into the underlying * message buffer, i.e. remvoe all headroom and treat the msgb as * completely empty. It also initializes the control buffer to zero. */ 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)); } /*! \brief get pointer to data section of message buffer * \param[in] msg message buffer * \returns pointer to data section of message buffer */ uint8_t *msgb_data(const struct msgb *msg) { return msg->data; } /*! \brief get length of message buffer * \param[in] msg message buffer * \returns length of data section in message buffer */ uint16_t msgb_length(const struct msgb *msg) { return msg->len; } /*! \brief Set the talloc context for \ref msgb_alloc * \param[in] ctx talloc context to be used as root for msgb allocations */ void msgb_set_talloc_ctx(void *ctx) { tall_msgb_ctx = ctx; } /*! \brief Return a (static) buffer containing a hexdump of the msg * \param[in] msg message buffer * \returns a pointer to a static char array */ const char *msgb_hexdump(const struct msgb *msg) { static char buf[4100]; int buf_offs = 0; int nchars; const unsigned char *start = msg->data; const unsigned char *lxhs[4]; int i; lxhs[0] = msg->l1h; lxhs[1] = msg->l2h; lxhs[2] = msg->l3h; lxhs[3] = msg->l4h; for (i = 0; i < ARRAY_SIZE(lxhs); i++) { if (!lxhs[i]) continue; if (lxhs[i] < msg->data) goto out_of_range; if (lxhs[i] > msg->tail) goto out_of_range; nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs, "%s[L%d]> ", osmo_hexdump(start, lxhs[i] - start), i+1); if (nchars < 0 || nchars + buf_offs >= sizeof(buf)) return "ERROR"; buf_offs += nchars; start = lxhs[i]; } nchars = snprintf(buf + buf_offs, sizeof(buf) - buf_offs, "%s", osmo_hexdump(start, msg->tail - start)); if (nchars < 0 || nchars + buf_offs >= sizeof(buf)) return "ERROR"; return buf; out_of_range: nchars = snprintf(buf, sizeof(buf) - buf_offs, "!!! L%d out of range", i+1); return buf; } /*! @} */ libosmocore-0.9.0/src/msgfile.c000066400000000000000000000055051261607044000164230ustar00rootroot00000000000000/* * Parse a simple file with messages, e.g used for USSD messages * * (C) 2010 by Holger Hans Peter Freyther * (C) 2010 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #define _WITH_GETLINE #include #include #include #include #include #include #include static struct osmo_config_entry * alloc_entry(struct osmo_config_list *entries, const char *mcc, const char *mnc, const char *option, const char *text) { struct osmo_config_entry *entry = talloc_zero(entries, struct osmo_config_entry); if (!entry) return NULL; entry->mcc = talloc_strdup(entry, mcc); entry->mnc = talloc_strdup(entry, mnc); entry->option = talloc_strdup(entry, option); entry->text = talloc_strdup(entry, text); llist_add_tail(&entry->list, &entries->entry); return entry; } static struct osmo_config_list *alloc_entries(void *ctx) { struct osmo_config_list *entries; entries = talloc_zero(ctx, struct osmo_config_list); if (!entries) return NULL; INIT_LLIST_HEAD(&entries->entry); return entries; } /* * split a line like 'foo:Text'. */ static void handle_line(struct osmo_config_list *entries, char *line) { int i; const int len = strlen(line); char *items[3]; int last_item = 0; /* Skip comments from the file */ if (line[0] == '#') return; for (i = 0; i < len; ++i) { if (line[i] == '\n' || line[i] == '\r') line[i] = '\0'; else if (line[i] == ':' && last_item < 3) { line[i] = '\0'; items[last_item++] = &line[i + 1]; } } if (last_item == 3) { alloc_entry(entries, &line[0] , items[0], items[1], items[2]); return; } /* nothing found */ } struct osmo_config_list *osmo_config_list_parse(void *ctx, const char *filename) { struct osmo_config_list *entries; size_t n; char *line; FILE *file; file = fopen(filename, "r"); if (!file) return NULL; entries = alloc_entries(ctx); if (!entries) { fclose(file); return NULL; } n = 2342; line = NULL; while (getline(&line, &n, file) != -1) { handle_line(entries, line); free(line); line = NULL; } fclose(file); return entries; } libosmocore-0.9.0/src/panic.c000066400000000000000000000033221261607044000160620ustar00rootroot00000000000000/* Panic handling */ /* * (C) 2010 by Sylvain Munaut * * 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. * */ /*! \addtogroup utils * @{ */ /*! \file panic.c */ #include #include #include #include "../config.h" static osmo_panic_handler_t osmo_panic_handler = (void*)0; #ifndef PANIC_INFLOOP #include #include static void osmo_panic_default(const char *fmt, va_list args) { vfprintf(stderr, fmt, args); osmo_generate_backtrace(); abort(); } #else static void osmo_panic_default(const char *fmt, va_list args) { while (1); } #endif /*! \brief Terminate the current program with a panic */ void osmo_panic(const char *fmt, ...) { va_list args; va_start(args, fmt); if (osmo_panic_handler) osmo_panic_handler(fmt, args); else osmo_panic_default(fmt, args); va_end(args); } /*! \brief Set the panic handler */ void osmo_set_panic_handler(osmo_panic_handler_t h) { osmo_panic_handler = h; } /*! @} */ libosmocore-0.9.0/src/plugin.c000066400000000000000000000033721261607044000162730ustar00rootroot00000000000000/* plugin infrastructure */ /* (C) 2010 by Harald Welte * * 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. * */ /*! \file plugin.c * \brief Routines for loading and managing shared library plug-ins. */ #include "../config.h" #if HAVE_DLFCN_H #include #include #include #include #include #include /*! \brief Load all plugins available in given directory * \param[in] directory full path name of directory containing plug-ins * \returns number of plugins loaded in case of success, negative in case of error */ int osmo_plugin_load_all(const char *directory) { unsigned int num = 0; char fname[PATH_MAX]; DIR *dir; struct dirent *entry; dir = opendir(directory); if (!dir) return -errno; while ((entry = readdir(dir))) { snprintf(fname, sizeof(fname), "%s/%s", directory, entry->d_name); if (dlopen(fname, RTLD_NOW)) num++; } closedir(dir); return num; } #else int osmo_plugin_load_all(const char *directory) { return 0; } #endif /* HAVE_DLFCN_H */ libosmocore-0.9.0/src/rate_ctr.c000066400000000000000000000125321261607044000165760ustar00rootroot00000000000000/* utility routines for keeping conters about events and the event rates */ /* (C) 2009-2010 by Harald Welte * * 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. * */ /*! \addtogroup rate_ctr * @{ */ /*! \file rate_ctr.c */ #include #include #include #include #include #include #include static LLIST_HEAD(rate_ctr_groups); static void *tall_rate_ctr_ctx; /*! \brief Allocate a new group of counters according to description * \param[in] ctx \ref talloc context * \param[in] desc Rate counter group description * \param[in] idx Index of new counter group */ 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; } /*! \brief Free the memory for the specified group of counters */ void rate_ctr_group_free(struct rate_ctr_group *grp) { llist_del(&grp->list); talloc_free(grp); } /*! \brief Add a number to the counter */ void rate_ctr_add(struct rate_ctr *ctr, int inc) { ctr->current += inc; } /*! \brief Return the counter difference since the last call to this function */ int64_t rate_ctr_difference(struct rate_ctr *ctr) { int64_t result = ctr->current - ctr->previous; ctr->previous = ctr->current; return result; } static void interval_expired(struct rate_ctr *ctr, enum rate_ctr_intv intv) { /* calculate rate over last interval */ ctr->intv[intv].rate = ctr->current - ctr->intv[intv].last; /* save current counter for next interval */ ctr->intv[intv].last = ctr->current; /* update the rate of the next bigger interval. This will * be overwritten when that next larger interval expires */ if (intv + 1 < ARRAY_SIZE(ctr->intv)) ctr->intv[intv+1].rate += ctr->intv[intv].rate; } static struct osmo_timer_list rate_ctr_timer; static uint64_t timer_ticks; /* The one-second interval has expired */ static void rate_ctr_group_intv(struct rate_ctr_group *grp) { unsigned int i; for (i = 0; i < grp->desc->num_ctr; i++) { struct rate_ctr *ctr = &grp->ctr[i]; interval_expired(ctr, RATE_CTR_INTV_SEC); if ((timer_ticks % 60) == 0) interval_expired(ctr, RATE_CTR_INTV_MIN); if ((timer_ticks % (60*60)) == 0) interval_expired(ctr, RATE_CTR_INTV_HOUR); if ((timer_ticks % (24*60*60)) == 0) interval_expired(ctr, RATE_CTR_INTV_DAY); } } static void rate_ctr_timer_cb(void *data) { struct rate_ctr_group *ctrg; /* Increment number of ticks before we calculate intervals, * as a counter value of 0 would already wrap all counters */ timer_ticks++; llist_for_each_entry(ctrg, &rate_ctr_groups, list) rate_ctr_group_intv(ctrg); osmo_timer_schedule(&rate_ctr_timer, 1, 0); } /*! \brief Initialize the counter module */ int rate_ctr_init(void *tall_ctx) { tall_rate_ctr_ctx = tall_ctx; rate_ctr_timer.cb = rate_ctr_timer_cb; osmo_timer_schedule(&rate_ctr_timer, 1, 0); return 0; } /*! \brief Search for counter group based on group name and index */ struct rate_ctr_group *rate_ctr_get_group_by_name_idx(const char *name, const unsigned int idx) { struct rate_ctr_group *ctrg; llist_for_each_entry(ctrg, &rate_ctr_groups, list) { if (!ctrg->desc) continue; if (!strcmp(ctrg->desc->group_name_prefix, name) && ctrg->idx == idx) { return ctrg; } } return NULL; } /*! \brief Search for counter group based on group name */ const struct rate_ctr *rate_ctr_get_by_name(const struct rate_ctr_group *ctrg, const char *name) { int i; const struct rate_ctr_desc *ctr_desc; if (!ctrg->desc) return NULL; for (i = 0; i < ctrg->desc->num_ctr; i++) { ctr_desc = &ctrg->desc->ctr_desc[i]; if (!strcmp(ctr_desc->name, name)) { return &ctrg->ctr[i]; } } return NULL; } int rate_ctr_for_each_counter(struct rate_ctr_group *ctrg, rate_ctr_handler_t handle_counter, void *data) { int rc = 0; int i; for (i = 0; i < ctrg->desc->num_ctr; i++) { struct rate_ctr *ctr = &ctrg->ctr[i]; rc = handle_counter(ctrg, ctr, &ctrg->desc->ctr_desc[i], data); if (rc < 0) return rc; } return rc; } int rate_ctr_for_each_group(rate_ctr_group_handler_t handle_group, void *data) { struct rate_ctr_group *statg; int rc = 0; llist_for_each_entry(statg, &rate_ctr_groups, list) { rc = handle_group(statg, data); if (rc < 0) return rc; } return rc; } /*! @} */ libosmocore-0.9.0/src/rbtree.c000066400000000000000000000204161261607044000162560ustar00rootroot00000000000000/* Red Black Trees (C) 1999 Andrea Arcangeli (C) 2002 David Woodhouse This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA linux/lib/rbtree.c */ #include static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { struct rb_node *right = node->rb_right; struct rb_node *parent = rb_parent(node); if ((node->rb_right = right->rb_left)) rb_set_parent(right->rb_left, node); right->rb_left = node; rb_set_parent(right, parent); if (parent) { if (node == parent->rb_left) parent->rb_left = right; else parent->rb_right = right; } else root->rb_node = right; rb_set_parent(node, right); } static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { struct rb_node *left = node->rb_left; struct rb_node *parent = rb_parent(node); if ((node->rb_left = left->rb_right)) rb_set_parent(left->rb_right, node); left->rb_right = node; rb_set_parent(left, parent); if (parent) { if (node == parent->rb_right) parent->rb_right = left; else parent->rb_left = left; } else root->rb_node = left; rb_set_parent(node, left); } void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; while ((parent = rb_parent(node)) && rb_is_red(parent)) { gparent = rb_parent(parent); if (parent == gparent->rb_left) { { register struct rb_node *uncle = gparent->rb_right; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_right == node) { register struct rb_node *tmp; __rb_rotate_left(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_right(gparent, root); } else { { register struct rb_node *uncle = gparent->rb_left; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_left == node) { register struct rb_node *tmp; __rb_rotate_right(parent, root); tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(gparent); __rb_rotate_left(gparent, root); } } rb_set_black(root->rb_node); } static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { struct rb_node *other; while ((!node || rb_is_black(node)) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_right || rb_is_black(other->rb_right)) { rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_left || rb_is_black(other->rb_left)) { rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; } } } if (node) rb_set_black(node); } void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color; if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { struct rb_node *old = node, *left; node = node->rb_right; while ((left = node->rb_left) != NULL) node = left; if (rb_parent(old)) { if (rb_parent(old)->rb_left == old) rb_parent(old)->rb_left = node; else rb_parent(old)->rb_right = node; } else root->rb_node = node; child = node->rb_right; parent = rb_parent(node); color = rb_color(node); if (parent == old) { parent = node; } else { if (child) rb_set_parent(child, parent); parent->rb_left = child; node->rb_right = old->rb_right; rb_set_parent(old->rb_right, node); } node->rb_parent_color = old->rb_parent_color; node->rb_left = old->rb_left; rb_set_parent(old->rb_left, node); goto color; } parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } /* * This function returns the first node (in sort order) of the tree. */ struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_left) n = n->rb_left; return n; } struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n; n = root->rb_node; if (!n) return NULL; while (n->rb_right) n = n->rb_right; return n; } struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a right-hand child, go down and then left as far as we can. */ if (node->rb_right) { node = node->rb_right; while (node->rb_left) node=node->rb_left; return (struct rb_node *)node; } /* No right-hand children. Everything down and left is smaller than us, so any 'next' node must be in the general direction of our parent. Go up the tree; any time the ancestor is a right-hand child of its parent, keep going up. First time it's a left-hand child of its parent, said parent is our 'next' node. */ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent; return parent; } struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent; if (rb_parent(node) == node) return NULL; /* If we have a left-hand child, go down and then right as far as we can. */ if (node->rb_left) { node = node->rb_left; while (node->rb_right) node=node->rb_right; return (struct rb_node *)node; } /* No left-hand children. Go up till we find an ancestor which is a right-hand child of its parent */ while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent; return parent; } void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim); /* Set the surrounding nodes to point to the replacement */ if (parent) { if (victim == parent->rb_left) parent->rb_left = new; else parent->rb_right = new; } else { root->rb_node = new; } if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; } libosmocore-0.9.0/src/select.c000066400000000000000000000101101261607044000162400ustar00rootroot00000000000000/* 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 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "../config.h" #ifdef HAVE_SYS_SELECT_H /*! \addtogroup select * @{ */ /*! \file select.c * \brief select loop abstraction */ static int maxfd = 0; static LLIST_HEAD(osmo_fds); static int unregistered_count; /*! \brief Register a new file descriptor with select loop abstraction * \param[in] fd osmocom file descriptor to be registered */ int osmo_fd_register(struct osmo_fd *fd) { int flags; /* make FD nonblocking */ flags = fcntl(fd->fd, F_GETFL); if (flags < 0) return flags; flags |= O_NONBLOCK; flags = fcntl(fd->fd, F_SETFL, flags); if (flags < 0) return flags; /* set close-on-exec flag */ flags = fcntl(fd->fd, F_GETFD); if (flags < 0) return flags; flags |= FD_CLOEXEC; flags = fcntl(fd->fd, F_SETFD, flags); if (flags < 0) return flags; /* Register FD */ if (fd->fd > maxfd) maxfd = fd->fd; #ifdef BSC_FD_CHECK struct osmo_fd *entry; llist_for_each_entry(entry, &osmo_fds, list) { if (entry == fd) { fprintf(stderr, "Adding a osmo_fd that is already in the list.\n"); return 0; } } #endif llist_add_tail(&fd->list, &osmo_fds); return 0; } /*! \brief Unregister a file descriptor from select loop abstraction * \param[in] fd osmocom file descriptor to be unregistered */ void osmo_fd_unregister(struct osmo_fd *fd) { unregistered_count++; llist_del(&fd->list); } /*! \brief select main loop integration * \param[in] polling should we pollonly (1) or block on select (0) */ int osmo_select_main(int polling) { struct osmo_fd *ufd, *tmp; fd_set readset, writeset, exceptset; int work = 0, rc; struct timeval no_time = {0, 0}; FD_ZERO(&readset); FD_ZERO(&writeset); FD_ZERO(&exceptset); /* prepare read and write fdsets */ llist_for_each_entry(ufd, &osmo_fds, list) { if (ufd->when & BSC_FD_READ) FD_SET(ufd->fd, &readset); if (ufd->when & BSC_FD_WRITE) FD_SET(ufd->fd, &writeset); if (ufd->when & BSC_FD_EXCEPT) FD_SET(ufd->fd, &exceptset); } osmo_timers_check(); if (!polling) osmo_timers_prepare(); rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : osmo_timers_nearest()); if (rc < 0) return 0; /* fire timers */ osmo_timers_update(); /* call registered callback functions */ restart: unregistered_count = 0; llist_for_each_entry_safe(ufd, tmp, &osmo_fds, list) { int flags = 0; if (FD_ISSET(ufd->fd, &readset)) { flags |= BSC_FD_READ; FD_CLR(ufd->fd, &readset); } if (FD_ISSET(ufd->fd, &writeset)) { flags |= BSC_FD_WRITE; FD_CLR(ufd->fd, &writeset); } if (FD_ISSET(ufd->fd, &exceptset)) { flags |= BSC_FD_EXCEPT; FD_CLR(ufd->fd, &exceptset); } if (flags) { work = 1; ufd->cb(ufd, flags); } /* ugly, ugly hack. If more than one filedescriptor was * 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 */ libosmocore-0.9.0/src/serial.c000066400000000000000000000120451261607044000162510ustar00rootroot00000000000000/* * serial.c * * Utility functions to deal with serial ports * * Copyright (C) 2011 Sylvain Munaut * * 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. */ /*! \addtogroup serial * @{ */ /*! \file serial.c * Osmocom serial port helpers */ #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include #if 0 # define dbg_perror(x) perror(x) #else # define dbg_perror(x) do { } while (0) #endif /*! \brief Open serial device and does base init * \param[in] dev Path to the device node to open * \param[in] baudrate Baudrate constant (speed_t: B9600, B...) * \returns >=0 file descriptor in case of success or negative errno. */ int osmo_serial_init(const char *dev, speed_t baudrate) { int rc, fd=0, v24; struct termios tio; /* Open device */ fd = open(dev, O_RDWR | O_NOCTTY); if (fd < 0) { dbg_perror("open"); return -errno; } /* Configure serial interface */ rc = tcgetattr(fd, &tio); if (rc < 0) { dbg_perror("tcgetattr()"); rc = -errno; goto error; } cfsetispeed(&tio, baudrate); cfsetospeed(&tio, baudrate); tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); tio.c_cflag |= (CREAD | CLOCAL | CS8); tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); tio.c_iflag |= (INPCK | ISTRIP); tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR); tio.c_oflag &= ~(OPOST | ONLCR); rc = tcsetattr(fd, TCSANOW, &tio); if (rc < 0) { dbg_perror("tcsetattr()"); rc = -errno; goto error; } /* Set ready to read/write */ v24 = TIOCM_DTR | TIOCM_RTS; rc = ioctl(fd, TIOCMBIS, &v24); if (rc < 0) { dbg_perror("ioctl(TIOCMBIS)"); rc = -errno; goto error; } return fd; error: if (fd) close(fd); return rc; } static int _osmo_serial_set_baudrate(int fd, speed_t baudrate) { int rc; struct termios tio; rc = tcgetattr(fd, &tio); if (rc < 0) { dbg_perror("tcgetattr()"); return -errno; } cfsetispeed(&tio, baudrate); cfsetospeed(&tio, baudrate); rc = tcsetattr(fd, TCSANOW, &tio); if (rc < 0) { dbg_perror("tcgetattr()"); return -errno; } return 0; } /*! \brief Change current baudrate * \param[in] fd File descriptor of the open device * \param[in] baudrate Baudrate constant (speed_t: B9600, B...) * \returns 0 for success or negative errno. */ int osmo_serial_set_baudrate(int fd, speed_t baudrate) { osmo_serial_clear_custom_baudrate(fd); return _osmo_serial_set_baudrate(fd, baudrate); } /*! \brief Change current baudrate to a custom one using OS specific method * \param[in] fd File descriptor of the open device * \param[in] baudrate Baudrate as integer * \returns 0 for success or negative errno. * * This function might not work on all OS or with all type of serial adapters */ int osmo_serial_set_custom_baudrate(int fd, int baudrate) { #ifdef __linux__ int rc; struct serial_struct ser_info; rc = ioctl(fd, TIOCGSERIAL, &ser_info); if (rc < 0) { dbg_perror("ioctl(TIOCGSERIAL)"); return -errno; } ser_info.flags = ASYNC_SPD_CUST | ASYNC_LOW_LATENCY; ser_info.custom_divisor = ser_info.baud_base / baudrate; rc = ioctl(fd, TIOCSSERIAL, &ser_info); if (rc < 0) { dbg_perror("ioctl(TIOCSSERIAL)"); return -errno; } return _osmo_serial_set_baudrate(fd, B38400); /* 38400 is a kind of magic ... */ #elif defined(__APPLE__) #ifndef IOSSIOSPEED #define IOSSIOSPEED _IOW('T', 2, speed_t) #endif int rc; unsigned int speed = baudrate; rc = ioctl(fd, IOSSIOSPEED, &speed); if (rc < 0) { dbg_perror("ioctl(IOSSIOSPEED)"); return -errno; } return 0; #else #warning osmo_serial_set_custom_baudrate: unsupported platform return 0; #endif } /*! \brief Clear any custom baudrate * \param[in] fd File descriptor of the open device * \returns 0 for success or negative errno. * * This function might not work on all OS or with all type of serial adapters */ int osmo_serial_clear_custom_baudrate(int fd) { #ifdef __linux__ int rc; struct serial_struct ser_info; rc = ioctl(fd, TIOCGSERIAL, &ser_info); if (rc < 0) { dbg_perror("ioctl(TIOCGSERIAL)"); return -errno; } ser_info.flags = ASYNC_LOW_LATENCY; ser_info.custom_divisor = 0; rc = ioctl(fd, TIOCSSERIAL, &ser_info); if (rc < 0) { dbg_perror("ioctl(TIOCSSERIAL)"); return -errno; } #endif return 0; } /*! @} */ libosmocore-0.9.0/src/signal.c000066400000000000000000000056631261607044000162570ustar00rootroot00000000000000/* Generic signalling/notification infrastructure */ /* (C) 2009 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 #include #include #include #include #include /*! \addtogroup signal * @{ */ /*! \file signal.c */ void *tall_sigh_ctx; static LLIST_HEAD(signal_handler_list); struct signal_handler { struct llist_head entry; unsigned int subsys; osmo_signal_cbfn *cbfn; void *data; }; /*! \brief Register a new signal handler * \param[in] subsys Subsystem number * \param[in] cbfn Callback function * \param[in] data Data passed through to callback */ int osmo_signal_register_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data) { struct signal_handler *sig_data; sig_data = talloc(tall_sigh_ctx, struct signal_handler); if (!sig_data) return -ENOMEM; memset(sig_data, 0, sizeof(*sig_data)); sig_data->subsys = subsys; sig_data->data = data; sig_data->cbfn = cbfn; /* FIXME: check if we already have a handler for this subsys/cbfn/data */ llist_add_tail(&sig_data->entry, &signal_handler_list); return 0; } /*! \brief Unregister signal handler * \param[in] subsys Subsystem number * \param[in] cbfn Callback function * \param[in] data Data passed through to callback */ void osmo_signal_unregister_handler(unsigned int subsys, osmo_signal_cbfn *cbfn, void *data) { struct signal_handler *handler; llist_for_each_entry(handler, &signal_handler_list, entry) { if (handler->cbfn == cbfn && handler->data == data && subsys == handler->subsys) { llist_del(&handler->entry); talloc_free(handler); break; } } } /*! \brief dispatch (deliver) a new signal to all registered handlers * \param[in] subsys Subsystem number * \param[in] signal Signal number, * \param[in] signal_data Data to be passed along to handlers */ void osmo_signal_dispatch(unsigned int subsys, unsigned int signal, void *signal_data) { struct signal_handler *handler; llist_for_each_entry(handler, &signal_handler_list, entry) { if (handler->subsys != subsys) continue; (*handler->cbfn)(subsys, signal, handler->data, signal_data); } } /*! @} */ libosmocore-0.9.0/src/sim/000077500000000000000000000000001261607044000154145ustar00rootroot00000000000000libosmocore-0.9.0/src/sim/Makefile.am000066400000000000000000000015031261607044000174470ustar00rootroot00000000000000# 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 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -fPIC -Wall $(PCSC_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) if ENABLE_PCSC # FIXME: only build the PC/SC dependent part conditional, but always build other parts noinst_HEADERS = sim_int.h gsm_int.h lib_LTLIBRARIES = libosmosim.la libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c \ card_fs_sim.c card_fs_usim.c card_fs_uicc.c card_fs_isim.c card_fs_tetra.c libosmosim_la_LDFLAGS = -version-info $(LIBVERSION) libosmosim_la_LIBADD = \ $(top_builddir)/src/libosmocore.la \ $(top_builddir)/src/gsm/libosmogsm.la \ $(PCSC_LIBS) endif libosmocore-0.9.0/src/sim/card_fs_isim.c000066400000000000000000000064741261607044000202150ustar00rootroot00000000000000/* 3GPP ISIM specific structures / routines */ /* * (C) 2014 by Harald Welte * * 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 #include #include #include #include #include "sim_int.h" #include "gsm_int.h" /* TS 31.103 Version 11.2.0 Release 11 / Chapoter 7.1.3 */ const struct osim_card_sw ts31_103_sw[] = { { 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Authentication error, incorrect MAC", }, OSIM_CARD_SW_LAST }; static const struct osim_card_sw *isim_card_sws[] = { ts31_103_sw, ts102221_uicc_sw, NULL }; /* TS 31.103 Version 11.2.0 Release 11 / Chapoter 4.2 */ static const struct osim_file_desc isim_ef_in_adf_isim[] = { EF_TRANSP_N(0x6F02, 0x02, "EF.IMPI", 0, 1, 256, "IMS private user identity"), EF_TRANSP_N(0x6F03, 0x05, "EF.DOMAIN", 0, 1, 256, "Home Network Domain Name"), EF_LIN_FIX_N(0x6F04, 0x04, "EF.IMPU", 0, 1, 256, "IMS public user identity"), EF_TRANSP_N(0x6FAD, 0x03, "EF.AD", 0, 4, 16, "Administrative Data"), EF_LIN_FIX_N(0x6F06, 0x06, "EF.ARR", 0, 1, 256, "Access Rule TLV data objects"), EF_TRANSP_N(0x6F07, 0x07, "EF.IST", F_OPTIONAL, 1, 16, "ISIM Service Table"), EF_LIN_FIX_N(0x6F09, SFI_NONE, "EF.P-CSCF", F_OPTIONAL, 1, 256, "P-CSCF Address"), EF_TRANSP_N(0x6FD5, SFI_NONE, "EF.GBABP", F_OPTIONAL, 1, 35, "GBA Bootstrapping parameters"), EF_LIN_FIX_N(0x6FD7, SFI_NONE, "EF.GBANL", F_OPTIONAL, 1, 256, "NAF Key Identifier TLV Objects"), EF_LIN_FIX_N(0x6FDD, SFI_NONE, "EF.NAFKCA", F_OPTIONAL, 1, 256, "NAF Key Centre Address"), EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176, "Short messages"), EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 4, "SMS status"), EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30, "Short message status reports"), EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 29, 64, "Short message service parameters"), EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 256, "UICC IARI"), }; /* Annex E - TS 101 220 */ static const uint8_t adf_isim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04 }; struct osim_card_profile *osim_cprof_isim(void *ctx) { struct osim_card_profile *cprof; struct osim_file_desc *mf; cprof = talloc_zero(ctx, struct osim_card_profile); cprof->name = "3GPP ISIM"; cprof->sws = isim_card_sws; mf = alloc_df(cprof, 0x3f00, "MF"); cprof->mf = mf; /* ADF.USIM with its EF siblings */ add_adf_with_ef(mf, adf_isim_aid, sizeof(adf_isim_aid), "ADF.ISIM", isim_ef_in_adf_isim, ARRAY_SIZE(isim_ef_in_adf_isim)); return cprof; } libosmocore-0.9.0/src/sim/card_fs_sim.c000066400000000000000000000404461261607044000200410ustar00rootroot00000000000000/* classic SIM card specific structures/routines */ /* * (C) 2012-2014 by Harald Welte * * 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 #include #include #include #include #include "sim_int.h" /* 3GPP TS 51.011 / Chapter 9.4 */ static const struct osim_card_sw ts11_11_sw[] = { { 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command", }, { 0x9100, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command - proactive command from SIM pending", }, { 0x9e00, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command - response data for SIM data download", }, { 0x9f00, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command - response data available", }, { 0x9300, 0xffff, SW_TYPE_STR, SW_CLS_POSTP, .u.str = "SIM Application Toolkit is busy, command cannot be executed at present", }, { 0x9200, 0xfff0, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Memory management - Command successful but after using an internal updat retry X times", }, { 0x9240, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Memory management - Memory problem", }, { 0x9400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - no EF selected", }, { 0x9402, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - out of range (invalid address)", }, { 0x9404, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - file ID not found / pattern not found", }, { 0x9408, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - file is inconsistent with the command", }, { 0x9802, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - no CHV initialized", }, { 0x9804, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - access condition not fulfilled", }, { 0x9808, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - in contradiction with CHV status", }, { 0x9810, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - in contradiction with invalidation status", }, { 0x9840, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - unsuccessful CHV verification, no attempt left", }, { 0x9850, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - increase cannot be performed, max value reached", }, { 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - incorrect parameter P3", }, { 0x6b00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - incorrect parameter P1 or P2", }, { 0x6d00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - unknown instruction code", }, { 0x6e00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - wrong instruction class", }, { 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - technical problem with no diagnostic given", }, OSIM_CARD_SW_LAST }; static const struct osim_card_sw *sim_card_sws[] = { ts11_11_sw, NULL }; static int iccid_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { struct osim_decoded_element *elem; elem = element_alloc(dd, "ICCID", ELEM_T_BCD, ELEM_REPR_DEC); elem->length = len; elem->u.buf = talloc_memdup(elem, data, len); return 0; } static int elp_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { int i, num_lp = len / 2; for (i = 0; i < num_lp; i++) { uint8_t *cur = data + i*2; struct osim_decoded_element *elem; elem = element_alloc(dd, "Language Code", ELEM_T_STRING, ELEM_REPR_NONE); elem->u.buf = (uint8_t *) talloc_strndup(elem, (const char *) cur, 2); } return 0; } /* 10.3.1 */ int gsm_lp_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { int i; for (i = 0; i < len; i++) { struct osim_decoded_element *elem; elem = element_alloc(dd, "Language Code", ELEM_T_UINT8, ELEM_REPR_DEC); elem->u.u8 = data[i]; } return 0; } /* 10.3.2 */ int gsm_imsi_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { struct osim_decoded_element *elem; if (len < 2) return -EINVAL; elem = element_alloc(dd, "IMSI", ELEM_T_BCD, ELEM_REPR_DEC); elem->length = data[0]; elem->u.buf = talloc_memdup(elem, data+1, len-1); return 0; } /* 10.3.3 */ static int gsm_kc_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { struct osim_decoded_element *kc, *cksn; if (len < 9) return -EINVAL; kc = element_alloc(dd, "Kc", ELEM_T_BYTES, ELEM_REPR_HEX); kc->u.buf = talloc_memdup(kc, data, 8); cksn = element_alloc(dd, "CKSN", ELEM_T_UINT8, ELEM_REPR_DEC); cksn->u.u8 = data[8]; return 0; } /* 10.3.4 */ static int gsm_plmnsel_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { int i, n_plmn = len / 3; if (n_plmn < 1) return -EINVAL; for (i = 0; i < n_plmn; i++) { uint8_t *cur = data + 3*i; struct osim_decoded_element *elem, *mcc, *mnc; uint8_t ra_buf[6]; struct gprs_ra_id ra_id; memset(ra_buf, 0, sizeof(ra_buf)); memcpy(ra_buf, cur, 3); gsm48_parse_ra(&ra_id, ra_buf); elem = element_alloc(dd, "PLMN", ELEM_T_GROUP, ELEM_REPR_NONE); mcc = element_alloc_sub(elem, "MCC", ELEM_T_UINT16, ELEM_REPR_DEC); mcc->u.u16 = ra_id.mcc; mnc = element_alloc_sub(elem, "MNC", ELEM_T_UINT16, ELEM_REPR_DEC); mnc->u.u16 = ra_id.mnc; } return 0; } /* 10.3.5 */ int gsm_hpplmn_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { struct osim_decoded_element *elem; elem = element_alloc(dd, "Time interval", ELEM_T_UINT8, ELEM_REPR_DEC); elem->u.u8 = *data; return 0; } /* Chapter 10.1. Contents of the EFs at the MF level */ static const struct osim_file_desc sim_ef_in_mf[] = { EF_TRANSP(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10, "ICC Identification", &iccid_decode, NULL), EF_TRANSP(0x2F05, SFI_NONE, "EF.PL", F_OPTIONAL, 2, 20, "Preferred language", &elp_decode, NULL), }; /* Chapter 10.3.x Contents of files at the GSM application level */ static const struct osim_file_desc sim_ef_in_gsm[] = { EF_TRANSP(0x6F05, SFI_NONE, "EF.LP", 0, 1, 16, "Language preference", &gsm_lp_decode, NULL), EF_TRANSP(0x6F07, SFI_NONE, "EF.IMSI", 0, 9, 9, "IMSI", &gsm_imsi_decode, NULL), EF_TRANSP(0x6F20, SFI_NONE, "EF.Kc", 0, 9, 9, "Ciphering key Kc", &gsm_kc_decode, NULL), EF_TRANSP(0x6F30, SFI_NONE, "EF.PLMNsel", F_OPTIONAL, 24, 72, "PLMN selector", &gsm_plmnsel_decode, NULL), EF_TRANSP(0x6F31, SFI_NONE, "EF.HPPLMN", 0, 1, 1, "Higher Priority PLMN search period", &gsm_hpplmn_decode, NULL), EF_TRANSP_N(0x6F37, SFI_NONE, "EF.ACMmax", F_OPTIONAL, 3, 3, "ACM maximum value"), EF_TRANSP_N(0x6F38, SFI_NONE, "EF.SST", 0, 2, 16, "SIM service table"), EF_CYCLIC_N(0x6F39, SFI_NONE, "EF.ACM", F_OPTIONAL, 3, 3, "Accumulated call meter"), EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.GID1", F_OPTIONAL, 1, 8, "Group Identifier Level 1"), EF_TRANSP_N(0x6F3F, SFI_NONE, "EF.GID2", F_OPTIONAL, 1, 8, "Group Identifier Level 2"), EF_TRANSP_N(0x6F46, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17, "Service Provider Name"), EF_TRANSP_N(0x6F41, SFI_NONE, "EF.PUCT", F_OPTIONAL, 5, 5, "Price per unit and currency table"), EF_TRANSP_N(0x6F45, SFI_NONE, "EF.CBMI", F_OPTIONAL, 2, 32, "Cell broadcast massage identifier selection"), EF_TRANSP_N(0x6F74, SFI_NONE, "EF.BCCH", 0, 16, 16, "Broadcast control channels"), EF_TRANSP_N(0x6F78, SFI_NONE, "EF.ACC", 0, 2, 2, "Access control class"), EF_TRANSP_N(0x6F7B, SFI_NONE, "EF.FPLMN", 0, 12, 12, "Forbidden PLMNs"), EF_TRANSP_N(0x6F7E, SFI_NONE, "EF.LOCI", 0, 11, 11, "Location information"), EF_TRANSP_N(0x6FAD, SFI_NONE, "EF.AD", 0, 3, 8, "Administrative data"), EF_TRANSP_N(0x6FAE, SFI_NONE, "EF.Phase", 0, 1, 1, "Phase identification"), EF_TRANSP_N(0x6FB1, SFI_NONE, "EF.VGCS", F_OPTIONAL, 4, 80, "Voice Group Call Service"), EF_TRANSP_N(0x6FB2, SFI_NONE, "EF.VGCSS", F_OPTIONAL, 7, 7, "Voice Group Call Service Status"), EF_TRANSP_N(0x6FB3, SFI_NONE, "EF.VBS", F_OPTIONAL, 4, 80, "Voice Broadcast Service"), EF_TRANSP_N(0x6FB4, SFI_NONE, "EF.VBSS", F_OPTIONAL, 7, 7, "Voice Broadcast Service Status"), EF_TRANSP_N(0x6FB5, SFI_NONE, "EF.eMLPP", F_OPTIONAL, 2, 2, "enhanced Mult Level Pre-emption and Priority"), EF_TRANSP_N(0x6FB6, SFI_NONE, "EF.AAeM", F_OPTIONAL, 1, 1, "Automatic Answer for eMLPP Service"), EF_TRANSP_N(0x6F48, SFI_NONE, "EF.CBMID", F_OPTIONAL, 2, 32, "Cell Broadcast Message Identifier for Data Download"), EF_TRANSP_N(0x6FB7, SFI_NONE, "EF.ECC", F_OPTIONAL, 3, 15, "Emergency Call Code"), EF_TRANSP_N(0x6F50, SFI_NONE, "EF.CBMIR", F_OPTIONAL, 4, 64, "Cell broadcast message identifier range selection"), EF_TRANSP_N(0x6F2C, SFI_NONE, "EF.DCK", F_OPTIONAL, 16, 16, "De-personalization Control Keys"), EF_TRANSP_N(0x6F32, SFI_NONE, "EF.CNL", F_OPTIONAL, 6, 60, "Co-operative Network List"), EF_LIN_FIX_N(0x6F51, SFI_NONE, "EF.NIA", F_OPTIONAL, 1, 17, "Network's Indication of Alerting"), EF_TRANSP_N(0x6F52, SFI_NONE, "EF.KcGPRS", F_OPTIONAL, 9, 9, "GPRS Ciphering key KcGPRS"), EF_TRANSP_N(0x6F53, SFI_NONE, "EF.LOCIGPRS", F_OPTIONAL, 14, 14, "GPRS location information"), EF_TRANSP_N(0x6F54, SFI_NONE, "EF.SUME", F_OPTIONAL, 1, 64, "SetUpMenu Elements"), EF_TRANSP_N(0x6F60, SFI_NONE, "EF.PLMNwAcT", F_OPTIONAL, 40, 80, "User controlled PLMN Selector with Access Technology"), EF_TRANSP_N(0x6F61, SFI_NONE, "EF.OPLMNwAcT", F_OPTIONAL, 40, 80, "Operator controlled PLMN Selector with Access Technology"), EF_TRANSP_N(0x6F62, SFI_NONE, "EF.HPLMNwAcT", F_OPTIONAL, 5, 20, "HPLMN Selector with Access Technology"), EF_TRANSP_N(0x6F63, SFI_NONE, "EF.CPBCCH", F_OPTIONAL, 2, 20, "CPBCCH Information"), EF_TRANSP_N(0x6F64, SFI_NONE, "EF.InvScan", F_OPTIONAL, 1, 1, "Investigation Scan"), EF_LIN_FIX_N(0x6FC5, SFI_NONE, "EF.PNN", F_OPTIONAL, 3, 20, "PLMN Network Name"), EF_LIN_FIX_N(0x6FC6, SFI_NONE, "EF.OPL", F_OPTIONAL, 8, 8, "PLMN Operator PLMN List"), EF_LIN_FIX_N(0x6FC7, SFI_NONE, "EF.MBDN", F_OPTIONAL, 14, 30, "Mailbox Dialling Number"), EF_LIN_FIX_N(0x6FC9, SFI_NONE, "EF.MBI", F_OPTIONAL, 4, 4, "Maibox Identifier"), EF_LIN_FIX_N(0x6FCA, SFI_NONE, "EF.MWIS", F_OPTIONAL, 5, 5, "Message Waiting Indication Status"), EF_LIN_FIX_N(0x6FCB, SFI_NONE, "EF.CFIS", F_OPTIONAL, 16, 16, "Call Forwarding Indication Status"), EF_LIN_FIX_N(0x6FC8, SFI_NONE, "EF.EXT6", F_OPTIONAL, 13, 13, "Extension6 (MBDN)"), EF_LIN_FIX_N(0x6FCC, SFI_NONE, "EF.EXT7", F_OPTIONAL, 13, 13, "Extension7 (CFIS)"), EF_TRANSP_N(0x6FCD, SFI_NONE, "EF.SPDI", F_OPTIONAL, 1, 32, "Extension7 (CFIS)"), EF_LIN_FIX_N(0x6FCE, SFI_NONE, "EF.MMSN", F_OPTIONAL, 4, 32, "MMS Notification"), EF_LIN_FIX_N(0x6FCF, SFI_NONE, "EF.EXT8", F_OPTIONAL, 2, 18, "Extension8 (MMSN)"), EF_TRANSP_N(0x6FD0, SFI_NONE, "EF.MMSICP", F_OPTIONAL, 1, 64, "MMS Issuer Connectivity Parameters"), EF_LIN_FIX_N(0x6FD1, SFI_NONE, "EF.MMSUP", F_OPTIONAL, 1, 64, "MMS User Preferences"), EF_TRANSP_N(0x6FD2, SFI_NONE, "EF.MMSUCP", F_OPTIONAL, 1, 64, "MMS User Connectivity Parameters"), }; /* 10.4.1 Contents of the files at the SoLSA level */ static const struct osim_file_desc sim_ef_in_solsa[] = { EF_TRANSP_N(0x4F30, SFI_NONE, "EF.SAI", F_OPTIONAL, 1, 32, "SoLSA Access Indicator"), EF_LIN_FIX_N(0x4F31, SFI_NONE, "EF.SLL", F_OPTIONAL, 1, 32, "SoLSA LSA List"), /* LSA Descriptor files */ }; /* 10.4.2 Contents of files at the MExE level */ static const struct osim_file_desc sim_ef_in_mexe[] = { EF_TRANSP_N(0x4F40, SFI_NONE, "EF.MExE-ST", F_OPTIONAL, 1, 8, "MExE Service table"), EF_LIN_FIX_N(0x4F41, SFI_NONE, "EF.ORPK", F_OPTIONAL, 11, 32, "Operator Root Public Key"), EF_LIN_FIX_N(0x4F42, SFI_NONE, "EF.ARPK", F_OPTIONAL, 11, 32, "Administrator Root Public Key"), EF_LIN_FIX_N(0x4F43, SFI_NONE, "EF.TRPK", F_OPTIONAL, 11, 32, "Third Party Root Public Key"), }; /* 10.5 Contents of files at the telecom level */ static const struct osim_file_desc sim_ef_in_telecom[] = { EF_LIN_FIX_N(0x6F3A, SFI_NONE, "EF.ADN", F_OPTIONAL, 14, 30, "Abbreviated dialling numbers"), EF_LIN_FIX_N(0x6F3B, SFI_NONE, "EF.FDN", F_OPTIONAL, 14, 30, "Fixed dialling numbers"), EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176, "Short messages"), EF_LIN_FIX_N(0x6F3D, SFI_NONE, "EF.CCP", F_OPTIONAL, 14, 14, "Capability configuration parameters"), EF_LIN_FIX_N(0x6F4F, SFI_NONE, "EF.ECCP", F_OPTIONAL, 15, 15, "Extended Capability configuration parameters"), EF_LIN_FIX_N(0x6F40, SFI_NONE, "EF.MSISDN", F_OPTIONAL, 14, 30, "MSISDN"), EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 28, 44, "Short message service parameters"), EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 3, "SMS Status"), EF_CYCLIC_N(0x6F44, SFI_NONE, "EF.LND", F_OPTIONAL, 14, 30, "Last number dialled"), EF_LIN_FIX_N(0x6F49, SFI_NONE, "EF.SDN", F_OPTIONAL, 14, 30, "Service Dialling Numbers"), EF_LIN_FIX_N(0x6F4A, SFI_NONE, "EF.EXT1", F_OPTIONAL, 13, 13, "Extension 1 (ADN/SSC, MSISDN, LND)"), EF_LIN_FIX_N(0x6F4B, SFI_NONE, "EF.EXT2", F_OPTIONAL, 13, 13, "Extension 2 (FDN/SSC)"), EF_LIN_FIX_N(0x6F4C, SFI_NONE, "EF.EXT3", F_OPTIONAL, 13, 13, "Extension 3 (SDN)"), EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 31, "Barred dialling numbers"), EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13, "Extension 4 (BDN/SSC)"), EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30, "Short message status reports"), EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 1, 17, "Comparison Method Information"), }; /* 10.6.1 Contents of files at the telecom graphics level */ const struct osim_file_desc sim_ef_in_graphics[] = { EF_LIN_FIX_N(0x4F20, SFI_NONE, "EF.IMG", F_OPTIONAL, 11, 38, "Image"), }; int osim_int_cprof_add_gsm(struct osim_file_desc *mf) { struct osim_file_desc *gsm; gsm = add_df_with_ef(mf, 0x7F20, "DF.GSM", sim_ef_in_gsm, ARRAY_SIZE(sim_ef_in_gsm)); /* Chapter 10.2: DFs at the GSM Application Level */ add_df_with_ef(gsm, 0x5F30, "DF.IRIDIUM", NULL, 0); add_df_with_ef(gsm, 0x5F31, "DF.GLOBALSTAR", NULL, 0); add_df_with_ef(gsm, 0x5F32, "DF.ICO", NULL, 0); add_df_with_ef(gsm, 0x5F33, "DF.ACeS", NULL, 0); add_df_with_ef(gsm, 0x5F3C, "DF.MExE", sim_ef_in_mexe, ARRAY_SIZE(sim_ef_in_mexe)); add_df_with_ef(gsm, 0x5F40, "DF.EIA/TIA-533", NULL, 0); add_df_with_ef(gsm, 0x5F60, "DF.CTS", NULL, 0); add_df_with_ef(gsm, 0x5F70, "DF.SoLSA", sim_ef_in_solsa, ARRAY_SIZE(sim_ef_in_solsa)); return 0; } int osim_int_cprof_add_telecom(struct osim_file_desc *mf) { struct osim_file_desc *tc; tc = add_df_with_ef(mf, 0x7F10, "DF.TELECOM", sim_ef_in_telecom, ARRAY_SIZE(sim_ef_in_telecom)); add_df_with_ef(tc, 0x5F50, "DF.GRAPHICS", sim_ef_in_graphics, ARRAY_SIZE(sim_ef_in_graphics)); add_df_with_ef(mf, 0x7F22, "DF.IS-41", NULL, 0); add_df_with_ef(mf, 0x7F23, "DF.FP-CTS", NULL, 0); /* TS 11.19 */ return 0; } struct osim_card_profile *osim_cprof_sim(void *ctx) { struct osim_card_profile *cprof; struct osim_file_desc *mf; int rc; cprof = talloc_zero(ctx, struct osim_card_profile); cprof->name = "GSM SIM"; cprof->sws = sim_card_sws; mf = alloc_df(cprof, 0x3f00, "MF"); cprof->mf = mf; /* According to Figure 8 */ add_filedesc(mf, sim_ef_in_mf, ARRAY_SIZE(sim_ef_in_mf)); rc = osim_int_cprof_add_gsm(mf); rc |= osim_int_cprof_add_telecom(mf); if (rc != 0) { talloc_free(cprof); return NULL; } return cprof; } libosmocore-0.9.0/src/sim/card_fs_tetra.c000066400000000000000000000235251261607044000203670ustar00rootroot00000000000000/* TETRA SIM card specific structures/routines */ /* * (C) 2014 by Harald Welte * * 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 #include #include #include #include #include "sim_int.h" #include "gsm_int.h" /* EN 300 812 V2.1.1 (2001-12) 9.4 */ static const struct osim_card_sw tsim_sw[] = { { 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command", }, { 0x9f00, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command - response data available", }, { 0x9300, 0xffff, SW_TYPE_STR, SW_CLS_POSTP, .u.str = "SIM Application Toolkit is busy, command cannot be executed at present", }, { 0x9200, 0xfff0, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Memory management - Command successful but after using an internal updat retry X times", }, { 0x9240, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Memory management - Memory problem", }, { 0x9400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - no EF selected", }, { 0x9402, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - out of range (invalid address)", }, { 0x9404, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - file ID not found / pattern not found", }, { 0x9408, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Referencing management - file is inconsistent with the command", }, { 0x9802, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - no CHV initialized", }, { 0x9804, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - access condition not fulfilled", }, { 0x9808, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - in contradiction with CHV status", }, { 0x9810, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - in contradiction with invalidation status", }, { 0x9840, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - unsuccessful CHV verification, no attempt left", }, { 0x9860, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - manipulation flag set", }, { 0x9870, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - SwMI authentication unsuccessful", }, { 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - incorrect parameter P3", }, { 0x6b00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - incorrect parameter P1 or P2", }, { 0x6d00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - unknown instruction code", }, { 0x6e00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - wrong instruction class", }, { 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application independent - technical problem with no diagnostic given", }, OSIM_CARD_SW_LAST }; static const struct osim_card_sw *tsim_card_sws[] = { tsim_sw, NULL }; /* Chapter 10.2.x */ static const struct osim_file_desc sim_ef_in_mf[] = { EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10, "ICC Identification"), EF_TRANSP_N(0x2F00, SFI_NONE, "EF.DIR", F_OPTIONAL, 8, 54, "Application directory"), EF_TRANSP_N(0x2F05, SFI_NONE, "EF.LP", 0, 2, 32, "Language preference"), }; //////////////////////////// /* Chapter 10.3.x */ static const struct osim_file_desc sim_ef_in_tetra[] = { EF_TRANSP_N(0x6F01, SFI_NONE, "EF.SST", 0, 4, 8, "SIM service table"), EF_TRANSP_N(0x6F02, SFI_NONE, "EF.ITSI", 0, 6, 6, "ITSI"), EF_TRANSP_N(0x6F03, SFI_NONE, "EF.ITSIDIS", 0, 1, 1, "ITSI Disable"), EF_TRANSP_N(0x6F05, SFI_NONE, "EF.SCT", 0, 4, 4, "Subscriber Class Table"), EF_TRANSP_N(0x6F06, SFI_NONE, "EF.PHASE", 0, 1, 1, "Phase identification"), EF_KEY_N(0x6F07, SFI_NONE, "EF.CCK", F_OPTIONAL, 12, 12, "Common Cipher Key"), EF_TRANSP_N(0x6F08, SFI_NONE, "EF.CCKLOC", F_OPTIONAL, 31, 31, "CCK location areas"), EF_KEY_N(0x6F09, SFI_NONE, "EF.SCK", F_OPTIONAL, 12, 12, "Static Cipher Keys"), /* X+4 for each record, suggested 1 to 10 */ EF_LIN_FIX_N(0x6F0A, SFI_NONE, "EF.GSSIS", 0, 5, 21, "Static Cipher Keys"), /* 2 for each record, one for each recod in GSSIS */ EF_LIN_FIX_N(0x6F0B, SFI_NONE, "EF.GRDS", 0, 2, 2, "Group related data for static GSSIS"), EF_LIN_FIX_N(0x6F0C, SFI_NONE, "EF.GSSID", 0, 5, 21, "Dynamic GSSIs"), /* 2 for each record, one for each recod in GSSID */ EF_LIN_FIX_N(0x6F0D, SFI_NONE, "EF.GRDD", 0, 2, 2, "Dynamic GSSIs"), EF_LIN_FIX_N(0x6F0E, SFI_NONE, "EF.GCK", F_OPTIONAL, 12, 12, "Group Cipher Keys"), EF_KEY_N(0x6F0F, SFI_NONE, "EF.MGCK", F_OPTIONAL, 12, 12, "Modified Group Cipher Keys"), EF_TRANSP_N(0x6F10, SFI_NONE, "EF.GINFO", 0, 9, 9, "User's group information"), EF_TRANSP_N(0x6F11, SFI_NONE, "EF.SEC", 0, 1, 1, "Security settings"), EF_CYCLIC_N(0x6F12, SFI_NONE, "EF.FORBID", 0, 3, 3, "Security settings"), EF_CYCLIC_N(0x6F13, SFI_NONE, "EF.PREF", F_OPTIONAL, 3, 3, "Preferred networks"), EF_TRANSP_N(0x6F14, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17, "Service Provider Name"), EF_TRANSP_N(0x6F15, SFI_NONE, "EF.LOCI", F_OPTIONAL, 7, 7, "Location Information"), EF_TRANSP_N(0x6F16, SFI_NONE, "EF.DNWRK", 0, 3, 3, "Broadcast network information"), EF_LIN_FIX_N(0x6F17, SFI_NONE, "EF.NWT", 0, 5, 5, "Network table"), EF_LIN_FIX_N(0x6F18, SFI_NONE, "EF.GWT", F_OPTIONAL, 13, 13, "Gateway table"), EF_LIN_FIX_N(0x6F19, SFI_NONE, "EF.CMT", F_OPTIONAL, 5, 20, "Call Modifier Table"), EF_LIN_FIX_N(0x6F1A, SFI_NONE, "EF.ADNGWT", F_OPTIONAL, 13, 28, "Abbreviated Dialling Number with Gateways"), EF_LIN_FIX_N(0x6F1C, SFI_NONE, "EF.ADNTETRA", F_OPTIONAL, 9, 23, "Abbreviated dialling numbers for TETRA network"), EF_LIN_FIX_N(0x6F1D, SFI_NONE, "EF.EXTA", F_OPTIONAL, 20, 20, "Extension A"), EF_LIN_FIX_N(0x6F1E, SFI_NONE, "EF.FDNGWT", F_OPTIONAL, 13, 28, "Fixed dialling numbers with Gateways"), EF_LIN_FIX_N(0x6F1F, SFI_NONE, "EF.GWTEXT2", F_OPTIONAL, 13, 13, "Gateway Extension2"), EF_LIN_FIX_N(0x6F20, SFI_NONE, "EF.FDNTETRA", F_OPTIONAL, 9, 25, "Fixed dialling numbers for TETRA network"), EF_LIN_FIX_N(0x6F21, SFI_NONE, "EF.EXTB", F_OPTIONAL, 20, 20, "Extension B"), EF_LIN_FIX_N(0x6F22, SFI_NONE, "EF.LNDGWT", F_OPTIONAL, 13, 28, "Last number dialled with Gateways"), EF_CYCLIC_N(0x6F23, SFI_NONE, "EF.LNDTETRA", F_OPTIONAL, 9, 23, "Last numbers dialled for TETRA network"), EF_LIN_FIX_N(0x6F24, SFI_NONE, "EF.SDNGWT", F_OPTIONAL, 13, 28, "Service Dialling Numbers with gateway"), EF_LIN_FIX_N(0x6F25, SFI_NONE, "EF.GWTEXT3", F_OPTIONAL, 13, 13, "Gateway Extension3"), EF_LIN_FIX_N(0x6F26, SFI_NONE, "EF.SDNTETRA", F_OPTIONAL, 8, 22, "Service Dialling Nubers for TETRA network"), EF_LIN_FIX_N(0x6F27, SFI_NONE, "EF.STXT", F_OPTIONAL, 5, 18, "Status message texts"), EF_LIN_FIX_N(0x6F28, SFI_NONE, "EF.MSGTXT", F_OPTIONAL, 5, 18, "SDS-1 message texts"), EF_LIN_FIX_N(0x6F29, SFI_NONE, "EF.SDS123", F_OPTIONAL, 46, 46, "Status and SDS type 1, 2 and 3 message storage"), EF_LIN_FIX_N(0x6F2A, SFI_NONE, "EF.SDS4", F_OPTIONAL, 255, 255, "Status and SDS type 4 message storage"), EF_LIN_FIX_N(0x6F2B, SFI_NONE, "EF.MSGEXT", F_OPTIONAL, 16, 16, "Message Extension"), EF_LIN_FIX_N(0x6F2C, SFI_NONE, "EF.EADDR", 0, 17, 17, "Emergency adresses"), EF_TRANSP_N(0x6F2D, SFI_NONE, "EF.EINFO", 0, 2, 2, "Emergency call information"), EF_LIN_FIX_N(0x6F2E, SFI_NONE, "EF.DMOCh", F_OPTIONAL, 4, 4, "DMO channel information"), EF_TRANSP_N(0x6F2F, SFI_NONE, "EF.MSCh", F_OPTIONAL, 1, 16, "MS allocation of DMO channels"), EF_TRANSP_N(0x6F30, SFI_NONE, "EF.KH", F_OPTIONAL, 6, 6, "List of Key Holders"), EF_LIN_FIX_N(0x6F31, SFI_NONE, "EF.REPGATE", F_OPTIONAL, 2, 2, "DMO repeater and gateway list"), EF_TRANSP_N(0x6F32, SFI_NONE, "EF.AD", F_OPTIONAL, 2, 2, "Administrative Data"), EF_TRANSP_N(0x6F33, SFI_NONE, "EF.PREF_LA", F_OPTIONAL, 2, 2, "Preferred location areas"), EF_CYCLIC_N(0x6F34, SFI_NONE, "EF.LNDComp", F_OPTIONAL, 3, 3, "Composite LND file"), EF_TRANSP_N(0x6F35, SFI_NONE, "EF.DFLTSTSTGGT", F_OPTIONAL, 16, 16, "Status Default Target"), EF_TRANSP_N(0x6F36, SFI_NONE, "EF.SDSMEM_STATUS", F_OPTIONAL, 7, 7, "SDS Memory Status"), EF_TRANSP_N(0x6F37, SFI_NONE, "EF.WELCOME", F_OPTIONAL, 32, 32, "Welcome Message"), EF_LIN_FIX_N(0x6F38, SFI_NONE, "EF.SDSR", F_OPTIONAL, 2, 2, "SDS delivery report"), EF_LIN_FIX_N(0x6F39, SFI_NONE, "EF.SDSP", F_OPTIONAL, 20, 35, "SDS parameters"), EF_TRANSP_N(0x6F46, SFI_NONE, "EF.DIALSC", 0, 5, 5, "Dialling schemes for TETRA network"), EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.APN", F_OPTIONAL, 65, 65, "APN table"), EF_LIN_FIX_N(0x6FC0, SFI_NONE, "EF.PNI", F_OPTIONAL, 14, 14, "Private Number Information"), }; struct osim_card_profile *osim_cprof_tsim(void *ctx) { struct osim_card_profile *cprof; struct osim_file_desc *mf; int rc; cprof = talloc_zero(ctx, struct osim_card_profile); cprof->name = "TETRA SIM"; cprof->sws = tsim_card_sws; mf = alloc_df(cprof, 0x3f00, "MF"); cprof->mf = mf; add_filedesc(mf, sim_ef_in_mf, ARRAY_SIZE(sim_ef_in_mf)); add_df_with_ef(mf, 0x7F20, "DF.TETRA", sim_ef_in_tetra, ARRAY_SIZE(sim_ef_in_tetra)); rc = osim_int_cprof_add_telecom(mf); if (rc != 0) { talloc_free(cprof); return NULL; } return cprof; } libosmocore-0.9.0/src/sim/card_fs_uicc.c000066400000000000000000000170231261607044000201670ustar00rootroot00000000000000/* ETSI UICC specific structures / routines */ /* * (C) 2012 by Harald Welte * * 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 #include /* TS 102 221 V10.0.0 / 10.2.1 */ const struct osim_card_sw ts102221_uicc_sw[] = { { 0x9000, 0xffff, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command", }, { 0x9100, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command, extra info proactive", }, { 0x9200, 0xff00, SW_TYPE_STR, SW_CLS_OK, .u.str = "Normal ending of the command, extra info regarding transfer session", }, { 0x9300, 0xff00, SW_TYPE_STR, SW_CLS_POSTP, .u.str = "SIM Application Toolkit is busy, command cannot be executed at present", }, { 0x6200, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "No information given, state of non volatile memory unchanged", }, { 0x6281, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Part of returned data may be corrupted", }, { 0x6282, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "End of file/record reached before reading Le bytes", }, { 0x6283, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Selected file invalidated", }, { 0x6285, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Selected file in termination state", }, { 0x62f1, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "More data available", }, { 0x62f2, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "More data available and proactive command pending", }, { 0x62f3, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Response data available", }, { 0x63f1, 0xffff, SW_TYPE_STR, SW_CLS_WARN, .u.str = "More data expected", }, { 0x63c0, 0xfff0, SW_TYPE_STR, SW_CLS_WARN, .u.str = "Verification falied, X retries remaining", }, { 0x6400, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Execution - No information given, state of non-volatile memory unchanged", }, { 0x6500, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Execution - No information given, state of non-volatile memory changed", }, { 0x6581, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Execution - Memory problem", }, { 0x6700, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Wrong length", }, { 0x6700, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Command dependent error", }, { 0x6b00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Wrong parameter(s) P1-P2", }, { 0x6d00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Instruction code not supported or valid", }, { 0x6e00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Class not supported", }, { 0x6f00, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Technical problem, no precise diagnostics", }, { 0x6f00, 0xff00, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Checking - Command dependent error", }, { 0x6800, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Function in CLA not supported - No information given", }, { 0x6881, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Function in CLA not supported - Logical channel not supported", }, { 0x6882, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Function in CLA not supportied - Secure messaging not supported", }, { 0x6900, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - No information given", }, { 0x6981, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Command incompatible with file structure", }, { 0x6982, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Security status not satisfied", }, { 0x6983, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Authentication/PIN method blocked", }, { 0x6984, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Referenced data invalidated", }, { 0x6985, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Conditions of use not satisfied", }, { 0x6986, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - Noe EF selected", }, { 0x6989, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Command not allowed - secure channel - security not satisfied", }, { 0x6a80, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Incorrect parameters in the data field", }, { 0x6a81, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Function not supported", }, { 0x6a82, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - File not found", }, { 0x6a83, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Record not found", }, { 0x6a84, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Not enough memory space", }, { 0x6a86, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Incorrect parameters P1 to P2", }, { 0x6a87, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Lc inconsistent with P1 ot P2", }, { 0x6a88, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Wrong parameters - Referenced data not found", }, { 0x9850, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application error - INCREASE cannot be performed, max value reached", }, { 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application error - Authentication error, application specific", }, { 0x9863, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Application error - Security session or association expired", }, OSIM_CARD_SW_LAST }; const struct value_string ts102221_fcp_vals[14] = { { UICC_FCP_T_FCP, "File control parameters" }, { UICC_FCP_T_FILE_SIZE, "File size" }, { UICC_FCP_T_TOT_F_SIZE, "Total size of files" }, { UICC_FCP_T_FILE_DESC, "File descriptor" }, { UICC_FCP_T_FILE_ID, "File identifier" }, { UICC_FCP_T_DF_NAME, "DF name" }, { UICC_FCP_T_SFID, "Short file identifier" }, { UICC_FCP_T_LIFEC_STS, "Lifecycle status integer" }, { UICC_FCP_T_SEC_ATTR_REFEXP, "Security attributes (Referenced/Expanded)" }, { UICC_FCP_T_SEC_ATTR_COMP, "Security attributes (Compact)" }, { UICC_FCP_T_PROPRIETARY, "Proprietary" }, { UICC_FCP_T_SEC_ATTR_EXP, "Security attributes (Expanded)" }, { UICC_FCP_T_PIN_STS_DO, "PIN Status DO" }, { 0, NULL } }; /* FIXME: Ber-TLV ?? */ const struct tlv_definition ts102221_fcp_tlv_def = { .def = { [UICC_FCP_T_FCP] = { TLV_TYPE_TLV }, [UICC_FCP_T_FILE_SIZE] = { TLV_TYPE_TLV }, [UICC_FCP_T_TOT_F_SIZE] = { TLV_TYPE_TLV }, [UICC_FCP_T_FILE_DESC] = { TLV_TYPE_TLV }, [UICC_FCP_T_FILE_ID] = { TLV_TYPE_TLV }, [UICC_FCP_T_DF_NAME] = { TLV_TYPE_TLV }, [UICC_FCP_T_SFID] = { TLV_TYPE_TLV }, [UICC_FCP_T_LIFEC_STS] = { TLV_TYPE_TLV }, [UICC_FCP_T_SEC_ATTR_REFEXP] = { TLV_TYPE_TLV }, [UICC_FCP_T_SEC_ATTR_COMP] = { TLV_TYPE_TLV }, [UICC_FCP_T_PROPRIETARY] = { TLV_TYPE_TLV }, [UICC_FCP_T_SEC_ATTR_EXP] = { TLV_TYPE_TLV }, [UICC_FCP_T_PIN_STS_DO] = { TLV_TYPE_TLV }, }, }; /* Annex E - TS 101 220 */ static const uint8_t adf_uicc_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x01 }; libosmocore-0.9.0/src/sim/card_fs_usim.c000066400000000000000000000355431261607044000202300ustar00rootroot00000000000000/* 3GPP USIM specific structures / routines */ /* * (C) 2012-2014 by Harald Welte * * 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 #include #include #include #include #include "sim_int.h" #include "gsm_int.h" /* TS 31.102 Version 7.7.0 / Chapter 7.3 */ const struct osim_card_sw ts31_102_sw[] = { { 0x9862, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Authentication error, incorrect MAC", }, { 0x9864, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Authentication error, security context not supported", }, { 0x9865, 0xffff, SW_TYPE_STR, SW_CLS_ERROR, .u.str = "Security management - Key freshness error", }, OSIM_CARD_SW_LAST }; static const struct osim_card_sw *usim_card_sws[] = { ts31_102_sw, ts102221_uicc_sw, NULL }; /* TS 102 221 Chapter 13.1 */ static const struct osim_file_desc uicc_ef_in_mf[] = { EF_LIN_FIX_N(0x2f00, SFI_NONE, "EF.DIR", 0, 1, 32, "Application directory"), EF_TRANSP_N(0x2FE2, SFI_NONE, "EF.ICCID", 0, 10, 10, "ICC Identification"), EF_TRANSP_N(0x2F05, SFI_NONE, "EF.PL", 0, 2, 20, "Preferred Languages"), EF_LIN_FIX_N(0x2F06, SFI_NONE, "EF.ARR", F_OPTIONAL, 1, 256, "Access Rule Reference"), }; /* 31.102 Chapter 4.4.3 */ static const struct osim_file_desc usim_ef_in_df_gsm_access[] = { EF_TRANSP_N(0x4f20, 0x01, "EF.Kc", 0, 9, 9, "Ciphering Key Kc"), EF_TRANSP_N(0x4f52, 0x02, "EF.KcGPRS", F_OPTIONAL, 9, 9, "GPRS Ciphering key KcGPRS"), EF_TRANSP_N(0x4f63, SFI_NONE, "EF.CPBCCH", F_OPTIONAL, 2, 20, "CPBCCH Information"), EF_TRANSP_N(0x4f64, SFI_NONE, "EF.invSCAN", F_OPTIONAL, 1, 1, "Investigation Scan"), }; /* 31.102 Chapter 4.2 */ static const struct osim_file_desc usim_ef_in_adf_usim[] = { EF_TRANSP(0x6F05, 0x02, "EF.LI", 0, 2, 16, "Language Indication", &gsm_lp_decode, NULL), EF_TRANSP(0x6F07, 0x07, "EF.IMSI", 0, 9, 9, "IMSI", &gsm_imsi_decode, NULL), EF_TRANSP_N(0x6F08, 0x08, "EF.Keys", 0, 33, 33, "Ciphering and Integrity Keys"), EF_TRANSP_N(0x6F09, 0x09, "EF.KeysPS", 0, 33, 33, "Ciphering and Integrity Keys for Packet Switched domain"), EF_TRANSP_N(0x6F60, 0x0A, "EF.PLMNwAcT", F_OPTIONAL, 40, 80, "User controlled PLMN Selector with Access Technology"), EF_TRANSP(0x6F31, 0x12, "EF.HPPLMN", 0, 1, 1, "Higher Priority PLMN search period", &gsm_hpplmn_decode, NULL), EF_TRANSP_N(0x6F37, SFI_NONE, "EF.ACMmax", F_OPTIONAL, 3, 3, "ACM maximum value"), EF_TRANSP_N(0x6F38, 0x04, "EF.UST", 0, 1, 16, "USIM Service Table"), EF_CYCLIC_N(0x6F39, SFI_NONE, "EF.ACM", F_OPTIONAL, 3, 3, "Accumulated call meter"), EF_TRANSP_N(0x6F3E, SFI_NONE, "EF.GID1", F_OPTIONAL, 1, 4, "Group Identifier Level 1"), EF_TRANSP_N(0x6F3F, SFI_NONE, "EF.GID2", F_OPTIONAL, 1, 4, "Group Identifier Level 2"), EF_TRANSP_N(0x6F46, SFI_NONE, "EF.SPN", F_OPTIONAL, 17, 17, "Service Provider Name"), EF_TRANSP_N(0x6F41, SFI_NONE, "EF.PUCT", F_OPTIONAL, 5, 5, "Price per unit and currency table"), EF_TRANSP_N(0x6F45, SFI_NONE, "EF.CBMI", F_OPTIONAL, 2, 32, "Cell broadcast massage identifier selection"), EF_TRANSP_N(0x6F78, 0x06, "EF.ACC", 0, 2, 2, "Access control class"), EF_TRANSP_N(0x6F7B, 0x0D, "EF.FPLMN", 0, 12, 36, "Forbidden PLMNs"), EF_TRANSP_N(0x6F7E, 0x0B, "EF.LOCI", 0, 11, 11, "Location information"), EF_TRANSP_N(0x6FAD, 0x03, "EF.AD", 0, 5, 16, "Administrative data"), EF_TRANSP_N(0x6F48, 0x0E, "EF.CBMID", F_OPTIONAL, 2, 32, "Cell Broadcast Message Identifier for Data Download"), EF_TRANSP_N(0x6FB7, 0x01, "EF.ECC", F_OPTIONAL, 5, 32, "Emergency Call Code"), EF_TRANSP_N(0x6F50, SFI_NONE, "EF.CBMIR", F_OPTIONAL, 4, 32, "Cell broadcast message identifier range selection"), EF_TRANSP_N(0x6F73, 0x0C, "EF.PSLOCI", 0, 14, 14, "Pacet Switched location information"), EF_LIN_FIX_N(0x6F3B, SFI_NONE, "EF.FDN", F_OPTIONAL, 14, 32, "Fixed dialling numbers"), EF_LIN_FIX_N(0x6F3C, SFI_NONE, "EF.SMS", F_OPTIONAL, 176, 176, "Short messages"), EF_LIN_FIX_N(0x6F40, SFI_NONE, "EF.MSISDN", F_OPTIONAL, 14, 32, "MSISDN"), EF_LIN_FIX_N(0x6F42, SFI_NONE, "EF.SMSP", F_OPTIONAL, 28, 64, "Short message service parameters"), EF_TRANSP_N(0x6F43, SFI_NONE, "EF.SMSS", F_OPTIONAL, 2, 8, "SMS Status"), EF_LIN_FIX_N(0x6F49, SFI_NONE, "EF.SDN", F_OPTIONAL, 14, 32, "Service Dialling Numbers"), EF_LIN_FIX_N(0x6F4B, SFI_NONE, "EF.EXT2", F_OPTIONAL, 13, 13, "Extension 2"), EF_LIN_FIX_N(0x6F4C, SFI_NONE, "EF.EXT3", F_OPTIONAL, 13, 13, "Extension 3"), EF_LIN_FIX_N(0x6F47, SFI_NONE, "EF.SMSR", F_OPTIONAL, 30, 30, "Short message status reports"), EF_CYCLIC_N(0x6F80, 0x14, "EF.ICI", F_OPTIONAL, 28, 64, "Incoming Calling Information"), EF_CYCLIC_N(0x6F81, 0x15, "EF.OCI", F_OPTIONAL, 27, 64, "Outgoing Calling Information"), EF_CYCLIC_N(0x6F82, SFI_NONE, "EF.ICT", F_OPTIONAL, 3, 3, "Incoming Call Timer"), EF_CYCLIC_N(0x6F83, SFI_NONE, "EF.OCT", F_OPTIONAL, 3, 3, "Outgoing Call Timer"), EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT5", F_OPTIONAL, 13, 13, "Extension 5"), EF_LIN_FIX_N(0x6F4F, 0x16, "EF.CCP2", F_OPTIONAL, 15, 32, "Capability Configuration Parameters 2"), EF_TRANSP_N(0x6FB5, SFI_NONE, "EF.eMLPP", F_OPTIONAL, 2, 2, "enhanced Multi Level Precedence and Pre-emption"), EF_TRANSP_N(0x6FB6, SFI_NONE, "EF.AAeM", F_OPTIONAL, 1, 1, "Automatic Answer for eMLPP Service"), EF_TRANSP_N(0x6FC3, SFI_NONE, "EF.Hiddenkey", F_OPTIONAL, 4, 4, "Key for hidden phone book entries"), EF_LIN_FIX_N(0x6F4D, SFI_NONE, "EF.BDN", F_OPTIONAL, 15, 32, "Barred Dialling Numbers"), EF_LIN_FIX_N(0x6F4E, SFI_NONE, "EF.EXT4", F_OPTIONAL, 13, 13, "Extension 4"), EF_LIN_FIX_N(0x6F58, SFI_NONE, "EF.CMI", F_OPTIONAL, 2, 16, "Comparison Method Information"), EF_TRANSP_N(0x6F56, 0x05, "EF.EST", F_OPTIONAL, 1, 8, "Enhanced Services Table"), EF_TRANSP_N(0x6F57, SFI_NONE, "EF.ACL", F_OPTIONAL, 2, 256, "Access Point Name Control List"), EF_TRANSP_N(0x6F2C, SFI_NONE, "EF.DCK", F_OPTIONAL, 16, 16, "Depersonalisation Control Keys"), EF_TRANSP_N(0x6F32, SFI_NONE, "EF.CNL", F_OPTIONAL, 6, 46, "Co-operative Network List"), EF_TRANSP_N(0x6F5B, 0x0F, "EF.START-HFN", 0, 6, 6, "Initialisation values for Hyperframe number"), EF_TRANSP_N(0x6F5C, 0x10, "EF.THRESHOLD", 0, 3, 3, "Maximum value of START"), EF_TRANSP_N(0x6F61, 0x11, "EF.OPLMNwAcT", F_OPTIONAL, 40, 80, "Operator controlled PLMN Selector with Access Technology"), EF_TRANSP_N(0x6F62, 0x13, "EF.HPLMNwAcT", F_OPTIONAL, 5, 30, "HPLMN Selector with Access Technology"), EF_LIN_FIX_N(0x6F06, 0x17, "EF.ARR", 0, 1, 256, "Access Rule Reference"), EF_TRANSP_N(0x6FC4, SFI_NONE, "EF.NETPAR", 0, 46, 256, "Network Parameters"), EF_LIN_FIX_N(0x6FC5, 0x19, "EF.PNN", F_OPTIONAL, 3, 128, "PLMN Network Name"), EF_LIN_FIX_N(0x6FC6, 0x1A, "EF.OPL", F_OPTIONAL, 8, 32, "Operator PLMN List"), EF_LIN_FIX_N(0x6FC7, SFI_NONE, "EF.MBDN", F_OPTIONAL, 14, 32, "Mailbox Dialling Numbers"), EF_LIN_FIX_N(0x6FC8, SFI_NONE, "EF.EXT6", F_OPTIONAL, 13, 13, "Extension 6"), EF_LIN_FIX_N(0x6FC9, SFI_NONE, "EF.MBI", F_OPTIONAL, 4, 5, "Mailbox Identifier"), EF_LIN_FIX_N(0x6FCA, SFI_NONE, "EF.MWIS", F_OPTIONAL, 5, 6, "Message Waiting Indication Status"), EF_LIN_FIX_N(0x6FCB, SFI_NONE, "EF.CFIS", F_OPTIONAL, 16, 16, "Call Forwarding Indication Status"), EF_LIN_FIX_N(0x6FCC, SFI_NONE, "EF.EXT7", F_OPTIONAL, 13, 13, "Extension 7"), EF_TRANSP_N(0x6FCD, 0x1B, "EF.SPDI", F_OPTIONAL, 1, 64, "Service Provider Display Information"), EF_LIN_FIX_N(0x6FCE, SFI_NONE, "EF.MMSN", F_OPTIONAL, 4, 32, "MMS Notification"), EF_LIN_FIX_N(0x6FCF, SFI_NONE, "EF.EXT8", F_OPTIONAL, 3, 16, "Extension 8"), EF_TRANSP_N(0x6FD0, SFI_NONE, "EF.MMSICP", F_OPTIONAL, 3, 256, "MMS Issuer Connectivity Parameters"), EF_LIN_FIX_N(0x6FD1, SFI_NONE, "EF.MMSUP", F_OPTIONAL, 1, 64, "MMS User Preferences"), EF_TRANSP_N(0x6FD2, SFI_NONE, "EF.MMSUCP", F_OPTIONAL, 1, 64, "MMS User Connectivity Parameters"), EF_LIN_FIX_N(0x6FD3, SFI_NONE, "EF.NIA", F_OPTIONAL, 2, 64, "Network's Indication of Alerting"), EF_TRANSP_N(0x6FB1, SFI_NONE, "EF.VGCS", F_OPTIONAL, 4, 80, "Voice Group Call Service"), EF_TRANSP_N(0x6FB2, SFI_NONE, "EF.VGCSS", F_OPTIONAL, 7, 7, "Voice Group Call Service Status"), EF_TRANSP_N(0x6FB3, SFI_NONE, "EF.VBS", F_OPTIONAL, 4, 80, "Voice Broadcast Service"), EF_TRANSP_N(0x6FB4, SFI_NONE, "EF.VBSS", F_OPTIONAL, 7, 7, "Voice Broadcast Service Status"), EF_TRANSP_N(0x6FD4, SFI_NONE, "EF.VGCSCA", F_OPTIONAL, 2, 40, "Voice Group Call Service Ciphering Algorithm"), EF_TRANSP_N(0x6FD5, SFI_NONE, "EF.VBSCA", F_OPTIONAL, 2, 40, "Voice Broadcast Service Ciphering Algorithm"), EF_TRANSP_N(0x6FD6, SFI_NONE, "EF.GBABP", F_OPTIONAL, 4, 128, "GBA Bootstrapping parameters"), EF_LIN_FIX_N(0x6FD7, SFI_NONE, "EF.MSK", F_OPTIONAL, 20, 84, "MBMS Serviec Key List"), EF_LIN_FIX_N(0x6FD8, SFI_NONE, "EF.MUK", F_OPTIONAL, 1, 128, "MBMS User Key"), EF_LIN_FIX_N(0x6FDA, SFI_NONE, "EF.GBANL", F_OPTIONAL, 1, 128, "GBA NAF List"), EF_TRANSP_N(0x6FD9, 0x1D, "EF.EHPLMN", F_OPTIONAL, 3, 30, "Equivalent HPLMN"), EF_TRANSP_N(0x6FDB, SFI_NONE, "EF.EHPLMNPI", F_OPTIONAL, 1, 1, "Equivalent HPLMN Presentation Indication"), EF_TRANSP_N(0x6FDC, SFI_NONE, "EF.LRPLMNSI", F_OPTIONAL, 1, 1, "Last RPLMN Selection Indication"), EF_LIN_FIX_N(0x6FDD, SFI_NONE, "EF.NAFKCA", F_OPTIONAL, 1, 128, "NAF Key Centre Address"), EF_TRANSP_N(0x6FDE, SFI_NONE, "EF.SPNI", F_OPTIONAL, 1, 128, "Service Provider Name Icon"), EF_LIN_FIX_N(0x6FDF, SFI_NONE, "EF.PNNI", F_OPTIONAL, 1, 128, "PLMN Network Name Icon"), EF_LIN_FIX_N(0x6FE2, SFI_NONE, "EF.NCP-IP", F_OPTIONAL, 2, 256, "Network Connectivity Parameters for USIM IP Connections"), EF_TRANSP_N(0x6FE3, 0x1E, "EF.EPSLOCI", F_OPTIONAL, 18, 18, "EPS location information"), EF_LIN_FIX_N(0x6FE4, 0x18, "EF.EPSNSC", F_OPTIONAL, 54, 128, "EPS NAS Security Context"), EF_TRANSP_N(0x6FE6, SFI_NONE, "EF.UFC", F_OPTIONAL, 1, 8, "USAT Facility Control"), EF_TRANSP_N(0x6FE8, SFI_NONE, "EF.NASCONFIG", F_OPTIONAL, 1, 128, "Non Access Stratum Configuration"), EF_LIN_FIX_N(0x6FE7, SFI_NONE, "EF.UICCIARI", F_OPTIONAL, 1, 32, "UICC IARI"), EF_TRANSP_N(0x6FEC, SFI_NONE, "EF.PWS", F_OPTIONAL, 1, 32, "Public Warning System"), }; /* 31.102 Chapter 4.4.1 */ static const struct osim_file_desc usim_ef_in_solsa[] = { EF_TRANSP_N(0x4F30, SFI_NONE, "EF.SAI", F_OPTIONAL, 2, 32, "SoLSA Access Indicator"), EF_LIN_FIX_N(0x4F31, SFI_NONE, "EF.SLL", F_OPTIONAL, 11, 32, "SoLSA LSA List"), /* LSA descriptor files 4Fxx, hard to represent here */ }; /* 31.102 Chapter 4.4.4 */ static const struct osim_file_desc usim_ef_in_df_mexe[] = { EF_TRANSP_N(0x4F40, SFI_NONE, "EF.MexE-ST", F_OPTIONAL, 1, 4, "MexE Service table"), EF_LIN_FIX_N(0x4F41, SFI_NONE, "EF.ORPK", F_OPTIONAL, 10, 16, "Operator Root Public Key"), EF_LIN_FIX_N(0x4F42, SFI_NONE, "EF.ARPK", F_OPTIONAL, 10, 16, "Administrator Root Public Key"), EF_LIN_FIX_N(0x4F43, SFI_NONE, "EF.TPRPK", F_OPTIONAL, 10, 16, "Third Party Root Public Key"), /* TKCDF files 4Fxx, hard to represent here */ }; /* 31.102 Chapter 4.4.5 */ static const struct osim_file_desc usim_ef_in_df_wlan[] = { EF_TRANSP_N(0x4F41, 0x01, "EF.Pseudo", F_OPTIONAL, 2, 16, "Pseudonym"), EF_TRANSP_N(0x4F42, 0x02, "EF.UPLMNWLAN", F_OPTIONAL, 30, 60, "User controlled PLMN selector for I-WLAN Access"), EF_TRANSP_N(0x4F43, 0x03, "EF.OPLMNWLAN", F_OPTIONAL, 30, 60, "Operator controlled PLMN selector for I-WLAN Access"), EF_LIN_FIX_N(0x4F44, 0x04, "EF.UWSIDL", F_OPTIONAL, 1, 16, "User controlled WLAN Specific Identifier List"), EF_LIN_FIX_N(0x4F45, 0x05, "EF.OWSIDL", F_OPTIONAL, 1, 16, "Operator controlled WLAN Specific Identifier List"), EF_TRANSP_N(0x4F46, 0x06, "EF.WRI", F_OPTIONAL, 1, 64, "WLAN Reauthentication Identity"), EF_LIN_FIX_N(0x4F47, 0x07, "EF.HWSIDL", F_OPTIONAL, 1, 16, "Home I-WLAN Specific Identifier List"), EF_TRANSP_N(0x4F48, 0x08, "EF.WEPLMNPI", F_OPTIONAL, 1, 1, "I-WLAN Equivalent HPLMN Presentation Indication"), EF_TRANSP_N(0x4F49, 0x09, "EF.WHPI", F_OPTIONAL, 1, 1, "I-WLAN HPLMN Priority Indiation"), EF_TRANSP_N(0x4F4A, 0x0a, "EF.WLRPLMN", F_OPTIONAL, 3, 3, "I-WLAN Last Registered PLMN"), }; /* 31.102 Chapter 4.4.6 */ static const struct osim_file_desc usim_ef_in_df_hnb[] = { EF_LIN_FIX_N(0x4F81, 0x01, "EF.ACSGL", F_OPTIONAL, 1, 128, "Allowed CSG Lists"), EF_LIN_FIX_N(0x4F82, 0x02, "EF.CSGT", F_OPTIONAL, 1, 64, "CSG Type"), EF_LIN_FIX_N(0x4F83, 0x03, "EF.HNBN", F_OPTIONAL, 1, 64, "Home NodeB Name"), EF_LIN_FIX_N(0x4F84, 0x04, "EF.OCSGL", F_OPTIONAL, 1, 64, "Operator CSG List"), EF_LIN_FIX_N(0x4F85, 0x05, "EF.OCSGT", F_OPTIONAL, 1, 64, "Operator CSG Type"), EF_LIN_FIX_N(0x4F86, 0x06, "EF.OHNBN", F_OPTIONAL, 1, 64, "Oprator Home NodeB Name"), }; /* Annex E - TS 101 220 */ static const uint8_t adf_usim_aid[] = { 0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02 }; struct osim_card_profile *osim_cprof_usim(void *ctx) { struct osim_card_profile *cprof; struct osim_file_desc *mf, *uadf; int rc; cprof = talloc_zero(ctx, struct osim_card_profile); cprof->name = "3GPP USIM"; cprof->sws = usim_card_sws; mf = alloc_df(cprof, 0x3f00, "MF"); cprof->mf = mf; /* Core UICC Files */ add_filedesc(mf, uicc_ef_in_mf, ARRAY_SIZE(uicc_ef_in_mf)); /* ADF.USIM with its EF siblings */ uadf = add_adf_with_ef(mf, adf_usim_aid, sizeof(adf_usim_aid), "ADF.USIM", usim_ef_in_adf_usim, ARRAY_SIZE(usim_ef_in_adf_usim)); /* DFs under ADF.USIM */ add_df_with_ef(uadf, 0x5F3A, "DF.PHONEBOOK", NULL, 0); add_df_with_ef(uadf, 0x5F3B, "DF.GSM-ACCESS", usim_ef_in_df_gsm_access, ARRAY_SIZE(usim_ef_in_df_gsm_access)); add_df_with_ef(uadf, 0x5F3C, "DF.MexE", usim_ef_in_df_mexe, ARRAY_SIZE(usim_ef_in_df_mexe)); add_df_with_ef(uadf, 0x5F40, "DF.WLAN", usim_ef_in_df_wlan, ARRAY_SIZE(usim_ef_in_df_wlan)); /* Home-NodeB (femtocell) */ add_df_with_ef(uadf, 0x5F50, "DF.HNB", usim_ef_in_df_hnb, ARRAY_SIZE(usim_ef_in_df_hnb)); /* Support of Localised Service Areas */ add_df_with_ef(uadf, 0x5F70, "DF.SoLSA", usim_ef_in_solsa, ARRAY_SIZE(usim_ef_in_solsa)); /* OMA BCAST Smart Card Profile */ add_df_with_ef(uadf, 0x5F80, "DF.BCAST", NULL, 0); /* DF.GSM and DF.TELECOM hierarchy as sub-directory of MF */ rc = osim_int_cprof_add_gsm(mf); rc |= osim_int_cprof_add_telecom(mf); if (rc != 0) { talloc_free(cprof); return NULL; } return cprof; } libosmocore-0.9.0/src/sim/core.c000066400000000000000000000167151261607044000165220ustar00rootroot00000000000000/* Core routines for SIM/UICC/USIM access */ /* * (C) 2012 by Harald Welte * * 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 #include #include #include #include struct osim_decoded_data *osim_file_decode(struct osim_file *file, int len, uint8_t *data) { struct osim_decoded_data *dd; if (!file->desc->ops.parse) return NULL; dd = talloc_zero(file, struct osim_decoded_data); if (!dd) return NULL; dd->file = file; if (file->desc->ops.parse(dd, file->desc, len, data) < 0) { talloc_free(dd); return NULL; } else return dd; } struct msgb *osim_file_encode(const struct osim_file_desc *desc, const struct osim_decoded_data *data) { if (!desc->ops.encode) return NULL; return desc->ops.encode(desc, data); } static struct osim_decoded_element * __element_alloc(void *ctx, const char *name, enum osim_element_type type, enum osim_element_repr repr) { struct osim_decoded_element *elem; elem = talloc_zero(ctx, struct osim_decoded_element); if (!elem) return NULL; elem->name = name; elem->type = type; elem->representation = repr; if (elem->type == ELEM_T_GROUP) INIT_LLIST_HEAD(&elem->u.siblings); return elem; } struct osim_decoded_element * element_alloc(struct osim_decoded_data *dd, const char *name, enum osim_element_type type, enum osim_element_repr repr) { struct osim_decoded_element *elem; elem = __element_alloc(dd, name, type, repr); if (!elem) return NULL; llist_add_tail(&elem->list, &dd->decoded_elements); return elem; } struct osim_decoded_element * element_alloc_sub(struct osim_decoded_element *ee, const char *name, enum osim_element_type type, enum osim_element_repr repr) { struct osim_decoded_element *elem; elem = __element_alloc(ee, name, type, repr); if (!elem) return NULL; llist_add(&elem->list, &ee->u.siblings); return elem; } void add_filedesc(struct osim_file_desc *root, const struct osim_file_desc *in, int num) { int i; for (i = 0; i < num; i++) { struct osim_file_desc *ofd = talloc_memdup(root, &in[i], sizeof(*in)); llist_add_tail(&ofd->list, &root->child_list); } } struct osim_file_desc *alloc_df(void *ctx, uint16_t fid, const char *name) { struct osim_file_desc *mf; mf = talloc_zero(ctx, struct osim_file_desc); if (!mf) return NULL; mf->type = TYPE_DF; mf->fid = fid; mf->short_name = name; INIT_LLIST_HEAD(&mf->child_list); return mf; } struct osim_file_desc * add_df_with_ef(struct osim_file_desc *parent, uint16_t fid, const char *name, const struct osim_file_desc *in, int num) { struct osim_file_desc *df; df = alloc_df(parent, fid, name); if (!df) return NULL; df->parent = parent; llist_add_tail(&df->list, &parent->child_list); add_filedesc(df, in, num); return df; } struct osim_file_desc * add_adf_with_ef(struct osim_file_desc *parent, const uint8_t *adf_name, uint8_t adf_name_len, const char *name, const struct osim_file_desc *in, int num) { struct osim_file_desc *df; df = alloc_df(parent, 0xffff, name); if (!df) return NULL; df->type = TYPE_ADF; df->df_name = adf_name; df->df_name_len = adf_name_len; df->parent = parent; llist_add_tail(&df->list, &parent->child_list); add_filedesc(df, in, num); return df; } struct osim_file_desc * osim_file_find_name(struct osim_file_desc *parent, const char *name) { struct osim_file_desc *ofd; llist_for_each_entry(ofd, &parent->child_list, list) { if (!strcmp(ofd->short_name, name)) { return ofd; } } return NULL; } /*! \brief Generate an APDU message and initialize APDU command header * \param[in] cla CLASS byte * \param[in] ins INSTRUCTION byte * \param[in] p1 Parameter 1 byte * \param[in] p2 Parameter 2 byte * \param[in] lc number of bytes in the command data field Nc, which will encoded in 0, 1 or 3 bytes into Lc * \param[in] le maximum number of bytes expected in the response data field, which will encoded in 0, 1, 2 or 3 bytes into Le * \returns an APDU message generated using provided APDU parameters * * This function generates an APDU message, as defined in ISO/IEC 7816-4:2005(E) §5.1. * The APDU command header, command and response fields lengths are initialized using the parameters. * The APDU case is determined by the command and response fields lengths. */ struct msgb *osim_new_apdumsg(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint16_t lc, uint16_t le) { struct osim_apdu_cmd_hdr *ch; struct msgb *msg = msgb_alloc(lc+le+sizeof(*ch)+2, "APDU"); if (!msg) return NULL; ch = (struct osim_apdu_cmd_hdr *) msgb_put(msg, sizeof(*ch)); msg->l2h = (uint8_t *) ch; ch->cla = cla; ch->ins = ins; ch->p1 = p1; ch->p2 = p2; msgb_apdu_lc(msg) = lc; msgb_apdu_le(msg) = le; if (lc == 0 && le == 0) msgb_apdu_case(msg) = APDU_CASE_1; else if (lc == 0 && le >= 1) { if (le <= 256) msgb_apdu_case(msg) = APDU_CASE_2S; else msgb_apdu_case(msg) = APDU_CASE_2E; } else if (le == 0 && lc >= 1) { if (lc <= 255) msgb_apdu_case(msg) = APDU_CASE_3S; else msgb_apdu_case(msg) = APDU_CASE_3E; } else if (lc >= 1 && le >= 1) { if (lc <= 255 && le <= 256) msgb_apdu_case(msg) = APDU_CASE_4S; else msgb_apdu_case(msg) = APDU_CASE_4E; } return msg; } /* FIXME: do we want to mark this as __thread? */ static char sw_print_buf[256]; char *osim_print_sw(const struct osim_card_hdl *ch, uint16_t sw_in) { const struct osim_card_sw *csw; if (!ch || !ch->prof) goto ret_def; csw = osim_find_sw(ch->prof, sw_in); if (!csw) goto ret_def; switch (csw->type) { case SW_TYPE_STR: snprintf(sw_print_buf, sizeof(sw_print_buf), "%04x (%s)", sw_in, csw->u.str); break; default: goto ret_def; } sw_print_buf[sizeof(sw_print_buf)-1] = '\0'; return sw_print_buf; ret_def: snprintf(sw_print_buf, sizeof(sw_print_buf), "%04x (Unknown)", sw_in); sw_print_buf[sizeof(sw_print_buf)-1] = '\0'; return sw_print_buf; } const struct osim_card_sw *osim_find_sw(const struct osim_card_profile *cp, uint16_t sw_in) { const struct osim_card_sw **sw_lists = cp->sws; const struct osim_card_sw *sw_list, *sw; for (sw_list = *sw_lists++; sw_list != NULL; sw = sw_list = *sw_lists++) { for (sw = sw_list; sw->code != 0 && sw->mask != 0; sw++) { if ((sw_in & sw->mask) == sw->code) return sw; } } return NULL; } enum osim_card_sw_class osim_sw_class(const struct osim_card_profile *cp, uint16_t sw_in) { const struct osim_card_sw *csw = osim_find_sw(cp, sw_in); if (!csw) return SW_CLS_NONE; return csw->class; } int default_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data) { struct osim_decoded_element *elem; elem = element_alloc(dd, "Unknown Payload", ELEM_T_BYTES, ELEM_REPR_HEX); elem->u.buf = talloc_memdup(elem, data, len); return 0; } libosmocore-0.9.0/src/sim/gsm_int.h000066400000000000000000000010221261607044000172200ustar00rootroot00000000000000#include #include int osim_int_cprof_add_gsm(struct osim_file_desc *mf); int osim_int_cprof_add_telecom(struct osim_file_desc *mf); int gsm_hpplmn_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); int gsm_lp_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); int gsm_imsi_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); libosmocore-0.9.0/src/sim/reader.c000066400000000000000000000145651261607044000170350ustar00rootroot00000000000000/* Card reader abstraction for libosmosim */ /* * (C) 2012 by Harald Welte * * 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 #include #include #include #include #include #include #include #include "sim_int.h" /* remove the SW from end of the message */ static int get_sw(struct msgb *resp) { int ret; if (!msgb_apdu_de(resp) || msgb_apdu_le(resp) < 2) return -EIO; ret = msgb_get_u16(resp); return ret; } /* According to ISO7816-4 Annex A */ static int transceive_apdu_t0(struct osim_card_hdl *st, struct msgb *amsg) { struct osim_reader_hdl *rh = st->reader; struct msgb *tmsg = msgb_alloc(1024, "TPDU"); struct osim_apdu_cmd_hdr *tpduh; uint8_t *cur; uint16_t sw; int rc, num_resp = 0; if (!tmsg) return -ENOMEM; /* create TPDU header from APDU header */ tpduh = (struct osim_apdu_cmd_hdr *) msgb_put(tmsg, sizeof(*tpduh)); memcpy(tpduh, msgb_apdu_h(amsg), sizeof(*tpduh)); switch (msgb_apdu_case(amsg)) { case APDU_CASE_1: tpduh->p3 = 0x00; break; case APDU_CASE_2S: tpduh->p3 = msgb_apdu_le(amsg); break; case APDU_CASE_2E: if (msgb_apdu_le(amsg) <= 256) { /* case 2E.1 */ tpduh->p3 = msgb_apdu_le(amsg) & 0xff; } else { /* case 2E.2 */ tpduh->p3 = 0; msgb_put_u16(tmsg, msgb_apdu_le(amsg)); } break; case APDU_CASE_3S: case APDU_CASE_4S: tpduh->p3 = msgb_apdu_lc(amsg); cur = msgb_put(tmsg, tpduh->p3); memcpy(cur, msgb_apdu_dc(amsg), tpduh->p3); break; case APDU_CASE_3E: case APDU_CASE_4E: if (msgb_apdu_lc(amsg) < 256) { /* Case 3E.1 */ tpduh->p3 = msgb_apdu_lc(amsg); } else { /* Case 3E.2 */ /* FXIME: Split using ENVELOPE! */ return -1; } break; } transceive_again: /* store pointer to start of response */ tmsg->l3h = tmsg->tail; /* transceive */ rc = rh->ops->transceive(st->reader, tmsg); if (rc < 0) { msgb_free(tmsg); return rc; } msgb_apdu_sw(tmsg) = get_sw(tmsg); /* increase number of responsese received */ num_resp++; /* save SW */ sw = msgb_apdu_sw(tmsg); printf("sw = 0x%04x\n", sw); msgb_apdu_sw(amsg) = sw; switch (msgb_apdu_case(amsg)) { case APDU_CASE_1: case APDU_CASE_3S: /* just copy SW */ break; case APDU_CASE_2S: case_2s: switch (sw >> 8) { case 0x67: /* Case 2S.2: Le definitely not accepted */ break; case 0x6c: /* Case 2S.3: Le not accepted, La indicated */ tpduh->p3 = sw & 0xff; /* re-issue the command with La as */ goto transceive_again; break; case 0x90: /* Case 2S.1, fall-through */ case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: /* Case 2S.4 */ /* copy response data over */ cur = msgb_put(amsg, msgb_l3len(tmsg)); memcpy(cur, tmsg->l3h, msgb_l3len(tmsg)); } break; case APDU_CASE_4S: /* FIXME: this is 4S.2 only for 2nd... response: */ if (num_resp >= 2) goto case_2s; switch (sw >> 8) { case 0x60: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: /* Case 4S.1: Command not accepted: just copy SW */ break; case 0x90: /* case 4S.2: Command accepted */ tpduh->ins = 0xC0; tpduh->p1 = tpduh->p2 = 0; tpduh->p3 = msgb_apdu_le(amsg); /* strip off current result */ msgb_get(tmsg, msgb_length(tmsg)-sizeof(*tpduh)); goto transceive_again; break; case 0x61: /* Case 4S.3: command accepted with info added */ case 0x9F: /* FIXME: This is specific to SIM cards */ tpduh->ins = 0xC0; tpduh->p1 = tpduh->p2 = 0; tpduh->p3 = OSMO_MIN(msgb_apdu_le(amsg), sw & 0xff); /* strip off current result */ msgb_get(tmsg, msgb_length(tmsg)-sizeof(*tpduh)); goto transceive_again; break; } /* Case 4S.2: Command accepted: just copy SW */ /* Case 4S.4: Just copy SW */ break; case APDU_CASE_2E: if (msgb_apdu_le(amsg) <= 256) { /* Case 2E.1: Le <= 256 */ goto case_2s; } switch (sw >> 8) { case 0x67: /* Case 2E.2a: wrong length, abort */ break; case 0x6c: /* Case 2E.2b: wrong length, La given */ tpduh->p3 = sw & 0xff; /* re-issue the command with La as given */ goto transceive_again; break; case 0x90: /* Case 2E.2c: */ break; case 0x61: /* Case 2E.2d: more data available */ /* FIXME: issue yet another GET RESPONSE */ break; } break; case APDU_CASE_3E: /* FIXME: handling for ENVELOPE splitting */ break; case APDU_CASE_4E: break; } msgb_free(tmsg); /* compute total length of response data */ msgb_apdu_le(amsg) = amsg->tail - msgb_apdu_de(amsg); return sw; } /* FIXME: T=1 According to ISO7816-4 Annex B */ int osim_transceive_apdu(struct osim_chan_hdl *st, struct msgb *amsg) { switch (st->card->proto) { case OSIM_PROTO_T0: return transceive_apdu_t0(st->card, amsg); default: return -ENOTSUP; } } struct osim_reader_hdl *osim_reader_open(enum osim_reader_driver driver, int idx, const char *name, void *ctx) { const struct osim_reader_ops *ops; struct osim_reader_hdl *rh; switch (driver) { case OSIM_READER_DRV_PCSC: ops = &pcsc_reader_ops; break; default: return NULL; } rh = ops->reader_open(idx, name, ctx); if (!rh) return NULL; rh->ops = ops; /* FIXME: for now we only do T=0 on all readers */ rh->proto_supported = (1 << OSIM_PROTO_T0); return rh; } struct osim_card_hdl *osim_card_open(struct osim_reader_hdl *rh, enum osim_proto proto) { struct osim_card_hdl *ch; if (!(rh->proto_supported & (1 << proto))) return NULL; ch = rh->ops->card_open(rh, proto); if (!ch) return NULL; ch->proto = proto; return ch; } libosmocore-0.9.0/src/sim/reader_pcsc.c000066400000000000000000000073461261607044000200440ustar00rootroot00000000000000/* PC/SC Card reader backend for libosmosim */ /* * (C) 2012 by Harald Welte * * 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 #include #include #include #include #include #include #include #include "sim_int.h" #define PCSC_ERROR(rv, text) \ if (rv != SCARD_S_SUCCESS) { \ fprintf(stderr, text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \ goto end; \ } else { \ printf(text ": OK\n\n"); \ } struct pcsc_reader_state { SCARDCONTEXT hContext; SCARDHANDLE hCard; DWORD dwActiveProtocol; const SCARD_IO_REQUEST *pioSendPci; SCARD_IO_REQUEST pioRecvPci; char *name; }; static struct osim_reader_hdl *pcsc_reader_open(int num, const char *id, void *ctx) { struct osim_reader_hdl *rh; struct pcsc_reader_state *st; LONG rc; LPSTR mszReaders = NULL; DWORD dwReaders; unsigned int num_readers; char *ptr; /* FIXME: implement matching on id or num */ rh = talloc_zero(ctx, struct osim_reader_hdl); st = rh->priv = talloc_zero(rh, struct pcsc_reader_state); rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &st->hContext); PCSC_ERROR(rc, "SCardEstablishContext"); dwReaders = SCARD_AUTOALLOCATE; rc = SCardListReaders(st->hContext, NULL, (LPSTR)&mszReaders, &dwReaders); PCSC_ERROR(rc, "SCardListReaders"); num_readers = 0; ptr = mszReaders; while (*ptr != '\0') { ptr += strlen(ptr)+1; num_readers++; } if (num_readers == 0) goto end; st->name = talloc_strdup(rh, mszReaders); st->dwActiveProtocol = -1; return rh; end: talloc_free(rh); return NULL; } static struct osim_card_hdl *pcsc_card_open(struct osim_reader_hdl *rh, enum osim_proto proto) { struct pcsc_reader_state *st = rh->priv; struct osim_card_hdl *card; struct osim_chan_hdl *chan; LONG rc; if (proto != OSIM_PROTO_T0) return NULL; rc = SCardConnect(st->hContext, st->name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0, &st->hCard, &st->dwActiveProtocol); PCSC_ERROR(rc, "SCardConnect"); st->pioSendPci = SCARD_PCI_T0; card = talloc_zero(rh, struct osim_card_hdl); INIT_LLIST_HEAD(&card->channels); card->reader = rh; rh->card = card; /* create a default channel */ chan = talloc_zero(card, struct osim_chan_hdl); chan->card = card; llist_add(&chan->list, &card->channels); return card; end: return NULL; } static int pcsc_transceive(struct osim_reader_hdl *rh, struct msgb *msg) { struct pcsc_reader_state *st = rh->priv; DWORD rlen = msgb_tailroom(msg); LONG rc; printf("TX: %s\n", osmo_hexdump(msg->data, msg->len)); rc = SCardTransmit(st->hCard, st->pioSendPci, msg->data, msgb_length(msg), &st->pioRecvPci, msg->tail, &rlen); PCSC_ERROR(rc, "SCardEndTransaction"); printf("RX: %s\n", osmo_hexdump(msg->tail, rlen)); msgb_put(msg, rlen); msgb_apdu_le(msg) = rlen; return 0; end: return -EIO; } const struct osim_reader_ops pcsc_reader_ops = { .name = "PC/SC", .reader_open = pcsc_reader_open, .card_open = pcsc_card_open, .transceive = pcsc_transceive, }; libosmocore-0.9.0/src/sim/sim_int.h000066400000000000000000000025621261607044000172340ustar00rootroot00000000000000#ifndef _SIM_INT_H #include struct osim_decoded_element * element_alloc(struct osim_decoded_data *dd, const char *name, enum osim_element_type type, enum osim_element_repr repr); struct osim_decoded_element * element_alloc_sub(struct osim_decoded_element *ee, const char *name, enum osim_element_type type, enum osim_element_repr repr); extern const struct osim_card_sw ts102221_uicc_sw[0]; int default_decode(struct osim_decoded_data *dd, const struct osim_file_desc *desc, int len, uint8_t *data); void add_filedesc(struct osim_file_desc *root, const struct osim_file_desc *in, int num); struct osim_file_desc *alloc_df(void *ctx, uint16_t fid, const char *name); struct osim_file_desc * add_df_with_ef(struct osim_file_desc *parent, uint16_t fid, const char *name, const struct osim_file_desc *in, int num); struct osim_file_desc * add_adf_with_ef(struct osim_file_desc *parent, const uint8_t *adf_name, uint8_t adf_name_len, const char *name, const struct osim_file_desc *in, int num); struct osim_reader_ops { const char *name; struct osim_reader_hdl *(*reader_open)(int idx, const char *name, void *ctx); struct osim_card_hdl *(*card_open)(struct osim_reader_hdl *rh, enum osim_proto proto); int (*transceive)(struct osim_reader_hdl *rh, struct msgb *msg); }; extern const struct osim_reader_ops pcsc_reader_ops; #endif libosmocore-0.9.0/src/socket.c000066400000000000000000000241661261607044000162710ustar00rootroot00000000000000/* * (C) 2011 by Harald Welte * * 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" /*! \addtogroup socket * @{ */ /*! \file socket.c * \brief Osmocom socket convenience functions */ #ifdef HAVE_SYS_SOCKET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \brief Initialize a socket (including bind/connect) * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] host remote host name or IP address in string form * \param[in] port remote port number in host byte order * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT * * This function creates a new socket of the designated \a family, \a * type and \a proto and optionally binds or connects it, depending on * the value of \a flags parameter. */ int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto, const char *host, uint16_t port, unsigned int flags) { struct addrinfo hints, *result, *rp; int sfd, rc, on = 1; char portbuf[16]; if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) return -EINVAL; sprintf(portbuf, "%u", port); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = family; if (type == SOCK_RAW) { /* Workaround for glibc, that returns EAI_SERVICE (-8) if * SOCK_RAW and IPPROTO_GRE is used. */ hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; } else { hints.ai_socktype = type; hints.ai_protocol = proto; } if (flags & OSMO_SOCK_F_BIND) hints.ai_flags |= AI_PASSIVE; rc = getaddrinfo(host, portbuf, &hints, &result); if (rc != 0) { perror("getaddrinfo returned NULL"); return -EINVAL; } for (rp = result; rp != NULL; rp = rp->ai_next) { /* Workaround for glibc again */ if (type == SOCK_RAW) { rp->ai_socktype = SOCK_RAW; rp->ai_protocol = proto; } sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (flags & OSMO_SOCK_F_NONBLOCK) { if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) { perror("cannot set this socket unblocking"); close(sfd); return -EINVAL; } } if (flags & OSMO_SOCK_F_CONNECT) { rc = connect(sfd, rp->ai_addr, rp->ai_addrlen); if (rc != -1 || (rc == -1 && errno == EINPROGRESS)) break; } else { rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (rc < 0) { perror("cannot setsockopt socket"); break; } if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; } close(sfd); } freeaddrinfo(result); if (rp == NULL) { perror("unable to connect/bind socket"); return -ENODEV; } setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /* Make sure to call 'listen' on a bound, connection-oriented sock */ if (flags & OSMO_SOCK_F_BIND) { switch (type) { case SOCK_STREAM: case SOCK_SEQPACKET: listen(sfd, 10); break; } } return sfd; } /*! \brief fill \ref osmo_fd for a give sfd * \param[out] ofd file descriptor (will be filled in) * \param[in] sfd socket file descriptor * * This function fills the \a ofd structure. */ static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd) { int rc; if (sfd < 0) return sfd; ofd->fd = sfd; ofd->when = BSC_FD_READ; rc = osmo_fd_register(ofd); if (rc < 0) { close(sfd); return rc; } return sfd; } /*! \brief Initialize a socket and fill \ref osmo_fd * \param[out] ofd file descriptor (will be filled in) * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] host remote host name or IP address in string form * \param[in] port remote port number in host byte order * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT * * This function creates (and optionall binds/connects) a socket using * \ref osmo_sock_init, but also fills the \a ofd structure. */ int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto, const char *host, uint16_t port, unsigned int flags) { return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags)); } /*! \brief Initialize a socket and fill \ref sockaddr * \param[out] ss socket address (will be filled in) * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT * * This function creates (and optionall binds/connects) a socket using * \ref osmo_sock_init, but also fills the \a ss structure. */ int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type, uint8_t proto, unsigned int flags) { char host[NI_MAXHOST]; uint16_t port; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int s, sa_len; /* determine port and host from ss */ switch (ss->sa_family) { case AF_INET: sin = (struct sockaddr_in *) ss; sa_len = sizeof(struct sockaddr_in); port = ntohs(sin->sin_port); break; case AF_INET6: sin6 = (struct sockaddr_in6 *) ss; sa_len = sizeof(struct sockaddr_in6); port = ntohs(sin6->sin6_port); break; default: return -EINVAL; } s = getnameinfo(ss, sa_len, host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (s != 0) { perror("getnameinfo failed"); return s; } return osmo_sock_init(ss->sa_family, type, proto, host, port, flags); } static int sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b, unsigned int len) { struct sockaddr_in *sin_a, *sin_b; struct sockaddr_in6 *sin6_a, *sin6_b; if (a->sa_family != b->sa_family) return 0; switch (a->sa_family) { case AF_INET: sin_a = (struct sockaddr_in *)a; sin_b = (struct sockaddr_in *)b; if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr, sizeof(struct in_addr))) return 1; break; case AF_INET6: sin6_a = (struct sockaddr_in6 *)a; sin6_b = (struct sockaddr_in6 *)b; if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, sizeof(struct in6_addr))) return 1; break; } return 0; } /*! \brief Determine if the given address is a local address * \param[in] addr Socket Address * \param[in] addrlen Length of socket address in bytes * \returns 1 if address is local, 0 otherwise. */ int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen) { struct ifaddrs *ifaddr, *ifa; if (getifaddrs(&ifaddr) == -1) { perror("getifaddrs"); return -EIO; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) return 1; } return 0; } /*! \brief Initialize a unix domain socket (including bind/connect) * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] socket_path path to identify the socket * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT * * This function creates a new unix domain socket, \a * type and \a proto and optionally binds or connects it, depending on * the value of \a flags parameter. */ int osmo_sock_unix_init(uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags) { struct sockaddr_un local; int sfd, rc, on = 1; unsigned int namelen; if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) return -EINVAL; local.sun_family = AF_UNIX; strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); local.sun_path[sizeof(local.sun_path) - 1] = '\0'; #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else namelen = strlen(local.sun_path) + offsetof(struct sockaddr_un, sun_path); #endif sfd = socket(AF_UNIX, type, proto); if (sfd < 0) return -1; if (flags & OSMO_SOCK_F_CONNECT) { rc = connect(sfd, (struct sockaddr *)&local, namelen); if (rc < 0) goto err; } else { unlink(local.sun_path); rc = bind(sfd, (struct sockaddr *)&local, namelen); if (rc < 0) goto err; } if (flags & OSMO_SOCK_F_NONBLOCK) { if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) { perror("cannot set this socket unblocking"); close(sfd); return -EINVAL; } } if (flags & OSMO_SOCK_F_BIND) { rc = listen(sfd, 10); if (rc < 0) goto err; } return sfd; err: close(sfd); return -1; } /*! \brief Initialize a unix domain socket and fill \ref osmo_fd * \param[out] ofd file descriptor (will be filled in) * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP * \param[in] socket_path path to identify the socket * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT * * This function creates (and optionall binds/connects) a socket using * \ref osmo_sock_unix_init, but also fills the \a ofd structure. */ int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto, const char *socket_path, unsigned int flags) { return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags)); } #endif /* HAVE_SYS_SOCKET_H */ /*! @} */ libosmocore-0.9.0/src/stat_item.c000066400000000000000000000150761261607044000167720ustar00rootroot00000000000000/* utility routines for keeping conters about events and the event rates */ /* (C) 2015 by Sysmocom s.f.m.c. GmbH * (C) 2009-2010 by Harald Welte * * 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. * */ /*! \addtogroup osmo_stat_item * @{ */ /*! \file stat_item.c */ #include #include #include #include #include #include #include static LLIST_HEAD(osmo_stat_item_groups); static int32_t global_value_id = 0; static void *tall_stat_item_ctx; /*! \brief Allocate a new group of counters according to description * \param[in] ctx \ref talloc context * \param[in] desc Statistics item group description * \param[in] idx Index of new stat item group */ struct osmo_stat_item_group *osmo_stat_item_group_alloc(void *ctx, const struct osmo_stat_item_group_desc *desc, unsigned int idx) { unsigned int group_size; unsigned int items_size = 0; unsigned int item_idx; void *items; struct osmo_stat_item_group *group; group_size = sizeof(struct osmo_stat_item_group) + desc->num_items * sizeof(struct osmo_stat_item *); if (!ctx) ctx = tall_stat_item_ctx; group = talloc_zero_size(ctx, group_size); if (!group) return NULL; group->desc = desc; group->idx = idx; /* Get combined size of all items */ for (item_idx = 0; item_idx < desc->num_items; item_idx++) { unsigned int size; size = sizeof(struct osmo_stat_item) + sizeof(struct osmo_stat_item_value) * desc->item_desc[item_idx].num_values; /* Align to pointer size */ size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1); /* Store offsets into the item array */ group->items[item_idx] = (void *)items_size; items_size += size; } items = talloc_zero_size(group, items_size); if (!items) { talloc_free(group); return NULL; } /* Update item pointers */ for (item_idx = 0; item_idx < desc->num_items; item_idx++) { struct osmo_stat_item *item = (struct osmo_stat_item *) ((uint8_t *)items + (int)group->items[item_idx]); unsigned int i; group->items[item_idx] = item; item->last_offs = desc->item_desc[item_idx].num_values - 1; item->last_value_index = -1; item->desc = &desc->item_desc[item_idx]; for (i = 0; i <= item->last_offs; i++) { item->values[i].value = desc->item_desc[item_idx].default_value; item->values[i].id = STAT_ITEM_NOVALUE_ID; } } llist_add(&group->list, &osmo_stat_item_groups); return group; } /*! \brief Free the memory for the specified group of counters */ void osmo_stat_item_group_free(struct osmo_stat_item_group *grp) { llist_del(&grp->list); talloc_free(grp); } void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value) { item->last_offs += 1; if (item->last_offs >= item->desc->num_values) item->last_offs = 0; global_value_id += 1; if (global_value_id == STAT_ITEM_NOVALUE_ID) global_value_id += 1; item->values[item->last_offs].value = value; item->values[item->last_offs].id = global_value_id; } int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_idx, int32_t *value) { const struct osmo_stat_item_value *next_value; const struct osmo_stat_item_value *item_value = NULL; int idx_delta; int next_offs; next_offs = item->last_offs; next_value = &item->values[next_offs]; while (next_value->id - *next_idx >= 0 && next_value->id != STAT_ITEM_NOVALUE_ID) { item_value = next_value; next_offs -= 1; if (next_offs < 0) next_offs = item->desc->num_values - 1; if (next_offs == item->last_offs) break; next_value = &item->values[next_offs]; } if (!item_value) /* All items have been read */ return 0; *value = item_value->value; idx_delta = item_value->id + 1 - *next_idx; *next_idx = item_value->id + 1; return idx_delta; } /*! \brief Skip all values of this item and update idx accordingly */ int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *idx) { int discarded = item->values[item->last_offs].id + 1 - *idx; *idx = item->values[item->last_offs].id + 1; return discarded; } /*! \brief Skip all values of all items and update idx accordingly */ int osmo_stat_item_discard_all(int32_t *idx) { int discarded = global_value_id + 1 - *idx; *idx = global_value_id + 1; return discarded; } /*! \brief Initialize the stat item module */ int osmo_stat_item_init(void *tall_ctx) { tall_stat_item_ctx = tall_ctx; return 0; } /*! \brief Search for item group based on group name and index */ struct osmo_stat_item_group *osmo_stat_item_get_group_by_name_idx( const char *name, const unsigned int idx) { struct osmo_stat_item_group *statg; llist_for_each_entry(statg, &osmo_stat_item_groups, list) { if (!statg->desc) continue; if (!strcmp(statg->desc->group_name_prefix, name) && statg->idx == idx) return statg; } return NULL; } /*! \brief Search for item group based on group name */ const struct osmo_stat_item *osmo_stat_item_get_by_name( const struct osmo_stat_item_group *statg, const char *name) { int i; const struct osmo_stat_item_desc *item_desc; if (!statg->desc) return NULL; for (i = 0; i < statg->desc->num_items; i++) { item_desc = &statg->desc->item_desc[i]; if (!strcmp(item_desc->name, name)) { return statg->items[i]; } } return NULL; } int osmo_stat_item_for_each_item(struct osmo_stat_item_group *statg, osmo_stat_item_handler_t handle_item, void *data) { int rc = 0; int i; for (i = 0; i < statg->desc->num_items; i++) { struct osmo_stat_item *item = statg->items[i]; rc = handle_item(statg, item, data); if (rc < 0) return rc; } return rc; } int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data) { struct osmo_stat_item_group *statg; int rc = 0; llist_for_each_entry(statg, &osmo_stat_item_groups, list) { rc = handle_group(statg, data); if (rc < 0) return rc; } return rc; } /*! @} */ libosmocore-0.9.0/src/statistics.c000066400000000000000000000037111261607044000171640ustar00rootroot00000000000000/* utility routines for keeping some statistics */ /* (C) 2009 by Harald Welte * * 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 #include #include #include static LLIST_HEAD(counters); void *tall_ctr_ctx; struct osmo_counter *osmo_counter_alloc(const char *name) { struct osmo_counter *ctr = talloc_zero(tall_ctr_ctx, struct osmo_counter); if (!ctr) return NULL; ctr->name = name; llist_add_tail(&ctr->list, &counters); return ctr; } void osmo_counter_free(struct osmo_counter *ctr) { llist_del(&ctr->list); talloc_free(ctr); } int osmo_counters_for_each(int (*handle_counter)(struct osmo_counter *, void *), void *data) { struct osmo_counter *ctr; int rc = 0; llist_for_each_entry(ctr, &counters, list) { rc = handle_counter(ctr, data); if (rc < 0) return rc; } return rc; } struct osmo_counter *osmo_counter_get_by_name(const char *name) { struct osmo_counter *ctr; llist_for_each_entry(ctr, &counters, list) { if (!strcmp(ctr->name, name)) return ctr; } return NULL; } int osmo_counter_difference(struct osmo_counter *ctr) { int delta = ctr->value - ctr->previous; ctr->previous = ctr->value; return delta; } libosmocore-0.9.0/src/stats.c000066400000000000000000000402161261607044000161310ustar00rootroot00000000000000/* * (C) 2015 by Sysmocom s.f.m.c. GmbH * * Author: Jacob Erlbeck * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STATS_DEFAULT_INTERVAL 5 /* secs */ #define STATS_DEFAULT_STATSD_BUFLEN 256 static LLIST_HEAD(osmo_stats_reporter_list); static void *osmo_stats_ctx = NULL; static int is_initialised = 0; static int32_t current_stat_item_index = 0; static struct osmo_stats_config s_stats_config = { .interval = STATS_DEFAULT_INTERVAL, }; struct osmo_stats_config *osmo_stats_config = &s_stats_config; static struct osmo_timer_list osmo_stats_timer; static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep); static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep); static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta); static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int value); static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta); static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int value); static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data, int data_len); static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep); static int update_srep_config(struct osmo_stats_reporter *srep) { int rc = 0; if (srep->running) { if (srep->close) rc = srep->close(srep); srep->running = 0; } if (!srep->enabled) return rc; if (srep->open) rc = srep->open(srep); else rc = 0; if (rc < 0) srep->enabled = 0; else srep->running = 1; return rc; } static void osmo_stats_timer_cb(void *data) { int interval = osmo_stats_config->interval; if (!llist_empty(&osmo_stats_reporter_list)) osmo_stats_report(); osmo_timer_schedule(&osmo_stats_timer, interval, 0); } static int start_timer() { if (!is_initialised) return -ESRCH; osmo_stats_timer.cb = osmo_stats_timer_cb; osmo_timer_schedule(&osmo_stats_timer, 0, 1); return 0; } struct osmo_stats_reporter *osmo_stats_reporter_alloc(enum osmo_stats_reporter_type type, const char *name) { struct osmo_stats_reporter *srep; srep = talloc_zero(osmo_stats_ctx, struct osmo_stats_reporter); OSMO_ASSERT(srep); srep->type = type; if (name) srep->name = talloc_strdup(srep, name); srep->fd = -1; llist_add(&srep->list, &osmo_stats_reporter_list); return srep; } void osmo_stats_reporter_free(struct osmo_stats_reporter *srep) { osmo_stats_reporter_disable(srep); llist_del(&srep->list); talloc_free(srep); } void osmo_stats_init(void *ctx) { osmo_stats_ctx = ctx; osmo_stat_item_discard_all(¤t_stat_item_index); is_initialised = 1; start_timer(); } struct osmo_stats_reporter *osmo_stats_reporter_find(enum osmo_stats_reporter_type type, const char *name) { struct osmo_stats_reporter *srep; llist_for_each_entry(srep, &osmo_stats_reporter_list, list) { if (srep->type != type) continue; if (srep->name != name) { if (name == NULL || srep->name == NULL || strcmp(name, srep->name) != 0) continue; } return srep; } return NULL; } int osmo_stats_reporter_set_remote_addr(struct osmo_stats_reporter *srep, const char *addr) { int rc; struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr; struct in_addr inaddr; if (!srep->have_net_config) return -ENOTSUP; OSMO_ASSERT(addr != NULL); rc = inet_pton(AF_INET, addr, &inaddr); if (rc <= 0) return -EINVAL; sock_addr->sin_addr = inaddr; sock_addr->sin_family = AF_INET; srep->dest_addr_len = sizeof(*sock_addr); talloc_free(srep->dest_addr_str); srep->dest_addr_str = talloc_strdup(srep, addr); return update_srep_config(srep); } int osmo_stats_reporter_set_remote_port(struct osmo_stats_reporter *srep, int port) { struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->dest_addr; if (!srep->have_net_config) return -ENOTSUP; srep->dest_port = port; sock_addr->sin_port = htons(port); return update_srep_config(srep); } int osmo_stats_reporter_set_local_addr(struct osmo_stats_reporter *srep, const char *addr) { int rc; struct sockaddr_in *sock_addr = (struct sockaddr_in *)&srep->bind_addr; struct in_addr inaddr; if (!srep->have_net_config) return -ENOTSUP; if (addr) { rc = inet_pton(AF_INET, addr, &inaddr); if (rc <= 0) return -EINVAL; } else { inaddr.s_addr = INADDR_ANY; } sock_addr->sin_addr = inaddr; sock_addr->sin_family = AF_INET; srep->bind_addr_len = addr ? sizeof(*sock_addr) : 0; talloc_free(srep->bind_addr_str); srep->bind_addr_str = addr ? talloc_strdup(srep, addr) : NULL; return update_srep_config(srep); } int osmo_stats_reporter_set_mtu(struct osmo_stats_reporter *srep, int mtu) { if (!srep->have_net_config) return -ENOTSUP; if (mtu < 0) return -EINVAL; srep->mtu = mtu; return update_srep_config(srep); } int osmo_stats_reporter_set_max_class(struct osmo_stats_reporter *srep, enum osmo_stats_class class_id) { if (class_id == OSMO_STATS_CLASS_UNKNOWN) return -EINVAL; srep->max_class = class_id; return 0; } int osmo_stats_set_interval(int interval) { if (interval <= 0) return -EINVAL; osmo_stats_config->interval = interval; if (is_initialised) start_timer(); return 0; } int osmo_stats_reporter_set_name_prefix(struct osmo_stats_reporter *srep, const char *prefix) { talloc_free(srep->name_prefix); srep->name_prefix = prefix ? talloc_strdup(srep, prefix) : NULL; return update_srep_config(srep); } int osmo_stats_reporter_enable(struct osmo_stats_reporter *srep) { srep->enabled = 1; return update_srep_config(srep); } int osmo_stats_reporter_disable(struct osmo_stats_reporter *srep) { srep->enabled = 0; return update_srep_config(srep); } static int osmo_stats_reporter_send(struct osmo_stats_reporter *srep, const char *data, int data_len) { int rc; rc = sendto(srep->fd, data, data_len, MSG_NOSIGNAL | MSG_DONTWAIT, &srep->dest_addr, srep->dest_addr_len); if (rc == -1) rc = -errno; return rc; } static int osmo_stats_reporter_send_buffer(struct osmo_stats_reporter *srep) { int rc; if (!srep->buffer || msgb_length(srep->buffer) == 0) return 0; rc = osmo_stats_reporter_send(srep, (const char *)msgb_data(srep->buffer), msgb_length(srep->buffer)); msgb_trim(srep->buffer, 0); return rc; } static int osmo_stats_reporter_check_config(struct osmo_stats_reporter *srep, unsigned int index, int class_id) { if (class_id == OSMO_STATS_CLASS_UNKNOWN) class_id = index != 0 ? OSMO_STATS_CLASS_SUBSCRIBER : OSMO_STATS_CLASS_GLOBAL; return class_id <= srep->max_class; } /*** log reporter ***/ struct osmo_stats_reporter *osmo_stats_reporter_create_log(const char *name) { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_LOG, name); srep->have_net_config = 0; srep->send_counter = osmo_stats_reporter_log_send_counter; srep->send_item = osmo_stats_reporter_log_send_item; return srep; } static int osmo_stats_reporter_log_send(struct osmo_stats_reporter *srep, const char *type, const char *name1, unsigned int index1, const char *name2, int value, const char *unit) { LOGP(DLSTATS, LOGL_INFO, "stats t=%s p=%s g=%s i=%u n=%s v=%d u=%s\n", type, srep->name_prefix ? srep->name_prefix : "", name1 ? name1 : "", index1, name2, value, unit ? unit : ""); return 0; } static int osmo_stats_reporter_log_send_counter(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta) { if (ctrg) return osmo_stats_reporter_log_send(srep, "c", ctrg->desc->group_name_prefix, ctrg->idx, desc->name, value, NULL); else return osmo_stats_reporter_log_send(srep, "c", NULL, 0, desc->name, value, NULL); } static int osmo_stats_reporter_log_send_item(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int value) { return osmo_stats_reporter_log_send(srep, "i", statg->desc->group_name_prefix, statg->idx, desc->name, value, desc->unit); } /*** statsd reporter ***/ struct osmo_stats_reporter *osmo_stats_reporter_create_statsd(const char *name) { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_alloc(OSMO_STATS_REPORTER_STATSD, name); srep->have_net_config = 1; srep->open = osmo_stats_reporter_statsd_open; srep->close = osmo_stats_reporter_statsd_close; srep->send_counter = osmo_stats_reporter_statsd_send_counter; srep->send_item = osmo_stats_reporter_statsd_send_item; return srep; } static int osmo_stats_reporter_statsd_open(struct osmo_stats_reporter *srep) { int sock; int rc; int buffer_size = STATS_DEFAULT_STATSD_BUFLEN; if (srep->fd != -1) osmo_stats_reporter_statsd_close(srep); sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) return -errno; if (srep->bind_addr_len > 0) { rc = bind(sock, &srep->bind_addr, srep->bind_addr_len); if (rc == -1) goto failed; } srep->fd = sock; if (srep->mtu > 0) { buffer_size = srep->mtu - 20 /* IP */ - 8 /* UDP */; srep->agg_enabled = 1; } srep->buffer = msgb_alloc(buffer_size, "stats buffer"); return 0; failed: rc = -errno; close(sock); return rc; } static int osmo_stats_reporter_statsd_close(struct osmo_stats_reporter *srep) { int rc; if (srep->fd == -1) return -EBADF; osmo_stats_reporter_send_buffer(srep); rc = close(srep->fd); srep->fd = -1; msgb_free(srep->buffer); srep->buffer = NULL; return rc == -1 ? -errno : 0; } static int osmo_stats_reporter_statsd_send(struct osmo_stats_reporter *srep, const char *name1, unsigned int index1, const char *name2, int value, const char *unit) { char *buf; int buf_size; int nchars, rc = 0; char *fmt = NULL; int old_len = msgb_length(srep->buffer); if (name1) { if (index1 != 0) fmt = "%1$s.%2$s.%6$u.%3$s:%4$d|%5$s"; else fmt = "%1$s.%2$s.%3$s:%4$d|%5$s"; } else { fmt = "%1$s.%2$0.0s%3$s:%4$d|%5$s"; } if (!srep->name_prefix) fmt += 5; /* skip prefix part */ if (srep->agg_enabled) { if (msgb_length(srep->buffer) > 0 && msgb_tailroom(srep->buffer) > 0) { msgb_put_u8(srep->buffer, '\n'); } } buf = (char *)msgb_put(srep->buffer, 0); buf_size = msgb_tailroom(srep->buffer); nchars = snprintf(buf, buf_size, fmt, srep->name_prefix, name1, name2, value, unit, index1); if (nchars >= buf_size) { /* Truncated */ /* Restore original buffer (without trailing LF) */ msgb_trim(srep->buffer, old_len); /* Send it */ rc = osmo_stats_reporter_send_buffer(srep); /* Try again */ buf = (char *)msgb_put(srep->buffer, 0); buf_size = msgb_tailroom(srep->buffer); nchars = snprintf(buf, buf_size, fmt, srep->name_prefix, name1, name2, value, unit, index1); if (nchars >= buf_size) return -EMSGSIZE; } if (nchars > 0) msgb_trim(srep->buffer, msgb_length(srep->buffer) + nchars); if (!srep->agg_enabled) rc = osmo_stats_reporter_send_buffer(srep); return rc; } static int osmo_stats_reporter_statsd_send_counter(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta) { if (ctrg) return osmo_stats_reporter_statsd_send(srep, ctrg->desc->group_name_prefix, ctrg->idx, desc->name, delta, "c"); else return osmo_stats_reporter_statsd_send(srep, NULL, 0, desc->name, delta, "c"); } static int osmo_stats_reporter_statsd_send_item(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int value) { return osmo_stats_reporter_statsd_send(srep, statg->desc->group_name_prefix, statg->idx, desc->name, value, desc->unit); } /*** generic rate counter support ***/ static int osmo_stats_reporter_send_counter(struct osmo_stats_reporter *srep, const struct rate_ctr_group *ctrg, const struct rate_ctr_desc *desc, int64_t value, int64_t delta) { if (!srep->send_counter) return 0; return srep->send_counter(srep, ctrg, desc, value, delta); } static int rate_ctr_handler( struct rate_ctr_group *ctrg, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *sctx_) { struct osmo_stats_reporter *srep; int64_t delta = rate_ctr_difference(ctr); if (delta == 0) return 0; llist_for_each_entry(srep, &osmo_stats_reporter_list, list) { if (!srep->running) continue; if (!osmo_stats_reporter_check_config(srep, ctrg->idx, ctrg->desc->class_id)) return 0; osmo_stats_reporter_send_counter(srep, ctrg, desc, ctr->current, delta); /* TODO: handle result (log?, inc counter(!)?) or remove it */ } return 0; } static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_) { rate_ctr_for_each_counter(ctrg, rate_ctr_handler, sctx_); return 0; } /*** stat item support ***/ static int osmo_stats_reporter_send_item(struct osmo_stats_reporter *srep, const struct osmo_stat_item_group *statg, const struct osmo_stat_item_desc *desc, int32_t value) { if (!srep->send_item) return 0; return srep->send_item(srep, statg, desc, value); } static int osmo_stat_item_handler( struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_) { struct osmo_stats_reporter *srep; int32_t idx = current_stat_item_index; int32_t value; while (osmo_stat_item_get_next(item, &idx, &value) > 0) { llist_for_each_entry(srep, &osmo_stats_reporter_list, list) { if (!srep->running) continue; if (!osmo_stats_reporter_check_config(srep, statg->idx, statg->desc->class_id)) return 0; osmo_stats_reporter_send_item(srep, statg, item->desc, value); } } return 0; } static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *sctx_) { osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, sctx_); osmo_stat_item_discard_all(¤t_stat_item_index); return 0; } /*** osmo counter support ***/ static int handle_counter(struct osmo_counter *counter, void *sctx_) { struct osmo_stats_reporter *srep; struct rate_ctr_desc desc = {0}; /* Fake a rate counter description */ desc.name = counter->name; desc.description = counter->description; int delta = osmo_counter_difference(counter); if (delta == 0) return 0; llist_for_each_entry(srep, &osmo_stats_reporter_list, list) { if (!srep->running) continue; osmo_stats_reporter_send_counter(srep, NULL, &desc, counter->value, delta); /* TODO: handle result (log?, inc counter(!)?) */ } return 0; } /*** main reporting function ***/ static void flush_all_reporters() { struct osmo_stats_reporter *srep; llist_for_each_entry(srep, &osmo_stats_reporter_list, list) { if (!srep->running) continue; osmo_stats_reporter_send_buffer(srep); } } int osmo_stats_report() { osmo_counters_for_each(handle_counter, NULL); rate_ctr_for_each_group(rate_ctr_group_handler, NULL); osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL); flush_all_reporters(); return 0; } libosmocore-0.9.0/src/strrb.c000066400000000000000000000122041261607044000161230ustar00rootroot00000000000000/* Ringbuffer implementation, tailored for logging. * This is a lossy ringbuffer. It keeps up to N of the newest messages, * overwriting the oldest as newer ones come in. * * (C) 2012-2013, Katerina Barone-Adesi * 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. * */ /*! \file strrb.c * \brief Lossy string ringbuffer for logging; keeps newest messages. */ #include #include #include #include /* Ringbuffer assumptions, invarients, and notes: * - start is the index of the first used index slot in the ring buffer. * - end is the index of the next index slot in the ring buffer. * - start == end => buffer is empty * - Consequence: the buffer can hold at most size - 1 messages * (if this were not the case, full and empty buffers would be indistinguishable * given the conventions in this implementation). * - Whenever the ringbuffer is full, start is advanced. The second oldest * message becomes unreachable by valid indexes (end is not a valid index) * and the oldest message is overwritten (if there was a message there, which * is the case unless this is the first time the ringbuffer becomes full). */ /*! \brief Create an empty, initialized osmo_strrb. * \param[in] ctx The talloc memory context which should own this. * \param[in] rb_size The number of message slots the osmo_strrb can hold. * \returns A struct osmo_strrb* on success, NULL in case of error. * * This function creates and initializes a ringbuffer. * Note that the ringbuffer stores at most rb_size - 1 messages. */ struct osmo_strrb *osmo_strrb_create(TALLOC_CTX * ctx, size_t rb_size) { struct osmo_strrb *rb = NULL; unsigned int i; rb = talloc_zero(ctx, struct osmo_strrb); if (!rb) goto alloc_error; /* start and end are zero already, which is correct */ rb->size = rb_size; rb->buffer = talloc_array(rb, char *, rb->size); if (!rb->buffer) goto alloc_error; for (i = 0; i < rb->size; i++) { rb->buffer[i] = talloc_zero_size(rb->buffer, RB_MAX_MESSAGE_SIZE); if (!rb->buffer[i]) goto alloc_error; } return rb; alloc_error: /* talloc_free(NULL) is safe */ talloc_free(rb); return NULL; } /*! \brief Check if an osmo_strrb is empty. * \param[in] rb The osmo_strrb to check. * \returns True if the osmo_strrb is empty, false otherwise. */ bool osmo_strrb_is_empty(const struct osmo_strrb *rb) { return rb->end == rb->start; } /*! \brief Return a pointer to the Nth string in the osmo_strrb. * \param[in] rb The osmo_strrb to search. * \param[in] string_index The index sought (N), zero-indexed. * * Return a pointer to the Nth string in the osmo_strrb. * Return NULL if there is no Nth string. * Note that N is zero-indexed. * \returns A pointer to the target string on success, NULL in case of error. */ const char *osmo_strrb_get_nth(const struct osmo_strrb *rb, unsigned int string_index) { unsigned int offset = string_index + rb->start; if ((offset >= rb->size) && (rb->start > rb->end)) offset -= rb->size; if (_osmo_strrb_is_bufindex_valid(rb, offset)) return rb->buffer[offset]; return NULL; } bool _osmo_strrb_is_bufindex_valid(const struct osmo_strrb *rb, unsigned int bufi) { if (osmo_strrb_is_empty(rb)) return 0; if (bufi >= rb->size) return 0; if (rb->start < rb->end) return (bufi >= rb->start) && (bufi < rb->end); return (bufi < rb->end) || (bufi >= rb->start); } /*! \brief Count the number of log messages in an osmo_strrb. * \param[in] rb The osmo_strrb to count the elements of. * * \returns The number of log messages in the osmo_strrb. */ size_t osmo_strrb_elements(const struct osmo_strrb *rb) { if (rb->end < rb->start) return rb->end + (rb->size - rb->start); return rb->end - rb->start; } /*! \brief Add a string to the osmo_strrb. * \param[in] rb The osmo_strrb to add to. * \param[in] data The string to add. * * Add a message to the osmo_strrb. * Older messages will be overwritten as necessary. * \returns 0 normally, 1 as a warning (ie, if data was truncated). */ int osmo_strrb_add(struct osmo_strrb *rb, const char *data) { size_t len = strlen(data); int ret = 0; if (len >= RB_MAX_MESSAGE_SIZE) { len = RB_MAX_MESSAGE_SIZE - 1; ret = 1; } memcpy(rb->buffer[rb->end], data, len); rb->buffer[rb->end][len] = '\0'; rb->end += 1; rb->end %= rb->size; /* The buffer is full; oldest message is forgotten - see notes above */ if (rb->end == rb->start) { rb->start += 1; rb->start %= rb->size; } return ret; } libosmocore-0.9.0/src/talloc.c000066400000000000000000001164171261607044000162600ustar00rootroot00000000000000/* 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 . */ /* 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 #include #include #define __USE_GNU #include #undef __USE_GNU #include #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; } libosmocore-0.9.0/src/timer.c000066400000000000000000000161721261607044000161170ustar00rootroot00000000000000/* * (C) 2008,2009 by Holger Hans Peter Freyther * (C) 2011 by Harald Welte * All Rights Reserved * * Authors: Holger Hans Peter Freyther * Harald Welte * Pablo Neira Ayuso * * 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. * */ /*! \addtogroup timer * @{ */ /*! \file timer.c */ #include #include #include #include #include #include /* These store the amount of time that we wait until next timer expires. */ static struct timeval nearest; static struct timeval *nearest_p; static struct rb_root timer_root = RB_ROOT; static void __add_timer(struct osmo_timer_list *timer) { struct rb_node **new = &(timer_root.rb_node); struct rb_node *parent = NULL; while (*new) { struct osmo_timer_list *this; this = container_of(*new, struct osmo_timer_list, node); parent = *new; if (timercmp(&timer->timeout, &this->timeout, <)) new = &((*new)->rb_left); else new = &((*new)->rb_right); } rb_link_node(&timer->node, parent, new); rb_insert_color(&timer->node, &timer_root); } /*! \brief add a new timer to the timer management * \param[in] timer the timer that should be added */ void osmo_timer_add(struct osmo_timer_list *timer) { osmo_timer_del(timer); timer->active = 1; INIT_LLIST_HEAD(&timer->list); __add_timer(timer); } /*! \brief schedule a timer at a given future relative time * \param[in] timer the to-be-added timer * \param[in] seconds number of seconds from now * \param[in] microseconds number of microseconds from now * * This function can be used to (re-)schedule a given timer at a * specified number of seconds+microseconds in the future. It will * internally add it to the timer management data structures, thus * osmo_timer_add() is automatically called. */ void osmo_timer_schedule(struct osmo_timer_list *timer, int seconds, int microseconds) { struct timeval current_time; gettimeofday(¤t_time, NULL); timer->timeout.tv_sec = seconds; timer->timeout.tv_usec = microseconds; timeradd(&timer->timeout, ¤t_time, &timer->timeout); osmo_timer_add(timer); } /*! \brief delete a timer from timer management * \param[in] timer the to-be-deleted timer * * This function can be used to delete a previously added/scheduled * timer from the timer management code. */ void osmo_timer_del(struct osmo_timer_list *timer) { if (timer->active) { timer->active = 0; rb_erase(&timer->node, &timer_root); /* make sure this is not already scheduled for removal. */ if (!llist_empty(&timer->list)) llist_del_init(&timer->list); } } /*! \brief check if given timer is still pending * \param[in] timer the to-be-checked timer * \return 1 if pending, 0 otherwise * * This function can be used to determine whether a given timer * has alredy expired (returns 0) or is still pending (returns 1) */ int osmo_timer_pending(struct osmo_timer_list *timer) { return timer->active; } /*! \brief compute the remaining time of a timer * \param[in] timer the to-be-checked timer * \param[in] now the current time (NULL if not known) * \param[out] remaining remaining time until timer fires * \return 0 if timer has not expired yet, -1 if it has * * This function can be used to determine the amount of time * remaining until the expiration of the timer. */ int osmo_timer_remaining(const struct osmo_timer_list *timer, const struct timeval *now, struct timeval *remaining) { struct timeval current_time; if (!now) gettimeofday(¤t_time, NULL); else current_time = *now; timersub(&timer->timeout, ¤t_time, remaining); if (remaining->tv_sec < 0) return -1; return 0; } /*! \brief Determine time between now and the nearest timer * \returns pointer to timeval of nearest timer, NULL if there is none * * if we have a nearest time return the delta between the current * time and the time of the nearest timer. * If the nearest timer timed out return NULL and then we will * dispatch everything after the select */ struct timeval *osmo_timers_nearest(void) { /* nearest_p is exactly what we need already: NULL if nothing is * waiting, {0,0} if we must dispatch immediately, and the correct * delay if we need to wait */ return nearest_p; } static void update_nearest(struct timeval *cand, struct timeval *current) { if (cand->tv_sec != LONG_MAX) { if (timercmp(cand, current, >)) timersub(cand, current, &nearest); else { /* loop again inmediately */ nearest.tv_sec = 0; nearest.tv_usec = 0; } nearest_p = &nearest; } else { nearest_p = NULL; } } /*! \brief Find the nearest time and update nearest_p */ void osmo_timers_prepare(void) { struct rb_node *node; struct timeval current; gettimeofday(¤t, NULL); node = rb_first(&timer_root); if (node) { struct osmo_timer_list *this; this = container_of(node, struct osmo_timer_list, node); update_nearest(&this->timeout, ¤t); } else { nearest_p = NULL; } } /*! \brief fire all timers... and remove them */ int osmo_timers_update(void) { struct timeval current_time; struct rb_node *node; struct llist_head timer_eviction_list; struct osmo_timer_list *this; int work = 0; gettimeofday(¤t_time, NULL); INIT_LLIST_HEAD(&timer_eviction_list); for (node = rb_first(&timer_root); node; node = rb_next(node)) { this = container_of(node, struct osmo_timer_list, node); if (timercmp(&this->timeout, ¤t_time, >)) break; llist_add(&this->list, &timer_eviction_list); } /* * The callbacks might mess with our list and in this case * even llist_for_each_entry_safe is not safe to use. To allow * osmo_timer_del to be called from within the callback we need * to restart the iteration for each element scheduled for removal. * * The problematic scenario is the following: Given two timers A * and B that have expired at the same time. Thus, they are both * in the eviction list in this order: A, then B. If we remove * timer B from the A's callback, we continue with B in the next * iteration step, leading to an access-after-release. */ restart: llist_for_each_entry(this, &timer_eviction_list, list) { osmo_timer_del(this); this->cb(this->data); work = 1; goto restart; } return work; } int osmo_timers_check(void) { struct rb_node *node; int i = 0; for (node = rb_first(&timer_root); node; node = rb_next(node)) { i++; } return i; } /*! @} */ libosmocore-0.9.0/src/utils.c000066400000000000000000000142031261607044000161300ustar00rootroot00000000000000/* * (C) 2011 by Harald Welte * (C) 2011 by Sylvain Munaut * (C) 2014 by Nils O. SelÃ¥sdal * * 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 #include #include #include #include /*! \addtogroup utils * @{ */ /*! \file utils.c */ static char namebuf[255]; /*! \brief get human-readable string for given value * \param[in] vs Array of value_string tuples * \param[in] val Value to be converted * \returns pointer to human-readable string */ 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); namebuf[sizeof(namebuf) - 1] = '\0'; return namebuf; } /*! \brief get numeric value for given human-readable string * \param[in] vs Array of value_string tuples * \param[in] str human-readable string * \returns numeric value (>0) or negative numer in case of error */ 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; } /*! \brief Convert BCD-encoded digit into printable character * \param[in] bcd A single BCD-encoded digit * \returns single printable character */ char osmo_bcd2char(uint8_t bcd) { if (bcd < 0xa) return '0' + bcd; else return 'A' + (bcd - 0xa); } /*! \brief Convert number in ASCII to BCD value * \param[in] c ASCII character * \returns BCD encoded value of character */ uint8_t osmo_char2bcd(char c) { return c - 0x30; } /*! \brief Parse a string ocntaining hexadecimal digits * \param[in] str string containing ASCII encoded hexadecimal digits * \param[out] b output buffer * \param[in] max_len maximum space in output buffer * \returns number of parsed octets, or -1 on error */ int osmo_hexparse(const char *str, uint8_t *b, int max_len) { int i, l, v; l = strlen(str); if ((l&1) || ((l>>1) > max_len)) return -1; memset(b, 0x00, max_len); for (i=0; i= '0' && c <= '9') v = c - '0'; else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a'); else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A'); else return -1; b[i>>1] |= v << (i&1 ? 0 : 4); } return i>>1; } static char hexd_buff[4096]; static const char hex_chars[] = "0123456789abcdef"; static char *_osmo_hexdump(const unsigned char *buf, int len, char *delim) { int i; char *cur = hexd_buff; hexd_buff[0] = 0; for (i = 0; i < len; i++) { const char *delimp = delim; int len_remain = sizeof(hexd_buff) - (cur - hexd_buff); if (len_remain < 3) break; *cur++ = hex_chars[buf[i] >> 4]; *cur++ = hex_chars[buf[i] & 0xf]; while (len_remain > 1 && *delimp) { *cur++ = *delimp++; len_remain--; } *cur = 0; } hexd_buff[sizeof(hexd_buff)-1] = 0; return hexd_buff; } /*! \brief Convert a sequence of unpacked bits to ASCII string * \param[in] bits A sequence of unpacked bits * \param[in] len Length of bits */ char *osmo_ubit_dump(const uint8_t *bits, unsigned int len) { int i; if (len > sizeof(hexd_buff)-1) len = sizeof(hexd_buff)-1; memset(hexd_buff, 0, sizeof(hexd_buff)); for (i = 0; i < len; i++) { char outch; switch (bits[i]) { case 0: outch = '0'; break; case 0xff: outch = '?'; break; case 1: outch = '1'; break; default: outch = 'E'; break; } hexd_buff[i] = outch; } hexd_buff[sizeof(hexd_buff)-1] = 0; return hexd_buff; } /*! \brief Convert binary sequence to hexadecimal ASCII string * \param[in] buf pointer to sequence of bytes * \param[in] len length of buf in number of bytes * \returns pointer to zero-terminated string * * This function will print a sequence of bytes as hexadecimal numbers, * adding one space character between each byte (e.g. "1a ef d9") */ char *osmo_hexdump(const unsigned char *buf, int len) { return _osmo_hexdump(buf, len, " "); } /*! \brief Convert binary sequence to hexadecimal ASCII string * \param[in] buf pointer to sequence of bytes * \param[in] len length of buf in number of bytes * \returns pointer to zero-terminated string * * This function will print a sequence of bytes as hexadecimal numbers, * without any space character between each byte (e.g. "1aefd9") */ char *osmo_hexdump_nospc(const unsigned char *buf, int len) { return _osmo_hexdump(buf, len, ""); } /* Compat with previous typo to preserve abi */ char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) #if defined(__MACH__) && defined(__APPLE__) ; #else __attribute__((weak, alias("osmo_hexdump_nospc"))); #endif #include "../config.h" #ifdef HAVE_CTYPE_H #include /*! \brief Convert an entire string to lower case * \param[out] out output string, caller-allocated * \param[in] in input string */ void osmo_str2lower(char *out, const char *in) { unsigned int i; for (i = 0; i < strlen(in); i++) out[i] = tolower(in[i]); out[strlen(in)] = '\0'; } /*! \brief Convert an entire string to upper case * \param[out] out output string, caller-allocated * \param[in] in input string */ void osmo_str2upper(char *out, const char *in) { unsigned int i; for (i = 0; i < strlen(in); i++) out[i] = toupper(in[i]); out[strlen(in)] = '\0'; } #endif /* HAVE_CTYPE_H */ /*! @} */ libosmocore-0.9.0/src/vty/000077500000000000000000000000001261607044000154465ustar00rootroot00000000000000libosmocore-0.9.0/src/vty/Makefile.am000066400000000000000000000010751261607044000175050ustar00rootroot00000000000000# 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=3:0:0 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -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 stats_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la endif libosmocore-0.9.0/src/vty/buffer.c000066400000000000000000000263641261607044000170760ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include #include #include #include /* 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, unsigned 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, (unsigned 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 , 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; } libosmocore-0.9.0/src/vty/command.c000066400000000000000000002341121261607044000172330ustar00rootroot00000000000000/* $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 #include #include #include #include #include #define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include /*! \addtogroup command * @{ */ /*! \file command.c */ #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 = ""; /*! \brief print the version (and optionally copyright) information * * 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; } /*! \brief Install top node of command vector. */ void install_node(struct cmd_node *node, int (*func) (struct vty *)) { vector_set_index(cmdvec, node->node, node); node->func = func; node->cmd_vector = vector_init(VECTOR_MIN_SIZE); } /* Compare two command's string. Used in sort_node (). */ static int cmp_node(const void *p, const void *q) { struct cmd_element *a = *(struct cmd_element **)p; struct cmd_element *b = *(struct cmd_element **)q; return strcmp(a->string, b->string); } static int cmp_desc(const void *p, const void *q) { struct desc *a = *(struct desc **)p; struct desc *b = *(struct desc **)q; return strcmp(a->cmd, b->cmd); } static int is_config_child(struct vty *vty) { if (vty->node <= CONFIG_NODE) return 0; else if (vty->node > CONFIG_NODE && vty->node < _LAST_OSMOVTY_NODE) return 1; else if (host.app_info->is_config_node) return host.app_info->is_config_node(vty, vty->node); else return vty->node > CONFIG_NODE; } /*! \brief Sort each node's command element according to command string. */ void sort_node(void) { 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; } } /*! \brief 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); } /*! \brief Fetch next description. Used in \ref 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; } /*! \brief 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; } /*! \brief 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; } static char *xml_escape(const char *inp) { int _strlen; char *out, *out_ptr; int len = 0, i, j; if (!inp) return NULL; _strlen = strlen(inp); for (i = 0; i < _strlen; ++i) { switch (inp[i]) { case '"': len += 6; break; case '\'': len += 6; break; case '<': len += 4; break; case '>': len += 4; break; case '&': len += 5; break; default: len += 1; break; } } out = talloc_size(NULL, len + 1); if (!out) return NULL; out_ptr = out; #define ADD(out, str) \ for (j = 0; j < strlen(str); ++j) \ *(out++) = str[j]; for (i = 0; i < _strlen; ++i) { switch (inp[i]) { case '"': ADD(out_ptr, """); break; case '\'': ADD(out_ptr, "'"); break; case '<': ADD(out_ptr, "<"); break; case '>': ADD(out_ptr, ">"); break; case '&': ADD(out_ptr, "&"); break; default: *(out_ptr++) = inp[i]; break; } } #undef ADD out_ptr[0] = '\0'; return out; } /* * Write one cmd_element as XML to the given VTY. */ static int vty_dump_element(struct cmd_element *cmd, struct vty *vty) { char *xml_string = xml_escape(cmd->string); vty_out(vty, " %s", xml_string, VTY_NEWLINE); vty_out(vty, " %s", VTY_NEWLINE); int j; for (j = 0; j < vector_count(cmd->strvec); ++j) { vector descvec = vector_slot(cmd->strvec, j); int i; for (i = 0; i < vector_active(descvec); ++i) { char *xml_param, *xml_doc; struct desc *desc = vector_slot(descvec, i); if (desc == NULL) continue; xml_param = xml_escape(desc->cmd); xml_doc = xml_escape(desc->str); vty_out(vty, " %s", xml_param, xml_doc, VTY_NEWLINE); talloc_free(xml_param); talloc_free(xml_doc); } } vty_out(vty, " %s", VTY_NEWLINE); vty_out(vty, " %s", VTY_NEWLINE); talloc_free(xml_string); return 0; } /* * Dump all nodes and commands associated with a given node as XML to the VTY. */ static int vty_dump_nodes(struct vty *vty) { int i, j; vty_out(vty, "%s", VTY_NEWLINE); for (i = 0; i < vector_active(cmdvec); ++i) { struct cmd_node *cnode; cnode = vector_slot(cmdvec, i); if (!cnode) continue; vty_out(vty, " %s", i, VTY_NEWLINE); for (j = 0; j < vector_active(cnode->cmd_vector); ++j) { struct cmd_element *elem; elem = vector_slot(cnode->cmd_vector, j); vty_dump_element(elem, vty); } vty_out(vty, " %s", VTY_NEWLINE); } vty_out(vty, "%s", VTY_NEWLINE); return 0; } /*! \brief Install a command into a node * \param[in] ntype Node Type * \param[cmd] element to be installed */ void install_element(int 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 = 0, any_match, extend_match, ipv4_prefix_match, ipv4_match, ipv6_prefix_match, ipv6_match, range_match, vararg_match, partly_match, exact_match, }; static enum match_type cmd_ipv4_match(const char *str) { const char *sp; int dots = 0, nums = 0; char buf[4]; if (str == NULL) return partly_match; for (;;) { memset(buf, 0, sizeof(buf)); sp = str; while (*str != '\0') { if (*str == '.') { if (dots >= 3) return no_match; if (*(str + 1) == '.') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit((int)*str)) return no_match; str++; } if (str - sp > 3) return no_match; strncpy(buf, sp, str - sp); if (atoi(buf) > 255) return no_match; nums++; if (*str == '\0') break; str++; } if (nums < 4) return partly_match; return exact_match; } static enum match_type cmd_ipv4_prefix_match(const char *str) { const char *sp; int dots = 0; char buf[4]; if (str == NULL) return partly_match; for (;;) { memset(buf, 0, sizeof(buf)); sp = str; while (*str != '\0' && *str != '/') { if (*str == '.') { if (dots == 3) return no_match; if (*(str + 1) == '.' || *(str + 1) == '/') return no_match; if (*(str + 1) == '\0') return partly_match; dots++; break; } if (!isdigit((int)*str)) return no_match; str++; } if (str - sp > 3) return no_match; strncpy(buf, sp, str - sp); if (atoi(buf) > 255) return no_match; if (dots == 3) { if (*str == '/') { if (*(str + 1) == '\0') return partly_match; str++; break; } else if (*str == '\0') return partly_match; } if (*str == '\0') return partly_match; str++; } sp = str; while (*str != '\0') { if (!isdigit((int)*str)) return no_match; str++; } if (atoi(sp) > 32) return no_match; return exact_match; } #define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" #define STATE_START 1 #define STATE_COLON 2 #define STATE_DOUBLE 3 #define STATE_ADDR 4 #define STATE_DOT 5 #define STATE_SLASH 6 #define STATE_MASK 7 #ifdef HAVE_IPV6 static enum match_type cmd_ipv6_match(const char *str) { int state = STATE_START; int colons = 0, nums = 0, double_colon = 0; const char *sp = NULL; struct sockaddr_in6 sin6_dummy; int ret; if (str == NULL) return partly_match; if (strspn(str, IPV6_ADDR_STR) != strlen(str)) return no_match; /* use inet_pton that has a better support, * for example inet_pton can support the automatic addresses: * ::1.2.3.4 */ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); if (ret == 1) return exact_match; while (*str != '\0') { switch (state) { case STATE_START: if (*str == ':') { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0') colons++; sp = str + 1; state = STATE_ADDR; } double_colon++; nums++; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '\0') { if (str - sp > 3) return no_match; nums++; state = STATE_COLON; } if (*(str + 1) == '.') state = STATE_DOT; break; case STATE_DOT: state = STATE_ADDR; break; default: break; } if (nums > 8) return no_match; if (colons > 7) return no_match; str++; } #if 0 if (nums < 11) return partly_match; #endif /* 0 */ return exact_match; } static enum match_type cmd_ipv6_prefix_match(const char *str) { int state = STATE_START; int colons = 0, nums = 0, double_colon = 0; int mask; const char *sp = NULL; char *endptr = NULL; if (str == NULL) return partly_match; if (strspn(str, IPV6_PREFIX_STR) != strlen(str)) return no_match; while (*str != '\0' && state != STATE_MASK) { switch (state) { case STATE_START: if (*str == ':') { if (*(str + 1) != ':' && *(str + 1) != '\0') return no_match; colons--; state = STATE_COLON; } else { sp = str; state = STATE_ADDR; } continue; case STATE_COLON: colons++; if (*(str + 1) == '/') return no_match; else if (*(str + 1) == ':') state = STATE_DOUBLE; else { sp = str + 1; state = STATE_ADDR; } break; case STATE_DOUBLE: if (double_colon) return no_match; if (*(str + 1) == ':') return no_match; else { if (*(str + 1) != '\0' && *(str + 1) != '/') colons++; sp = str + 1; if (*(str + 1) == '/') state = STATE_SLASH; else state = STATE_ADDR; } double_colon++; nums += 1; break; case STATE_ADDR: if (*(str + 1) == ':' || *(str + 1) == '.' || *(str + 1) == '\0' || *(str + 1) == '/') { if (str - sp > 3) return no_match; for (; sp <= str; sp++) if (*sp == '/') return no_match; nums++; if (*(str + 1) == ':') state = STATE_COLON; else if (*(str + 1) == '.') state = STATE_DOT; else if (*(str + 1) == '/') state = STATE_SLASH; } break; case STATE_DOT: state = STATE_ADDR; break; case STATE_SLASH: if (*(str + 1) == '\0') return partly_match; state = STATE_MASK; break; default: break; } if (nums > 11) return no_match; if (colons > 7) return no_match; str++; } if (state < STATE_MASK) return partly_match; mask = strtol(str, &endptr, 10); if (*endptr != '\0') return no_match; if (mask < 0 || mask > 128) return no_match; /* I don't know why mask < 13 makes command match partly. Forgive me to make this comments. I Want to set static default route because of lack of function to originate default in ospf6d; sorry yasu if (mask < 13) return partly_match; */ return exact_match; } #endif /* HAVE_IPV6 */ #define DECIMAL_STRLEN_MAX 10 static int cmd_range_match(const char *range, const char *str) { char *p; char buf[DECIMAL_STRLEN_MAX + 1]; char *endptr = NULL; if (str == NULL) return 1; if (range[1] == '-') { signed long min = 0, max = 0, val; val = strtol(str, &endptr, 10); if (*endptr != '\0') return 0; range += 2; p = strchr(range, '-'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; min = -strtol(buf, &endptr, 10); if (*endptr != '\0') return 0; range = p + 1; p = strchr(range, '>'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; max = strtol(buf, &endptr, 10); if (*endptr != '\0') return 0; if (val < min || val > max) return 0; } else { unsigned long min, max, val; val = strtoul(str, &endptr, 10); if (*endptr != '\0') return 0; range++; p = strchr(range, '-'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; min = strtoul(buf, &endptr, 10); if (*endptr != '\0') return 0; range = p + 1; p = strchr(range, '>'); if (p == NULL) return 0; if (p - range > DECIMAL_STRLEN_MAX) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; max = strtoul(buf, &endptr, 10); if (*endptr != '\0') return 0; if (val < min || val > max) return 0; } return 1; } /* helper to retrieve the 'real' argument string from an optional argument */ static char * cmd_deopt(const char *str) { /* we've got "[blah]". We want to strip off the []s and redo the * match check for "blah" */ size_t len = strlen(str); char *tmp; if (len < 3) return NULL; /* tmp will hold a string of len-2 chars, so 'len' size is fine */ tmp = talloc_size(NULL, len); memcpy(tmp, (str + 1), len - 2); tmp[len - 2] = '\0'; return tmp; } static enum match_type cmd_match(const char *str, const char *command, enum match_type min, bool recur) { if (recur && CMD_OPTION(str)) { enum match_type ret; char *tmp = cmd_deopt(str); /* this would be a bug in a command, however handle it gracefully * as it we only discover it if a user tries to run it */ if (tmp == NULL) return no_match; ret = cmd_match(tmp, command, min, false); talloc_free(tmp); return ret; } else if (CMD_VARARG(str)) return vararg_match; else if (CMD_RANGE(str)) { if (cmd_range_match(str, command)) return range_match; } #ifdef HAVE_IPV6 else if (CMD_IPV6(str)) { if (cmd_ipv6_match(command) >= min) return ipv6_match; } else if (CMD_IPV6_PREFIX(str)) { if (cmd_ipv6_prefix_match(command) >= min) return ipv6_prefix_match; } #endif /* HAVE_IPV6 */ else if (CMD_IPV4(str)) { if (cmd_ipv4_match(command) >= min) return ipv4_match; } else if (CMD_IPV4_PREFIX(str)) { if (cmd_ipv4_prefix_match(command) >= min) return ipv4_prefix_match; } else if (CMD_VARIABLE(str)) return extend_match; else if (strncmp(command, str, strlen(command)) == 0) { if (strcmp(command, str) == 0) return exact_match; else if (partly_match >= min) return partly_match; } return no_match; } /* Filter vector at the specified index and by the given command string, to * the desired matching level (thus allowing part matches), and return match * type flag. */ static enum match_type cmd_filter(char *command, vector v, unsigned int index, enum match_type level) { unsigned int i; 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))) { enum match_type ret; ret = cmd_match (desc->cmd, command, level, true); if (ret != no_match) matched++; if (match_type < ret) match_type = ret; } if (!matched) vector_slot(v, i) = NULL; } } if (match_type == no_match) return no_match; /* 2nd pass: We now know the 'strongest' match type for the index, so we * go again and filter out commands whose argument (at this index) is * 'weaker'. E.g., if we have 2 commands: * * foo bar <1-255> * foo bar BLAH * * and the command string is 'foo bar 10', then we will get here with with * 'range_match' being the strongest match. However, if 'BLAH' came * earlier, it won't have been filtered out (as a CMD_VARIABLE allows "10"). * * If we don't do a 2nd pass and filter it out, the higher-layers will * consider this to be ambiguous. */ 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))) { enum match_type ret; ret = cmd_match(desc->cmd, command, any_match, true); if (ret >= match_type) 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; 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; const char *str = desc->cmd; if (CMD_OPTION(str)) if ((str = cmd_deopt(str)) == NULL) continue; switch (type) { case exact_match: if (!(CMD_VARIABLE (str)) && strcmp(command, str) == 0) match++; break; case partly_match: if (!(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_VARIABLE (str)) match++; break; case no_match: default: break; } if (CMD_OPTION(desc->cmd)) talloc_free((void*)str); } 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 = { "", "" }; /* 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++) { command = vector_slot(vline, i); if (!command) continue; match = cmd_filter(command, cmd_vector, i, any_match); 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); vector_free(matchvec); *status = CMD_ERR_AMBIGUOUS; return NULL; } else if (ret == 2) { vector_free(cmd_vector); vector_free(matchvec); *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(command, cmd_vector, index, any_match); /* Make description vector. */ for (i = 0; i < vector_active(cmd_vector); i++) { const char *string = NULL; vector strvec; cmd_element = vector_slot(cmd_vector, i); if (!cmd_element) continue; if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN)) continue; 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 = ""; 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++) { desc = vector_slot(descvec, j); if (!desc) continue; string = cmd_entry_function_desc (command, desc->cmd); if (!string) continue; /* 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; vector_free(cmd_vector); 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(command, cmd_vector, i, any_match); /* 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 */ /* * This function MUST eventually converge on a node when called repeatedly, * there must not be any cycles. * All 'config' nodes shall converge on CONFIG_NODE. * All other 'enable' nodes shall converge on ENABLE_NODE. * All 'view' only nodes shall converge on VIEW_NODE. * All other nodes shall converge on themselves or it must be ensured, * that the user's rights are not extended anyhow by calling this function. * * Note that these requirements also apply to all functions that are used * as go_parent_cb. * Note also that this function relies on the is_config_child callback to * recognize non-config nodes if go_parent_cb is not set. */ int vty_go_parent(struct vty *vty) { switch (vty->node) { case AUTH_NODE: case VIEW_NODE: case ENABLE_NODE: case CONFIG_NODE: break; case AUTH_ENABLE_NODE: vty->node = VIEW_NODE; break; case CFG_LOG_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; default: if (host.app_info->go_parent_cb) host.app_info->go_parent_cb(vty); else if (is_config_child(vty)) vty->node = CONFIG_NODE; else vty->node = VIEW_NODE; break; } 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(command, cmd_vector, index, any_match); if (match == vararg_match) break; ret = is_cmd_ambiguous(command, cmd_vector, index, match); if (ret == 1) { vector_free(cmd_vector); return CMD_ERR_AMBIGUOUS; } else if (ret == 2) { vector_free(cmd_vector); return CMD_ERR_NO_MATCH; } } /* Check matched count. */ matched_element = NULL; matched_count = 0; incomplete_count = 0; for (i = 0; i < vector_active(cmd_vector); i++) if ((cmd_element = vector_slot(cmd_vector, i))) { if (match == vararg_match || index >= cmd_element->cmdsize) { matched_element = cmd_element; #if 0 printf("DEBUG: %s\n", cmd_element->string); #endif matched_count++; } else { incomplete_count++; } } /* Finish of using cmd_vector. */ vector_free(cmd_vector); /* To execute command, matched_count must be 1. */ if (matched_count == 0) { if (incomplete_count) return CMD_ERR_INCOMPLETE; else return CMD_ERR_NO_MATCH; } if (matched_count > 1) return CMD_ERR_AMBIGUOUS; /* Argument treatment */ varflag = 0; argc = 0; for (i = 0; i < vector_active(vline); i++) { if (varflag) argv[argc++] = vector_slot(vline, i); else { vector descvec = vector_slot(matched_element->strvec, i); if (vector_active(descvec) == 1) { struct desc *desc = vector_slot(descvec, 0); if (CMD_VARARG(desc->cmd)) varflag = 1; if (varflag || CMD_VARIABLE(desc->cmd) || CMD_OPTION(desc->cmd)) argv[argc++] = vector_slot(vline, i); } else argv[argc++] = vector_slot(vline, i); } if (argc >= CMD_ARGC_MAX) return CMD_ERR_EXEED_ARGC_MAX; } /* For vtysh execution. */ if (cmd) *cmd = matched_element; if (matched_element->daemon) return CMD_SUCCESS_DAEMON; /* Execute matched command. */ return (*matched_element->func) (matched_element, vty, argc, argv); } int cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd, int vtysh) { int ret, saved_ret, tried = 0; enum node_type onode; void *oindex; onode = vty->node; oindex = vty->index; if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { vector shifted_vline; unsigned int index; vty->node = ENABLE_NODE; /* We can try it on enable node, cos' the vty is authenticated */ shifted_vline = vector_init(vector_count(vline)); /* use memcpy? */ for (index = 1; index < vector_active(vline); index++) { vector_set_index(shifted_vline, index - 1, vector_lookup(vline, index)); } ret = cmd_execute_command_real(shifted_vline, vty, cmd); vector_free(shifted_vline); vty->node = onode; return ret; } saved_ret = ret = cmd_execute_command_real(vline, vty, cmd); if (vtysh) return saved_ret; /* Go to parent for config nodes to attempt to find the right command */ while (ret != CMD_SUCCESS && ret != CMD_WARNING && is_config_child(vty)) { vty_go_parent(vty); ret = cmd_execute_command_real(vline, vty, cmd); tried = 1; if (ret == CMD_SUCCESS || ret == CMD_WARNING) { /* succesfull command, leave the node as is */ return ret; } } /* no command succeeded, reset the vty to the original node and return the error for this node */ if (tried) { vty->node = onode; vty->index = oindex; } return saved_ret; } /* Execute command by argument readline. */ int cmd_execute_command_strict(vector vline, struct vty *vty, struct cmd_element **cmd) { unsigned int i; unsigned int index; vector cmd_vector; struct cmd_element *cmd_element; struct cmd_element *matched_element; unsigned int matched_count, incomplete_count; int argc; const char *argv[CMD_ARGC_MAX]; int varflag; enum match_type match = 0; char *command; /* Make copy of command element */ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node)); for (index = 0; index < vector_active(vline); index++) if ((command = vector_slot(vline, index))) { int ret; match = cmd_filter(vector_slot(vline, index), cmd_vector, index, exact_match); /* 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 && is_config_child(vty)) { vty_go_parent(vty); ret = cmd_execute_command_strict(vline, vty, NULL); } cmd_free_strvec(vline); if (ret != CMD_SUCCESS && ret != CMD_WARNING && ret != CMD_ERR_NOTHING_TODO) return ret; } return CMD_SUCCESS; } /* Configration from terminal */ DEFUN(config_terminal, config_terminal_cmd, "configure terminal", "Configuration from vty interface\n" "Configuration terminal\n") { if (vty_config_lock(vty)) vty->node = CONFIG_NODE; else { vty_out(vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } /* Enable command */ DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") { /* If enable password is NULL, change to ENABLE_NODE */ if ((host.enable == NULL && host.enable_encrypt == NULL) || vty->type == VTY_SHELL_SERV) vty->node = ENABLE_NODE; else vty->node = AUTH_ENABLE_NODE; return CMD_SUCCESS; } /* Disable command */ DEFUN(disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") { if (vty->node == ENABLE_NODE) vty->node = VIEW_NODE; return CMD_SUCCESS; } /* Down vty node level. */ gDEFUN(config_exit, config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") { switch (vty->node) { case AUTH_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; default: if (vty->node > CONFIG_NODE) vty_go_parent (vty); break; } return CMD_SUCCESS; } /* End of configuration. */ gDEFUN(config_end, config_end_cmd, "end", "End current mode and change to enable mode.") { if (vty->node > ENABLE_NODE) { int last_node = CONFIG_NODE; /* Repeatedly call go_parent until a top node is reached. */ while (vty->node > CONFIG_NODE) { if (vty->node == last_node) { /* Ensure termination, this shouldn't happen. */ break; } last_node = vty->node; vty_go_parent(vty); } vty_config_unlock(vty); if (vty->node > ENABLE_NODE) vty->node = ENABLE_NODE; vty->index = NULL; vty->index_sub = NULL; } 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; } DEFUN(show_online_help, show_online_help_cmd, "show online-help", SHOW_STR "Online help\n") { vty_dump_nodes(vty); 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; } static int write_config_file(const char *config_file, char **outpath) { unsigned int i; int fd; struct cmd_node *node; char *config_file_tmp = NULL; char *config_file_sav = NULL; struct vty *file_vty; struct stat st; *outpath = NULL; /* Check and see if we are operating under vtysh configuration */ 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) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_tmp); talloc_free(config_file_tmp); talloc_free(config_file_sav); return -1; } /* 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) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav); talloc_free(config_file_sav); talloc_free(config_file_tmp); unlink(config_file_tmp); return -2; } /* Only link the .sav file if the original file exists */ if (stat(config_file, &st) == 0) { if (link(config_file, config_file_sav) != 0) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file_sav); talloc_free(config_file_sav); talloc_free(config_file_tmp); unlink(config_file_tmp); return -3; } sync(); if (unlink(config_file) != 0) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file); talloc_free(config_file_sav); talloc_free(config_file_tmp); unlink(config_file_tmp); return -4; } } if (link(config_file_tmp, config_file) != 0) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file); talloc_free(config_file_sav); talloc_free(config_file_tmp); unlink(config_file_tmp); return -5; } unlink(config_file_tmp); sync(); talloc_free(config_file_sav); talloc_free(config_file_tmp); if (chmod(config_file, 0666 & ~CONFIGFILE_MASK) != 0) { *outpath = talloc_strdup(tall_vty_cmd_ctx, config_file); return -6; } return 0; } /* 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") { char *failed_file; int rc; if (host.app_info->config_is_consistent) { rc = host.app_info->config_is_consistent(vty); if (!rc) { vty_out(vty, "Configuration is not consistent%s", VTY_NEWLINE); return CMD_WARNING; } } if (host.config == NULL) { vty_out(vty, "Can't save to configuration file, using vtysh.%s", VTY_NEWLINE); return CMD_WARNING; } rc = write_config_file(host.config, &failed_file); switch (rc) { case -1: vty_out(vty, "Can't open configuration file %s.%s", failed_file, VTY_NEWLINE); rc = CMD_WARNING; break; case -2: vty_out(vty, "Can't unlink backup configuration file %s.%s", failed_file, VTY_NEWLINE); rc = CMD_WARNING; break; case -3: vty_out(vty, "Can't backup old configuration file %s.%s", failed_file, VTY_NEWLINE); rc = CMD_WARNING; break; case -4: vty_out(vty, "Can't unlink configuration file %s.%s", failed_file, VTY_NEWLINE); rc = CMD_WARNING; break; case -5: vty_out(vty, "Can't save configuration file %s.%s", failed_file, VTY_NEWLINE); rc = CMD_WARNING; break; case -6: vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s", failed_file, strerror(errno), errno, VTY_NEWLINE); rc = CMD_WARNING; break; default: vty_out(vty, "Configuration saved to %s%s", host.config, VTY_NEWLINE); rc = CMD_SUCCESS; break; } talloc_free(failed_file); return rc; } 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(int 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); } void vty_install_default(int node) { install_default(node); install_element(node, &config_exit_cmd); if (node >= CONFIG_NODE) { /* It's not a top node. */ install_element(node, &config_end_cmd); } } /** * \brief Write the current running config to a given file * \param[in] vty the vty of the code * \param[in] filename where to store the file * \return 0 in case of success. * * If the filename already exists create a filename.sav * version with the current code. * */ int osmo_vty_write_config_file(const char *filename) { char *failed_file; int rc; rc = write_config_file(filename, &failed_file); talloc_free(failed_file); return rc; } /** * \brief Save the current state to the config file * \return 0 in case of success. * * If the filename already exists create a filename.sav * version with the current code. * */ int osmo_vty_save_config_file(void) { char *failed_file; int rc; if (host.config == NULL) return -7; rc = write_config_file(host.config, &failed_file); talloc_free(failed_file); return rc; } /* 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); install_element(VIEW_NODE, &show_online_help_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) { vty_install_default(ENABLE_NODE); install_element(ENABLE_NODE, &config_disable_cmd); install_element(ENABLE_NODE, &config_terminal_cmd); install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); } install_element (ENABLE_NODE, &show_startup_config_cmd); install_element(ENABLE_NODE, &show_version_cmd); install_element(ENABLE_NODE, &show_online_help_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); vty_install_default(CONFIG_NODE); } 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)); } /*! @} */ libosmocore-0.9.0/src/vty/logging_vty.c000066400000000000000000000434161261607044000201520ustar00rootroot00000000000000/* OpenBSC logging helper for the VTY */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2014 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 #include #include "../../config.h" #include #include #include #include #include #include #include #include #include #include #define LOG_STR "Configure logging sub-system\n" extern const struct log_info *osmo_log_info; static void _vty_output(struct log_target *tgt, unsigned int level, const char *line) { struct vty *vty = tgt->tgt_vty.vty; vty_out(vty, "%s", line); /* This is an ugly hack, but there is no easy way... */ if (strchr(line, '\n')) vty_out(vty, "\r"); } struct log_target *log_target_create_vty(struct vty *vty) { struct log_target *target; target = log_target_create(); if (!target) return NULL; target->tgt_vty.vty = vty; target->output = _vty_output; return target; } DEFUN(enable_logging, enable_logging_cmd, "logging enable", LOGGING_STR "Enables logging to this vty\n") { struct telnet_connection *conn; conn = (struct telnet_connection *) vty->priv; if (conn->dbg) { vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); return CMD_WARNING; } conn->dbg = log_target_create_vty(vty); if (!conn->dbg) return CMD_WARNING; log_add_target(conn->dbg); return CMD_SUCCESS; } struct log_target *osmo_log_vty2tgt(struct vty *vty) { struct telnet_connection *conn; if (vty->node == CFG_LOG_NODE) return vty->index; conn = (struct telnet_connection *) vty->priv; if (!conn->dbg) vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); return conn->dbg; } DEFUN(logging_fltr_all, logging_fltr_all_cmd, "logging filter all (0|1)", LOGGING_STR FILTER_STR "Do you want to log all messages?\n" "Only print messages matched by other filters\n" "Bypass filter and print all messages\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_set_all_filter(tgt, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(logging_use_clr, logging_use_clr_cmd, "logging color (0|1)", LOGGING_STR "Configure color-printing for log messages\n" "Don't use color for printing messages\n" "Use color for printing messages\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_set_use_color(tgt, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(logging_prnt_timestamp, logging_prnt_timestamp_cmd, "logging timestamp (0|1)", LOGGING_STR "Configure log message timestamping\n" "Don't prefix each log message\n" "Prefix each log message with current timestamp\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_set_print_timestamp(tgt, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(logging_prnt_ext_timestamp, logging_prnt_ext_timestamp_cmd, "logging print extended-timestamp (0|1)", LOGGING_STR "Log output settings\n" "Configure log message timestamping\n" "Don't prefix each log message\n" "Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_set_print_extended_timestamp(tgt, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(logging_prnt_cat, logging_prnt_cat_cmd, "logging print category (0|1)", LOGGING_STR "Log output settings\n" "Configure log message\n" "Don't prefix each log message\n" "Prefix each log message with category/subsystem name\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_set_print_category(tgt, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(logging_level, logging_level_cmd, NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */ NULL) /* same thing for helpstr. */ { int category = log_parse_category(argv[0]); int level = log_parse_level(argv[1]); struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; if (level < 0) { vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } /* Check for special case where we want to set global log level */ if (!strcmp(argv[0], "all")) { log_set_log_level(tgt, level); return CMD_SUCCESS; } if (category < 0) { vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } tgt->categories[category].enabled = 1; tgt->categories[category].loglevel = level; return CMD_SUCCESS; } DEFUN(logging_set_category_mask, logging_set_category_mask_cmd, "logging set-log-mask MASK", LOGGING_STR "Set the logmask of this logging target\n" "The logmask to use\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; log_parse_category_mask(tgt, argv[0]); return CMD_SUCCESS; } ALIAS_DEPRECATED(logging_set_category_mask, logging_set_category_mask_old_cmd, "logging set log mask MASK", LOGGING_STR "Decide which categories to output.\n" "Log commands\n" "Mask commands\n" "The logmask to use\n"); DEFUN(diable_logging, disable_logging_cmd, "logging disable", LOGGING_STR "Disables logging to this vty\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); struct telnet_connection *conn = (struct telnet_connection *) vty->priv; if (!tgt) return CMD_WARNING; log_del_target(tgt); talloc_free(tgt); conn->dbg = NULL; return CMD_SUCCESS; } static void vty_print_logtarget(struct vty *vty, const struct log_info *info, const struct log_target *tgt) { unsigned int i; vty_out(vty, " Global Loglevel: %s%s", log_level_str(tgt->loglevel), VTY_NEWLINE); vty_out(vty, " Use color: %s, Print Timestamp: %s%s", tgt->use_color ? "On" : "Off", tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE); vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE); for (i = 0; i < info->num_cat; i++) { const struct log_category *cat = &tgt->categories[i]; vty_out(vty, " %-10s %-10s %-8s %s%s", info->cat[i].name+1, log_level_str(cat->loglevel), cat->enabled ? "Enabled" : "Disabled", info->cat[i].description, VTY_NEWLINE); } vty_out(vty, " Log Filter 'ALL': %s%s", tgt->filter_map & LOG_FILTER_ALL ? "Enabled" : "Disabled", VTY_NEWLINE); /* print application specific filters */ if (info->print_fn) info->print_fn(vty, info, tgt); } #define SHOW_LOG_STR "Show current logging configuration\n" DEFUN(show_logging_vty, show_logging_vty_cmd, "show logging vty", SHOW_STR SHOW_LOG_STR "Show current logging configuration for this vty\n") { struct log_target *tgt = osmo_log_vty2tgt(vty); if (!tgt) return CMD_WARNING; vty_print_logtarget(vty, osmo_log_info, tgt); return CMD_SUCCESS; } DEFUN(show_alarms, show_alarms_cmd, "show alarms", SHOW_STR SHOW_LOG_STR "Show the contents of the logging ringbuffer\n") { int i, num_alarms; struct osmo_strrb *rb; struct log_target *tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL); if (!tgt) { vty_out(vty, "%% No alarms, run 'log alarms <2-32700>'%s", VTY_NEWLINE); return CMD_WARNING; } rb = tgt->tgt_rb.rb; num_alarms = osmo_strrb_elements(rb); vty_out(vty, "%% Showing %i alarms%s", num_alarms, VTY_NEWLINE); for (i = 0; i < num_alarms; i++) vty_out(vty, "%% %s%s", osmo_strrb_get_nth(rb, i), VTY_NEWLINE); return CMD_SUCCESS; } gDEFUN(cfg_description, cfg_description_cmd, "description .TEXT", "Save human-readable decription of the object\n" "Text until the end of the line\n") { char **dptr = vty->index_sub; if (!dptr) { vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); return CMD_WARNING; } if (*dptr) talloc_free(*dptr); *dptr = argv_concat(argv, argc, 0); if (!*dptr) return CMD_WARNING; return CMD_SUCCESS; } gDEFUN(cfg_no_description, cfg_no_description_cmd, "no description", NO_STR "Remove description of the object\n") { char **dptr = vty->index_sub; if (!dptr) { vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); return CMD_WARNING; } if (*dptr) { talloc_free(*dptr); *dptr = NULL; } return CMD_SUCCESS; } /* Support for configuration of log targets != the current vty */ struct cmd_node cfg_log_node = { CFG_LOG_NODE, "%s(config-log)# ", 1 }; #ifdef HAVE_SYSLOG_H #include static const int local_sysl_map[] = { [0] = LOG_LOCAL0, [1] = LOG_LOCAL1, [2] = LOG_LOCAL2, [3] = LOG_LOCAL3, [4] = LOG_LOCAL4, [5] = LOG_LOCAL5, [6] = LOG_LOCAL6, [7] = LOG_LOCAL7 }; /* From VTY core code */ extern struct host host; static int _cfg_log_syslog(struct vty *vty, int facility) { struct log_target *tgt; /* First delete the old syslog target, if any */ tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); if (tgt) log_target_destroy(tgt); tgt = log_target_create_syslog(host.app_info->name, 0, facility); if (!tgt) { vty_out(vty, "%% Unable to open syslog%s", VTY_NEWLINE); return CMD_WARNING; } log_add_target(tgt); vty->index = tgt; vty->node = CFG_LOG_NODE; return CMD_SUCCESS; } DEFUN(cfg_log_syslog_local, cfg_log_syslog_local_cmd, "log syslog local <0-7>", LOG_STR "Logging via syslog\n" "Syslog LOCAL facility\n" "Local facility number\n") { int local = atoi(argv[0]); int facility = local_sysl_map[local]; return _cfg_log_syslog(vty, facility); } static struct value_string sysl_level_names[] = { { LOG_AUTHPRIV, "authpriv" }, { LOG_CRON, "cron" }, { LOG_DAEMON, "daemon" }, { LOG_FTP, "ftp" }, { LOG_LPR, "lpr" }, { LOG_MAIL, "mail" }, { LOG_NEWS, "news" }, { LOG_USER, "user" }, { LOG_UUCP, "uucp" }, /* only for value -> string conversion */ { LOG_LOCAL0, "local 0" }, { LOG_LOCAL1, "local 1" }, { LOG_LOCAL2, "local 2" }, { LOG_LOCAL3, "local 3" }, { LOG_LOCAL4, "local 4" }, { LOG_LOCAL5, "local 5" }, { LOG_LOCAL6, "local 6" }, { LOG_LOCAL7, "local 7" }, { 0, NULL } }; DEFUN(cfg_log_syslog, cfg_log_syslog_cmd, "log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)", LOG_STR "Logging via syslog\n" "Security/authorization messages facility\n" "Clock daemon (cron/at) facility\n" "General system daemon facility\n" "Ftp daemon facility\n" "Line printer facility\n" "Mail facility\n" "News facility\n" "Generic facility\n" "UUCP facility\n") { int facility = get_string_value(sysl_level_names, argv[0]); return _cfg_log_syslog(vty, facility); } DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd, "no log syslog", NO_STR LOG_STR "Logging via syslog\n") { struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); if (!tgt) { vty_out(vty, "%% No syslog target found%s", VTY_NEWLINE); return CMD_WARNING; } log_target_destroy(tgt); return CMD_SUCCESS; } #endif /* HAVE_SYSLOG_H */ DEFUN(cfg_log_stderr, cfg_log_stderr_cmd, "log stderr", LOG_STR "Logging via STDERR of the process\n") { struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); if (!tgt) { tgt = log_target_create_stderr(); if (!tgt) { vty_out(vty, "%% Unable to create stderr log%s", VTY_NEWLINE); return CMD_WARNING; } log_add_target(tgt); } vty->index = tgt; vty->node = CFG_LOG_NODE; return CMD_SUCCESS; } DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd, "no log stderr", NO_STR LOG_STR "Logging via STDERR of the process\n") { struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); if (!tgt) { vty_out(vty, "%% No stderr logging active%s", VTY_NEWLINE); return CMD_WARNING; } log_target_destroy(tgt); return CMD_SUCCESS; } DEFUN(cfg_log_file, cfg_log_file_cmd, "log file .FILENAME", LOG_STR "Logging to text file\n" "Filename\n") { const char *fname = argv[0]; struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); if (!tgt) { tgt = log_target_create_file(fname); if (!tgt) { vty_out(vty, "%% Unable to create file `%s'%s", fname, VTY_NEWLINE); return CMD_WARNING; } log_add_target(tgt); } vty->index = tgt; vty->node = CFG_LOG_NODE; return CMD_SUCCESS; } DEFUN(cfg_no_log_file, cfg_no_log_file_cmd, "no log file .FILENAME", NO_STR LOG_STR "Logging to text file\n" "Filename\n") { const char *fname = argv[0]; struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); if (!tgt) { vty_out(vty, "%% No such log file `%s'%s", fname, VTY_NEWLINE); return CMD_WARNING; } log_target_destroy(tgt); return CMD_SUCCESS; } DEFUN(cfg_log_alarms, cfg_log_alarms_cmd, "log alarms <2-32700>", LOG_STR "Logging alarms to osmo_strrb\n" "Maximum number of messages to log\n") { struct log_target *tgt; unsigned int rbsize = atoi(argv[0]); tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL); if (tgt) log_target_destroy(tgt); tgt = log_target_create_rb(rbsize); if (!tgt) { vty_out(vty, "%% Unable to create osmo_strrb (size %u)%s", rbsize, VTY_NEWLINE); return CMD_WARNING; } log_add_target(tgt); vty->index = tgt; vty->node = CFG_LOG_NODE; return CMD_SUCCESS; } DEFUN(cfg_no_log_alarms, cfg_no_log_alarms_cmd, "no log alarms", NO_STR LOG_STR "Logging alarms to osmo_strrb\n") { struct log_target *tgt; tgt = log_target_find(LOG_TGT_TYPE_STRRB, NULL); if (!tgt) { vty_out(vty, "%% No osmo_strrb target found%s", VTY_NEWLINE); return CMD_WARNING; } log_target_destroy(tgt); return CMD_SUCCESS; } static int config_write_log_single(struct vty *vty, struct log_target *tgt) { int i; char level_lower[32]; switch (tgt->type) { case LOG_TGT_TYPE_VTY: return 1; break; case LOG_TGT_TYPE_STDERR: vty_out(vty, "log stderr%s", VTY_NEWLINE); break; case LOG_TGT_TYPE_SYSLOG: #ifdef HAVE_SYSLOG_H vty_out(vty, "log syslog %s%s", get_value_string(sysl_level_names, tgt->tgt_syslog.facility), VTY_NEWLINE); #endif break; case LOG_TGT_TYPE_FILE: vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); break; case LOG_TGT_TYPE_STRRB: vty_out(vty, "log alarms %zu%s", log_target_rb_avail_size(tgt), VTY_NEWLINE); break; } vty_out(vty, " logging filter all %u%s", tgt->filter_map & LOG_FILTER_ALL ? 1 : 0, VTY_NEWLINE); /* save filters outside of libosmocore, i.e. in app code */ if (osmo_log_info->save_fn) osmo_log_info->save_fn(vty, osmo_log_info, tgt); vty_out(vty, " logging color %u%s", tgt->use_color ? 1 : 0, VTY_NEWLINE); vty_out(vty, " logging print category %d%s", tgt->print_category ? 1 : 0, VTY_NEWLINE); if (tgt->print_ext_timestamp) vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE); else vty_out(vty, " logging timestamp %u%s", tgt->print_timestamp ? 1 : 0, VTY_NEWLINE); /* stupid old osmo logging API uses uppercase strings... */ osmo_str2lower(level_lower, log_level_str(tgt->loglevel)); vty_out(vty, " logging level all %s%s", level_lower, VTY_NEWLINE); for (i = 0; i < osmo_log_info->num_cat; i++) { const struct log_category *cat = &tgt->categories[i]; char cat_lower[32]; /* skip empty entries in the array */ if (!osmo_log_info->cat[i].name) continue; /* stupid old osmo logging API uses uppercase strings... */ osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1); osmo_str2lower(level_lower, log_level_str(cat->loglevel)); vty_out(vty, " logging level %s %s%s", cat_lower, level_lower, VTY_NEWLINE); } /* FIXME: levels */ return 1; } static int config_write_log(struct vty *vty) { struct log_target *dbg = vty->index; llist_for_each_entry(dbg, &osmo_log_target_list, entry) config_write_log_single(vty, dbg); return 1; } void logging_vty_add_cmds(const struct log_info *cat) { install_element_ve(&enable_logging_cmd); install_element_ve(&disable_logging_cmd); install_element_ve(&logging_fltr_all_cmd); install_element_ve(&logging_use_clr_cmd); install_element_ve(&logging_prnt_timestamp_cmd); install_element_ve(&logging_prnt_ext_timestamp_cmd); install_element_ve(&logging_prnt_cat_cmd); install_element_ve(&logging_set_category_mask_cmd); install_element_ve(&logging_set_category_mask_old_cmd); /* Logging level strings are generated dynamically. */ logging_level_cmd.string = log_vty_command_string(cat); logging_level_cmd.doc = log_vty_command_description(cat); install_element_ve(&logging_level_cmd); install_element_ve(&show_logging_vty_cmd); install_element_ve(&show_alarms_cmd); install_node(&cfg_log_node, config_write_log); vty_install_default(CFG_LOG_NODE); install_element(CFG_LOG_NODE, &logging_fltr_all_cmd); install_element(CFG_LOG_NODE, &logging_use_clr_cmd); install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd); install_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd); install_element(CFG_LOG_NODE, &logging_prnt_cat_cmd); install_element(CFG_LOG_NODE, &logging_level_cmd); install_element(CONFIG_NODE, &cfg_log_stderr_cmd); install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); install_element(CONFIG_NODE, &cfg_log_file_cmd); install_element(CONFIG_NODE, &cfg_no_log_file_cmd); install_element(CONFIG_NODE, &cfg_log_alarms_cmd); install_element(CONFIG_NODE, &cfg_no_log_alarms_cmd); #ifdef HAVE_SYSLOG_H install_element(CONFIG_NODE, &cfg_log_syslog_cmd); install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd); install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd); #endif } libosmocore-0.9.0/src/vty/stats_vty.c000066400000000000000000000263171261607044000176630ustar00rootroot00000000000000/* OpenBSC stats helper for the VTY */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2014 by Holger Hans Peter Freyther * (C) 2015 by Sysmocom s.f.m.c. GmbH * 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 #include #include "../../config.h" #include #include #include #include #include #include #include #define CFG_STATS_STR "Configure stats sub-system\n" #define CFG_REPORTER_STR "Configure a stats reporter\n" #define SHOW_STATS_STR "Show statistical values\n" struct cmd_node cfg_stats_node = { CFG_STATS_NODE, "%s(config-stats)# ", 1 }; static const struct value_string stats_class_strs[] = { { OSMO_STATS_CLASS_GLOBAL, "global" }, { OSMO_STATS_CLASS_PEER, "peer" }, { OSMO_STATS_CLASS_SUBSCRIBER, "subscriber" }, { 0, NULL } }; static struct osmo_stats_reporter *osmo_stats_vty2srep(struct vty *vty) { if (vty->node == CFG_STATS_NODE) return vty->index; return NULL; } static int set_srep_parameter_str(struct vty *vty, int (*fun)(struct osmo_stats_reporter *, const char *), const char *val, const char *param_name) { int rc; struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); OSMO_ASSERT(srep); rc = fun(srep, val); if (rc < 0) { vty_out(vty, "%% Unable to set %s: %s%s", param_name, strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } static int set_srep_parameter_int(struct vty *vty, int (*fun)(struct osmo_stats_reporter *, int), const char *val, const char *param_name) { int rc; int int_val; struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); OSMO_ASSERT(srep); int_val = atoi(val); rc = fun(srep, int_val); if (rc < 0) { vty_out(vty, "%% Unable to set %s: %s%s", param_name, strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_stats_reporter_local_ip, cfg_stats_reporter_local_ip_cmd, "local-ip ADDR", "Set the IP address to which we bind locally\n" "IP Address\n") { return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr, argv[0], "local address"); } DEFUN(cfg_no_stats_reporter_local_ip, cfg_no_stats_reporter_local_ip_cmd, "no local-ip", NO_STR "Set the IP address to which we bind locally\n") { return set_srep_parameter_str(vty, osmo_stats_reporter_set_local_addr, NULL, "local address"); } DEFUN(cfg_stats_reporter_remote_ip, cfg_stats_reporter_remote_ip_cmd, "remote-ip ADDR", "Set the remote IP address to which we connect\n" "IP Address\n") { return set_srep_parameter_str(vty, osmo_stats_reporter_set_remote_addr, argv[0], "remote address"); } DEFUN(cfg_stats_reporter_remote_port, cfg_stats_reporter_remote_port_cmd, "remote-port <1-65535>", "Set the remote port to which we connect\n" "Remote port number\n") { return set_srep_parameter_int(vty, osmo_stats_reporter_set_remote_port, argv[0], "remote port"); } DEFUN(cfg_stats_reporter_mtu, cfg_stats_reporter_mtu_cmd, "mtu <100-65535>", "Set the maximum packet size\n" "Size in byte\n") { return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu, argv[0], "mtu"); } DEFUN(cfg_no_stats_reporter_mtu, cfg_no_stats_reporter_mtu_cmd, "no mtu", NO_STR "Set the maximum packet size\n") { return set_srep_parameter_int(vty, osmo_stats_reporter_set_mtu, "0", "mtu"); } DEFUN(cfg_stats_reporter_prefix, cfg_stats_reporter_prefix_cmd, "prefix PREFIX", "Set the item name prefix\n" "The prefix string\n") { return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix, argv[0], "prefix string"); } DEFUN(cfg_no_stats_reporter_prefix, cfg_no_stats_reporter_prefix_cmd, "no prefix", NO_STR "Set the item name prefix\n") { return set_srep_parameter_str(vty, osmo_stats_reporter_set_name_prefix, "", "prefix string"); } DEFUN(cfg_stats_reporter_level, cfg_stats_reporter_level_cmd, "level (global|peer|subscriber)", "Set the maximum group level\n" "Report global groups only\n" "Report global and network peer related groups\n" "Report global, peer, and subscriber groups\n") { int level = get_string_value(stats_class_strs, argv[0]); int rc; struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); OSMO_ASSERT(srep); rc = osmo_stats_reporter_set_max_class(srep, level); if (rc < 0) { vty_out(vty, "%% Unable to set level: %s%s", strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return 0; } DEFUN(cfg_stats_reporter_enable, cfg_stats_reporter_enable_cmd, "enable", "Enable the reporter\n") { int rc; struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); OSMO_ASSERT(srep); rc = osmo_stats_reporter_enable(srep); if (rc < 0) { vty_out(vty, "%% Unable to enable the reporter: %s%s", strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_stats_reporter_disable, cfg_stats_reporter_disable_cmd, "disable", "Disable the reporter\n") { int rc; struct osmo_stats_reporter *srep = osmo_stats_vty2srep(vty); OSMO_ASSERT(srep); rc = osmo_stats_reporter_disable(srep); if (rc < 0) { vty_out(vty, "%% Unable to disable the reporter: %s%s", strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd, "stats reporter statsd", CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); if (!srep) { srep = osmo_stats_reporter_create_statsd(NULL); if (!srep) { vty_out(vty, "%% Unable to create statsd reporter%s", VTY_NEWLINE); return CMD_WARNING; } srep->max_class = OSMO_STATS_CLASS_GLOBAL; /* TODO: if needed, add osmo_stats_add_reporter(srep); */ } vty->index = srep; vty->node = CFG_STATS_NODE; return CMD_SUCCESS; } DEFUN(cfg_stats_interval, cfg_stats_interval_cmd, "stats interval <1-65535>", CFG_STATS_STR "Set the reporting interval\n" "Interval in seconds\n") { int rc; int interval = atoi(argv[0]); rc = osmo_stats_set_interval(interval); if (rc < 0) { vty_out(vty, "%% Unable to set interval: %s%s", strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd, "no stats reporter statsd", NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); if (!srep) { vty_out(vty, "%% No statsd logging active%s", VTY_NEWLINE); return CMD_WARNING; } osmo_stats_reporter_free(srep); return CMD_SUCCESS; } DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd, "stats reporter log", CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); if (!srep) { srep = osmo_stats_reporter_create_log(NULL); if (!srep) { vty_out(vty, "%% Unable to create log reporter%s", VTY_NEWLINE); return CMD_WARNING; } srep->max_class = OSMO_STATS_CLASS_GLOBAL; /* TODO: if needed, add osmo_stats_add_reporter(srep); */ } vty->index = srep; vty->node = CFG_STATS_NODE; return CMD_SUCCESS; } DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd, "no stats reporter log", NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") { struct osmo_stats_reporter *srep; srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); if (!srep) { vty_out(vty, "%% No log reporting active%s", VTY_NEWLINE); return CMD_WARNING; } osmo_stats_reporter_free(srep); return CMD_SUCCESS; } DEFUN(show_stats, show_stats_cmd, "show stats", SHOW_STR SHOW_STATS_STR) { vty_out_statistics_full(vty, ""); return CMD_SUCCESS; } static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep) { if (srep == NULL) return 0; switch (srep->type) { case OSMO_STATS_REPORTER_STATSD: vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE); break; case OSMO_STATS_REPORTER_LOG: vty_out(vty, "stats reporter log%s", VTY_NEWLINE); break; } vty_out(vty, " disable%s", VTY_NEWLINE); if (srep->have_net_config) { if (srep->dest_addr_str) vty_out(vty, " remote-ip %s%s", srep->dest_addr_str, VTY_NEWLINE); if (srep->dest_port) vty_out(vty, " remote-port %d%s", srep->dest_port, VTY_NEWLINE); if (srep->bind_addr_str) vty_out(vty, " local-ip %s%s", srep->bind_addr_str, VTY_NEWLINE); if (srep->mtu) vty_out(vty, " mtu %d%s", srep->mtu, VTY_NEWLINE); } if (srep->max_class) vty_out(vty, " level %s%s", get_value_string(stats_class_strs, srep->max_class), VTY_NEWLINE); if (srep->name_prefix && *srep->name_prefix) vty_out(vty, " prefix %s%s", srep->name_prefix, VTY_NEWLINE); else vty_out(vty, " no prefix%s", VTY_NEWLINE); if (srep->enabled) vty_out(vty, " enable%s", VTY_NEWLINE); return 1; } static int config_write_stats(struct vty *vty) { struct osmo_stats_reporter *srep; /* TODO: loop through all reporters */ srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); config_write_stats_reporter(vty, srep); srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); config_write_stats_reporter(vty, srep); vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE); return 1; } void osmo_stats_vty_add_cmds() { install_element_ve(&show_stats_cmd); install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd); install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd); install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd); install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd); install_element(CONFIG_NODE, &cfg_stats_interval_cmd); install_node(&cfg_stats_node, config_write_stats); vty_install_default(CFG_STATS_NODE); install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd); install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd); install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd); install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd); install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd); } libosmocore-0.9.0/src/vty/telnet_interface.c000066400000000000000000000123621261607044000211310ustar00rootroot00000000000000/* minimalistic telnet/network interface it might turn into a wire interface */ /* (C) 2009 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \addtogroup telnet_interface * @{ */ /*! \file telnet_interface.c */ /* per connection data */ LLIST_HEAD(active_connections); static void *tall_telnet_ctx; /* per network data */ static int telnet_new_connection(struct osmo_fd *fd, unsigned int what); static struct osmo_fd server_socket = { .when = BSC_FD_READ, .cb = telnet_new_connection, .priv_nr = 0, }; /*! \brief Initialize telnet based VTY interface listening to 127.0.0.1 * \param[in] tall_ctx \ref talloc context * \param[in] priv private data to be passed to callback * \param[in] port UDP port number */ int telnet_init(void *tall_ctx, void *priv, int port) { return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); } /*! \brief Initialize telnet based VTY interface * \param[in] tall_ctx \ref talloc context * \param[in] priv private data to be passed to callback * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...) * \param[in] port UDP port number */ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) { int rc; tall_telnet_ctx = talloc_named_const(tall_ctx, 1, "telnet_connection"); rc = osmo_sock_init_ofd( &server_socket, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, ip, port, OSMO_SOCK_F_BIND ); server_socket.data = priv; return (rc < 0) ? -1 : 0; } extern struct host host; /*! \brief close a telnet connection */ int telnet_close_client(struct osmo_fd *fd) { struct telnet_connection *conn = (struct telnet_connection*)fd->data; close(fd->fd); osmo_fd_unregister(fd); if (conn->dbg) { log_del_target(conn->dbg); talloc_free(conn->dbg); } llist_del(&conn->entry); talloc_free(conn); return 0; } static int client_data(struct osmo_fd *fd, unsigned int what) { struct telnet_connection *conn = fd->data; int rc = 0; if (what & BSC_FD_READ) { conn->fd.when &= ~BSC_FD_READ; rc = vty_read(conn->vty); } /* vty might have been closed from vithin vty_read() */ if (rc == -EBADF) return rc; if (what & BSC_FD_WRITE) { rc = buffer_flush_all(conn->vty->obuf, fd->fd); if (rc == BUFFER_EMPTY) conn->fd.when &= ~BSC_FD_WRITE; } return rc; } static int telnet_new_connection(struct osmo_fd *fd, unsigned int what) { struct telnet_connection *connection; struct sockaddr_in sockaddr; socklen_t len = sizeof(sockaddr); int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len); if (new_connection < 0) { LOGP(0, LOGL_ERROR, "telnet accept failed\n"); return new_connection; } connection = talloc_zero(tall_telnet_ctx, struct telnet_connection); connection->priv = fd->data; connection->fd.data = connection; connection->fd.fd = new_connection; connection->fd.when = BSC_FD_READ; connection->fd.cb = client_data; osmo_fd_register(&connection->fd); llist_add_tail(&connection->entry, &active_connections); 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; } /*! \brief callback from core VTY code about VTY related events */ void vty_event(enum event event, int sock, struct vty *vty) { struct vty_signal_data sig_data = { 0, }; struct telnet_connection *connection = vty->priv; struct osmo_fd *bfd; if (vty->type != VTY_TERM) return; sig_data.event = event; sig_data.sock = sock; sig_data.vty = vty; osmo_signal_dispatch(SS_L_VTY, S_VTY_EVENT, &sig_data); if (!connection) return; bfd = &connection->fd; 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 */ telnet_close_client(bfd); break; default: break; } } void telnet_exit(void) { struct telnet_connection *tc, *tc2; llist_for_each_entry_safe(tc, tc2, &active_connections, entry) telnet_close_client(&tc->fd); osmo_fd_unregister(&server_socket); close(server_socket.fd); talloc_free(tall_telnet_ctx); } /*! @} */ libosmocore-0.9.0/src/vty/utils.c000066400000000000000000000137451261607044000167640ustar00rootroot00000000000000/* utility routines for printing common objects in the Osmocom world */ /* (C) 2009-2010 by Harald Welte * * 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 #include #include #include #include #include #include #include #include #include #include #include /* \file utils.c */ /*! \addtogroup rate_ctr * @{ */ struct vty_out_context { struct vty *vty; const char *prefix; }; static int rate_ctr_handler( struct rate_ctr_group *ctrg, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *vctx_) { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; vty_out(vty, " %s%s: %8" PRIu64 " " "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", vctx->prefix, desc->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); return 0; } /*! \brief print a rate counter group to given VTY * \param[in] vty The VTY to which it should be printed * \param[in] prefix Any additional log prefix ahead of each line * \param[in] ctrg Rate counter group to be printed */ void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, struct rate_ctr_group *ctrg) { struct vty_out_context vctx = {vty, prefix}; vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx); } static int osmo_stat_item_handler( struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *vctx_) { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; vty_out(vty, " %s%s: %8" PRIi32 " %s%s", vctx->prefix, item->desc->description, osmo_stat_item_get_last(item), item->desc->unit, VTY_NEWLINE); return 0; } /*! \brief print a stat item group to given VTY * \param[in] vty The VTY to which it should be printed * \param[in] prefix Any additional log prefix ahead of each line * \param[in] statg Stat item group to be printed */ void vty_out_stat_item_group(struct vty *vty, const char *prefix, struct osmo_stat_item_group *statg) { struct vty_out_context vctx = {vty, prefix}; vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description, VTY_NEWLINE); osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx); } static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_) { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; if (statg->idx) vty_out(vty, "%s%s (%d):%s", vctx->prefix, statg->desc->group_description, statg->idx, VTY_NEWLINE); else vty_out(vty, "%s%s:%s", vctx->prefix, statg->desc->group_description, VTY_NEWLINE); osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx); return 0; } static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_) { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; if (ctrg->idx) vty_out(vty, "%s%s (%d):%s", vctx->prefix, ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE); else vty_out(vty, "%s%s:%s", vctx->prefix, ctrg->desc->group_description, VTY_NEWLINE); rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx); return 0; } static int handle_counter(struct osmo_counter *counter, void *vctx_) { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; vty_out(vty, " %s%s: %8lu%s", vctx->prefix, counter->description, osmo_counter_get(counter), VTY_NEWLINE); return 0; } void vty_out_statistics_full(struct vty *vty, const char *prefix) { struct vty_out_context vctx = {vty, prefix}; vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE); osmo_counters_for_each(handle_counter, &vctx); rate_ctr_for_each_group(rate_ctr_group_handler, &vctx); osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx); } /*! \brief Generate a VTY command string from value_string */ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals, const char *prefix, const char *sep, const char *end, int do_lower) { int len = 0, offset = 0, ret, rem; int size = strlen(prefix) + strlen(end); int sep_len = strlen(sep); const struct value_string *vs; char *str; for (vs = vals; vs->value || vs->str; vs++) size += strlen(vs->str) + sep_len; rem = size; str = talloc_zero_size(ctx, size); if (!str) return NULL; ret = snprintf(str + offset, rem, "%s", prefix); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); for (vs = vals; vs->value || vs->str; vs++) { if (vs->str) { int j, name_len = strlen(vs->str)+1; char name[name_len]; for (j = 0; j < name_len; j++) name[j] = do_lower ? tolower(vs->str[j]) : vs->str[j]; name[name_len-1] = '\0'; ret = snprintf(str + offset, rem, "%s%s", name, sep); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); } } offset -= sep_len; /* to remove the trailing sep */ rem += sep_len; ret = snprintf(str + offset, rem, "%s", end); if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); err: str[size-1] = '\0'; return str; } /*! @} */ libosmocore-0.9.0/src/vty/vector.c000066400000000000000000000076321261607044000171240ustar00rootroot00000000000000/* 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 #include #include #include #include #include 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; } libosmocore-0.9.0/src/vty/vty.c000066400000000000000000001127361261607044000164460ustar00rootroot00000000000000 /*! \mainpage libosmovty Documentation * * \section sec_intro Introduction * This library is a collection of common code used in various * GSM related sub-projects inside the Osmocom family of projects. It * has been imported/derived from the GNU Zebra project. * \n\n * libosmovty implements the interactive command-line on the VTY * (Virtual TTY) as well as configuration file parsing. * \n\n * Please note that C language projects inside Osmocom are typically * single-threaded event-loop state machine designs. As such, * routines in libosmovty are not thread-safe. If you must use them in * a multi-threaded context, you have to add your own locking. * * \section sec_copyright Copyright and License * Copyright © 1997-2007 - Kuninhiro Ishiguro\n * Copyright © 2008-2011 - Harald Welte, Holger Freyther and contributors\n * All rights reserved. \n\n * The source code of libosmovty is licensed 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.\n * See or COPYING included in the source * code package istelf.\n * The information detailed here is provided AS IS with NO WARRANTY OF * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. * \n\n * * \section sec_contact Contact and Support * Community-based support is available at the OpenBSC mailing list * \n * Commercial support options available upon request from * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* \addtogroup vty * @{ */ /*! \file vty.c */ #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 password_check; void *tall_vty_ctx; static void vty_clear_buf(struct vty *vty) { memset(vty->buf, 0, vty->max); } /*! \brief Allocate a new vty interface structure */ struct vty *vty_new(void) { 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; } } } } /*! \brief Close a given 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); } /*! \brief Return if this VTY is a shell or not */ int vty_shell(struct vty *vty) { return vty->type == VTY_SHELL ? 1 : 0; } /*! \brief VTY standard output function * \param[in] vty VTY to which we should print * \param[in] format variable-length format string */ 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, (unsigned 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; } /*! \brief print a newline on the given VTY */ int vty_out_newline(struct vty *vty) { const char *p = vty_newline(vty); buffer_put(vty->obuf, p, strlen(p)); return 0; } /*! \brief return the current index of a given VTY */ void *vty_current_index(struct vty *vty) { return vty->index; } /*! \brief return the current node of a given VTY */ int vty_current_node(struct vty *vty) { return vty->node; } /*! \brief Lock the configuration to a given VTY * \param[in] vty VTY to which the config shall be locked * \returns 1 on success, 0 on error * * This shall be used to make sure only one VTY at a given time has * access to modify the configuration */ int vty_config_lock(struct vty *vty) { if (vty_config == 0) { vty->config = 1; vty_config = 1; } return vty->config; } /*! \brief Unlock the configuration from a given VTY * \param[in] vty VTY from which the configuration shall be unlocked * \returns 0 in case of success */ 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) { const char *app_name = ""; if (host.app_info->name) app_name = host.app_info->name; vty_out(vty, "Welcome to the %s control interface%s%s", app_name, VTY_NEWLINE, VTY_NEWLINE); if (host.app_info->copyright) vty_out(vty, "%s", host.app_info->copyright); if (host.motdfile) { FILE *f; char buf[4096]; f = fopen(host.motdfile, "r"); if (f) { while (fgets(buf, sizeof(buf), f)) { char *s; /* work backwards to ignore trailling isspace() */ for (s = buf + strlen(buf); (s > buf) && isspace(*(s - 1)); s--) ; *s = '\0'; vty_out(vty, "%s%s", buf, VTY_NEWLINE); } fclose(f); } else vty_out(vty, "MOTD file not found%s", VTY_NEWLINE); } else if (host.motd) vty_out(vty, "%s", host.motd); } /* Put out prompt and wait input from user. */ static void vty_prompt(struct vty *vty) { struct utsname names; const char *hostname; if (vty->type == VTY_TERM) { hostname = host.app_info->name; if (!hostname) { uname(&names); hostname = names.nodename; } vty_out(vty, cmd_prompt(vty->node), hostname); } } /* Command execution over the vty interface. */ static int vty_command(struct vty *vty, char *buf) { int ret; vector vline; /* Split readline string up into the vector */ vline = cmd_make_strvec(buf); if (vline == NULL) return CMD_SUCCESS; ret = cmd_execute_command(vline, vty, NULL, 0); if (ret != CMD_SUCCESS) switch (ret) { case CMD_WARNING: if (vty->type == VTY_FILE) vty_out(vty, "Warning...%s", VTY_NEWLINE); break; case CMD_ERR_AMBIGUOUS: vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); break; case CMD_ERR_NO_MATCH: vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE); break; case CMD_ERR_INCOMPLETE: vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE); break; } cmd_free_strvec(vline); return ret; } static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; /* Basic function to write buffer to vty. */ static void vty_write(struct vty *vty, const char *buf, size_t nbytes) { if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) return; /* Should we do buffering here ? And make vty_flush (vty) ? */ buffer_put(vty->obuf, buf, nbytes); } /* Ensure length of input buffer. Is buffer is short, double it. */ static void vty_ensure(struct vty *vty, int length) { if (vty->max <= length) { vty->max *= 2; vty->buf = talloc_realloc_size(vty, vty->buf, vty->max); // FIXME: check return } } /* Basic function to insert character into vty. */ static void vty_self_insert(struct vty *vty, char c) { int i; int length; vty_ensure(vty, vty->length + 1); length = vty->length - vty->cp; memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); vty->buf[vty->cp] = c; vty_write(vty, &vty->buf[vty->cp], length + 1); for (i = 0; i < length; i++) vty_write(vty, &telnet_backward_char, 1); vty->cp++; vty->length++; } /* Self insert character 'c' in overwrite mode. */ static void vty_self_insert_overwrite(struct vty *vty, char c) { vty_ensure(vty, vty->length + 1); vty->buf[vty->cp++] = c; if (vty->cp > vty->length) vty->length++; if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) return; vty_write(vty, &c, 1); } /* Insert a word into vty interface with overwrite mode. */ static void vty_insert_word_overwrite(struct vty *vty, char *str) { int len = strlen(str); vty_write(vty, str, len); strcpy(&vty->buf[vty->cp], str); vty->cp += len; vty->length = vty->cp; } /* Forward character. */ static void vty_forward_char(struct vty *vty) { if (vty->cp < vty->length) { vty_write(vty, &vty->buf[vty->cp], 1); vty->cp++; } } /* Backward character. */ static void vty_backward_char(struct vty *vty) { if (vty->cp > 0) { vty->cp--; vty_write(vty, &telnet_backward_char, 1); } } /* Move to the beginning of the line. */ static void vty_beginning_of_line(struct vty *vty) { while (vty->cp) vty_backward_char(vty); } /* Move to the end of the line. */ static void vty_end_of_line(struct vty *vty) { while (vty->cp < vty->length) vty_forward_char(vty); } /* Add current command line to the history buffer. */ static void vty_hist_add(struct vty *vty) { int index; if (vty->length == 0) return; index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1; /* Ignore the same string as previous one. */ if (vty->hist[index]) if (strcmp(vty->buf, vty->hist[index]) == 0) { vty->hp = vty->hindex; return; } /* Insert history entry. */ if (vty->hist[vty->hindex]) talloc_free(vty->hist[vty->hindex]); vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf); /* History index rotation. */ vty->hindex++; if (vty->hindex == VTY_MAXHIST) vty->hindex = 0; vty->hp = vty->hindex; } /* Get telnet window size. */ static int vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) { #ifdef TELNET_OPTION_DEBUG int i; for (i = 0; i < nbytes; i++) { switch (buf[i]) { case IAC: vty_out (vty, "IAC "); break; case WILL: vty_out (vty, "WILL "); break; case WONT: vty_out (vty, "WONT "); break; case DO: vty_out (vty, "DO "); break; case DONT: vty_out (vty, "DONT "); break; case SB: vty_out (vty, "SB "); break; case SE: vty_out (vty, "SE "); break; case TELOPT_ECHO: vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); break; case TELOPT_SGA: vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); break; case TELOPT_NAWS: vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); break; default: vty_out (vty, "%x ", buf[i]); break; } } vty_out (vty, "%s", VTY_NEWLINE); #endif /* TELNET_OPTION_DEBUG */ switch (buf[0]) { case SB: vty->sb_len = 0; vty->iac_sb_in_progress = 1; return 0; break; case SE: { if (!vty->iac_sb_in_progress) return 0; if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) { vty->iac_sb_in_progress = 0; return 0; } switch (vty->sb_buf[0]) { case TELOPT_NAWS: if (vty->sb_len != TELNET_NAWS_SB_LEN) vty_out(vty,"RFC 1073 violation detected: telnet NAWS option " "should send %d characters, but we received %lu", TELNET_NAWS_SB_LEN, (unsigned 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", (unsigned 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); /* call the exit function of the specific node */ if (vty->node > CONFIG_NODE) vty_go_parent(vty); else (*config_exit_cmd.func) (NULL, vty, 0, NULL); vty_prompt(vty); vty->cp = 0; } /* When '^Z' is received from vty, move down to the enable mode. */ static void vty_end_config(struct vty *vty) { vty_out(vty, "%s", VTY_NEWLINE); /* FIXME: we need to call the exit function of the specific node * in question, not this generic one that doesn't know all nodes */ switch (vty->node) { case VIEW_NODE: case ENABLE_NODE: /* Nothing to do. */ break; case CONFIG_NODE: case VTY_NODE: vty_config_unlock(vty); vty->node = ENABLE_NODE; break; case CFG_LOG_NODE: vty->node = CONFIG_NODE; break; default: /* Unknown node, we have to ignore it. */ break; } vty_prompt(vty); vty->cp = 0; } /* Delete a charcter at the current point. */ static void vty_delete_char(struct vty *vty) { int i; int size; if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) return; if (vty->length == 0) { vty_down_level(vty); return; } if (vty->cp == vty->length) return; /* completion need here? */ size = vty->length - vty->cp; vty->length--; memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); vty->buf[vty->length] = '\0'; vty_write(vty, &vty->buf[vty->cp], size - 1); vty_write(vty, &telnet_space_char, 1); for (i = 0; i < size; i++) vty_write(vty, &telnet_backward_char, 1); } /* Delete a character before the point. */ static void vty_delete_backward_char(struct vty *vty) { if (vty->cp == 0) return; vty_backward_char(vty); vty_delete_char(vty); } /* Kill rest of line from current point. */ static void vty_kill_line(struct vty *vty) { int i; int size; size = vty->length - vty->cp; if (size == 0) return; for (i = 0; i < size; i++) vty_write(vty, &telnet_space_char, 1); for (i = 0; i < size; i++) vty_write(vty, &telnet_backward_char, 1); memset(&vty->buf[vty->cp], 0, size); vty->length = vty->cp; } /* Kill line from the beginning. */ static void vty_kill_line_from_beginning(struct vty *vty) { vty_beginning_of_line(vty); vty_kill_line(vty); } /* Delete a word before the point. */ static void vty_forward_kill_word(struct vty *vty) { while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') vty_delete_char(vty); while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') vty_delete_char(vty); } /* Delete a word before the point. */ static void vty_backward_kill_word(struct vty *vty) { while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ') vty_delete_backward_char(vty); while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ') vty_delete_backward_char(vty); } /* Transpose chars before or at the point. */ static void vty_transpose_chars(struct vty *vty) { char c1, c2; /* If length is short or point is near by the beginning of line then return. */ if (vty->length < 2 || vty->cp < 1) return; /* In case of point is located at the end of the line. */ if (vty->cp == vty->length) { c1 = vty->buf[vty->cp - 1]; c2 = vty->buf[vty->cp - 2]; vty_backward_char(vty); vty_backward_char(vty); vty_self_insert_overwrite(vty, c1); vty_self_insert_overwrite(vty, c2); } else { c1 = vty->buf[vty->cp]; c2 = vty->buf[vty->cp - 1]; vty_backward_char(vty); vty_self_insert_overwrite(vty, c1); vty_self_insert_overwrite(vty, c2); } } /* Do completion at vty interface. */ static void vty_complete_command(struct vty *vty) { int i; int ret; char **matched = NULL; vector vline; if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) return; vline = cmd_make_strvec(vty->buf); if (vline == NULL) return; /* In case of 'help \t'. */ if (isspace((int)vty->buf[vty->length - 1])) vector_set(vline, '\0'); matched = cmd_complete_command(vline, vty, &ret); cmd_free_strvec(vline); vty_out(vty, "%s", VTY_NEWLINE); switch (ret) { case CMD_ERR_AMBIGUOUS: vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); vty_prompt(vty); vty_redraw_line(vty); break; case CMD_ERR_NO_MATCH: /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */ vty_prompt(vty); vty_redraw_line(vty); break; case CMD_COMPLETE_FULL_MATCH: vty_prompt(vty); vty_redraw_line(vty); vty_backward_pure_word(vty); vty_insert_word_overwrite(vty, matched[0]); vty_self_insert(vty, ' '); talloc_free(matched[0]); break; case CMD_COMPLETE_MATCH: vty_prompt(vty); vty_redraw_line(vty); vty_backward_pure_word(vty); vty_insert_word_overwrite(vty, matched[0]); talloc_free(matched[0]); break; case CMD_COMPLETE_LIST_MATCH: for (i = 0; matched[i] != NULL; i++) { if (i != 0 && ((i % 6) == 0)) vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, "%-10s ", matched[i]); talloc_free(matched[i]); } vty_out(vty, "%s", VTY_NEWLINE); vty_prompt(vty); vty_redraw_line(vty); break; case CMD_ERR_NOTHING_TODO: vty_prompt(vty); vty_redraw_line(vty); break; default: break; } if (matched) vector_only_index_free(matched); } static void vty_describe_fold(struct vty *vty, int cmd_width, unsigned int desc_width, struct desc *desc) { char *buf; const char *cmd, *p; int pos; cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd; if (desc_width <= 0) { vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE); return; } buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold"); if (!buf) return; for (p = desc->str; strlen(p) > desc_width; p += pos + 1) { for (pos = desc_width; pos > 0; pos--) if (*(p + pos) == ' ') break; if (pos == 0) break; strncpy(buf, p, pos); buf[pos] = '\0'; vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE); cmd = ""; } vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE); talloc_free(buf); } /* Describe matched command function. */ static void vty_describe_command(struct vty *vty) { int ret; vector vline; vector describe; unsigned int i, width, desc_width; struct desc *desc, *desc_cr = NULL; vline = cmd_make_strvec(vty->buf); /* In case of '> ?'. */ if (vline == NULL) { vline = vector_init(1); vector_set(vline, '\0'); } else if (isspace((int)vty->buf[vty->length - 1])) vector_set(vline, '\0'); describe = cmd_describe_command(vline, vty, &ret); vty_out(vty, "%s", VTY_NEWLINE); /* Ambiguous error. */ switch (ret) { case CMD_ERR_AMBIGUOUS: cmd_free_strvec(vline); vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE); vty_prompt(vty); vty_redraw_line(vty); return; break; case CMD_ERR_NO_MATCH: cmd_free_strvec(vline); vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); vty_prompt(vty); vty_redraw_line(vty); return; break; } /* Get width of command string. */ width = 0; for (i = 0; i < vector_active(describe); i++) if ((desc = vector_slot(describe, i)) != NULL) { unsigned int len; if (desc->cmd[0] == '\0') continue; len = strlen(desc->cmd); if (desc->cmd[0] == '.') len--; if (width < len) width = len; } /* Get width of description string. */ desc_width = vty->width - (width + 6); /* Print out description. */ for (i = 0; i < vector_active(describe); i++) if ((desc = vector_slot(describe, i)) != NULL) { if (desc->cmd[0] == '\0') continue; if (strcmp(desc->cmd, "") == 0) { desc_cr = desc; continue; } if (!desc->str) vty_out(vty, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen(desc->str)) vty_out(vty, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else vty_describe_fold(vty, width, desc_width, desc); #if 0 vty_out(vty, " %-*s %s%s", width desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str ? desc->str : "", VTY_NEWLINE); #endif /* 0 */ } if ((desc = desc_cr)) { if (!desc->str) vty_out(vty, " %-s%s", desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen(desc->str)) vty_out(vty, " %-*s %s%s", width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else vty_describe_fold(vty, width, desc_width, desc); } cmd_free_strvec(vline); vector_free(describe); vty_prompt(vty); vty_redraw_line(vty); } /* ^C stop current input and do not add command line to the history. */ static void vty_stop_input(struct vty *vty) { vty->cp = vty->length = 0; vty_clear_buf(vty); vty_out(vty, "%s", VTY_NEWLINE); switch (vty->node) { case VIEW_NODE: case ENABLE_NODE: /* Nothing to do. */ break; case CONFIG_NODE: case VTY_NODE: vty_config_unlock(vty); vty->node = ENABLE_NODE; break; case CFG_LOG_NODE: vty->node = CONFIG_NODE; break; default: /* Unknown node, we have to ignore it. */ break; } vty_prompt(vty); /* Set history pointer to the latest one. */ vty->hp = vty->hindex; } #define CONTROL(X) ((X) - '@') #define VTY_NORMAL 0 #define VTY_PRE_ESCAPE 1 #define VTY_ESCAPE 2 /* Escape character command map. */ static void vty_escape_map(unsigned char c, struct vty *vty) { switch (c) { case ('A'): vty_previous_line(vty); break; case ('B'): vty_next_line(vty); break; case ('C'): vty_forward_char(vty); break; case ('D'): vty_backward_char(vty); break; default: break; } /* Go back to normal mode. */ vty->escape = VTY_NORMAL; } /* Quit print out to the buffer. */ static void vty_buffer_reset(struct vty *vty) { buffer_reset(vty->obuf); vty_prompt(vty); vty_redraw_line(vty); } /*! \brief 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); return -EBADF; } 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; } /*! \brief 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 (!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 (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 (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") { password_check = 1; return CMD_SUCCESS; } DEFUN(no_vty_login, no_vty_login_cmd, "no login", NO_STR "Enable password checking\n") { password_check = 0; 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 (!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, }; /*! \brief Reset all VTY status. */ void vty_reset(void) { 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(void) { return vty_cwd; } int vty_shell_serv(struct vty *vty) { return vty->type == VTY_SHELL_SERV ? 1 : 0; } void vty_init_vtysh(void) { vtyvec = vector_init(VECTOR_MIN_SIZE); } extern void *tall_bsc_ctx; /*! \brief Initialize VTY layer * \param[in] app_info application information */ /* 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); vty_install_default(VTY_NODE); install_element(VTY_NODE, &vty_login_cmd); install_element(VTY_NODE, &no_vty_login_cmd); } /*! \brief Read the configuration file using the VTY code * \param[in] file_name file name of the configuration file * \param[in] priv private data to be passed to \ref vty_read_file */ 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; } /*! @} */ libosmocore-0.9.0/src/write_queue.c000066400000000000000000000065161261607044000173360ustar00rootroot00000000000000/* 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 #include /*! \addtogroup write_queue * @{ */ /*! \file write_queue.c */ /*! \brief Select loop function for write queue handling * \param[in] fd osmocom file descriptor * \param[in] what bit-mask of events that have happened * * This function is provided so that it can be registered with the * select loop abstraction code (\ref osmo_fd::cb). */ int osmo_wqueue_bfd_cb(struct osmo_fd *fd, unsigned int what) { struct osmo_wqueue *queue; int rc; queue = container_of(fd, struct osmo_wqueue, bfd); if (what & BSC_FD_READ) { rc = queue->read_cb(fd); if (rc == -EBADF) goto err_badfd; } if (what & BSC_FD_EXCEPT) { rc = queue->except_cb(fd); if (rc == -EBADF) goto err_badfd; } if (what & BSC_FD_WRITE) { struct msgb *msg; fd->when &= ~BSC_FD_WRITE; /* the queue might have been emptied */ if (!llist_empty(&queue->msg_queue)) { --queue->current_length; msg = msgb_dequeue(&queue->msg_queue); rc = queue->write_cb(fd, msg); msgb_free(msg); if (rc == -EBADF) goto err_badfd; if (!llist_empty(&queue->msg_queue)) fd->when |= BSC_FD_WRITE; } } err_badfd: /* Return value is not checked in osmo_select_main() */ return 0; } /*! \brief Initialize a \ref osmo_wqueue structure * \param[in] queue Write queue to operate on * \param[in] max_length Maximum length of write queue */ void osmo_wqueue_init(struct osmo_wqueue *queue, int max_length) { queue->max_length = max_length; queue->current_length = 0; queue->read_cb = NULL; queue->write_cb = NULL; queue->bfd.cb = osmo_wqueue_bfd_cb; INIT_LLIST_HEAD(&queue->msg_queue); } /*! \brief Enqueue a new \ref msgb into a write queue * \param[in] queue Write queue to be used * \param[in] data to-be-enqueued message buffer */ int osmo_wqueue_enqueue(struct osmo_wqueue *queue, struct msgb *data) { // if (queue->current_length + 1 >= queue->max_length) // LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n"); ++queue->current_length; msgb_enqueue(&queue->msg_queue, data); queue->bfd.when |= BSC_FD_WRITE; return 0; } /*! \brief Clear a \ref osmo_wqueue * \param[in] queue Write queue to be cleared * * This function will clear (remove/release) all messages in it. */ void osmo_wqueue_clear(struct osmo_wqueue *queue) { while (!llist_empty(&queue->msg_queue)) { struct msgb *msg = msgb_dequeue(&queue->msg_queue); msgb_free(msg); } queue->current_length = 0; queue->bfd.when &= ~BSC_FD_WRITE; } /*! @} */ libosmocore-0.9.0/tests/000077500000000000000000000000001261607044000151775ustar00rootroot00000000000000libosmocore-0.9.0/tests/Makefile.am000066400000000000000000000147001261607044000172350ustar00rootroot00000000000000AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ smscb/smscb_test bits/bitrev_test a5/a5_test \ conv/conv_test auth/milenage_test lapd/lapd_test \ gsm0808/gsm0808_test gsm0408/gsm0408_test \ gb/bssgp_fc_test gb/gprs_bssgp_test gb/gprs_ns_test \ kasumi/kasumi_test logging/logging_test fr/fr_test \ loggingrb/loggingrb_test strrb/strrb_test \ vty/vty_test comp128/comp128_test utils/utils_test \ smscb/gsm0341_test stats/stats_test if ENABLE_MSGFILE check_PROGRAMS += msgfile/msgfile_test endif utils_utils_test_SOURCES = utils/utils_test.c utils_utils_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la stats_stats_test_SOURCES = stats/stats_test.c stats_stats_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la a5_a5_test_SOURCES = a5/a5_test.c a5_a5_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la kasumi_kasumi_test_SOURCES = kasumi/kasumi_test.c kasumi_kasumi_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libgsmint.la comp128_comp128_test_SOURCES = comp128/comp128_test.c comp128_comp128_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la auth_milenage_test_SOURCES = auth/milenage_test.c auth_milenage_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la bits_bitrev_test_SOURCES = bits/bitrev_test.c bits_bitrev_test_LDADD = $(top_builddir)/src/libosmocore.la conv_conv_test_SOURCES = conv/conv_test.c conv_conv_test_LDADD = $(top_builddir)/src/libosmocore.la gsm0808_gsm0808_test_SOURCES = gsm0808/gsm0808_test.c gsm0808_gsm0808_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la gsm0408_gsm0408_test_SOURCES = gsm0408/gsm0408_test.c gsm0408_gsm0408_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la lapd_lapd_test_SOURCES = lapd/lapd_test.c lapd_lapd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la msgfile_msgfile_test_SOURCES = msgfile/msgfile_test.c msgfile_msgfile_test_LDADD = $(top_builddir)/src/libosmocore.la smscb_smscb_test_SOURCES = smscb/smscb_test.c smscb_smscb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la smscb_gsm0341_test_SOURCES = smscb/gsm0341_test.c smscb_gsm0341_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la sms_sms_test_SOURCES = sms/sms_test.c sms_sms_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la timer_timer_test_SOURCES = timer/timer_test.c timer_timer_test_LDADD = $(top_builddir)/src/libosmocore.la ussd_ussd_test_SOURCES = ussd/ussd_test.c ussd_ussd_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la gb_bssgp_fc_test_SOURCES = gb/bssgp_fc_test.c gb_bssgp_fc_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la gb_gprs_bssgp_test_SOURCES = gb/gprs_bssgp_test.c gb_gprs_bssgp_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL) gb_gprs_ns_test_SOURCES = gb/gprs_ns_test.c gb_gprs_ns_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL) logging_logging_test_SOURCES = logging/logging_test.c logging_logging_test_LDADD = $(top_builddir)/src/libosmocore.la fr_fr_test_SOURCES = fr/fr_test.c fr_fr_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gb/libosmogb.la $(top_builddir)/src/vty/libosmovty.la $(LIBRARY_DL) loggingrb_loggingrb_test_SOURCES = loggingrb/loggingrb_test.c loggingrb_loggingrb_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/vty/libosmovty.la strrb_strrb_test_SOURCES = strrb/strrb_test.c strrb_strrb_test_LDADD = $(top_builddir)/src/libosmocore.la vty_vty_test_SOURCES = vty/vty_test.c vty_vty_test_LDADD = $(top_builddir)/src/vty/libosmovty.la $(top_builddir)/src/libosmocore.la # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ echo '# Signature of the current package.' && \ echo 'm4_define([AT_PACKAGE_NAME],' && \ echo ' [$(PACKAGE_NAME)])' && \ echo 'm4_define([AT_PACKAGE_TARNAME],' && \ echo ' [$(PACKAGE_TARNAME)])' && \ echo 'm4_define([AT_PACKAGE_VERSION],' && \ echo ' [$(PACKAGE_VERSION)])' && \ echo 'm4_define([AT_PACKAGE_STRING],' && \ echo ' [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \ echo ' [$(PACKAGE_BUGREPORT)])'; \ echo 'm4_define([AT_PACKAGE_URL],' && \ echo ' [$(PACKAGE_URL)])'; \ } >'$(srcdir)/package.m4' EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ timer/timer_test.ok sms/sms_test.ok ussd/ussd_test.ok \ smscb/smscb_test.ok bits/bitrev_test.ok a5/a5_test.ok \ conv/conv_test.ok auth/milenage_test.ok \ lapd/lapd_test.ok gsm0408/gsm0408_test.ok \ gsm0808/gsm0808_test.ok gb/bssgp_fc_tests.err \ gb/bssgp_fc_tests.ok gb/bssgp_fc_tests.sh \ gb/gprs_bssgp_test.ok \ gb/gprs_ns_test.ok kasumi/kasumi_test.ok \ msgfile/msgfile_test.ok msgfile/msgconfig.cfg \ logging/logging_test.ok logging/logging_test.err \ fr/fr_test.ok loggingrb/logging_test.ok \ loggingrb/logging_test.err strrb/strrb_test.ok \ vty/vty_test.ok comp128/comp128_test.ok \ utils/utils_test.ok stats/stats_test.ok DISTCLEANFILES = atconfig TESTSUITE = $(srcdir)/testsuite check-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) installcheck-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ $(TESTSUITEFLAGS) clean-local: test ! -f '$(TESTSUITE)' || \ $(SHELL) '$(TESTSUITE)' --clean AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at mv $@.tmp $@ libosmocore-0.9.0/tests/a5/000077500000000000000000000000001261607044000155045ustar00rootroot00000000000000libosmocore-0.9.0/tests/a5/a5_test.c000066400000000000000000000110471261607044000172170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include // make compiler happy void _a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct); void _a5_4(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct); static const uint8_t key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; static const uint32_t fn = 123456; static const uint8_t dl[] = { /* A5/0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A5/1 */ 0xcb, 0xa2, 0x55, 0x76, 0x17, 0x5d, 0x3b, 0x1c, 0x7b, 0x2f, 0x29, 0xa8, 0xc1, 0xb6, 0x00, /* A5/2 */ 0x45, 0x9c, 0x88, 0xc3, 0x82, 0xb7, 0xff, 0xb3, 0x98, 0xd2, 0xf9, 0x6e, 0x0f, 0x14, 0x80, }; static const uint8_t ul[] = { /* A5/0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A5/1 */ 0xd9, 0x03, 0x5e, 0x0f, 0x2a, 0xec, 0x13, 0x9a, 0x05, 0xd4, 0xa8, 0x7b, 0xb1, 0x64, 0x80, /* A5/2 */ 0xf0, 0x3a, 0xac, 0xde, 0xe3, 0x5b, 0x5e, 0x65, 0x80, 0xba, 0xab, 0xc0, 0x59, 0x26, 0x40, }; static inline bool print_a5(int n, int k, const char * dir, const ubit_t * out, const char * block) { uint8_t len = 114 / 8 + 1, buf[len], res[len]; printf("A5/%d - %s: %s => ", n, dir, osmo_ubit_dump(out, 114)); osmo_hexparse(block, res, len); osmo_ubit2pbit(buf, out, 114); if (0 != memcmp(buf, res, len)) { printf("FAIL\nGOT: [%d] %s\nEXP: [%d] %s\n", k, osmo_hexdump_nospc(buf, len), k, osmo_hexdump_nospc(res, len)); return false; } printf("OK\n"); return true; } static inline bool test_a53(const char * kc, uint32_t count, const char * block1, const char * block2) { ubit_t dlout[114], ulout[114]; uint8_t key[8]; osmo_hexparse(kc, key, 8); _a5_3(key, count, dlout, NULL, false); _a5_3(key, count, NULL, ulout, false); return print_a5(3, 8, "DL", dlout, block1) & print_a5(3, 8, "UL", ulout, block2); } static inline bool test_a54(const char * kc, uint32_t count, const char * block1, const char * block2) { ubit_t dlout[114], ulout[114]; uint8_t key[16]; osmo_hexparse(kc, key, 16); _a5_4(key, count, dlout, NULL, false); _a5_4(key, count, NULL, ulout, false); return print_a5(4, 8, "DL", dlout, block1) & print_a5(4, 8, "UL", ulout, block2); } int main(int argc, char **argv) { ubit_t exp[114], out[114]; int n, i; for (n=0; n<3; n++) { /* "Randomize" */ for (i=0; i<114; i++) out[i] = i & 1; /* DL */ osmo_pbit2ubit(exp, &dl[15*n], 114); osmo_a5(n, key, fn, out, NULL); printf("A5/%d - DL: %s", n, osmo_ubit_dump(out, 114)); if (!memcmp(exp, out, 114)) printf(" => OK\n"); else { printf(" => BAD\n"); printf(" Expected: %s", osmo_ubit_dump(out, 114)); fprintf(stderr, "[!] A5/%d DL failed", n); exit(1); } /* UL */ osmo_pbit2ubit(exp, &ul[15*n], 114); osmo_a5(n, key, fn, NULL, out); printf("A5/%d - UL: %s", n, osmo_ubit_dump(out, 114)); if (!memcmp(exp, out, 114)) printf(" => OK\n"); else { printf(" => BAD\n"); printf(" Expected: %s", osmo_ubit_dump(out, 114)); fprintf(stderr, "[!] A5/%d UL failed", n); exit(1); } } // test vectors from 3GPP TS 55.217 and TS 55.218 test_a53("2BD6459F82C5BC00", 0x24F20F, "889EEAAF9ED1BA1ABBD8436232E440", "5CA3406AA244CF69CF047AADA2DF40"); test_a53("952C49104881FF48", 0x061272, "FB4D5FBCEE13A33389285686E9A5C0", "25090378E0540457C57E367662E440"); test_a53("EFA8B2229E720C2A", 0x33FD3F, "0E4015755A336469C3DD8680E30340", "6F10669E2B4E18B042431A28E47F80"); test_a53("952C49104881FF48", 0x061527, "AB7DB38A573A325DAA76E4CB800A40", "4C4B594FEA9D00FE8978B7B7BC1080"); test_a53("3451F23A43BD2C87", 0x0E418C, "75F7C4C51560905DFBA05E46FB54C0", "192C95353CDF979E054186DF15BF00"); test_a53("CAA2639BE82435CF", 0x2FF229, "301437E4D4D6565D4904C631606EC0", "F0A3B8795E264D3E1A82F684353DC0"); test_a53("7AE67E87400B9FA6", 0x2F24E5, "F794290FEF643D2EA348A7796A2100", "CB6FA6C6B8A705AF9FEFE975818500"); test_a53("58AF69935540698B", 0x05446B, "749CA4E6B691E5A598C461D5FE4740", "31C9E444CD04677ADAA8A082ADBC40"); test_a53("017F81E5F236FE62", 0x156B26, "2A6976761E60CC4E8F9F52160276C0", "A544D8475F2C78C35614128F1179C0"); test_a53("1ACA8B448B767B39", 0x0BC3B5, "A4F70DC5A2C9707F5FA1C60EB10640", "7780B597B328C1400B5C74823E8500"); test_a54("3D43C388C9581E337FF1F97EB5C1F85E", 0x35D2CF, "A2FE3034B6B22CC4E33C7090BEC340", "170D7497432FF897B91BE8AECBA880"); test_a54("A4496A64DF4F399F3B4506814A3E07A1", 0x212777, "89CDEE360DF9110281BCF57755A040", "33822C0C779598C9CBFC49183AF7C0"); return 0; } libosmocore-0.9.0/tests/a5/a5_test.ok000066400000000000000000000075701261607044000174140ustar00rootroot00000000000000A5/0 - DL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK A5/0 - UL: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 => OK A5/1 - DL: 110010111010001001010101011101100001011101011101001110110001110001111011001011110010100110101000110000011011011000 => OK A5/1 - UL: 110110010000001101011110000011110010101011101100000100111001101000000101110101001010100001111011101100010110010010 => OK A5/2 - DL: 010001011001110010001000110000111000001010110111111111111011001110011000110100101111100101101110000011110001010010 => OK A5/2 - UL: 111100000011101010101100110111101110001101011011010111100110010110000000101110101010101111000000010110010010011001 => OK A5/3 - DL: 100010001001111011101010101011111001111011010001101110100001101010111011110110000100001101100010001100101110010001 => OK A5/3 - UL: 010111001010001101000000011010101010001001000100110011110110100111001111000001000111101010101101101000101101111101 => OK A5/3 - DL: 111110110100110101011111101111001110111000010011101000110011001110001001001010000101011010000110111010011010010111 => OK A5/3 - UL: 001001010000100100000011011110001110000001010100000001000101011111000101011111100011011001110110011000101110010001 => OK A5/3 - DL: 000011100100000000010101011101010101101000110011011001000110100111000011110111011000011010000000111000110000001101 => OK A5/3 - UL: 011011110001000001100110100111100010101101001110000110001011000001000010010000110001101000101000111001000111111110 => OK A5/3 - DL: 101010110111110110110011100010100101011100111010001100100101110110101010011101101110010011001011100000000000101001 => OK A5/3 - UL: 010011000100101101011001010011111110101010011101000000001111111010001001011110001011011110110111101111000001000010 => OK A5/3 - DL: 011101011111011111000100110001010001010101100000100100000101110111111011101000000101111001000110111110110101010011 => OK A5/3 - UL: 000110010010110010010101001101010011110011011111100101111001111000000101010000011000011011011111000101011011111100 => OK A5/3 - DL: 001100000001010000110111111001001101010011010110010101100101110101001001000001001100011000110001011000000110111011 => OK A5/3 - UL: 111100001010001110111000011110010101111000100110010011010011111000011010100000101111011010000100001101010011110111 => OK A5/3 - DL: 111101111001010000101001000011111110111101100100001111010010111010100011010010001010011101111001011010100010000100 => OK A5/3 - UL: 110010110110111110100110110001101011100010100111000001011010111110011111111011111110100101110101100000011000010100 => OK A5/3 - DL: 011101001001110010100100111001101011011010010001111001011010010110011000110001000110000111010101111111100100011101 => OK A5/3 - UL: 001100011100100111100100010001001100110100000100011001110111101011011010101010001010000010000010101011011011110001 => OK A5/3 - DL: 001010100110100101110110011101100001111001100000110011000100111010001111100111110101001000010110000000100111011011 => OK A5/3 - UL: 101001010100010011011000010001110101111100101100011110001100001101010110000101000001001010001111000100010111100111 => OK A5/3 - DL: 101001001111011100001101110001011010001011001001011100000111111101011111101000011100011000001110101100010000011001 => OK A5/3 - UL: 011101111000000010110101100101111011001100101000110000010100000000001011010111000111010010000010001111101000010100 => OK A5/4 - DL: 101000101111111000110000001101001011011010110010001011001100010011100011001111000111000010010000101111101100001101 => OK A5/4 - UL: 000101110000110101110100100101110100001100101111111110001001011110111001000110111110100010101110110010111010100010 => OK A5/4 - DL: 100010011100110111101110001101100000110111111001000100010000001010000001101111001111010101110111010101011010000001 => OK A5/4 - UL: 001100111000001000101100000011000111011110010101100110001100100111001011111111000100100100011000001110101111011111 => OK libosmocore-0.9.0/tests/auth/000077500000000000000000000000001261607044000161405ustar00rootroot00000000000000libosmocore-0.9.0/tests/auth/milenage_test.c000066400000000000000000000050511261607044000211250ustar00rootroot00000000000000 #include #include #include #include #include #include int milenage_opc_gen(uint8_t *opc, const uint8_t *k, const uint8_t *op); static void dump_auth_vec(struct osmo_auth_vector *vec) { printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand))); if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) { printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn))); printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik))); printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck))); printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len)); } if (vec->auth_types & OSMO_AUTH_TYPE_GSM) { printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres))); printf("Kc:\t%s\n", osmo_hexdump(vec->kc, sizeof(vec->kc))); } } static struct osmo_sub_auth_data test_aud = { .type = OSMO_AUTH_TYPE_UMTS, .algo = OSMO_AUTH_ALG_MILENAGE, .u.umts = { .opc = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, .k = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, .amf = { 0x00, 0x00 }, .sqn = 0x22, }, }; static int opc_test(const struct osmo_sub_auth_data *aud) { int rc; uint8_t opc[16]; #if 0 const uint8_t op[16] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; #else const uint8_t op[16] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; #endif rc = milenage_opc_gen(opc, aud->u.umts.k, op); printf("OP:\t%s\n", osmo_hexdump(op, sizeof(op))); printf("OPC:\t%s\n", osmo_hexdump(opc, sizeof(opc))); return rc; } int main(int argc, char **argv) { struct osmo_auth_vector _vec; struct osmo_auth_vector *vec = &_vec; uint8_t _rand[16]; int rc; #if 0 srand(time(NULL)); *(uint32_t *)&_rand[0] = rand(); *(uint32_t *)(&_rand[4]) = rand(); *(uint32_t *)(&_rand[8]) = rand(); *(uint32_t *)(&_rand[12]) = rand(); #else memset(_rand, 0, sizeof(_rand)); #endif memset(vec, 0, sizeof(*vec)); rc = osmo_auth_gen_vec(vec, &test_aud, _rand); if (rc < 0) { fprintf(stderr, "error generating auth vector\n"); exit(1); } dump_auth_vec(vec); const uint8_t auts[14] = { 0x87, 0x11, 0xa0, 0xec, 0x9e, 0x16, 0x37, 0xdf, 0x17, 0xf8, 0x0b, 0x38, 0x4e, 0xe4 }; rc = osmo_auth_gen_vec_auts(vec, &test_aud, auts, _rand, _rand); if (rc < 0) { printf("AUTS failed\n"); } else { printf("AUTS success: SEQ.MS = %llu\n", (unsigned long long)test_aud.u.umts.sqn); } opc_test(&test_aud); exit(0); } libosmocore-0.9.0/tests/auth/milenage_test.ok000066400000000000000000000006531261607044000213170ustar00rootroot00000000000000RAND: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 AUTN: ec 93 20 c2 c2 12 00 00 c8 b7 de 2a 34 49 f1 bd IK: 12 cb 2d d3 e0 ec 83 78 f6 fc 1d 60 6c 61 9f 47 CK: 72 00 a1 84 d8 f2 c7 58 fb df 87 90 0d db f2 75 RES: e9 fc 88 cc c8 a3 53 81 SRES: 21 5f db 4d Kc: 6d e8 16 a7 59 a4 29 12 AUTS success: SEQ.MS = 33 OP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 OPC: c6 a1 3b 37 87 8f 5b 82 6f 4f 81 62 a1 c8 d8 79 libosmocore-0.9.0/tests/bits/000077500000000000000000000000001261607044000161405ustar00rootroot00000000000000libosmocore-0.9.0/tests/bits/bitrev_test.c000066400000000000000000000150371261607044000206440ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include static const uint8_t input[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; static const uint8_t exp_out[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static char s[18]; enum END {LE, BE}; static inline const char * end2str(enum END e) { if (e == LE) return "LE"; return "BE"; } /* convenience wrappers */ static inline uint64_t load64(enum END e, const uint8_t *buf, unsigned nbytes) { return (e == BE) ? osmo_load64be_ext(buf, nbytes) : osmo_load64le_ext(buf, nbytes); } static inline uint32_t load32(enum END e, const uint8_t *buf, unsigned nbytes) { return (e == BE) ? osmo_load32be_ext(buf, nbytes) : osmo_load32le_ext(buf, nbytes); } static inline uint16_t load16(enum END e, const uint8_t *buf) { return (e == BE) ? osmo_load16be(buf) : osmo_load16le(buf); } static inline void store64(enum END e, uint64_t t, uint8_t *buf, unsigned nbytes) { (e == BE) ? osmo_store64be_ext(t, buf, nbytes) : osmo_store64le_ext(t, buf, nbytes); } static inline void store32(enum END e, uint64_t t, uint8_t *buf, unsigned nbytes) { (e == BE) ? osmo_store32be_ext(t, buf, nbytes) : osmo_store32le_ext(t, buf, nbytes); } static inline void store16(enum END e, uint64_t t, uint8_t *buf) { (e == BE) ? osmo_store16be(t, buf) : osmo_store16le(t, buf); } /* helper functions */ static inline bool printcheck(bool chk, unsigned nbytes, enum END e, bool b) { if (!chk) { printf("%u %s FAILED", nbytes * 8, end2str(e)); return true; } printf("%u %s OK", nbytes * 8, end2str(e)); return b; } static inline bool dumpcheck(const char *dump, const char *s, unsigned nbytes, bool chk, enum END e, bool b) { bool x = printcheck(chk, nbytes, e, b); if (!dump) return x; int m = memcmp(s, dump, nbytes); if (0 == m) { printf(", storage OK"); return x; } printf(", [%d]", m); return true; } /* printcheckXX(): load/store 'test' and check against 'expected' value, compare to 'dump' buffer if given and print if necessary */ static inline void printcheck64(enum END e, unsigned nbytes, uint64_t test, uint64_t expected, const char *dump, bool print) { uint8_t buf[nbytes]; store64(e, test, buf, nbytes); char *s = osmo_hexdump_nospc(buf, nbytes); uint64_t result = load64(e, buf, nbytes); print = dumpcheck(dump, s, nbytes, result == expected, e, print); if (print) printf(": buffer %s known buffer %s loaded %.16" PRIx64 " expected %.16" PRIx64, s, dump, result, expected); printf("\n"); } static inline void printcheck32(enum END e, unsigned nbytes, uint32_t test, uint32_t expected, const char *dump, bool print) { uint8_t buf[nbytes]; store32(e, test, buf, nbytes); char *s = osmo_hexdump_nospc(buf, nbytes); uint32_t result = load32(e, buf, nbytes); print = dumpcheck(dump, s, nbytes, result == expected, e, print); if (print) printf(": buffer %s known buffer %s loaded %.8" PRIx32 " expected %.8" PRIx32, s, dump, result, expected); printf("\n"); } static inline void printcheck16(enum END e, uint32_t test, uint32_t expected, const char *dump, bool print) { uint8_t buf[2]; store16(e, test, buf); char *s = osmo_hexdump_nospc(buf, 2); uint16_t result = load16(e, buf); print = dumpcheck(dump, s, 2, result == expected, e, print); if (print) printf(": buffer %s known buffer %s loaded %.4" PRIx16 " expected %.4" PRIx16, s, dump, result, expected); printf("\n"); } /* compute expected value - zero excessive bytes */ static inline uint64_t exp64(enum END e, unsigned nbytes, uint64_t value) { uint8_t adj = 64 - nbytes * 8; uint64_t v = value << adj; return (e == LE) ? v >> adj : v; } static inline uint32_t exp32(enum END e, unsigned nbytes, uint32_t value) { uint8_t adj = 32 - nbytes * 8; uint32_t v = value << adj; return (e == LE) ? v >> adj : v; } /* run actual tests - if 'test' is 0 than generate random test value internally */ static inline void check64(uint64_t test, uint64_t expected, unsigned nbytes, enum END e) { bool print = true; if (0 == test && 0 == expected) { test = ((uint64_t)rand() << 32) + rand(); expected = exp64(e, nbytes, test); print = false; } snprintf(s, 17, "%.16" PRIx64, expected); printcheck64(e, nbytes, test, expected, (BE == e) ? s : NULL, print); } static inline void check32(uint32_t test, uint32_t expected, unsigned nbytes, enum END e) { bool print = true; if (0 == test && 0 == expected) { test = rand(); expected = exp32(e, nbytes, test); print = false; } snprintf(s, 17, "%.8" PRIx32, expected); printcheck32(e, nbytes, test, expected, (BE == e) ? s : NULL, print); } static inline void check16(uint16_t test, enum END e) { bool print = true; if (0 == test) { test = (uint16_t)rand(); print = false; } snprintf(s, 17, "%.4" PRIx16, test); printcheck16(e, test, test, (BE == e) ? s : NULL, print); } int main(int argc, char **argv) { uint8_t out[ARRAY_SIZE(input)]; unsigned int offs; srand(time(NULL)); for (offs = 0; offs < sizeof(out); offs++) { uint8_t *start = out + offs; uint8_t len = sizeof(out) - offs; memcpy(out, input, sizeof(out)); printf("INORDER: %s\n", osmo_hexdump(start, len)); osmo_revbytebits_buf(start, len); printf("REVERSED: %s\n", osmo_hexdump(start, len)); if (memcmp(start, exp_out + offs, len)) { printf("EXPECTED: %s\n", osmo_hexdump(exp_out+offs, len)); fprintf(stderr, "REVERSED != EXPECTED!\n"); exit(1); } printf("\n"); } printf("checking byte packing...\n"); printf("running static tests...\n"); check64(0xDEADBEEFF00DCAFE, 0xDEADBEEFF00DCAFE, 8, BE); check64(0xDEADBEEFF00DCAFE, 0xADBEEFF00DCAFE00, 7, BE); check64(0xDEADBEEFF00DCAFE, 0xBEEFF00DCAFE0000, 6, BE); check64(0xDEADBEEFF00DCAFE, 0xEFF00DCAFE000000, 5, BE); check64(0xDEADBEEFF00DCAFE, 0xDEADBEEFF00DCAFE, 8, LE); check64(0xDEADBEEFF00DCAFE, 0x00ADBEEFF00DCAFE, 7, LE); check64(0xDEADBEEFF00DCAFE, 0x0000BEEFF00DCAFE, 6, LE); check64(0xDEADBEEFF00DCAFE, 0x000000EFF00DCAFE, 5, LE); check32(0xBABEFACE, 0xBABEFACE, 4, BE); check32(0xBABEFACE, 0xBEFACE00, 3, BE); check32(0xBABEFACE, 0xBABEFACE, 4, LE); check32(0xBABEFACE, 0x00BEFACE, 3, LE); check16(0xB00B, BE); check16(0xB00B, LE); printf("running random tests...\n"); check64(0, 0, 8, BE); check64(0, 0, 7, BE); check64(0, 0, 6, BE); check64(0, 0, 5, BE); check64(0, 0, 8, LE); check64(0, 0, 7, LE); check64(0, 0, 6, LE); check64(0, 0, 5, LE); check32(0, 0, 4, BE); check32(0, 0, 3, BE); check32(0, 0, 4, LE); check32(0, 0, 3, LE); check16(0, BE); check16(0, LE); return 0; } libosmocore-0.9.0/tests/bits/bitrev_test.ok000066400000000000000000000040251261607044000210260ustar00rootroot00000000000000INORDER: 01 02 04 08 10 20 40 80 REVERSED: 80 40 20 10 08 04 02 01 INORDER: 02 04 08 10 20 40 80 REVERSED: 40 20 10 08 04 02 01 INORDER: 04 08 10 20 40 80 REVERSED: 20 10 08 04 02 01 INORDER: 08 10 20 40 80 REVERSED: 10 08 04 02 01 INORDER: 10 20 40 80 REVERSED: 08 04 02 01 INORDER: 20 40 80 REVERSED: 04 02 01 INORDER: 40 80 REVERSED: 02 01 INORDER: 80 REVERSED: 01 checking byte packing... running static tests... 64 BE OK, storage OK: buffer deadbeeff00dcafe known buffer deadbeeff00dcafe loaded deadbeeff00dcafe expected deadbeeff00dcafe 56 BE OK, storage OK: buffer adbeeff00dcafe known buffer adbeeff00dcafe00 loaded adbeeff00dcafe00 expected adbeeff00dcafe00 48 BE OK, storage OK: buffer beeff00dcafe known buffer beeff00dcafe0000 loaded beeff00dcafe0000 expected beeff00dcafe0000 40 BE OK, storage OK: buffer eff00dcafe known buffer eff00dcafe000000 loaded eff00dcafe000000 expected eff00dcafe000000 64 LE OK: buffer feca0df0efbeadde known buffer (null) loaded deadbeeff00dcafe expected deadbeeff00dcafe 56 LE OK: buffer feca0df0efbead known buffer (null) loaded 00adbeeff00dcafe expected 00adbeeff00dcafe 48 LE OK: buffer feca0df0efbe known buffer (null) loaded 0000beeff00dcafe expected 0000beeff00dcafe 40 LE OK: buffer feca0df0ef known buffer (null) loaded 000000eff00dcafe expected 000000eff00dcafe 32 BE OK, storage OK: buffer babeface known buffer babeface loaded babeface expected babeface 24 BE OK, storage OK: buffer beface known buffer beface00 loaded beface00 expected beface00 32 LE OK: buffer cefabeba known buffer (null) loaded babeface expected babeface 24 LE OK: buffer cefabe known buffer (null) loaded 00beface expected 00beface 16 BE OK, storage OK: buffer b00b known buffer b00b loaded b00b expected b00b 16 LE OK: buffer 0bb0 known buffer (null) loaded b00b expected b00b running random tests... 64 BE OK, storage OK 56 BE OK, storage OK 48 BE OK, storage OK 40 BE OK, storage OK 64 LE OK 56 LE OK 48 LE OK 40 LE OK 32 BE OK, storage OK 24 BE OK, storage OK 32 LE OK 24 LE OK 16 BE OK, storage OK 16 LE OK libosmocore-0.9.0/tests/comp128/000077500000000000000000000000001261607044000163705ustar00rootroot00000000000000libosmocore-0.9.0/tests/comp128/comp128_test.c000066400000000000000000005036001261607044000207700ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include static struct osmo_sub_auth_data test_aux2 = { .type = OSMO_AUTH_TYPE_GSM, .algo = OSMO_AUTH_ALG_COMP128v2, .u.gsm = { .ki = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, } }; static struct osmo_sub_auth_data test_aux3 = { .type = OSMO_AUTH_TYPE_GSM, .algo = OSMO_AUTH_ALG_COMP128v3, .u.gsm = { .ki = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA }, } }; void print_check(int rc, char *res, struct osmo_auth_vector *vec) { uint8_t buf[12]; osmo_hexparse(res, buf, 12); if (0 != memcmp(buf, vec->sres, 4)) { printf("%d FAIL SRES:\n", rc); printf("OUT: %s\n", osmo_hexdump_nospc(vec->sres, 4)); printf("EXP: %s\n", osmo_hexdump_nospc(buf, 4)); } if (0 != memcmp(buf+4, vec->kc, 8)) { printf("%d FAIL Kc:\n", rc); printf("OUT: %s\n", osmo_hexdump_nospc(vec->kc, 8)); printf("EXP: %s\n", osmo_hexdump_nospc(buf+4, 8)); } else printf("%d OK\n", rc); } void test_comp128v3(char *rand, char *res) { struct osmo_auth_vector _vec; struct osmo_auth_vector *vec = &_vec; uint8_t _rand[16]; osmo_hexparse(rand, _rand, 16); int rc = osmo_auth_gen_vec(vec, &test_aux3, _rand); print_check(rc, res, vec); } void test_comp128v2(char *rand, char *res) { struct osmo_auth_vector _vec; struct osmo_auth_vector *vec = &_vec; uint8_t _rand[16]; osmo_hexparse(rand, _rand, 16); int rc = osmo_auth_gen_vec(vec, &test_aux2, _rand); print_check(rc, res, vec); } int main(int argc, char **argv) { printf("COMP128v2 support: %d\n", osmo_auth_alg_parse("COMP128v2")); printf("COMP128v3 support: %d\n", osmo_auth_alg_parse("COMP128v3")); test_comp128v2("00000000000000000000000000000000", "34B4225BF16B96E118A85800"); test_comp128v2("00102030405060708090A0B0C0D0E0F0", "A892A8EFD6D33E3650372C00"); test_comp128v2("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "F699F0BABA87114F0350BC00"); test_comp128v2("000102030405060708090A0B0C0D0E0F", "A5B4C7CA0514C4E1B25CBC00"); test_comp128v2("F918FB140A2D63E10B9B3354C93D5816", "C1AD6FE372383E0D6AAC7C00"); test_comp128v2("CBFE7E4156F94F1E6ECA59C194A9BED4", "3DEF079216A74B97B4ADC800"); test_comp128v2("B65CB9DDC667BBDA3F493FB4BA2CA2E9", "A846E7EA48C2E85F1C115800"); test_comp128v2("23E0F6ED14ED2596A3B11453786E7C2C", "73280CD483E7DCDE26F94400"); test_comp128v2("CB5CD183A1D26D1CF75D5987D20CAE75", "055DF715899926BD0D6A0400"); test_comp128v2("1E99C6D07A0914E101FC2094406FB46F", "0A500680A860B1678C749400"); test_comp128v2("F9213F7452CC291C6B64F990DFC219CE", "6273439D25CA01E321505C00"); test_comp128v2("21C5CCC4CDDF8D016F82788BF2863718", "404F028B1F31A67A71734400"); test_comp128v2("1561F279CC3FC8D7FDF7703C3755003B", "7081294CCCF3AF9D87CBBC00"); test_comp128v2("360EBBC222F116936F55772663FE0131", "D30CD39641B7771E2CDA2800"); test_comp128v2("D7FDC4BEFF3E172607BCAFD0FFB8414E", "656222C3E481A1AE7B42BC00"); test_comp128v2("D832114007244CEB3B05AA50F60A1DC3", "EAE9BC2A4D1DD802E9ADF000"); test_comp128v2("CCB8CAA7DC27AF527F75385BC9CC943A", "2A43B559362A6DDED82E4800"); test_comp128v2("48BD98472929D5D66FDEF005FD1B67D5", "0EA9FCAF684D3416C187C800"); test_comp128v2("97A67270ABC7807CF3F7D2D668D9EF2C", "510DA1D26FA933C86D0D2C00"); test_comp128v2("5CF1FC3916B9631D04E763003CB0AFB1", "EE2E83CF08A00B0A1966C800"); test_comp128v2("18F91C80B5CDC5A5A95A187EF66AB0C0", "0182E8AFDC4092FEE382F000"); test_comp128v2("248B68303F09476AE2E72342636E6D9E", "D78DF0808D19FBF8B703D400"); test_comp128v2("340D4544F8559C61EDF7F4216E70C576", "2480A75BF304AC7AC73F3800"); test_comp128v2("CCFE030B6436D949082ACA45077C9193", "85A18AEA5B9BC2B7A376F400"); test_comp128v2("3D0754700F6A1D2C704921EC67AF3ED2", "2D337465329B474F67975800"); test_comp128v2("9165BF37C63326F8CE9CD03F9A1378BC", "DCAEA3C710D0D67DD5B3F400"); test_comp128v2("9DCFA432DFC4F1FF19A441F32A5D8597", "F010B99B63B7E6F75DE6C400"); test_comp128v2("1DE9D2AC71DF86E5A599017A692CB4B8", "EB93C952E8C616D6C2979400"); test_comp128v2("261326154E44C794F5A91935182CFF40", "B27DE0650A1FC35AB045EC00"); test_comp128v2("3F299BB3EE46BBB4ADB5493B4A2CBE5E", "67838DEDAA2242B6BB237800"); test_comp128v2("CFD033BE76FE261250042457B40445A2", "863CA702CF08A47B3CBD5800"); test_comp128v2("FCCF9EE60A80EC9F7BE413A50CE60869", "14D1B0D8254D474C999CC000"); test_comp128v2("A966B4A739358CFD6EF22A9A660785DF", "6894BA1EF235BFA46A0E7000"); test_comp128v2("C1F8AD801A7441908B0597E186EBF2C0", "C4E9F13F0C19A76F7EC9CC00"); test_comp128v2("ACDF541A969C68703E5DE37ED0B7CA09", "832463CBF662B2A72D4BF400"); test_comp128v2("274F7ECBD2AF639D9479FF4C73815E1E", "D1D5AA0F491ED442716D4C00"); test_comp128v2("3E4FC9C886E4E5A8EE89AF61231857CB", "323C4D161E55610BCB0CCC00"); test_comp128v2("E0F4130624669860131BD80D85A3F1F2", "07DAEF70D14DFB9988C5D800"); test_comp128v2("DD8C39429A107F263B2A5F596A501905", "1417946345034965053FB000"); test_comp128v2("A95D7E54A42604BAB557A0BDA4F50CC1", "D1683BA6DF50CAC2508CA400"); test_comp128v2("140C6662233AD66D5AC825D80A9A8272", "A0FAA92970DC0E98AB406000"); test_comp128v2("EEA61DDED13D0E0F2DCCB71ADD551B7B", "7240AF352D215FE4B4762C00"); test_comp128v2("E4C84D1860B03C3DC2010FB6AB3E4C47", "30576C1054878023B505D800"); test_comp128v2("747AF0FE3B3E903AEB6191EA01DC9F4E", "4CBAD22E8500464CFD7E4800"); test_comp128v2("E607B88BE14E5CEE43594EF51A0725BB", "1D79DBF69D7E2350225D9C00"); test_comp128v2("4002BDDC83EF54ABAE5442B0A8E976B9", "6CB6D7673428D2D2E84BC800"); test_comp128v2("AB8B27FE3F278CFDAE085F236A0A28E5", "06407FCB02EBF61123C57000"); test_comp128v2("6883B190F8EC18305BAAF6429E817BC4", "C17A6B9AB444C1A9DD448800"); test_comp128v2("18DC6B25398F64FAA4A740C617E9D591", "F88F9FA28DE0500403AC6400"); test_comp128v2("2735F287D3AEDDD35C204628322D3899", "00F5692A5F350599985AC400"); test_comp128v2("5FD7E3F81F3671F547B94D9F6BE7A348", "FC9DCBBB4FD0580F9D196C00"); test_comp128v2("4A316F08F912FC534D8E204129C78974", "855C814274800A5C45B3B000"); test_comp128v2("960CB8BE1B5025F3EBBB55EB0D79890E", "E1CAF3B52DDA529D0B694000"); test_comp128v2("282461C3FEF2ACC7F53E640AAA8279FA", "52F8D1327B2A1B5319025800"); test_comp128v2("BA4DF10402E2E5956DE8DA1886CF14E3", "DB2D97F66B679B4D12661800"); test_comp128v2("33B82F56356D44D6A790ADD4F4CE9FDC", "694EF7DB3E35F50726159800"); test_comp128v2("01FDE145E42A4A0F7CC97B8CF4A216C1", "4391900FCA30173E63A39C00"); test_comp128v2("61259899F937B7403B2EC70ED9D32384", "F51BAF50F8CE1A64DCD25000"); test_comp128v2("0E6F0DC32693A7EF677C37BC44CE8600", "C110634B037031745F401800"); test_comp128v2("1283E739566C46FC1041C2EDF46ACA63", "4C74EFAB8A6522A010F09C00"); test_comp128v2("CE7B368B575950EE69D82D3CEEF59E7D", "FE9FF88A94F0A490335EFC00"); test_comp128v2("DB0D3AE84486ED465CFD507F6B321E2F", "76D1679FAABBBF1FD3E79000"); test_comp128v2("BBA5E23EED1E43EB3AFCE2ABC5FDF71F", "022E2C5CFE2B4C3EBDC41000"); test_comp128v2("B0B9EDB4BB979E3CA9630F8CCC3FCAA2", "1775CD2F9C4A6DCE9503C400"); test_comp128v2("4A68C3FBD3A74468D486308DD1412D31", "77249441DF2502778FF3E400"); test_comp128v2("9AB0D6DCD8251E4BBB78BFACF5823C7A", "E7499452B5C45C1A7D15BC00"); test_comp128v2("B62C6CD3A87425B29DA8E226B28C3617", "D4A1FC8107EFFD9ABDB01400"); test_comp128v2("A094A045959D9A589F3BBD28D908CBAE", "A20ADDC80477942E1B390800"); test_comp128v2("3C76356D4AB3CDEB6AF36AE27653676F", "F192C4B354CFDC97186CD400"); test_comp128v2("04ADB833E08A91A26BA932A9BE9DC4FE", "A86826C01F6B291290F21C00"); test_comp128v2("0CD25169C9CE123475F394ED818CDACC", "F0219A28F767E219F029D800"); test_comp128v2("758F9B775D80F66C2F46F45C13D14EA6", "A20754DA43C43FC4C387D800"); test_comp128v2("501418ED3CFC481399ABE1522CE9FB1E", "42764FF7641AB6D55C796000"); test_comp128v2("2D7333EFC9B3DB86C43ED4A440BB02D3", "9AE0F630A52EE42058852C00"); test_comp128v2("BEE9E64A0165F7EDEDED06BB7AC0D11C", "B436CC2DA8B4E083D0967400"); test_comp128v2("A342BA3D24D7CE59E44C5F1E6F3F28AB", "47BC33CA9A6D94596C8B0C00"); test_comp128v2("423580E3B8B42BE68E2FCF0805A369A7", "34A0F45FE614F578B9504400"); test_comp128v2("C81DF1E755DCA874A97FE2C8A2030A45", "2B5D9AE2B659D2920CAF9800"); test_comp128v2("00B065F6BBD7142C0D810D81348143E1", "0CB323266C1993A44D335000"); test_comp128v2("D0DE9876CCA615D16BE541659FD8E0A8", "F313600462BC4C2BC056DC00"); test_comp128v2("1C02F9836A1C0C334AC68873C1A95DE2", "E913C4B6BEDC8F011042CC00"); test_comp128v2("CC2686858921BD2A970F3364C1BD8154", "CD6AB5F8BE1967E637F11C00"); test_comp128v2("EC5A44CE58F654A751AD1F87C7468E22", "E5B372CDD142BA7523ADE800"); test_comp128v2("F7B93A2ECA12B2F0A7AD2B550781A311", "AC1D56004C3DB4248A2B9800"); test_comp128v2("95A26343BDAE219F9A77E2D68B8D9715", "B8E2274BA75A01A1B844FC00"); test_comp128v2("D9EF207AF47750EBF69E81669A0D7168", "90BE7292A33AFF976AF50000"); test_comp128v2("CB095420C757A88E23E845F90010CE60", "5D5267614E4868BC9019EC00"); test_comp128v2("43DA2FBE69364674ABE7A67EA1079F7A", "11602A06755795316CC2D000"); test_comp128v2("9F3145932F5712D7767278650BF038C3", "62F7DA68B860F0EAC85AF000"); test_comp128v2("6AC7C8825DE45A64EE61E175A4212CA2", "1564273CFC0D44D3AA813000"); test_comp128v2("A81B3F8E35F3B77A681E581E81992245", "DEE4D4C1C210AD3A88208400"); test_comp128v2("DA6EDF93A83A4F3390C7A74448F3EA58", "DADEFCC4C903978B5D968000"); test_comp128v2("FEABCD9A4566FAB5CCC225649A9B51E8", "990248308A158336AD8CB400"); test_comp128v2("F5A9320721897F9C4010CC26806DCC6C", "4E9483834C11A3258B735400"); test_comp128v2("6A3BD4D5FC62586DA382EEB13EC5B98B", "C0BC1F742AF062B2C57F6800"); test_comp128v2("657502B51422A9DF5D02215A3211720D", "ACD94B7C1B42B827ABB4B000"); test_comp128v2("E601807D685AC33884F10BEE2B0F7155", "8E555A2E5483666FF8E9EC00"); test_comp128v2("D081597E0807CDCC345E64EE06A656EE", "252F83F942D3CBF225A5BC00"); test_comp128v2("AE911B26A2AAAE1690541AD7C4AEC12C", "C09DFAD45B847939AA6F5C00"); test_comp128v2("D32460B8EA2DE870AEEF9591E2B945E0", "415F30A507DFB65D21E92C00"); test_comp128v2("3EF290EAC6ED5506430691F6652AD256", "BFDA7F8A4AA126AD5D832C00"); test_comp128v2("96E50BC3013669B9453CECED3379BB9E", "3127EBB45E2170FF88B6B800"); test_comp128v2("50CD8707C942E07374A604419F328627", "997A146971E7F9F173930000"); test_comp128v2("F5D436A55F52CF178EC755D9B851C2A5", "526DA1545DB57E300BD2AC00"); test_comp128v2("59D00634939E1296D628D762D098359B", "29CB71E3C1D6D8F598FCC800"); test_comp128v2("A50B3BB3252C3C9E16C12396F3894607", "A1107E2B8910AAD9F4098400"); test_comp128v2("89F8344B973576425ABDFE3466A7C99F", "3335275BFB59F6DD11903C00"); test_comp128v2("17095FF0861CCDBBE7EC9DDD97CF9FD8", "1952EB05C39002CB2533F800"); test_comp128v2("00319ECA71633B48AF1CF6050FF612EC", "8DD0BD48A4287D3987225800"); test_comp128v2("FE2BC8ECEC5406BAD2992A903A516162", "4DF20DADD02875F1F6DB8C00"); test_comp128v2("2719B57237F55AA6F967EFAFFAB491D3", "E9584869A08303981DDF3000"); test_comp128v2("E9895484AC5350C6020D47BECFE5FB53", "183CE56373954D52E0BFF000"); test_comp128v2("2E4CCA78B5FD8FEAE8F593DA046F7D39", "B2EAAA3360B2AABD8923C400"); test_comp128v2("F3B1A9F9D5F83540037EA0EA78A8DE90", "12147C976D7BB13BFE9BCC00"); test_comp128v2("DD32C4C62D3CE942B5FFBEB6D4A5F2F8", "45407DABB4B14DDFC8FA5800"); test_comp128v2("B34F7F0BD6B2D777C0817F82E3052AC3", "FDC03BCD2DBD999561344800"); test_comp128v2("83780E5C93048E6A2A060346ED2BC544", "6AF2A4F18731A281B75B5C00"); test_comp128v2("B64A86EE50BB1C02134F11CB431C5069", "36AC5A0B2CB95F686E3F1000"); test_comp128v2("FCFEB28D9732B937496FE96CD11B24B1", "E23CB44D586FE9FD19FCF000"); test_comp128v2("61664DE891B324D7C1402E7B80C68B17", "EE3A9940C7A09853F8D9FC00"); test_comp128v2("8B173E8CA34CF9DA56DBE336196941EA", "F703154C9730C5AA58FBD800"); test_comp128v2("4D21217A48E9EFE24B4704EC10B85C6D", "D56C16F7F97B8CEC06BEF800"); test_comp128v2("FFC69A387F0435BD58C13FCB913F60DB", "5F63F9197F0028ABC70F4000"); test_comp128v2("AEDEB9AEF7408837DD4BE51498ECA4AC", "1216B27EBE081A8B58880000"); test_comp128v2("14A053B1F95E86448F73D2FFEFC7FE3B", "2B9BD41CABF325CB93BD8C00"); test_comp128v2("6881901AF0FE3333627FEE84795A70FA", "651BD7D5B6DB33C5D1B0EC00"); test_comp128v2("196577B50A4C9BF7A208E4533219D468", "5E251C34B40822FCC476C800"); test_comp128v2("2ED5196DC635F8603196BE9549BED8AF", "BA9B3440ABEF67B108421C00"); test_comp128v2("FE09F4C92966BA06163AA644D6CE5D7D", "E07AA3D335E25B9303671000"); test_comp128v2("18A117FDE347F64B3CA01F0F10659596", "4059BD729905578A62A29000"); test_comp128v2("6877CF2C157251B5A97904F8EB337B8B", "2B1A0A31984ED54C1D3E4C00"); test_comp128v2("4236F749801A93FE1FD95324A24CE1C8", "2092ADDFF5D50EE60E4ADC00"); test_comp128v2("B9A4020F7EFDA03BCFB46FE31200708E", "80685309ED5215AD37C5C400"); test_comp128v2("574B8C41C4594499D037D3BF5084F167", "218AF13232BA26852E95B800"); test_comp128v2("5D7E99E0D35742E0D80A85F243606F28", "1B54FEDD20031F7C11533000"); test_comp128v2("40EB7A728783CFB17363744C8960C966", "ABBD5EC9F78951FC73C11C00"); test_comp128v2("6C7826A1F1B40323CF57C7909A83E2D2", "91C94F666B08EBA4EF7E0C00"); test_comp128v2("61E2456FB48EFBDB946DF0A3F298BF41", "9E39FFDC1C8573E4DF224800"); test_comp128v2("0B3BE969A08BF4CE4BE98D6CC0BC7309", "152512315F2CD6A3EC1D3400"); test_comp128v2("A6C41AB03860DD83608098111AAEB4E9", "57A99A60C2B9DA9E26C85C00"); test_comp128v2("6CD88694601229E809C13775C4E761E5", "18017C263C41817B9ED6A000"); test_comp128v2("53F16F6A6B6D2513CBBD0D63CBCFF990", "115CC0B613458C1F17DA3400"); test_comp128v2("4FAC9C89DEAD012D875350CCCD6DF9B0", "3CA402F00396AE4AE375F000"); test_comp128v2("61C960DAE03E40FC1F56BC1C71D37E06", "4E5128E99E89BC59D51BE000"); test_comp128v2("4DD845C43A81DF92E90B83504E159B85", "35C382EFF558E66A0822F400"); test_comp128v2("2315D98737F65C690EE46743434DC239", "36A2E24DC3B5834D198F2400"); test_comp128v2("A63680047A7750B42313F2D3B878662A", "C3D10EB4D222A75C63383800"); test_comp128v2("3934F208F33EB583661F36869C73FBA8", "890536E7B8FEB54E97526400"); test_comp128v2("4A9F494AE875A5314FC40DEEB9DB9985", "A14AE3B8501B7B5885947800"); test_comp128v2("E6BC8770CA7ADC109185683CF4666D9F", "69748080DBE0959AACCDBC00"); test_comp128v2("D2EBF8E248B694CFFD41BA7428668159", "1401C448FD719A421363C800"); test_comp128v2("A7AF83C2D4448241547B3425A8CD34C0", "5A8B14DBC9E50CD5C3616000"); test_comp128v2("D95594E46451C524B586913F80965237", "830C1CF57919CF7C3CDC2800"); test_comp128v2("C00B2713CC2E4E2355DA502E1B521001", "FDDFA77D186A5B4BDBD12400"); test_comp128v2("0B8D598E6F13F1236DACC84E456F0E01", "B471887B4A040637264DE800"); test_comp128v2("C652FDF2912983742E177C0EA30F2E61", "C42F84FD9CD6E7FC37536400"); test_comp128v2("6B655E5F7B56B51D7E254A9AE69E44C8", "48721AE298AF66D7CDCC9000"); test_comp128v2("5A900B611A5B17D82D4A2EC8E6544090", "F4A51C7F4E896653E5140000"); test_comp128v2("554C91377E43AD28215056E8AEA5DF4C", "B566C947F785B9B830080400"); test_comp128v2("54725198690B36F0C6A4DE05B0E79E47", "BBBBDA64D8B739E6D5038C00"); test_comp128v2("0BF03AF40DED5898A8D1C657846ECDE3", "4EE99919522D100349F9F000"); test_comp128v2("971B0989CDE6313ACE7690BD2C2BEE09", "590684B1A33F255BB9D40400"); test_comp128v2("32E1BE35019575931694A33B7B0AA049", "BAA62EC15FCD29F7BCC9A000"); test_comp128v2("7B601512FB8D061C1B6F7FA423733F8C", "82C1C97CC83A920346C47800"); test_comp128v2("F0A3B5BDC514E4F20060D83A61A9C711", "684138DD3D04B3B15E11CC00"); test_comp128v2("4F2F20EEE54D82CBA6FA087B242CA6A8", "7F445E9AF2735B2C29E33000"); test_comp128v2("8F357399AAD7B59AD790D55C55FBE3CC", "86DB5E7D9F1E83A870067000"); test_comp128v2("511141F30BE586D039080ECD3822F2EA", "D358B8FFEBE9FF38F0EF6800"); test_comp128v2("B62E2C043BDCC5A1741ECE60DDE598CC", "D5E1BBBA61272C8E9375E000"); test_comp128v2("1F0F950D094BF35F9657D68E5018B9A8", "BD37BD448CF9A5DDD7E87000"); test_comp128v2("DE30802335604EDE8F943169A72E6A68", "EC184C8434883302B00D1400"); test_comp128v2("3DBB9938C7C4B14E1D5D07DD616B4E0E", "AC5A0D23DA694B6128547C00"); test_comp128v2("D26257D4C9CE19B09774A9E0C86E335A", "4C8C3DA2D021243593C7EC00"); test_comp128v2("B7CD31F340EE67C49D651E1C3042D2C1", "A20D985A6C900427C48CB000"); test_comp128v2("D763E4A31FCD50F595E4666BD91B8EA1", "3BAD6E389FF168688284A000"); test_comp128v2("41DEF6924472D84434CE2C7D96881F3A", "B0EE0C304599D3AC58018800"); test_comp128v2("0CDEFB71C93D7B5EBBD6CFD85EDF9C12", "7C225B5EE5301B2F71E1B800"); test_comp128v2("C2A41CBC63F2F8D34FF1E47FC9B7660C", "DF4D16A7EFB8B8BB68450000"); test_comp128v2("7F490D52DF1330100CC6D4EEAF8253EF", "C501BF91F2BFF6B811393400"); test_comp128v2("124A768E3593AF51297D2740AD0200A9", "4CE1215F737876BD5E89C800"); test_comp128v2("39B8F95D2406756066A53C4E705F5190", "017D2D2F27C605B783B7E800"); test_comp128v2("1F4511450C91823B333BF3372B1616CB", "7EDE3A2F55D86D2692B00800"); test_comp128v2("0762815735FF7B22BB73A35F2B98BCF3", "C5F15A6612CE3312682BF400"); test_comp128v2("3E3A81D6C95B5BE0866EA8E077241131", "0F8E225A21CEE88570BF8800"); test_comp128v2("CDC633D112AB14B86799836799589EE0", "41D8946A92C3F5DD4D4F5400"); test_comp128v2("E1655B3EADFAD31AD5A077A22DF28AFC", "16D609AEE5FC4A24CCDD4000"); test_comp128v2("C73E501E952DD572189853B6BB173A4D", "A9E890D3DC0B1D4A0B295C00"); test_comp128v2("A1D2F09DF9A48878D0124C469BD7F413", "35DF82BD4899DC7437B6D400"); test_comp128v2("BE1CA7C52223B34080503DDD9F84FB7E", "8449AFF4E3C8F31BD76DFC00"); test_comp128v2("44D343E09285E36C1ACC9494753A8584", "624DB556732775366D708C00"); test_comp128v2("61D91B4E360F4F5C7EA25DFFDB081AA7", "8276AF99F7EF234573014400"); test_comp128v2("FDC7AD732965351CEC95D5AF89254921", "7E1DB73398B30B957DF8CC00"); test_comp128v2("1A944BAAFE83C95B9349782F5974B663", "5A51D44FBD0A2BAE38195400"); test_comp128v2("2941043DC4CD9975CCD5B57B11055787", "5A506CD76E9E008F4141C000"); test_comp128v2("E5547C0BE6192FDC79A0E77EC8E7D726", "6CCCCB1940F444EBD2FDCC00"); test_comp128v2("A53F937180266161BEFF102A4AF616C5", "5287128B3152083765EA9C00"); test_comp128v2("9A15EFCB21BF0E8002FEDADF8542D8B9", "4EFF43E5E4F8FEE77F8FFC00"); test_comp128v2("99F84D1074D0E4A89AB99E79894D990E", "01A6BEC2FB1491F16AD90800"); test_comp128v2("7C09FC1D74F44970D826CD75BD76E4C6", "4AB57C72DE6516B54FDFA000"); test_comp128v2("34C365219A6CB004FFAC21098E9BF4F8", "E8DACB225DA9120AE8B7F800"); test_comp128v2("64938FBDE2C7C8BD27DE7DE71D936F8F", "EE4AE4535176DB9BF2B7D400"); test_comp128v2("D6F53EF3D8213BD92E370F9565119A36", "D3F4E0550CA140F329096000"); test_comp128v2("A3728A2E35B9109737BF41A7CE3BA88E", "AD7857ACE57489FCE8475400"); test_comp128v2("C5A46566E17F20CBB22ACBA0829B3A35", "53188D1D79B9AC71E12B7C00"); test_comp128v2("49C327A37B243C502211059ADC2B928E", "92D49AE27BAF11CD61DF3400"); test_comp128v2("4C013269FB730DD9B4B87867F285692E", "7F977070217337C997B27C00"); test_comp128v2("C08BDB2794795CFA4AB1626D23AA49EB", "4248294453FCAC270C119400"); test_comp128v2("6650F32A56DB39C02711D06D361AE5A2", "AC1869DD89ECFC26E19C9800"); test_comp128v2("DD10CB3DEA808A7D0C1F666AA3FDBED7", "15CBA9F20FCA32D7F03C8400"); test_comp128v2("C088FC4AABC5D08B3B842E90883EF20C", "B64B2FB41EB4CEA224AAD400"); test_comp128v2("9C3658205884DDE93389FC595DA95F3F", "A9C1907D30E26D51BA496C00"); test_comp128v2("D386F98BEC1386870DD353EF8BEFD0EC", "9F2DAB5417E2537C78DF1400"); test_comp128v2("32D13296BD30E931C2EF187B9C07E676", "099E66F116225F2E5FCA5400"); test_comp128v2("1B3F10C760DB7647994D6C1AB8B0E27C", "88B59F43AB9F746C3AC3D400"); test_comp128v2("7F56EFEA97E53ADBD8A28E6784D4454C", "A3A64F72AD0A3D1414876800"); test_comp128v2("DC15015AF77CA3472D9C722ED2C6765D", "202ECCA412CA0903D2E46C00"); test_comp128v2("5DF5D4D07734814F50D24C28D4D133D6", "75445158435F68E6138F0800"); test_comp128v2("0AA8ECCADE7C3DDE1E33CCCA904F87A4", "7C36F6063668D0871E35C400"); test_comp128v2("D5DA6CFA0EA9C893E0C31EAB8F07C265", "A4B70423146ED051AB678C00"); test_comp128v2("CA0DAB9743D4641EDA047DDCE7AECCA3", "43B666DCFFAF925608CA3000"); test_comp128v2("C8B74AC8B6096E5884601F4E1DDBD174", "1E8E15E0F1743837DB3CC000"); test_comp128v2("5F60A4B2AC4B3FE14259CFF7B9B9B9DA", "6EB7E1212271898211BB8C00"); test_comp128v2("E3CD19D5573DFFD6E493C4DD7337758F", "13130B7A1E66FF00ADD89C00"); test_comp128v2("9836A1AD51EE33A05C1AB926D13177F3", "803B815E3D4A015D69AE0800"); test_comp128v2("2A96EE958A39E9F57FD81B873F32F0C6", "82825219B94D4148364CEC00"); test_comp128v2("DEF7C00F0ED8BFA5E29FB0316BA10293", "C1599BCEA9E04812F33B3800"); test_comp128v2("CF8A6B0BCC3285827DB9D471479A515A", "60236043F506676EE874A400"); test_comp128v2("4332F66E5C8FE9B2A374A807ADD3489C", "E76B4BD39247441C69731800"); test_comp128v2("B6B1A9D4F4B82AC72D820EF143F52BF1", "BFCE82DD74C8ABF9F4C3F800"); test_comp128v2("41955313F015B3E391555E7EFD20D94A", "CFAE1992CBAFDC38113E1400"); test_comp128v2("99F3AF88D90DA2AB226EEDA86816B73C", "385E9AABC4B633422F7F4000"); test_comp128v2("E497BC27A587261123B53562665A0421", "EA6B9CAF5418DEE2B0C48C00"); test_comp128v2("A881F3D91DFBBB74427980D76B6331B2", "E1258AFE12F8EFE020B58C00"); test_comp128v2("8A5FB6205C5BBA89FE76DF2D32C9667D", "04608F77B0B37BC1645E1800"); test_comp128v2("E282B8E50A4C89F996967FE3C97D4376", "A34FA42A7429F439A09F7C00"); test_comp128v2("1BEA1A3C719CFF988D49FEB1E66BD4C5", "58095EA3DFF0E4F55C649400"); test_comp128v2("D21207460046447600836E81933C2987", "5D3B6182D866461F597F5800"); test_comp128v2("86DC003A1E7352404118C4A6BDF522D2", "522B03EB0142EB27B813C800"); test_comp128v2("FFF92D1920F3AAAB2ECE380817DB739C", "8D193BADBFAB72BF8AB02800"); test_comp128v2("6D169D93541BDE4543214B3905A573F9", "3026B85156E7E48078DD4800"); test_comp128v2("5E8B626C2461A81224E29EE751E578E4", "DCEC75A33337B20D084DB000"); test_comp128v2("E5DF990790E1F9D245F0840E90C845F9", "5D43D9C00D5DFEEDA9632400"); test_comp128v2("4F1733D5BDDB0FC3BCFF3A095EA63FAD", "C3190679E66A34EF6A7FB800"); test_comp128v2("47E6B7544D18B2FE7A6910368080CF69", "8AFAC9EA29F6FD5EF3CBF400"); test_comp128v2("A015E3BC2CC8B20D1F9BD245BE00D935", "6F8DDCB4C9CECC28493E8000"); test_comp128v2("850745B3A941130D04F76F07BB7DBD2D", "CE82AA1D16E2D020A1BE0000"); test_comp128v2("F9EE213796256B136B6503C06FECB90E", "BCB277A4EF1AA561D1341000"); test_comp128v2("326CD7C58707E7D934772FA1A14E16FA", "B8DE63537B49F9985ACFCC00"); test_comp128v2("8D495E6E54EEDDDF0EEF870FE4952568", "CC5F16CD030F0E8CD1A58800"); test_comp128v2("74E8879783B60C994471D29F7D06168B", "376BC13CA72BB41E5C3DDC00"); test_comp128v2("9EC9382241ED63D96B4D1C32344E1AE0", "D83B4BECB683510AB6948400"); test_comp128v2("F35E046CB410739F29DDBE66DD5F37D5", "1A5A5308BA48B041DB62D800"); test_comp128v2("286C8D04A4E136AD2871F6173640D84A", "7A89F034584E9F8550028C00"); test_comp128v2("A16B18F2C459C143EEFF761842BB578F", "484459E01603A7A52320AC00"); test_comp128v2("A8188E3D3E1C1BB3193E45E3D300FDE2", "C1B8C534338CF19DE6EC4800"); test_comp128v2("5C04D2F4663E98094BBF664207CA5AD8", "35187E89549EBFCC31894800"); test_comp128v2("21637583D2915E0AC78C44BF8D6B8529", "73D4A069B4EBE3A7F9B3C800"); test_comp128v2("7D30DF857DAED4EBC23AEFF59558B1B1", "1905B164D13A1493F43E7800"); test_comp128v2("0761A9113C550E71D4A001D6F2651E62", "191662FEB0B15243BAB64000"); test_comp128v2("1DF95FF090CAF75AC07F888E70F60BCD", "58EB8C5A5CECAAF234DDB800"); test_comp128v2("2A4E9F6AAE94DE7A282FE59371E75034", "14B519C690ACA88942B1A800"); test_comp128v2("50A4BA3A673775C82A273690ED2FE960", "92EB9C62BF57A3462A081400"); test_comp128v2("89E484024EF1C40A108D261A39298AFD", "2F2903D5DC9FFC4AAA17DC00"); test_comp128v2("10DCA6BDA94F80E7239120EDF9231AC7", "67B830467AA2C76B8A83B400"); test_comp128v2("70A961DF5CCF1C45D26AF50541C3ECCB", "B19AC7232C7E7ACEDFF6F800"); test_comp128v2("7B338E1357DBAF14CB0D1AB035928F71", "83EE66B91C1CFAFBE8C5A000"); test_comp128v2("F68F8CB5C1EAD184564C0B518D2DF608", "35E056CBD8D692CB54555400"); test_comp128v2("A0B4F049C406ED5FCC1AE8FC690BD520", "9556CEF79B478627D5D4E800"); test_comp128v2("9E8F808AAC6301867025A4C7B247FBD8", "58950816A5768A4FCCCFFC00"); test_comp128v2("AC5DAB1E100A506A6114696EDE6A064F", "94A4116760CE7D8897D08400"); test_comp128v2("8601AA0166D16DBE7320C9158D0700EF", "F4E00766FA137211703F6000"); test_comp128v2("7D0E95EBA4BD16E3C6B538FB723FAB93", "3D231451211EACBA3DD42800"); test_comp128v2("EB41290ABEDE397978C898B02219908D", "5EB704A737FD1C97848CC400"); test_comp128v2("230DAD3A95D90EC20EBC8EC573781B06", "CC0A164A00B54DBF2BA99400"); test_comp128v2("0769460FA069E0DE021693AF2B923CB3", "4AD863E830BE2F6013FD8C00"); test_comp128v2("DCF8186B27A4D60588375B514EE8217B", "6BA6F69D924AB5B806C22400"); test_comp128v2("E17C33D2F1B42CC9CC4BB68C372B86AB", "A315B3B4BBF2AFACF82EB400"); test_comp128v2("C74127FCD69A7DEDAF3EC080DD794C2B", "A288120EC5E5D774E60EE800"); test_comp128v2("D148DEDDC094E552A75B362858807E76", "037A7E48A6E230437E1AE400"); test_comp128v2("91F0824B24B3190318CB89B0758B6821", "D608EA0088C201318F3A4400"); test_comp128v2("7F5D92535D449D7412E16856C9A9CC83", "8C126E8E0155CAF47607AC00"); test_comp128v2("22D7AFB76E71CF4AA209B7B5B8711551", "7C7707A33EB1CAC94FE51C00"); test_comp128v2("8848CEADDCF76EC13D5D5309594E7A58", "7E141BD3C032FAAADDF1C400"); test_comp128v2("76D1736CF997CBD643A242F805917ADE", "4A422DED87839DE4F8639800"); test_comp128v2("96B7E7197A05CBDDE68CD566E833F907", "62E205A674974554C7F5A000"); test_comp128v2("928AFCA1A1DB27340BC5689F9ADCC3FD", "9A7A16C8AAA1A89D28CBAC00"); test_comp128v2("566561B80926991BC84FF3D782C43533", "4C67249BE59008455BAD8400"); test_comp128v2("AD7CCE408FE4D908234C4B9B7AD4586F", "54FF6E91AEFCACE32C6B0400"); test_comp128v2("270BE9B26BFD8E8F399D63CE342C3AD6", "BD3B66DBED973FD195CA9800"); test_comp128v2("6B09662F3C200C2079B15CCA03EE8550", "BAA793152333F7B78DE07400"); test_comp128v2("52A0DE611C43E8A2AA08C73B79122EF0", "8B619DFBFDCA21ED3246A800"); test_comp128v2("089249127245C7364E2DA1F4B12B4174", "1BB9BECD4723DAB665D9B000"); test_comp128v2("78A4E8D674E7A9BAF1A2EEC2BCA52C83", "1F4A8220DC3B790194AB3000"); test_comp128v2("D69D8E89A9DB9FDF5008A490DCA35932", "97F23433085310FEF92B0800"); test_comp128v2("303EC59372B82FEEF9776A77B89AD1EF", "D62B685B0469F2DCB8431000"); test_comp128v2("BF25B6FBA7D5B955F81A196F603DB6DD", "9EEF5B576A0D5904CFE16400"); test_comp128v2("A034EDBF71F3789C7AD0B7DD3E2983B7", "F3955289B3B925D317E41C00"); test_comp128v2("0F64D014BCA2B53B1FB6C5267C6AA869", "176AD9DFEF9FD87C1B26FC00"); test_comp128v2("E8A8E6665D25A32DEFED1BBFE032178D", "635E89FD4FAAFAEE81A6E800"); test_comp128v2("D61E52FB7BDDB6B08D79E5727701E327", "1A8FC1CFEAC2D83C73BEE800"); test_comp128v2("F526D40E8A4791776F76B754BCB6002E", "42230C725900932FEEB32C00"); test_comp128v2("33274BEAE8FCC71C0547FCD8370F53D1", "A647CFBD2B6015A5071F2800"); test_comp128v2("71B260044E7818F625536196647D609D", "9F6CAE8F4BA7E18167D1F800"); test_comp128v2("494D5B1DD29C4CE82825A0DE60D75B28", "747D3A2FD2456A63434BB400"); test_comp128v2("D57BC3C7180DC541ABFE858D10462A05", "171FA63B591C6B7002CB9C00"); test_comp128v2("75CDED92F2DA05D5F5A180371FE51F0C", "8D6064E886661C7493535400"); test_comp128v2("0A188392250D0CDB92FD3E224332F7B0", "788924D7FA3700DBC29A5800"); test_comp128v2("15D4AC837FC73BA6C4A0FE899D09718F", "7CCF880D84106A31F5F07400"); test_comp128v2("AC453FA3FDB5BF207156144B24AD1E7B", "F9DEC85BC2B8D3996D094800"); test_comp128v2("71F2AD4E06BB7F988C5004A0357F6970", "DF76F1CA8D1203DCFB8F5400"); test_comp128v2("9ACF6CF19BB44FEB4D95541AFC4A14A0", "261B9A333A06FCCEA0D19000"); test_comp128v2("3E26329310742282F2B9D1E3DC25033A", "15020ADE64D319AA40610000"); test_comp128v2("A6DDCDBA584140D229FB839D398F78A7", "3073F2C778550F75EC707000"); test_comp128v2("D3AD058E5268095511BEA6976BA94D02", "6C2CC3C6B0325705D3393800"); test_comp128v2("E4F0FF1E5296A93BDC84A3B0E03CD043", "5DF3547DC840423AD633F800"); test_comp128v2("DC5F59D24CD8961B0872CD4264F240E3", "4038666247DF1CBE432D0000"); test_comp128v2("BC9CE92196AEFEDB4A9C93D9CA8A006C", "3EA4747DE9A8B930EC780C00"); test_comp128v2("A7FDBBF43164D9C679C09F010ED57120", "A0D97A4D4B329DAC3C1D9400"); test_comp128v2("885F62DAEC03DC5C7C3B6D2985F1989F", "8E8C3BB5767DEB1A84B8AC00"); test_comp128v2("2BF7BCEA0DAEEFED5F1B9BF67C59C42F", "3E83E6A4BDA567C0C1C37C00"); test_comp128v2("9A90204CA9EBEDDB0E8E270C232430F4", "FE65162A4E81EAD4C0FAF000"); test_comp128v2("983A16E1E9F06C1E16CA66D0024295A8", "A31960B3BCA0409A77D76400"); test_comp128v2("894A65F0863621F6F4B0F7D2DB52AF61", "EABDE0292515ACBA40C8E800"); test_comp128v2("F12067DCF27C55BBEE33B0856124CD88", "82F1FFC3EB594F53CDF39400"); test_comp128v2("81821C13A89580DAEFA775D37D75C58E", "6DE6DC3F4AA7EBCF3BF69400"); test_comp128v2("296F16BACCB5B66E5316A21A82E603AC", "CC5D42E68A362BC4EB60E400"); test_comp128v2("5AD7081575AB7E65BA8DF2DE3B6B7909", "CEC011CC38CA5ED64CB93000"); test_comp128v2("84AB79B6A3A1F005563210592BE8604B", "63D53A5C888A72E5A18E0000"); test_comp128v2("06DB39540FFAFC31D275F3E56EB15824", "88AEDFAFE13E7632EA7CC000"); test_comp128v2("9E38292FDB67242052D0A445014910D0", "41BBF707586B1FCCCDE90400"); test_comp128v2("A371E96FAD96E1545D8E8808A07E07E3", "6A58E0D91F95F6DBB18CCC00"); test_comp128v2("BB10FDDCFD8C047EB0B51E96048CE459", "2DC85575F29CA4F11FC36C00"); test_comp128v2("5B4307D70E43F25A9CFFFD56370BBC94", "9F3DD6CE52DAA89234550C00"); test_comp128v2("42FA4C38D32C541EC2F1AD27DECA6C67", "13D083584650096DF3789C00"); test_comp128v2("B8FDEDEDCB6E23CF14E6EEA25DC419F7", "09F8ED29BB3C6C40A9196800"); test_comp128v2("2F83B170565F4977A89E3E09477CA1B3", "E1053E83AD4F056439AF0000"); test_comp128v2("7C2EC5FEA03C78F65D7D711DE30D76A6", "CC39D08EBE81752FB84A6000"); test_comp128v2("1EE80FBE225CD938D1800459985B61EA", "688E5F40E8C20BCD7E90AC00"); test_comp128v2("0F3D06972170FCE7CE9C7511293846D9", "D90B439D68E4F9229AF30000"); test_comp128v2("B15AAFD4778B30CBEBA6123A2DCF242D", "416073391C2A09D498896000"); test_comp128v2("F39B05FDE23CD3513FC1F9426CFFF0C0", "C833CCF0BA49B3417F93C800"); test_comp128v2("D778CA42BBA1BF18D77DFF99BBEC939B", "F3878F86134C23289EFE4C00"); test_comp128v2("E54F1991A18C175859242DB288BC581D", "589458131E50C6D50B946400"); test_comp128v2("3C5BEB096F9D117872C665B719EE1477", "C39175020428B806D442F400"); test_comp128v2("9412AB06825CA3411BBE178480A7D1EB", "70B4716B4A43ADD6217EA400"); test_comp128v2("06EBAED1B90E8A2D25C3AB1973905D5D", "ECB251D81D64096720ABA800"); test_comp128v2("FD229FC2ABD4D5CB1E24FD63EA77AD66", "4E74DCC86725C9249311E000"); test_comp128v2("D87E8FE1157A93AC00DD5D2948ECCAA3", "4EB7C5F4A40577BA424F0C00"); test_comp128v2("30884DEB37B796F54A3C04F3A86F010D", "3A2F6B2DB3E7387C9B85F400"); test_comp128v2("1BAC80F40908D0C03313C33CAE26F5DC", "9646ED009469732EC3EBCC00"); test_comp128v2("CD302E7008E2683A7244AADB9417E501", "7839F2AE68BC906AFC06E800"); test_comp128v2("1348D0CD2910306D9118E9796E303C1E", "FED0DE5F032BB902E4354C00"); test_comp128v2("70C3FB8FB3CA6942BD7C1DF5EDB10180", "7E9B37DB18F1AFC4D28BB000"); test_comp128v2("A4150041B47229B850660FDB84B65828", "D8A37D0FB9B2675AAFB9DC00"); test_comp128v2("5D67CF90EB0C63FB4951C40B5F91259E", "7CFDC3D570F1E7C38BBE5000"); test_comp128v2("88017172C4612F91C89EC43868C6FDF8", "4E838FF2E0008654C5CF0000"); test_comp128v2("23B4A866497F2C4A67353B396936CFC1", "3FA08D495D5063E30EB6F400"); test_comp128v2("ED2550ACBCC9821894FBDF3DBDC93297", "A45019C0B1E78F60BA837C00"); test_comp128v2("43C6573D014B23C691BB0DFD2040E9C9", "2901DE457C41F792D77D2C00"); test_comp128v2("AD204E6AF70B423ED521A4B3AEBF3358", "AF6620C0D42EC71B29135000"); test_comp128v2("EBF14EDEEC94BE21F6E2521841B71B4F", "C32B0095A611EE1FCF8A0800"); test_comp128v2("E4354B7F94D37F8974E8912FE63E65ED", "A8844009E0A67032FCB39C00"); test_comp128v2("5F5F2AD08E071C324A2A0A6FFC77B441", "61BB6EBDFFEA61A3DA29E400"); test_comp128v2("FAA048F624863639327522D41B31D392", "971B2475B2806A4741497400"); test_comp128v2("E98C633FB91EE72FC4F0CB18119A67E1", "FBDAE08872C1CCEC77EA8800"); test_comp128v2("6A8815193A45732FF6CC06AFD49F9316", "F26191BB253A328C7567B400"); test_comp128v2("FC8FF54ABE8958DB01DBE87E8178A69C", "E691DDCFEF1AB2B6B069A000"); test_comp128v2("027469E5A3803DC4C2876D95C1C678C6", "2438B8CBCA2A88A931EB9C00"); test_comp128v2("5159283F05722548C74A737741CD6509", "D46ADFFFCB068AF092F12000"); test_comp128v2("6AC3B272DD8CC8BB745A8D0B36C211A1", "DDFF97F3223A4B6BC232F800"); test_comp128v2("8AA83372D1D5EC88F72ED111E27A448E", "2E229F0F997B25213222F000"); test_comp128v2("3E7608319DCB2A0E65EC9AE2084EA982", "C8B76DFD9A01A240E1B5AC00"); test_comp128v2("2841232946DE470D2FF755C70C19905E", "DDAA67B52DCB035E9A2CF400"); test_comp128v2("A944542DF2373E12823C99DEA8DF170B", "0808D4ECBC4D77A0158FD800"); test_comp128v2("A2138146A6A0E2E642053DFDAA9E16BC", "74EF1291ADB07C95E02E3400"); test_comp128v2("0DDFDD1D9A82399E61C371CEA1F9CE38", "B58BFCFC24A6D75BCB169800"); test_comp128v2("9FB3D625B946962B54E2C17B20C42D45", "D7D987D5BF9DEDA710CA8400"); test_comp128v2("1B55779FB61B29B8BD86771D0BF16D63", "3686F4642D1F419AF0698C00"); test_comp128v2("39E34A86EC6FFB7055E7FAC2E0C974C4", "D271890EA753A7883B0D3800"); test_comp128v2("B4A93386400F4CA3FDCA51846BE80335", "457CB81CF3489FE6AE1BA400"); test_comp128v2("A313371F69544947FCC573B943DD9183", "9F548C3892CFF8A485074C00"); test_comp128v2("2012EFE25DC9AF67C46C0CBCF2EC7591", "4E80A17F41BD8EA8C55F1000"); test_comp128v2("486BD31661FFCE74A65CE0D558850D9F", "B613B59771B8352D12C27C00"); test_comp128v2("2186CAAECA47991101726EC2C6F6E67A", "BEDAEA0EA1943FE1FF221400"); test_comp128v2("4D42D4D87D70821014D0CD86BC38DA4D", "A2D95578B1FA0E16B0C3B000"); test_comp128v2("4DA30AC34F7EE91EFBBDC6DF5555B76D", "6937A53FF44D36E61351DC00"); test_comp128v2("795C15464D421AF29BBA1907DD10F028", "629184CDABC11E3CA0C56400"); test_comp128v2("B3CE0E7908B649E875005DE7E7FD1FD0", "24C51AEF24FF49A4A4747000"); test_comp128v2("D957396D9E3C4AF53C89E45C54DC0A5B", "17161024C0F22E10A7430400"); test_comp128v2("43B416FD921D9BA3CDA47EA123386302", "9810CA384A6F6647E51EEC00"); test_comp128v2("0B03AE1B7D664FC5B96E2A914C633D89", "DC3FCC1CB0099F7414302C00"); test_comp128v2("4A6BEE40BA96A40B7CE9E6844DFA73AC", "E9EA864579D55C68B4B9D000"); test_comp128v2("70FDC71A73F676D9C4EA330754B901C7", "049ECEC4A332DF7B51D98800"); test_comp128v2("7E61A21AF6B328C26E1D7325DFA25B36", "D7D91E06B4C43EDB795A8400"); test_comp128v2("C666E78CA92F658FA60C350504BC09C6", "4445B12A7082935CBEF71C00"); test_comp128v2("55020E9C0982E00235CE187B034B8591", "3B0A8F08523A46377BB9B000"); test_comp128v2("ED046D0B7B672F496ABF1C835EAA3C84", "64C19D9FA085AE2270B45C00"); test_comp128v2("175A73A9CA9507BC08465DFFA9B8A496", "59D1449FB47C046EEF89A000"); test_comp128v2("07C70D392BD451EE506780DFBD5DC83F", "12B436EB13A2F5EE6F3B2400"); test_comp128v2("F91C460017B34AA789A219167F5224B4", "D4557BAA009D10CF37E70400"); test_comp128v2("1DA7C45EA6A64514FFA689538ED5ED53", "4C6C7DB8436151A89E15D000"); test_comp128v2("E87DC47A13D472C5DB246BC2F8D2DB72", "F587FF0ADB6832FBD5257800"); test_comp128v2("FC7EA44A9CE859DD79E3D01FCFEDE8DF", "7A8E075D75CEC76E2E79D400"); test_comp128v2("6A03EA7604753B3C008D3DB463A7442B", "47CCC2AB07276A6A9BF0C400"); test_comp128v2("0017953B084C3B7424D0687D9C02A837", "4A0117D0F840E0A91E2C6000"); test_comp128v2("EEF338BCB48C15A37795C69AFC7C570E", "EB007C6F30EFF093E499D400"); test_comp128v2("53A6A76FED38FF6D62777B449E5B548C", "F00CDCCCC10ECB062682A000"); test_comp128v2("5C70D559D6F8B8DD99751D56C418FA35", "E3C5F362FF9ACACA40A3A400"); test_comp128v2("9CA9F9FEFE37D57BF633754AF7B3634C", "2534834DE550FC11D9268000"); test_comp128v2("0C23013C1C3C0954638F9414DF49E0B8", "A51D6BD87F72607CF707E800"); test_comp128v2("76D341F1D8D1C65514A96277AA457C54", "8900B6FD332891AFE38D5400"); test_comp128v2("647466BE40774C458ACE1760FB2CBFFC", "EB85994EA6C7F2A785620000"); test_comp128v2("DB0CC7E41AC5D78A9A09184DF4F90553", "21A4C2684DA076CDC5F05C00"); test_comp128v2("582C3F28F5E1380932C65D3544B7BDDA", "2006B84F9FD789AB7EF88000"); test_comp128v2("5F823650EEE1F31CEC3AADEFC452E429", "80C0352D91A88126384F8400"); test_comp128v2("7D0288577305FB4CF1979056E527E0B2", "F37749459DF1409694865C00"); test_comp128v2("45566B3BCE18DA92823C95C1D28BC600", "63290D3ABC575FFE70B0E800"); test_comp128v2("BC54B44285B1123AF6D3EDA43A5446A7", "0787BD187870B36B47403C00"); test_comp128v2("AFF23EA0922CAA395D87CE8F71E686FF", "EA9A6507116E59F885954800"); test_comp128v2("81406FA900C3787CEFA83332ECE83ADD", "B9A1B23CF74881FA72F13400"); test_comp128v2("17ED1CA689D152C83DDE7C21298B1621", "B9DF2AD19F79E00D39BC7800"); test_comp128v2("BD24FA43CF93AB8F615A2CB55205954E", "8FB777F02793A4A9E96BF800"); test_comp128v2("6A60A9D4076A4668ABDC434CFF4F4BF1", "3874A5322A30FC61EE2EFC00"); test_comp128v2("B4A18EC0063E6B403DE774801F83366A", "ED58E3518559614FF3CDC400"); test_comp128v2("5B4F77B27473E3E9E9F37A270B85AF0C", "3BE0FBEDB313262BC0BFD800"); test_comp128v2("AB88BAA258597D5459F9B1279809543E", "67E5E4AE690246C0D9E9A000"); test_comp128v2("A2951495A11BADAC3A91F653349FDB05", "3E6FBD4969707A42327F8C00"); test_comp128v2("AF7D610513988261234BFDCA9174D852", "7D1721E9A2057DBDFFC78000"); test_comp128v2("ACC7B916F1D53C75BBB5EAC0C0C9155F", "5B50D39BE87E86DB3FF7C000"); test_comp128v2("463C7145B20B01098C16142DCD76F3E8", "9D4B45528F381B44F58C8000"); test_comp128v2("0E5E028DDFA0A3C0C879FA07C9FE0B11", "366B62B32BB6DBA922F9C800"); test_comp128v2("131524709A0F2C72CF897E34BF7AD533", "8B444B299DE38B10C9931C00"); test_comp128v2("B2BD87BFF31207E0584989577E010E25", "58798AFBADD4F313CD4E4C00"); test_comp128v2("878C4B8B7047A04F24B85A039CF1CA08", "134AAC5663497B78E9ED6000"); test_comp128v2("4372DFCE989ADEAF69B3CE5541F2D44B", "5A4EAC4D5B411199843EC800"); test_comp128v2("F4FFEF059262212C6A9440F4BBA67303", "2147ADD96FADA5C3B0A82C00"); test_comp128v2("ECAF923AE9D823A2B5AA234584FA1956", "A7345726E548029DE8C13800"); test_comp128v2("BF988D41A1D5C109D0706B2068ADFE14", "C3CF7CD2693FFD82DCDF1C00"); test_comp128v2("449990955002E4111D56E0C20946C6BC", "63437E1008262606709BAC00"); test_comp128v2("2EE20958580CE4F8A8BFEFFCB1D28D0A", "C5F9DBA0853C4B619979B400"); test_comp128v2("B6AB470BD27B24DFCE750B3EC08EE38F", "2E4FF932B76DC48617597C00"); test_comp128v2("8AC1D7BB4F58B97A4973F3BE4AFDE91C", "5E1785F6777316F60EA67400"); test_comp128v2("495AC23A71E47B790E70823F557301A7", "97A06E87B1351A61E82E4000"); test_comp128v2("F62F24627A42462892EF3AEDFBD86BCE", "FAAB2671E38D5F25CC2DF000"); test_comp128v2("60372B92D3EE37510355F7CA2FF1AD89", "248B52F48F946258FEBB3800"); test_comp128v2("3A6CF14AE01A0CA1562D639A97AE49C7", "31F7D19EAB76B3A4015E7800"); test_comp128v2("D0FCAFC71D9A145F839F11688295A8C8", "7772C8610A17D87744943400"); test_comp128v2("842DD75BB5B1E7E201E248663A83CF62", "4C43DDCC9F35ECB2ECBAF800"); test_comp128v2("45AC11764243D64DFCDF35443F498A11", "CA15ECFAD53D0E487DA51000"); test_comp128v2("F0A05481CCC3E9759495DDF4D061F7FF", "5284304B591163CF2AA35400"); test_comp128v2("FCAB0F4F0DC5DDB74F6F45C3DE45A890", "1430B0D94E51B3F0970B2800"); test_comp128v2("96705A918F3F7DC4779EEE959DF56DDC", "11CD9894491DBACE093D5400"); test_comp128v2("3324BCE404ABB6FFA1B815FCE3653504", "10B5BF4FAD33E64AA1446400"); test_comp128v2("69C0EC0F65DB292E47D77861648220C1", "BF307A6E2EAAC31C03ACD000"); test_comp128v2("2CD92262C563D16CDA7076A3916B025D", "C3DC96C3F8982607CB34F000"); test_comp128v2("3874E5B8CA16D381A0B33E8846EF7678", "826BF9A7CE95C140A965CC00"); test_comp128v2("3368842420F16B5476313F02B16B3CDF", "0C34B5F2BBD5B6D336B5A000"); test_comp128v2("669E584D4DDB2E1EAE7E914C8541F311", "B9246D668912C36EBCD2D400"); test_comp128v2("8D428F26986A1C746CCA262426C7C0B2", "7BDB274C86342B7068033C00"); test_comp128v2("02B97CAD06351C5C3CF86D396020026D", "CB2BE5BC456ED73635071C00"); test_comp128v2("B4696D369884E4941D0E087B32E09D2B", "2BB0A12257D862AAA55E0000"); test_comp128v2("4EAB87DA796EC3488C342BDF311AEF22", "23A0BCDE4B0DCC2C8D89F800"); test_comp128v2("DCEBEE5F1C4C7A4E8A83B6D4CB236AEA", "84C90910A45004A63EEA0C00"); test_comp128v2("55855957EB252F0C59A6B1EC4506B9A3", "9C693CF049FE93DD6D755400"); test_comp128v2("FDE4AE6CCA16EF7FBC9AFE8067FD4E16", "1E9A9CEDCA101D2193375000"); test_comp128v2("3600B73545F54A51BFFF6ADF932AEBAE", "7CC316918A7AFFCA973BEC00"); test_comp128v2("202D67833ED8B105BDF66722B7E64F7F", "540ECE8E6B38E99DF7FBB800"); test_comp128v2("1868360A46FACED93D287EB937EED141", "EBDFE734375B0DAEE15E8000"); test_comp128v2("F65A50219A711BA33E5227885C5BD27C", "E87D7741411E095602402400"); test_comp128v2("FCD82A0A445AC9373D96BB9CE8AF6B61", "517805CE2B3DD5310B6DE000"); test_comp128v2("AC6AC085B92AB68E52620E350E1BB3F0", "758E969F192A9B2B20B20C00"); test_comp128v2("E5A32C2C398BE819497923869A8D3316", "97FC978698E4FF67A0F0DC00"); test_comp128v2("C7BC59BE121995B4AEB0332CED5A1198", "B5CC69159E1C6047190D4400"); test_comp128v2("827E9C6B80997F053AE704994119544A", "C073B256DE3FBBEB72696400"); test_comp128v2("7702362250B6F5F6F323230C598F9051", "1BE3AB6CA52CB8A98041E000"); test_comp128v2("00E5A0BD996A84C310238A05A5AE7495", "A31D2FDB0196FA415CD15000"); test_comp128v2("8D9CC3F666512DF3DB2056CBE7F62684", "1026ED1657B49F41317A5C00"); test_comp128v2("BDFA3EF9D36448BAE383A49FD6A977E1", "BB2F552110C1F5872EDD6800"); test_comp128v2("18D87AA310A0CB9B3CC8EF54439FFCC4", "FC770426413A7100746FCC00"); test_comp128v2("992813CB979D33BFBAC54D55CEB4B73C", "D0148FCED4882E99B5550C00"); test_comp128v2("833856A2E8B0EE42A5EFDFE505A2A98E", "C21F2CA3950B24C3D5718000"); test_comp128v2("D16805B6CFAC637A2A39924114036E5D", "322B554745D2B0DC386FAC00"); test_comp128v2("2E627A1B378BDD2A3ACC7486963D69C1", "00B201FDFC3C4AB6C6633000"); test_comp128v2("86F68E8A8FD9FE2457E9E1FE47D2523A", "E4FC49F68FFEF7ECBF3F2400"); test_comp128v2("F72BF5374387A730D780174FFFCF41EF", "80396F47B01D853208C4E800"); test_comp128v2("07A2299E7A5F5D56C0070412811B02BB", "EC7B17DB8911F53B2F97BC00"); test_comp128v2("EE2F57A690134B89D2E4B1C219CD7E03", "F492970E081E648CD3366000"); test_comp128v2("B2DB578B1D559A2778CBCB0216EE5144", "B3253ED8496B17C05D7B7C00"); test_comp128v2("6C80254249001719EA40C51F2C4BCD4C", "36E4172968201F89069D3400"); test_comp128v2("D1C28E94E15762073DBEB812F5F1BEE2", "DBED6738EA5E202530C78800"); test_comp128v2("0F784B5CAF051C4FE137D2FE59A28C00", "D6008F9F45FB8CC50677F000"); test_comp128v2("39B71CEAC0104DBA6743E2EC285BF7DE", "01FAF3FD3A79579778F7BC00"); test_comp128v2("261756F9DB55FDFC1116091B8D6BD58D", "50FCAC1DD42FE079AF7BB000"); test_comp128v2("57E47170F70AB261FA998017D838199F", "88EE9756212F1BDA4E12BC00"); test_comp128v2("02DE5B5F4AC153124891E768B5E3A1B6", "B42185D92AAB6A18859F3400"); test_comp128v2("224E9EEF82B29CF691BC9463D9FFC129", "DB92040E46BAB90675E89800"); test_comp128v2("524B0B8ED727F299E59957FA271A50B5", "456AE3A856C6FF5A6BFDB000"); test_comp128v2("242A954E612E1F0194CE8493A4CD1C18", "1F607A7E1DB1212B24A6E400"); test_comp128v2("A61AA82441B24BDD06AA6BB2CE77CF27", "58FF4B1B3DFBB3E3726E6C00"); test_comp128v2("E01B82CC43D3D72D8CCD60C92C62EE09", "C00041D883E5BAD0BF03AC00"); test_comp128v2("3AE3E93F044396DFBB5A529A26DA2553", "DB62C24686FE8014B399EC00"); test_comp128v2("653A180F0F7C98B94130A5C40BD11EBE", "9B9BCBC09E09E8D559ED1000"); test_comp128v2("CB92A03B9A262683AA1A39CCF8866BA5", "BA1BDB72BF6C2A94D92E4000"); test_comp128v2("C79C8FC1619F413BDEDA3F9F8FB7AB19", "2334AFE1C799B9815C7C4C00"); test_comp128v2("71DC6F9B718457B72CBF948304DF4168", "13230113D8C0A23FD5BA9C00"); test_comp128v2("A11D3C634DAC5379B5970D401EB6C512", "CF9956024F33B4A965371400"); test_comp128v2("05B7355A6962EEB393814579339EBC53", "CA1CD0788A724158BC84D400"); test_comp128v2("D494D03F59245A4FD88A87C4E842F8B3", "C8409116E5BA783DA8B89000"); test_comp128v2("FA2235589D8182BF643DD68FBD3924B6", "447CC4020F9F87565B5B5800"); test_comp128v2("3537CDFE59252A2054E7EC1BC16C5061", "9C8D45FB1480BE389112FC00"); test_comp128v2("D81AC6DF80593B62E4CC6D51EBF682BE", "DF6CAEF62BC536826FCD7800"); test_comp128v2("EF6FEDBC6F65EC72E71DD457D7F01E39", "545AC9FC051774F6ECD97800"); test_comp128v2("A926134D19884E0C5DFDFED7E5D43926", "315E8EF19510592D48DE8C00"); test_comp128v2("D40A68048F84E3C89159DCDC2F6CA432", "4E27DD965DCF6CEB40489000"); test_comp128v2("E13A16536ABEDC92A087707F6962664B", "F54DF012133B0CC1D91B8400"); test_comp128v2("443F8F2A487FE8CA0E4E36CB0394FA2F", "FE5A205335BA7EB4F9EB1C00"); test_comp128v2("4CBDFF21AE06C34FBCF8BCE67A33ECE0", "1A80583C4CDFF82675F59400"); test_comp128v2("CE08B54548CDE074E31630B0C5F2545E", "47904CB4205016ABEDD7FC00"); test_comp128v2("16C810823D7C5BF3D5A7DF540BE927BE", "BA3C7853EE826A7FC1E8CC00"); test_comp128v2("493E34068D98BE1CB0DC488E4215EB44", "A10DFDCC8CF7E13AA9F48000"); test_comp128v2("2B16221E154B8E737705BFE4EF686EEA", "FBE1504BD0786AB3CE8D5800"); test_comp128v2("40067BF44F7EBEEC97866D1D2B589C0E", "76C385ABD7A3198D4B2D0800"); test_comp128v2("AEE48475101B133756C5260C974C3CDC", "B6B159E0B62A3E135CA3E400"); test_comp128v2("7C337455E7D04CDABF9100A0DCB518C0", "7FEF52780DDF19198AEB3400"); test_comp128v2("34509DDB0528245C753B34D0A470161E", "79608012018A272CFC4FBC00"); test_comp128v2("B82EC07003C40E7F69415CFB29090181", "E31D8D19A1038B6F3D25BC00"); test_comp128v2("101CE5DD0A72E07C3759B23BD07CB713", "13389C0B65706D05753F4C00"); test_comp128v2("E3508CC1421B61CC1829F1AC5000A3E4", "F3A0E53D7E27598C99901400"); test_comp128v2("BE906939FEB3D53EE5E3B3F09AF34392", "44F8E711171F64FE3B7A9400"); test_comp128v2("58AE77015B346DC96112C7463CE44CF5", "217BCBDD9A7A15E4E3B9AC00"); test_comp128v2("01EB1E42FA9BFECEA63771A8EC8FF608", "1282EA813670CDA82B028800"); test_comp128v2("0287CC4F44011FFB7BFC45C753A7E764", "EDC47B4AD2F99E044B773000"); test_comp128v2("FFAC099B033FA5CE08C1E10751436B0D", "774EB65C534D5C554A4E8800"); test_comp128v2("AB57C444B024972E3CF20C2F885D7211", "8C6F76D58DB1181A94989400"); test_comp128v2("66B10CEDC32FC796527D98A4B9052E0E", "48BDC529943EC0D5E66CF000"); test_comp128v2("9D4FC614B4A5ECBF84C40E3F2B7B3B0D", "BAD7D26D697368A3931A5C00"); test_comp128v2("7529E5C6269C75FCABA03362FFAF0BA8", "4428CA3F074553B7CD193C00"); test_comp128v2("290F258C9AE1A88D5DB89D1C963ED8CB", "49A6A7D3EE75322C985B5800"); test_comp128v2("4A635EEB118CF0401AA539B3E78D9BC2", "E75EB6229EBFCD7651869C00"); test_comp128v2("A0A8CC5650A8C449C27358D13F5D149D", "33D344289813FD1762951400"); test_comp128v2("00347953F59FEE734FA747B5BBB492EC", "163D84E824A58AEA21C45400"); test_comp128v2("9C1DB2550ABDC1D83D3C6234C1F7216D", "DA743AF98691235ADBCB9400"); test_comp128v2("566FFA71F19564E0334521F522D247A9", "E40AB25D08CEB2A2E2961000"); test_comp128v2("2BE2558950270CE4EA4F041311B700B0", "9D3B8DC6DF02EE2215575000"); test_comp128v2("2C7CF5A9C76A34069A0086D67162CAE8", "C7C27DFBC668647941BFB800"); test_comp128v2("C6704C0DA2E47E546BF089365FBB7EEF", "3EB8772558CE07528DAFBC00"); test_comp128v2("A4789958665D25892122D9B7838A7665", "E3AEA74A7E4487671C72B000"); test_comp128v2("0359DF86F25D1EF335EDECE2F1300D89", "62C3FD4450596914203B4400"); test_comp128v2("34BC3AAB3035AAAA631514F56C19D85C", "9C6C6F2C7BF109B71BC24000"); test_comp128v2("2AE6301993E8679EE9783099EE4B74C4", "5827D1C48907242EC9693000"); test_comp128v2("8E688E8C19E4842AC61D673A47502BD8", "E937B1C11E000FE617CD9400"); test_comp128v2("635D70F76E8DD5A60C93E636555643A3", "1FC3F91D21ABDB110560D000"); test_comp128v2("CFBD75A9EF7C590502CB305E255B32B7", "5AA46577C4A19E56222EAC00"); test_comp128v2("52017B0F5A735EB4B4195B41C5ECDDAF", "141288BAA0C78E43A21F8800"); test_comp128v2("9B7F9780EE1DB174EB3DE3E0DCB4430B", "67DFFA9CDC3456FD0ACB4400"); test_comp128v2("CCB9CCC90211E81ED80A3663FE9520DD", "B296494F844BE19BD0BA5800"); test_comp128v2("094C25D5E5F556578FEC3D09E1F18FBE", "2ACF6FC60CE2E75D0A26C800"); test_comp128v2("8EC3B41444043CA925036ED10D358A08", "5026E7548AE49E190927A000"); test_comp128v2("90E7DF0DE887D9A66C7481D56918E113", "4B84AF649EA0CD5F0840D000"); test_comp128v2("968EE15F77994D3215AD0B3B830C8022", "353867C1CAEC7F26927B1800"); test_comp128v2("1DB07381378EFF68414A6DF0B19CFA74", "2DA3CF74819513D2FA280C00"); test_comp128v2("905553A290DD902F230AB5334EC3463A", "013A682A7041996B08865C00"); test_comp128v2("6C61792C1391DCBEA18157DA3A5383F7", "61BE5406A149B4FF8B5E9400"); test_comp128v2("8C1D6A7204459D8394710449B1D13BCE", "CEFE606815A3CE4E25379400"); test_comp128v2("52A2764A059CED2CC46E04FACAE047AB", "B9ED555DE46B9F27FA452800"); test_comp128v2("583303EC3575D11BF8340CC10F6FB42A", "DA213F2F44A4DF52BFE50C00"); test_comp128v2("ABDB49A4B210381D9622AD5822150096", "F3672C97FEFC462BDB03BC00"); test_comp128v2("711639E35CA2CA66EE65C4A45CDD6380", "536057FBB46AA0D25803FC00"); test_comp128v2("0C8DB765506BFF298F89022E51C308B0", "89EA94F579D333C66670CC00"); test_comp128v2("EE21F43BC9268966A546FDD100C3D841", "D4593228E6274177E5750000"); test_comp128v2("EA1A2B1A515C72C4F4097AA555C93705", "E1DDDB18724B2B66A8D08400"); test_comp128v2("0C9B8393D38BDF1065B162DF707D3009", "B878385FDB1AFC41854B6C00"); test_comp128v2("EC0493D80DA33AF6FF5FC42D62870052", "4ADA9E7AF7F353A9D9EC5800"); test_comp128v2("E41565124D918E28E7ED57AE770D7633", "C8C8CAEB0DF9667A2D34D400"); test_comp128v2("6F4B3D805D5DCB5672FA617066A4B2BB", "346EA70E1588E99C88A47800"); test_comp128v2("8A66D7FC4CF30B989EE579AA08872713", "4BE79F8A52FF99AE1C710000"); test_comp128v2("8C901D0043F0660C0A9BC60171ABDAB8", "148A92FF28DBB5CB7012DC00"); test_comp128v2("B80E227C8575C331DA9AE0F4050D218D", "4CE2F914C3EB439A03393C00"); test_comp128v2("9ECF1E38C9B9103C6185B00F3C4D93BD", "CB38318E5B3CF543530D4C00"); test_comp128v2("BAC15FB21C609560794F4C965685E290", "46B7D216BCBA27E2A4085000"); test_comp128v2("C4AA0670C7B8A667728DA0D594069C99", "12D51B9D3C7909002613F000"); test_comp128v2("D099EF68D7E3D1D47F23AD15AEE4B5F3", "93622C70D1D635CCF84E7800"); test_comp128v2("B33945EE00E962EEEBDE6A8FF4742E4B", "509F4F51D9BEB63CF9B59000"); test_comp128v2("0D473BB0B5BD4DEB25DEE36BB376C10E", "3FD579DB817D8A510BAF6400"); test_comp128v2("A14218445FC5A8E72C96B1F27D73AB52", "A3BEF92801B0252DF5DB1C00"); test_comp128v2("494C095A54E5CDB1A1F6095C42D760E4", "15A68280AEB04086C3638400"); test_comp128v2("8F90256E47BE4678036609D34B89544E", "2396E34F472750F9D3524000"); test_comp128v2("5C0927F8F0FD2307F2C6FECE9243F06A", "87AFF076277855F812670400"); test_comp128v2("C636207195E46AE24C49F4F9D0B786F0", "86C6671A2A8BA28EC6918400"); test_comp128v2("A7AD2E1B41F3761C59B29F0BFC5890F0", "10574CB3947EDCD7E9595400"); test_comp128v2("C9FE56B04AC1EDDB9E04E352E8199A3A", "5C1ABFC9A21F5D95E1DA0C00"); test_comp128v2("C3974645E95D98E4F9274CF62B0D885E", "ACCE97C26AA1DD1BB9340800"); test_comp128v2("82D095C92DF409D3473ECA08A5A81B65", "30BA06CE2F1AD2A3EAE8D000"); test_comp128v2("BFAA576B9A290496C47C3D498F8D4FE6", "B43D9C5448CC3F754EA0A800"); test_comp128v2("040B228CF2C6002BE385DEFD7DB55725", "62D7DDCF277DDBBB84289400"); test_comp128v2("F1E565C0B52EB2B1E24C17AA3848652A", "9F8A216B9C64DC5A03833400"); test_comp128v2("2E8A846052A19AF00098BF1AE1FDA9EB", "2C5BF370EA47936AE4802C00"); test_comp128v2("ACE3EC57D2DBFF11B9B8A81B4406FCB9", "6689168B45E7E9084283D000"); test_comp128v2("7414C5D415AB5574A82668573BB04166", "AFA7009AE75A5B6E1C8AF000"); test_comp128v2("A986D44920217FD956E7C4CBC3DE0527", "F9BF62C6B3C722659717AC00"); test_comp128v2("A08A9B02F531CB9D580DF8176A3A994E", "D379914524C2ED1D1B693000"); test_comp128v2("4C0F9E924F063A6243865DE1123CA045", "D7F6FEB5091DA38F0D1F9800"); test_comp128v2("D55B8C7718CF349F906071C76555C986", "564E13F008AA9129A319E400"); test_comp128v2("0613FF46EFDEB6A663D915B86A9D6C0B", "E75538CAD55D599EC6718C00"); test_comp128v2("B00D4FA90CD2DA85E1E220AEEFE2C5EC", "ADE280F0C17C8BF566451C00"); test_comp128v2("FD380C653CBF3A927A769324D9E00324", "3DD21D2EA170DDDFD5E16000"); test_comp128v2("A9172812C62A2FB789825C71C23BE469", "765EA88CBA94AA15DD3BB800"); test_comp128v2("59FA5C6FA03F2BD23E35CAFCAF58FD46", "546D5EE3655E43632DD94C00"); test_comp128v2("F6A4D79BF1DA9F46E41241CD2CD723FF", "74ADD5456F2CD3059AAF3400"); test_comp128v2("46077741E91463DCBB61F3BA88599848", "24D72BDB319C6EA927B37400"); test_comp128v2("46E2AFDEF73C2242C2EDA4C690C91E1A", "23B988C1308602402A4FC000"); test_comp128v2("C240BF323A1B10CF791397D47340B26B", "F13F3AD8A00FF908FC6BD800"); test_comp128v2("87839B0D4AB1B531495D00751BDCB435", "ADAD6944C3392006CC85E800"); test_comp128v2("635EFEF876F6318332BE61BE6B4B83E5", "4E763956FC5B9B2C37C09C00"); test_comp128v2("4A4A4AAFB83EC7B93840A3DADA92F1E9", "7032880172E7BCFB8C72E000"); test_comp128v2("915C8330D241274251BD71B7E6D37699", "E2003179F7CB405965011000"); test_comp128v2("E20E7DC2DF7BAE037EC7D880B3C26E90", "992AADDC12E81003488C9800"); test_comp128v2("EEB75A96C92304E40C2E0E806E918E9A", "BCA49B63865490E279409800"); test_comp128v2("BB1806312EF173FB73897A3D0BE6A053", "F731BB08A833760754AE0800"); test_comp128v2("64E576287B81C3442F95345C078D22DB", "B5DEBC603B1ECE7161C95000"); test_comp128v2("DCB334F708467997480B458DB66356E3", "CFBD2049D829D9C42D26E800"); test_comp128v2("39EC58A24300E932FF9258EBA374ED62", "A489F81E6E16D0F436977C00"); test_comp128v2("52FE65AB5569A4DDC91DB5EB747AD872", "712CF42C24F212EE4B80B000"); test_comp128v2("9401DD3CF0C494B91A3CB28A677A6F9F", "6958E170DB5879834C90AC00"); test_comp128v2("A7A70C832ACD99447B4DA063BB40A249", "8C4337552EC9121D9C0CB000"); test_comp128v2("F777485D595BCDAED8AA529E91A161F1", "97DE44F1E89882C3A91A5000"); test_comp128v2("8D25D899D7458F37D0F26C7D5FD3C0C9", "1E9604A46215B01EE4C11400"); test_comp128v2("2C75E9C40BBE9AC71B03E2B1A4FD8F77", "BA6F3343BB0A50DFC9F54800"); test_comp128v2("5E2FBA59B74C51357D5944849B5D5B01", "D5312B4F4774EF49E9318400"); test_comp128v2("B48D0294C24464CC796F7665C639A3A5", "DEF4ECF98FBCE2A4DF450000"); test_comp128v2("E1EE10EFABD753F49C13D0B6FFE567F1", "C6CF830856A1AAB6750F1C00"); test_comp128v2("328BE773AB9ACABE5EBDB67A72DAB593", "1246E6852A5B78EFC63F1400"); test_comp128v2("C6FA835395102B135A0DF22ED100A30C", "F7EC6507B3D8882E9F86E800"); test_comp128v2("42BA23DC01CE115FEE284E30D93C2EE1", "85061F767191A64FB6DF3800"); test_comp128v2("590C81F77828CC50FAE2322F88F2FF72", "4428F98DAC165B709AF39000"); test_comp128v2("DD5A394D5AF47943FFFCEED795E55131", "7BF03394EA112244A2E53000"); test_comp128v2("E4B350364A61282E9C7E109C200BB59D", "22A39338B073F44ACC3A7400"); test_comp128v2("2EC97054F229452094AB6E95713E2B73", "C7DC6BC4D8586FD0A278A400"); test_comp128v2("9949BBF69464815BBADB53732B830972", "221299171F63C0DF3AEF7800"); test_comp128v2("AE7021704039DB7C25139864145D2BD2", "1945B987B54CB70E5D718C00"); test_comp128v2("31E7C45E5C16698DB87FDC4414521085", "68EE8B49C4E327CD7EFF1000"); test_comp128v2("2C9301A7630A80058E6016DF1E61F67C", "22E69372E777618A21335800"); test_comp128v2("8BA8B91866F892579CF8EB18BFBCD4E9", "F3E2B573F70A66FB55C23C00"); test_comp128v2("312372D0F65B447765758D9CAF2434D9", "93609D380DFF429A870F0C00"); test_comp128v2("CA787B420C4399138BE2317A0DC94249", "9D8372124BE92CCA0D06B000"); test_comp128v2("27DAE7C8BAE72D001FC9002353E9B64E", "4E15629B933D694F68733C00"); test_comp128v2("26BAA1215821CA9F90E4CD0690C10F2E", "58103A993006A0C200359000"); test_comp128v2("A3D6F246FB435A7FF58E1E88B434AD71", "685673795E247655A1B4A800"); test_comp128v2("FF468FF55671C17282B41DD246D2792D", "26BDB78D824FF8CDBA768800"); test_comp128v2("600272165F44AF4C0E537AB5D8E66736", "3C79315DB6DAAEF9EA724400"); test_comp128v2("508F035C9376D9FCC41AAA7D1CC764A8", "41FD7AF4639235E4A02C1800"); test_comp128v2("F4D22B29DE342EEA7CD8E4E3F43A4943", "CD833E80A4BF33DF3CFA2000"); test_comp128v2("58843C6F5856B56CA9198695EFE17CF0", "5676CE8A0AE61F33C3ABF400"); test_comp128v2("6E3C73DB4F5CC678DCC7BFDEE568E826", "A247316EF8F882D42FCCBC00"); test_comp128v2("D37AE74AD0489776723124F6CE81D42D", "78355AEA1296483A3CBE8800"); test_comp128v2("F43BA41AD0DB41DA4F54FF5D6FEE4771", "DC1D9BAFDDD5EE83C1B8AC00"); test_comp128v2("B65FB4E50DD0050E79BAF78FD2F4B9BF", "B0E291BAF797A8275AD71400"); test_comp128v2("8DC778A2ADA661942266E0B86CD7DA91", "B9D294C0291018F04A09E400"); test_comp128v2("B6E293ECCA4A56F1D93C8242D5E6756C", "25ED1EE23A3C11FCE7D57000"); test_comp128v2("4B5B86CCE2090F22424EDA2C512197C8", "A365A6A867B02EE9343CD800"); test_comp128v2("9F250AC936FDA89F1966C3D3812BFBF4", "3A4AB781109515A1139EE800"); test_comp128v2("87CF86E273A7A8BE687D1802005D2A5E", "8ED03BDAA9E92833A6727000"); test_comp128v2("98CA9A659D2C4DB1A235DD54BA36EB77", "0AECFC5BE2B7C1DC6D0BC400"); test_comp128v2("53EA0452158CD2DF5F5AE2DCAAC86BFF", "29A163B42A2D557A70974400"); test_comp128v2("74B58541693349756299EE79D408C415", "56D28DFB499FDB30B87CA400"); test_comp128v2("16E25268D3F1498A42525626C2440505", "39FCE1A8505CD73BA1DAB400"); test_comp128v2("737D32452FEC46C026B249E7D77B9A80", "9D515ADFCD93432FE73A8800"); test_comp128v2("ADD0543912E4A632AC4C0814294751F9", "59D694C394485E65DADDBC00"); test_comp128v2("0F30160AF9D726612C00476DA06F2FD1", "6EEBE451744257F9435F6400"); test_comp128v2("0198B362FF964F890256674B225AE2EF", "DAB5FF67D8925B0DADD74C00"); test_comp128v2("F99975FC4D5AD6FDA57655CF1DC35FB3", "8396623C8B44C2904815EC00"); test_comp128v2("762CD396934B5E7916EB48D07E3D8383", "D597556F2C650DCDAB686800"); test_comp128v2("ACFD433259569169FEE586564F056F31", "CE6C1FC52C2CC10E886B6400"); test_comp128v2("C0C3AA12D8EFA599621B8219AEDDB39D", "F46A4FF31C0FB6ADA7FC3000"); test_comp128v2("88964441874790C6B1AD12C6BB907060", "CC9729FDA25819796E90D000"); test_comp128v2("BF65C58D03260691C1267E992128F675", "BC7F343F69A3B6C09C114000"); test_comp128v2("B13CE2B597DA27FB9DE4B9ADFF717495", "87BE699F1748597BCFA46800"); test_comp128v2("A2680E79DC76B80EE268FEE9C1EC945D", "18E19922978E90020F353800"); test_comp128v2("0B31B79AB27925553119466A3C845C49", "041D5919C3CA2542AE91AC00"); test_comp128v2("FFCA938AC5DDF97C526941838527CE9E", "FF7CAB0E892D9AD987AE2800"); test_comp128v2("36D5B7252A7B57BE2D4E06879E6975C4", "427F72A3254D4BA24C1E6000"); test_comp128v2("E4FEB6B8FB01025D6C479852FEA000C1", "825845CFF96383778D0ED800"); test_comp128v2("E138E7CD58527B7022BD1553C4E493F4", "71AE9F089F8B970EA986F400"); test_comp128v2("F0FD7F38EC52265FC33D60DC9553A5D7", "140CEF85332BCDDBC3B26400"); test_comp128v2("4F0BDF1C4BE4A35C065EAAEFAE646A01", "96E64BBB007D4761869BF000"); test_comp128v2("03706861BAEEF0DC1CEA996C3EA93D92", "C2CF16AE93CB94D5A85FB400"); test_comp128v2("B1259AB7C681443C290E0C1BF5224DC1", "DAD36B50AA8455AD5D6F8C00"); test_comp128v2("E225F155FA5CA852E151B7AD3BC40394", "9F480C67DF663A51B91C5000"); test_comp128v2("30098EF69DBBDD25FB9C87315D3020F1", "51D39392BCF956FF0F34F000"); test_comp128v2("749E0FD7B6ECD4B45E80C6E54A14BD0D", "44F77EF7111682F411EA7800"); test_comp128v2("5A5444F0E7E7C61CFB031F86E62EAFF6", "2F65BCBA94C07D4346DF7C00"); test_comp128v2("C9C7A48094A579C8A989B28848DED187", "CB88C47F30FAB985582B0800"); test_comp128v2("50449C950CE2DB6E0DF22609E005E175", "64EB723CBF43873DD2320000"); test_comp128v2("9DEEC383005D68C05C665CF484E371FE", "F941903FC1062E6FB20AFC00"); test_comp128v2("FB3BB14E175FA9172A2741E04F139B5C", "4E9CD42E9C244AB3690E8000"); test_comp128v2("24C01EBBC6B68F9BD4850D1BC360FEBF", "BDFFBF809BA31A516463A400"); test_comp128v2("FBF4841145F14A97DDE18D3ADE4CA794", "5CF60E45C59FAEAC54A66C00"); test_comp128v2("B3AD72EE04894CB4B446CCB338E31656", "39C9B3FA3B84200BD76B8C00"); test_comp128v2("3BFD7004020C1DE138BB6C78F2042D89", "5BB03480D9B6CA5180F82000"); test_comp128v2("024D288A0F95E4633BD0B05CD5B700FA", "39DFD93B2CBEBD1CBE42F400"); test_comp128v2("1AEC0EE2B2E999AB36583974C7668FDC", "68FA512A754892098A4F7800"); test_comp128v2("6BE0D7EC8A11ED5969386ED31F2AC1A0", "37161F968FC6655AFE02E000"); test_comp128v2("FD087BD12284E354ADA00FBD47EA0B99", "B1E477DCAE13CA518EEB7800"); test_comp128v2("5E342819ED86F7026BF73B76227A8CEE", "B002DE9D67667700161BEC00"); test_comp128v2("75D09A20C3586EC73944AB4C216D44F5", "8AD9F7E3FD40F9637AA0E400"); test_comp128v2("0E360DD744AA863F6E0341840510AA1B", "478446EF066F048C82A65800"); test_comp128v2("C048603EDA30D4858F8F6C48A9563B41", "4D53E54088CA31C0AD9A6400"); test_comp128v2("759445A6138231604DF7C13CCAE4BDEB", "5CB44BCCD6460A0950CAB000"); test_comp128v2("A91CF2204877ED8903E7B1AC9348BB22", "915775CBBD20EE58C44B0C00"); test_comp128v2("E21BA0D598D2FC61442E32449589112F", "4A83081F3332DCC326F5DC00"); test_comp128v2("04F3AA9BA2A1D95E8CDEA6FC34308017", "D6082B73B98D161CBD653800"); test_comp128v2("594A2A8792456A71705BDC4E89E2521E", "65929B7C79B8DEF106487000"); test_comp128v2("446D9EEB6FA4D645BB682CB86B8277B0", "5AC860D938A44B12E17F0000"); test_comp128v2("C951406FF94B294E9276024B879B0661", "AC12B1BC46CBCB5AF2B0DC00"); test_comp128v2("4E542E0D75649646B084DE8FB0285C77", "65BB3D00B13D1EF9E5173800"); test_comp128v2("A1268CF4BAF41B5D12BB865F0E3D8B22", "3BE83D7A359D3597AB3B2800"); test_comp128v2("36C3C451F2325C18FCDD73CC0FF21B18", "4B6718390394E16180E97000"); test_comp128v2("CE257F14B53AC96792EA2CE8B4A4E58E", "A519EDEB91EFC18B1256F000"); test_comp128v2("3F14F22F59C516912AF25CA62FA8F053", "F3BA543B2C728E3FA4D30800"); test_comp128v2("3A4F5220261EAD5516E8C517BD2ED831", "DD66EE67A690B8633E51E800"); test_comp128v2("495C634F6667460C254D97B35445707A", "C6F29CFA4B9CAED36E270800"); test_comp128v2("66EB5313B0C83A64FD376BE02E03166C", "5E895656A03CE5C83A4DC000"); test_comp128v2("35889435A0DCD4B799BE7695320B69CD", "ABFE28829FE27DE9E31B3000"); test_comp128v2("183B1F81EE25E31AB5DAAA33D015CBA6", "E80C570BEF8028830FB68000"); test_comp128v2("F92B33E2BF63BF05291C09F0213FF72D", "9FC5D30A549A662B0278A400"); test_comp128v2("E12E30F732A3A2A206467BB3F174FF2D", "FBF985953AC4308EC8678000"); test_comp128v2("E66C7ADE2AB71A9DDF8BA3A18F9C1302", "E2F0A7D8972CA55D5873E800"); test_comp128v2("59DF68C04CB938BB9CAFF8EBBB6C6CA5", "A12CE62E9B86E592D647C800"); test_comp128v2("5688229644E7F91AD75B47BAAE320C27", "445E1ACDA1CEB5490A7B5400"); test_comp128v2("8B26CFC065CE0BC44A97E33A5A07345C", "75F0660EB010AB40421F8C00"); test_comp128v2("77BE38B4815AF6DFBE17DD48BA9AB1C9", "A8F61F6D8CF832A592B4B800"); test_comp128v2("5E73AD699EEE8DE8460F2C01C2077CD6", "CA6BFA4A79B9104550A07C00"); test_comp128v2("1B546F3B274639D31AE0ECF3C378D180", "5A668DBD6AE23D628EBD1800"); test_comp128v2("57887332BE21016DAA2200E69A5E850C", "937CBB32F8A08E24E9550800"); test_comp128v2("F11C61AB9A2DCF1D73D2566C07F2F90B", "70BA40635D38BC070B1A0C00"); test_comp128v2("2DFE7BFB3589E3EB3E77895EDF0130C3", "7C0BF94042656E559D11C800"); test_comp128v2("C7F9F26B1CB9C77524810B97C649137B", "CC9F502181117A6015AEBC00"); test_comp128v2("E3579A2F7B2267295B14F6A2B2F09937", "FE52FA20DF09C6BAD47E9800"); test_comp128v2("7A2C147FF0868F41FF61BD482C7E21BA", "81B79CC89B3DC23A023F5800"); test_comp128v2("ED5E4033949163C5AB820CA16C51AFF6", "A82B3722F3589AC19CFA9C00"); test_comp128v2("22AB205407EF30ECCDDA49B779ADA405", "9B570A9C9E89BB11D1C04C00"); test_comp128v2("BD5EFBC04BE376CADFE0207ACD8AF095", "AB4D42353B1DA514E410AC00"); test_comp128v2("785180CB3BC2FE8792E424C407F12437", "03BF1E622929CAA732929000"); test_comp128v2("7E1DBB304169F6DE4F12151FDEA7B4E4", "F74A4BA566896445694F0000"); test_comp128v2("6A1668D9BF09C4B9087F7640A835AE9C", "C467FA442E9052B1A1944400"); test_comp128v2("096E3A437ED6A342BBA60D4484EB348C", "AF5684AA97791DB5F4F04000"); test_comp128v2("218223AE24CF97E6BFFFFF1F646ED3AC", "912BAC5C763BAFB866356800"); test_comp128v2("32E34FC02745575D7844046B5EC92297", "753BA5F36C33B0A8FF19C800"); test_comp128v2("78193A44A12EF231CA47659678D49122", "B812FB79D7769618FB98D400"); test_comp128v2("4C79BDF37D02F924936515FA9191535C", "3A3EAA1A02A8770D634AC400"); test_comp128v2("9149129D54A114706A17C92268E95788", "4BAE933AF24AC862AA02C400"); test_comp128v2("827123B8A0AFFF76512B9A4247F18263", "4E176C62EF8993DC49BCC000"); test_comp128v2("B7D362E0CAB7936076570676D3B00926", "F7142C9528BDE0C3410DDC00"); test_comp128v2("C4835D3873B027F4A04E01E09F10B567", "6CBDDC9F268BB2523D371400"); test_comp128v2("2C71B6B495E2280B5ADA4882926012E6", "6608CAED6ECE6BBF4E4C6C00"); test_comp128v2("4DFAD94ADEFED514A8336CF78DF291E3", "BC9E3927D64A5AE83FD09400"); test_comp128v2("39E9E060A7A89B40951170834EB358D0", "882709FBBF4C15A53ACAA800"); test_comp128v2("93FA22B2882936519A894C83FB71D7CA", "10CBC4E60938FF6E02BF9C00"); test_comp128v2("6D3054E4F523088E4672B051B4830D87", "8C235DDFDC54AC96423FD000"); test_comp128v2("9A9AD3AE912E007D1B7A773CE5995F5A", "C7440E0B278459AAA5452000"); test_comp128v2("5DE6E922819457D8A17A4495C432CC78", "346894BB2BB3E5396360F000"); test_comp128v2("CA9D461DE4EFE173DD0F70B21F0EF2E4", "897F09AACA45A443DF046400"); test_comp128v2("6CC4A0AD6363AA75173ECDDEF1867500", "5E1369255B428A3CAFE91000"); test_comp128v2("A83B7718967EC4E71FF17A13E504979D", "8ED613C3B5EFDCADB43EA800"); test_comp128v2("920D2CB5A77A118740641007D01050DA", "5CA75E88F9DDE10943C2E800"); test_comp128v2("A25AA14FAC524225CF34796E3B48D4E1", "486C8EC0519E9CF7BE04BC00"); test_comp128v2("079234F699B3D4F4E10E81A6F6CB4AF2", "953C821F1B60AFA612147400"); test_comp128v2("2C0FA3B7884EC1F49A1FD64D25D918CF", "66B1BE35A97A35B552B54000"); test_comp128v2("4809694A853437CEBC1430403F2C0AC1", "8B52048DACE2B2426E64D400"); test_comp128v2("CA0B1E50C790195CF83E867BBBA1B11F", "8A7B4F4A857CBB91D743FC00"); test_comp128v2("750AD2FEB9321E8161BDCC3737F294CA", "3AFA341A9F80B2C6DECFA400"); test_comp128v2("EFA25251D61F8DED93F2DD2F5C773DF0", "B05617847AAD49659499E000"); test_comp128v2("4372D23AD9D5985A321D47FF3B94311E", "2F0458EEAE7C37C9B0D28C00"); test_comp128v2("0E4D5FFBCC246C0B694D0BAD0C6E01AB", "BB9E49591CDEA7B38E42C400"); test_comp128v2("C2AF47D04C203797F5EC2CC841A3CEC5", "58EFB4158198FF36194F6400"); test_comp128v2("5A7F52669369320D3CD5883FA4EA3F2B", "6CCC2ECD466D42B7A4305400"); test_comp128v2("C50ED70A1FCF421FE6D3E68BAF7D90A8", "DB98CC5763982C52885DEC00"); test_comp128v2("09483A1EED2CF60CCE0DF670E077459C", "17C5625B1E4B638831C73800"); test_comp128v2("9E8BE0C3DF99181656D71A665B2F6E78", "8930954F0BEE0ECCE8FF7800"); test_comp128v2("2E1A3948DF968DACB8B50C7BE01F7457", "1AFC087AF7C3DFB650B5BC00"); test_comp128v2("DC70952E477B9CC06C507BEE1B1D0DF5", "2CD73DFE8A454951F8F5B000"); test_comp128v2("20C31EC84115A792269692DD24318D5E", "710E521A571C20908E4FDC00"); test_comp128v2("57EDCE38D0847365DDC3E2476AC3E1F2", "9C8BA4C70BFFA8D374427800"); test_comp128v2("2FE14DF7EC89DCFC40F4532DA4CD8FBF", "E929007EF952A2CBCAA1E800"); test_comp128v2("C891106C395E6FE27CB057717252D1D0", "426667B142F5B2C6DA154800"); test_comp128v2("E00C2B6A32C78A39943C5C47F2662676", "DFF8A3A85B25DC99325CEC00"); test_comp128v2("FEC57F0F123AD7D36B4B6F35703F64F2", "0E5DD6F7FDA06422FF0C2C00"); test_comp128v2("7EA718A0C7FA4D3C942BEDBFF0E8C27F", "0494E06696570FBC07B20800"); test_comp128v2("5202C1B460DF93CFC147F50DF0DB8AD2", "909CBBBB1234814F20B94C00"); test_comp128v2("A5BEC24FA1715EE2A004AA64ADB891D8", "79D301F620BB0AF9F434D800"); test_comp128v2("FE558B21568C8EC4F96ED18492327DD4", "20D3E5E9D2FD6C7B33644400"); test_comp128v2("72CD73EC048C026C289501852295D8AE", "A7BE173CBA66C90364D90C00"); test_comp128v2("68359A95139538BDFDA335F9766CB52F", "D9153BE65D53CA5E79DFF800"); test_comp128v2("FEED7553983FBBD1D9758393FDD5F1B7", "B8CEF24E0140134FCA4E4000"); test_comp128v2("0A07B08315437682AFB159924B0686BD", "4CAFF446C895B91BE7A06800"); test_comp128v2("3C326933EE7F409FF467A907DF0036F3", "F80FA736C483EA30DF9A7000"); test_comp128v2("95F16978198BE120A3080E7339C4DC16", "61B588B440110F1176018C00"); test_comp128v2("4FB88DCEA84BEC2D61D59D26AAB1E5B2", "63B99FB3D4828DF14A632400"); test_comp128v2("99052813B4FE1DE320C9D18250D7EC0A", "99B838E84079665C7AE32C00"); test_comp128v2("58B92356701F585C5DEF34A1867E1164", "6E4C4F64265374992B89F000"); test_comp128v2("D4730E1A155968372A2796C007DB61C2", "F24DBC3188928DD7F6DEB400"); test_comp128v2("31F061BE095DD8F0CB9B12915F677513", "432999E45E51F50F160E8000"); test_comp128v2("A8FA54FD77B174B01419DEC483C3BCFD", "960214386FC602BD0B634800"); test_comp128v2("B69C34EB57A1E9BAFEA2FC8BDDDACB97", "D31496A364D337F21BA43400"); test_comp128v2("E3DEFCCF108C176B427C6D558DAB2257", "FD4F744BDFF1BD5408F63400"); test_comp128v2("634413A6F936B67FB937E47831BA1744", "3D2045CEBCE10C5E3945FC00"); test_comp128v2("761FDBAD45F01BB614C990C5DE842A48", "7A29FE2AAB86F4F7BC520800"); test_comp128v2("EC9ED9B997C283EDFAD1D86D2B4A7164", "058544343376B8BE3F87B000"); test_comp128v2("81627CD5C956F5B71A9C0ED4E521420D", "4E313907E89F9F9B55398000"); test_comp128v2("3DCD0B3984C8247B8562EF6237E9545F", "17BB7D71DC7324EE7C747400"); test_comp128v2("6F27674CBCD953B4059F3B55F78E8795", "14900F7EC6BC6B801CF05400"); test_comp128v2("ABCE668D4532111068E0094A3A100E4D", "A18AF444D2BDC12B60B2F400"); test_comp128v2("280E9EBF56FAAD5DB2C5CF73CE296DEB", "20055334D6BD65B69284B000"); test_comp128v2("84608515AAF1D869F9EE47B6EF48EF0C", "78BA397766592C4E05A7E400"); test_comp128v2("1692DA2B7086E7AA8D3F3035C7107359", "87194D864264ABD4CB900400"); test_comp128v2("07DE064FA13210922ED16199034CA965", "2ADF1D09B4ADD164B76D1000"); test_comp128v2("75EF82FBCC14256470722FADF3776AE5", "CD3DDE9AA2529DD7A8C8F800"); test_comp128v2("B99DC3A08EB5341A32F8C62E4BFEE43D", "F417AB93249FE05D579B5800"); test_comp128v2("A4183E5E094E733AE11BDD7F6331043C", "90655E9480FB2602C4D18000"); test_comp128v2("28D40D70C58A581E2BA5DB2D8312BA8B", "535906A3650D559BBC8DB800"); test_comp128v2("13B5B284571B20D9EE9799C96BD40FC4", "B71466E5B1F11AD6B8AFA400"); test_comp128v2("0029C96AEEAE094A86983794BD582AF6", "836AAE69B149DD54877A2000"); test_comp128v2("DBF2EC98F31ABBA2722DBCF89EC6A44E", "68343042FC4F198337C3BC00"); test_comp128v2("083D0ED162BF1A80574B5906CC1F4AAA", "FA3ECFC6779C1AB430E4BC00"); test_comp128v2("4E1D795034EC45B8F9821D6CA8203BDB", "E816931A5308C59819AC2C00"); test_comp128v2("4EE4972E7F60987243A9799E63D662A3", "B16939018348AF781859E000"); test_comp128v2("849CF4F89D3233F3E584CE0C129D4BFA", "D9C2178D63D98597ADCA8C00"); test_comp128v2("57EDC728BCC2B30DA9B7BED3393C3CF9", "CE8A219B97FC38CF8EB29400"); test_comp128v2("9A3E2A0D88F960CBB1808EE7211C6362", "B4753C1822EE5832EAC3D800"); test_comp128v2("032219016ED376CF2D9CD997FB3ED6A6", "9D99910AFB22549BFA59CC00"); test_comp128v2("6465F4B9C6DBA457BE7EB0580642E4EA", "349CEB847C741124EECD5400"); test_comp128v2("EC5EAFC6E0CA5C7811E4DBE5188454D0", "FF3B8B9C7E49CAEB00BA7400"); test_comp128v2("FC779DF1C75EC65275971CA4DDA54445", "BDF0ACA83F71F5795FF91400"); test_comp128v2("97167B93AEAC688D65F5CCA5AA540628", "A8934C20176826D8E9210C00"); test_comp128v2("D6FFDE9C6396DBADD186625777F0EB1C", "25D38BAB18E0B1A4561EC400"); test_comp128v2("BBABE99B5DA9F492794D1B33F2E69306", "A2F9EB555D1860FCEB90D400"); test_comp128v2("31336FEC645D23BFBDB2F17A7D051ACD", "DFE297E427C7EBBB85125800"); test_comp128v2("F4CEA98E6B5D08FFDDEF9E83F02EFF76", "0793F349B87CE001C3D24000"); test_comp128v2("03C233F35A8980F0167C9C01FD381026", "1B936C46CFBC6210208A5C00"); test_comp128v2("83937A0CE40B24304B9FD30B2ADE62C8", "81449A34B68F541FC0D26800"); test_comp128v2("CF885925FF754C3D2E0F17FEAD4AB540", "FB623A02D722E99948756C00"); test_comp128v2("B263174AD8926BDBDEEC5E9A9E92424E", "4F2D03FD412BB66F8FB45000"); test_comp128v2("3493E0EE360CCA3459BAD5F44C23E804", "0C327FCEC6FCD5C9BD410400"); test_comp128v2("1CBE4486B6754A05800548C706835524", "A5B423FCA3BC87386CBCEC00"); test_comp128v2("96057C428E150BBCBD9AAA18405BC04A", "0683236771BBE74B4A2E1800"); test_comp128v2("F2B319F79573629E56759FF5BAC67303", "C89F69CBFCAFB5CB5F5BE400"); test_comp128v2("1DB5E2D4F572F50D2DC637921E46D270", "8B09544DFDD6BA2F6D8F8400"); test_comp128v2("8EAB71FD2C9F62866607E317946B8515", "7B0C4057E9CF10535B275800"); test_comp128v2("9033CFBD34995F33F7530BF78C207EB2", "34D5E0DF065F9CD9373DB800"); test_comp128v2("55B56528FE385672E006DF3E5F15593C", "71D3C14D2EEA2465AD0FC400"); test_comp128v2("52A4AFF178091E85625C39583A935096", "7EAA20BD18083FAE1FED9800"); test_comp128v2("7F3F6A273A8A75EEEA484F270209DE61", "695C9E3DE158905DB1B9B400"); test_comp128v2("B607EFEFC1959FA8B32E0979F241CD7F", "C3B8B80D01C9390D837DD000"); test_comp128v2("B5D74887A6DC4F652ADC13A10D4CDD9B", "19CC8430EDFC5FC268973800"); test_comp128v2("A09F045DD589B5223037DFEE3519A376", "E60E7F22341E66A3F3EE1C00"); test_comp128v2("F3E0ABB2B1E4F00647F2D5C236A1B26F", "7A776258F5C5649E5E201800"); test_comp128v2("C47160A0897A8422C528D7BFC8D20188", "D1D32C73E3DCA58B70BB3400"); test_comp128v2("F779D231ADDA823C8F9EE13C5CFF8327", "F1D9065561B6D0591BDAF000"); test_comp128v2("BAA3CD7BE838696F2F8F7161D6C8BC07", "2F4835836D00DE30A77A2800"); test_comp128v2("29D8058453F0FB8777E0116247D1624B", "D626DCE8C83BBCAEB1E60C00"); test_comp128v2("962F26AB8CC6CFE7004EBB98219F6E46", "3114E64D5E558C1CBBCC4400"); test_comp128v2("B4C608BC11A358456FD44A38ABECD461", "1EF6FD55FB22D74591F2DC00"); test_comp128v2("DBC87B90E5CC736D89A063C8E7EA12C0", "69A047C836101660FA0EE800"); test_comp128v2("E2FB2C67E647416BF2A58284677FACC5", "6995DFA7822EFFB01826D400"); test_comp128v2("6D2FF6CD3C9A5038B9B33755C6DDB410", "4A047503796C5AF0A83D7800"); test_comp128v2("3D20566CFBFCBED16DF8D3A0284A85E6", "835C3F58AE388B93F74E4400"); test_comp128v2("88F65F3F649962491D3E0FA045EE23EC", "9612855A987ADDA693BCCC00"); test_comp128v2("C6ABB80355AD24C621F34965507BDBB3", "98603A29610E403253C30C00"); test_comp128v2("C37280108B16E450EEED07BA7575FEEC", "2B1FC2879493BEAB8BA75C00"); test_comp128v2("F637E1EFDD8A8F577040337DE4817A7D", "00E125367FED4AAD7277E000"); test_comp128v2("EDE6AF5E598389D9BA050D5527405DE6", "904AA4D51E003AD87D170400"); test_comp128v2("AAEAA56781A803C19F7FD64ED2ED2DB8", "EFA8298CB630D5D9C3325000"); test_comp128v2("1B7AC6EB7CFE9DB7CBD20B3AC8BE73F2", "46DE7FDFC06B8AE2F3A4C000"); test_comp128v2("E6A8681B7B066BA7CBC43139B22C3DDA", "BA6F5DCB9895FFD2509C6C00"); test_comp128v2("A12237D9124C39E638D56647E6A86452", "D25F86768AA3E961C69C0400"); test_comp128v2("07066B004388D3DFB3B04A64AD83B7F7", "7650268E5DDA7BD220CF0800"); test_comp128v2("55A2BA46DE50A3C16EDF3100416146F0", "5FA039D0354C912D161E5000"); test_comp128v2("6109A82102094EA00C2DC1F5D1458EEF", "8D84E7F5DF9FBA1102E23C00"); test_comp128v2("F37ED30ED80B697C3C71DC8CF41CB486", "AFA2019028CBC07C803FF800"); test_comp128v2("80D00951B03829654F56D8577B2319DD", "259B7737DEA5AC6586C9CC00"); test_comp128v2("4ADE88B1C1450BD2C04DB298627E794D", "11F7BD5CD2F0457A912F5C00"); test_comp128v2("6B8CDA8600BB3D20418AC2D6AB2BE0FB", "6CB450249C7A80539ECCC400"); test_comp128v2("AF5D6A632867A1AED9B0591504B6C52A", "8976868E00E168C2BFCBD000"); test_comp128v2("D7881B90D486C90EFF4389A071039F0D", "F652A1C5A41ABBF0AF09BC00"); test_comp128v2("31744AC378C3A56E2E599A052133E3A1", "990811B8536A91B40FDF3000"); test_comp128v2("DB828FC7E2BA095663B1CAC857B1C882", "991E252E53D405FA7584CC00"); test_comp128v2("7789790E921E29D8CB10D2E90A6CFAD7", "C0615EBDF99A548693DB0400"); test_comp128v2("631510BEDF7A2CF31DA6E69F9AC78378", "6D875FDC78C70F8BCF206000"); test_comp128v2("41D1BA21CE28CB116716247CD1567A1D", "94903B7659431E454569A800"); test_comp128v2("0F98273C34B90E685BD7873383CB3BC7", "0C738DC3E630AC6FDAE9C400"); test_comp128v2("75BCBDB5B2E23F46B6366229281E5487", "68E6EF3AD2A857847D050C00"); test_comp128v2("A2293CC54F3395A790F86AA56A95E357", "B2FCBED0F540FB5ECEC9AC00"); test_comp128v2("6E92707ADB71C3808BA4E241DF3EDCF2", "D45945189B01B92C0ECBCC00"); test_comp128v2("4B469FF0DD32AC00928D4770977EA271", "C20B5CC8B92FF28E449FD400"); test_comp128v2("A0FC84522EECA927DFA68F9D07C70B0C", "451A254F4DEDE634F514D400"); test_comp128v2("FE996981505A918727D5BD232B0E157C", "646E03E626B160D204C3B000"); test_comp128v2("4F388F8FFBDA93A421167BD44F368647", "5F6F12C381E2E75AE300A000"); test_comp128v2("0086CFA9CA7E7AFE28C4497FD19AE133", "928AEEB2868BEDA13F275000"); test_comp128v2("4359ACB771F941FB8F52513D01CED563", "393130BF04B3D914DBCFF800"); test_comp128v2("A19C58E94C9EF7C363A627A72EE4C57C", "B1A84094A806E28A43B57800"); test_comp128v2("C165902B95C0CB08ED5DC0FAF3580F2D", "6CCB395862241FA3661D6800"); test_comp128v2("26BC352460FD073FE015A410A7CFA935", "61E88484BB88EB782141E400"); test_comp128v2("4C3F3A58186A0DB703180BE8E4AD5387", "EB8671AD139925539E964400"); test_comp128v2("E9CC86936DF7AECCA2B86736689DF416", "ED230A13BF046D8507B77C00"); test_comp128v2("2D90CEDC4BB18209C39572AC480BA46A", "E011AF24697A323D4CD4D400"); test_comp128v2("D769D834E79706D9D620286FD60B1882", "35943940C4DA30A49167C400"); test_comp128v2("FDAD1FA63E891B0D3BF81D8629FCEF99", "1C2DC750B9127257FC288800"); test_comp128v2("825721F453B177BFEF0BAE758AF1AD2A", "77052E1CE9499B1B7867F400"); test_comp128v2("2F3D588F1FE8E4CA01533D4A0DD9B4DA", "141618BC5D4E72E62A9F7400"); test_comp128v2("1B8A0B4D6916EF18BAD7CADCE39D2833", "AEF6463AB65B838A5109A800"); test_comp128v2("B32247244EC86BEEFC52D7C2491E9D74", "116F80B9090964FD599D3800"); test_comp128v2("3BE4339301C93DED261879486C6A8D90", "8E23D0F627E7802D1A3CD800"); test_comp128v2("C903F42E92B38595CE04FD53910EA6A1", "7E92DE635B26DC6051E8F400"); test_comp128v2("A24E8A73014E79FA74FBC2CF1CBAC711", "0989A5AB4FA9CBD9AD0D3C00"); test_comp128v2("2F3F558FA77482A4CC9922AFFC7802BE", "281A9A26975E9C768A7E1C00"); test_comp128v2("82910421BEFAACBAEBB5EB41DC83208C", "A2D604FA378CF8119DB73800"); test_comp128v2("53E78DBF2B389F15A2371DCCEE1E1DA2", "8ACB8E30CDBF4AF3839FFC00"); test_comp128v2("559E459FD9ECE082963014F6055E0E36", "436F9DDD7F7E58599A5ABC00"); test_comp128v2("719BF029031FC8D8650BC2699D5DDD18", "A66649CF28F4FCEC3C4E4C00"); test_comp128v2("723B50440DE3F461C4E5BB5BC9CCB5F0", "B8686043FB0F1413AA938400"); test_comp128v2("17D769E91662028370034FEFD2876A25", "635E411815C51636EF944400"); test_comp128v2("0C102DA3F7A14B3A85B2A92A31CA4789", "0EC89144BABEF0E7095C5C00"); test_comp128v2("AADB189813213B480EC68B9678281753", "AB74FA76105F492560E98800"); test_comp128v2("5919335F3F34EB77A0D13D8790E78DE2", "8B1EAD6A1613CB2D66A6E400"); test_comp128v2("6822677E661136749615304BC7FE951A", "5C94BA4C4C4C6A1A15AB8800"); test_comp128v2("2CC83DC6F20C2A7CF26E091238784A90", "9AFF51719A5284B6CD051400"); test_comp128v2("5729A297D31DCA3C80C1AC10AC176B22", "D00E1485CBA3A95D55FE6C00"); test_comp128v2("8C93C54A70BC96C53D439450A3FF9807", "FEC899718D29F53351630400"); test_comp128v2("85CDF4DB93CC4C8317EB38D365154735", "091A1DD86B98DC9CC88E7C00"); test_comp128v2("2ECDC19D1D190EE4E6AEF05FF23EEDE5", "5CBE8BB86ADDEF96A8CCB000"); test_comp128v2("0797E696BB2890ED056F4EB0528AE0CA", "793D86F92A994DFB06BC1C00"); test_comp128v2("C066293DCF36C84E6B3CFE96F94F9C47", "5F12425ECEF96D7AD1DE2800"); test_comp128v2("BBF3B6F26357F2D1079ACE9DFDFC5ACF", "1BBE191A388CC485ACC73C00"); test_comp128v2("B4A0038D686FBA95824576415B778DCC", "AC22616B710B0813F74BF400"); test_comp128v2("23D7856727E8712982B14CF92F15BEA3", "C5FBA55A7DE5D2383803B400"); test_comp128v2("56CA39A1C3CBFD2B69F2655FC24FE648", "C483786DD70F46392ECFF400"); test_comp128v2("06EB61F4D242712C3DF57BEFD0CDEFE1", "3F1ACAC22225021770211C00"); test_comp128v2("36C3122A765088E042535DD98BC9ED18", "4AFE30D76B2A655E030ECC00"); test_comp128v2("DF71036C59630C04C024ACD193606F4E", "C69F93BC5CD672FA60646400"); test_comp128v2("0A52F96B6B364526C5A69E166586D25A", "CCB7F20B61EBFAF3A43D1400"); test_comp128v2("C945FFBA58D767D0D10D9F782CEE0187", "C9843051992470B4CE005400"); test_comp128v2("FDFA26664DEC8A4924CB13A5FC20EE17", "6CD559CDDB082F8DA4352800"); test_comp128v2("12A863FDE1C0EE9B6D16DE221CFCB61A", "F78ABDD98DCA48EF73190C00"); test_comp128v2("3A5F7A1CB88C2C9956749106395F96F4", "34BC4920C153F06D3B7E3400"); test_comp128v2("604BDD6E1FFA40CF8C6F96DFCDD62DA8", "4984353CD8F24EEF1B6D7400"); test_comp128v2("9587947D970D63EBFE4AD39D0E82F512", "339EAC3E342374FC2A164C00"); test_comp128v2("1A6CD41A12ADD4531D8296CECDB12374", "3790B825CC997DA4B7A7D000"); test_comp128v2("D6E632FAEB35738451AD453137E9F3BE", "5EE75C98E3F881BE277A8400"); test_comp128v2("DD3FDEFF3C7F70BD04683F6DB3418F41", "BD09AC2921A2DB430D86F000"); test_comp128v2("527D3DC8958F7E48800A4061F1B4C52D", "616B6E288F8484D1C5FC3400"); test_comp128v2("49C88A8C09113B1B20755F2BD890084F", "4939E9DE1A45020E61931C00"); test_comp128v2("F8028EC02D6E7E00021452C58B0B60C7", "E5D8BED53BFDDC5D32820400"); test_comp128v2("205E4C48735F32916A0E1D3F3A0B6B21", "4AD530D94EA8D498044B4800"); test_comp128v2("B8690268A6F86560B3C5359C6BF6FA3A", "6A92D7E521805F27C5E67000"); test_comp128v2("5ACCD297E16C479EF11A21BFE70358D7", "FCF7BD1B1EA29CF4373EA400"); test_comp128v2("74B8193542AE198C23C6031F08FF43CA", "780DBFC6135777BDF9F79000"); test_comp128v2("CA25D7D6F552A94C5481386366D1A0F5", "99547CBD65A09C3CA2985C00"); test_comp128v2("DA5C2F5CF869E6D16ED05925CDD78840", "4934C178087623626F070C00"); test_comp128v2("B1851E619BA5D0A36461F6267B6A9413", "90A284CA11E5A5AC86EFC400"); test_comp128v2("994A371B63FE48129BCB11984A2604D9", "5BDF08835EEF4D01CBE41800"); test_comp128v2("0AE26AB49E6158225D94BF39FDB29EBA", "83B81B517E7C54D578B5B400"); test_comp128v2("E64B2E37042CB00C5C9624BA2EBB6DB1", "F059311F4B7EE022146C8400"); test_comp128v2("FCD24B6C00C5FBAAD39224D54B3FE4D2", "C8B1E798A5299804F9F06C00"); test_comp128v2("FEA2A9307DBD872BEA7C42B4517FF856", "FCDB2B1870F46D34F2BD9000"); test_comp128v2("D6F8D8379339F4B9B6C6C7EA4941880D", "B02031965E6AAD7912746800"); test_comp128v2("6C7D5314CD07485E80A9CBC3536E8E3C", "F625C41964CFF56A0CB92800"); test_comp128v2("7B1B09636A93D1D6089F3E8C45939ECF", "AB411511CC23630BB1068C00"); test_comp128v2("60BC3B66ACCB2FD8FBA2D180C345DDD4", "66ADE7E8DD5FD245FE67D000"); test_comp128v2("080B4DA8017F1C8076BEAEC2ED4C23D7", "67219CE095584AB1F3CE0400"); test_comp128v2("36B9E1DD94CE2C7D5C50B4F4782666D7", "837763889C34FFB59C0BF400"); test_comp128v2("7426357487507712D1544AF62DDE03DD", "E272A3916D75F94A9D18E800"); test_comp128v2("E3EC5110BF23B8292F7440D236070240", "F98D822F01565E3F776C1400"); test_comp128v2("1E12B4E8AF0B527E971E7B0F1912E6AD", "8092FAAEF7EF58F4B96CE400"); test_comp128v2("FF63EF8928F00762BD9204F407F224A2", "8954E660D5557692BF693400"); test_comp128v2("E6063EE906F69B00C636C2FEAC781869", "56EB75B9916E5DE0192A5C00"); test_comp128v2("967DD337ADAE9A73E570CDCA4BDF866E", "0A5806A9778A0B3E031BB800"); test_comp128v2("98D1CA43D4C21509F98120B0D6A1E7DF", "4CC4B4C66ABD3A5DA0638400"); test_comp128v2("1D97DF816F20FF7ADD1E41D01138B49F", "625867D3F8DC80B255FB2000"); test_comp128v2("E3946EEF6DAFEB57B967823218D603C2", "9FBD7D5310778C193D99B400"); test_comp128v2("8E81C1A706E1C873CF8AA371322021D3", "C588BF56F7CC23581225BC00"); test_comp128v2("20DA7808951EF3A9429114C3462F5747", "30DF8359FE34CE5C0374F800"); test_comp128v2("B0E6E1CA7B8C4E025636F5A66D09D29E", "D45B3366E8740A43367BC800"); test_comp128v2("60003F3AD24A644CD2D69EE03C01EF0E", "DAE6302331046B94B6741800"); test_comp128v2("A6E7C6E2615E1D7C356737AC52EE514F", "FE3A2765EAEAFC0F938D2C00"); test_comp128v2("DB1A6886B5DC43DD77A0261E1AFF8B2F", "B98EF65CC4BA464524735400"); test_comp128v2("C0A0C0D4B354E14F2ABF41F602630AA1", "18A96D6E34F8FD4798DA3800"); test_comp128v2("62BE756646EB90390EC21E6D21BB587D", "10401EE31F4A5BF98EDA6000"); test_comp128v2("7F2B44B17C9EC961778E1255E9B688A1", "C272B7FD9ACF088ADA573800"); test_comp128v2("C433B7A9DD753EDE601E7FCA21334158", "3B189C41DDFE0D439C0F3400"); test_comp128v2("5D998A557E253D40A32DA7CF97D5A29E", "EEC272A2BAD8F690B5F4CC00"); test_comp128v2("8D1B821D9FCF2BE276D723A435F9954F", "85004EEC5849D04F018DF800"); test_comp128v2("C8A5404B37F54719B284515CFB9B4001", "4AB5D01ABC448CF3105C6800"); test_comp128v2("9D3B27999D28125B877274228906EE0D", "DA8C14CD5CFD377A8376BC00"); test_comp128v2("E5F666FAE14359D65D2F6EB5EA45961E", "94AA2EB983B386BCF21B2800"); test_comp128v2("06F16C45CE7ED4E6201646F0C750D419", "1593E4E1BCCEC056923B9C00"); test_comp128v2("82FABF6D7B45CE4C64EB0C6E9DC502E8", "F6487E29D164CAC32D86D000"); test_comp128v2("541F1AC3AFD74DC7C70D6575555AD55D", "86D04EC42CAE37F01ACF6000"); test_comp128v2("C597908361D898A0413B7A66354A6065", "14AD14240E876852E6226800"); test_comp128v2("200438F0979DB724420285D43B2179AD", "F293B889DABDE43C1BE6B400"); test_comp128v2("739E0F37032ED78A0913D042A8C3FE4A", "8DD8DBC7F999094FD4F0E400"); test_comp128v2("16A8AD7D1C5631CFE5F4CB9CB9ED9163", "6D3F95B914469F0ABBB81800"); test_comp128v2("AC81B47EAB5649E55A9F6E0B93B4BF49", "2BA8B6AD2E1E2B4902330C00"); test_comp128v2("6B0267728AD6B956D254EAD2B770FB37", "C03BE3BC733D51F61B1C7C00"); test_comp128v2("10B1A550E224F719E76D98ACCC2C85D5", "7FC28C88085940FB0C555400"); test_comp128v2("E71F7EBF5F6D8804D1C223EB722BB46C", "58742C27AD8676AF08918000"); test_comp128v2("4C3F8FDDD8FCE4D20174F6286CDF71F1", "7CFD68608E450A2D7D20A000"); test_comp128v2("99F18874776E15A839C2A3E7C321EDB3", "58B30EF52C693A4B40BB0000"); test_comp128v2("8AC814B518EE9ED843E35E8EE9C25409", "C0FDDF957DE317A4FF92FC00"); test_comp128v2("17768D68621DA8385EF1348B274F372C", "44E1FDBBAB1D9EC1E358C000"); test_comp128v2("AB55D41FCAA07EC8E8C94315F698D793", "5A2F087594F2156AF1DE9800"); test_comp128v2("6CB52163A5B7FFE27654B0310B572B6E", "98AD77BE4612570C68FD3C00"); test_comp128v2("CAC07183EEB34F38E70334AB594FC570", "5927855548220C340D283800"); test_comp128v2("71B14D3013D7651AB586A96FDB2C55C4", "CC9BF8358407E57A3B448800"); test_comp128v2("6081353FC28741F50C7B103D6B2907C6", "AEB5F267FC32315C39DF4400"); test_comp128v2("6AF53DD62899A5F9336C3A52898EB0CE", "9BA4DB7E121A278A11EAD800"); test_comp128v2("2B746DC2022342A402ED432B27F6B243", "13305D58C682BFF823F3C400"); test_comp128v2("B16EAB6CF2E07F8A3AD61D2DAC6496C6", "4B890108A901763F0ABB1000"); test_comp128v2("20E2CEA085C2CF1B0C4706E4F8CE9196", "21EA802F554471BAE0D64400"); test_comp128v2("D1CE68C2764772B1264B93A71E305D3A", "297C675B8A2A277866A7F800"); test_comp128v2("504B4718D7CD40663A3FA48E87AD07EC", "ADAB984B10C1A3D0A14A1000"); test_comp128v2("C964C6996F55F06AE5320FDE2406007E", "A023AD3226510F472CCE6000"); test_comp128v2("BB308E478D79556474FA4E4B53FC151D", "5CD9540DC7AC8A3566D90C00"); test_comp128v2("29EA1AC88BB26824AE83CF126990FD0A", "69E85D6AD0832BEF5629A000"); test_comp128v2("873D1B21E6080852EC53F65086D30974", "0F17A7DC95E6E47C6ED51400"); test_comp128v3("00000000000000000000000000000000", "34B4225BF16B96E118A85986"); test_comp128v3("00102030405060708090A0B0C0D0E0F0", "A892A8EFD6D33E3650372F78"); test_comp128v3("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "F699F0BABA87114F0350BD8B"); test_comp128v3("000102030405060708090A0B0C0D0E0F", "A5B4C7CA0514C4E1B25CBFED"); test_comp128v3("C2FD8EB45A807D375CEC72D3599C1E04", "F123A4B0D526DA2AEB199FCC"); test_comp128v3("059BD4242157FEE159683C03B1530CF1", "8CD58D5C05D24653C730F1B4"); test_comp128v3("6A00543B7F4AA6F5938E3915F1A04F61", "9CB62587973B8079FFDDAA68"); test_comp128v3("B022CB9F45B6F2E05986825EB60ED3A2", "DD465462764C701D8E07230B"); test_comp128v3("9C36DFE0444A53EA8D64666EEF095829", "68DB77768CFA36F5D9584C2F"); test_comp128v3("65EB5166053C36EBE4149CA8E032F85B", "8236AA79048BF9FD23F22CC4"); test_comp128v3("FE7F96A0CA6743F4FB43725B65C98CEF", "C3D19C91C0BE1A8A80866991"); test_comp128v3("CA93097AC1F7FAD6AB300291D7378320", "47D77CC1AAAFF848FD02EBB6"); test_comp128v3("7ECCE6BCB6A960BE9F347FA4BAFF9806", "3F9CE85AFB1B11B747E41A1C"); test_comp128v3("537CECFE605EF84CEC6AACC26748D8DB", "23AD4EFE3BBD5437ACD58FEC"); test_comp128v3("77DA6AAB1D63954F7EB121F69939F087", "3782AA51C410DF352D5BA4C6"); test_comp128v3("6E6989BE6CEE7154543770AE80B1EF0D", "E3EA331104D11B0340EF41C5"); test_comp128v3("FA3C9311948C43A88A81F9F151218628", "DCAA5F8ABAE6FD1FE3D994A9"); test_comp128v3("CBAB8FBD3E6268579024BBE465A085AB", "7A08AD916948AC88F285F679"); test_comp128v3("D9E9545BBAB220B79C11B4268E4AF22E", "76F8883C06A19A0F3AA0FD70"); test_comp128v3("1F0260A9AE9FEE9D4463888C7E2B58DB", "08EE26FD13A86CD1AAD5B368"); test_comp128v3("9A3451F2C657E4BEDC052AC7E42A731E", "A8279A6015E5935099372857"); test_comp128v3("F1B858F9D05C34D05BD5D0483B037071", "F31AD0315CC5E86B76B97ECD"); test_comp128v3("67E29E6639A94DE8CF774F21D5FCD211", "6ACFB2184DD373148D5D582C"); test_comp128v3("66287B28DA9E5A4A3674842A416580E3", "0EAEDE610EB9744B2B95913A"); test_comp128v3("06CB6DE4CF812D00C494C2CC088D7FA8", "D41C2FB8F696C84D882DCF6E"); test_comp128v3("06A7F31DE8BC76B51A3D43968930B60C", "9CEA8DC24E04224753B9D218"); test_comp128v3("29D36234576AD8395163D1CC9826167C", "16D467073F5958EA424A09F5"); test_comp128v3("0A41667DE9BA1AC860500C4ECB359EE3", "22AC7CEFE26F3A4CB98E4B23"); test_comp128v3("15999866101B177430FE614EB17E76D9", "A1332C1FCE97402B4A743D8F"); test_comp128v3("C02AB81126B1990F77F810B82B797475", "3CA34AC67EAAB7048DEC15B3"); test_comp128v3("47061308D6D2A8B4DAEE2AC7A44D6842", "0282A3DF9C9A981A22572231"); test_comp128v3("8BB120BF99F7F51DBF66B7CBEF239874", "3B915FC6905D571E0244A15B"); test_comp128v3("2BA369767520B8C1E2CA69166546D1CF", "D9FA973CBA46C79E08993ED8"); test_comp128v3("E867952D2484B31C4DE74554B8CFD8DA", "D2C4A14D3FF5550E776E783C"); test_comp128v3("51A70B24DB7DFC4DABC254C6CD9BA81D", "FF770276E357757BF17B6A03"); test_comp128v3("D79459E2FE6E8E17510C4649E5EBBD43", "DD587C63DA11D7A39B0E51AB"); test_comp128v3("992511517261548767BC3A5EA0269459", "3123ADD2EBF5C4D7B3F4197C"); test_comp128v3("38112A87B58DB889B06F70A35B2BB039", "EA4365FEED09E1A34304A763"); test_comp128v3("4DC4F985E69146A65C0A853EF5C307D6", "57B9BE9FB46D951171CF1DBB"); test_comp128v3("F08DFB812F799107F8A817C6A93504B5", "5F67F38F8F729A1A34468A50"); test_comp128v3("2E7AD4225A84CAF9662B79D8AA4EC60B", "9CE2261C86FEC23836F8C25C"); test_comp128v3("904503A6A4B61660F24330E74C49183C", "8D434E4F0B2518ABAF9C92FF"); test_comp128v3("FC0B25783A74FE16F97AF6EEC84799C7", "E4869E47D43D385ADD6FED7B"); test_comp128v3("B351F09F9FC46188103A266FA50BC54F", "DA4E687A478A4C3EB77EA5EA"); test_comp128v3("453DE3EDBBC72BC9C65335B47536EB38", "2CC578A40472AD4C336D3880"); test_comp128v3("250133698D0068AAD95A2E0FA356FE47", "F7BDAD3677641DC625C40A3A"); test_comp128v3("482A117B4A39F9DD842E67F376E4AC8A", "C7C280868DFE252421111382"); test_comp128v3("B7B435E33F800DEC7881A5AA6DC7795B", "68ECFEF6FBCE86F0145B2710"); test_comp128v3("5528C93204329236879A8D0EB66EEDF6", "5A3B116AD3A228DF0C148E35"); test_comp128v3("2BD00A4ABA3837E76379B9C0257CE95B", "DBAE1F2F91E090C7F250A576"); test_comp128v3("897FAEE5AD4A10D443E1E581E1FADD2A", "5E0FAC279B3DB617F1D79E74"); test_comp128v3("50C13D10436296F6511294F0659346B4", "6774D9611D81D2B28EF62CB8"); test_comp128v3("42F5D66DDC05442E1B394FC346AFB794", "AE323C87CC24BB8816555868"); test_comp128v3("23978478FFFB8DFFA9160898AA04E67B", "F9E28ACDC3FEF9C732506146"); test_comp128v3("0C736BC43FA36DDE8B66F9E5F5F38468", "836E479A4C3F9060A5048129"); test_comp128v3("3D0F54C3DF060F76A908CE871B5A15CA", "2C8672011E95771FB6A01EC8"); test_comp128v3("F300DF8463DCCB4D8222A7021B005E92", "19243A7269EB1F75402A01DC"); test_comp128v3("7177B3EC6658F41C7245E773138A0F8E", "3790D6633E9FCB095F1D823B"); test_comp128v3("1A2FE0B12CB713888781DCE0E90B4776", "CB65E9845237344F55D7AE1A"); test_comp128v3("1BCB63AF37AC2DF53B077E413B588001", "00DB17E0FE288237A333C4A9"); test_comp128v3("F0A650FE37B236732D4B889CA6A6C835", "7FC46A51EC2B7BE0C66E1A6F"); test_comp128v3("68B09A32A5459C6F6E5472109FC164E1", "C8F1AF34F21E61D9CD2E6839"); test_comp128v3("1B57DD3CCEFE0F33E04EF164B25A124E", "A8486968444DAFB51C552932"); test_comp128v3("3CA34C720BEB4E2FEFEA6D04BA4FD876", "2B1C5B57D1249DACE613F90D"); test_comp128v3("CAF61E2E3773AC8891E12638871595C9", "4E03646AB273D7835417418A"); test_comp128v3("B1433F3F651E0E478A12AD2163681E48", "0204AEAE7AA03ED2B87985A6"); test_comp128v3("907DB9ADC521BEAA511AE36FE1A8084F", "96E8BC8BFA3054AE764A8BC2"); test_comp128v3("BBD2E95BE518C72529FC67918EF9AC4C", "9461716327C1CA6ECD8E3DD5"); test_comp128v3("5EFA59CB1257BCEBD7C52842485B1A7C", "FA69B871271C6D3BBE9C2C0F"); test_comp128v3("1B170AF028368F19F26CBEE3EA04B69C", "9801411E3847223CF3F6F5DF"); test_comp128v3("8A03489DB2DA7957AC574307457C539F", "18265685F3F9FDE17DF568E9"); test_comp128v3("9DE46F89BE1AEEE7E08E6E6C96136B4B", "948500624AA36F84F8A9144D"); test_comp128v3("3502F3348EF421C546A22C60F73D82BB", "3EB807C5B084B783A6ABEAA4"); test_comp128v3("266F0F71417652FDA262E209ABA9813A", "1AD344CCB780727F29F539A2"); test_comp128v3("F89ED91B773DB362E3B535ADE806309E", "6154B2555DAA2536747C8C00"); test_comp128v3("95898AC8F59AE30AE6C131F433609984", "F4231FEE2955D325C6358E8E"); test_comp128v3("FCF177BED3855914164948383A7BFCD3", "B013BDAFD7B01F6D12635E71"); test_comp128v3("A41287144BE0462B64F3BF9FC79AFA28", "11FF56DE220314FC01D2F427"); test_comp128v3("C37A6607EB0CC90AAE0E805987BA2BFF", "96AA9F8F1B82C816479F6F30"); test_comp128v3("B30688EDF5883129A3EEE07EFBD716CF", "2484F97D0E207448CFFCCABC"); test_comp128v3("F49EC19D30C40E62ED346BB1E973B3C5", "7F3DB7AD4F1E679D1C1BA290"); test_comp128v3("AA60565959700AFA4E24383A7852F98E", "40CA139963F8B07D82F93631"); test_comp128v3("72673BD9D3ECF3BC9948B6F96D44000B", "A29BDBCBAA7C7DFB9ADF7DDB"); test_comp128v3("F764D588E2AF04E183D9DC2008E54159", "892163F30BD023B629FEEBFC"); test_comp128v3("6225365E542C73635002C5D0F49D79E8", "9ABE4CC8526046189A15E8DE"); test_comp128v3("578FCABE430EB1F154DB178035C87340", "A08DCD277DAFEDDE2CF34B7F"); test_comp128v3("90D2277F8E73308C9FF488C22C64A371", "BDC38FC4489A714E45D465AD"); test_comp128v3("CCF9D01093489E9322A92CC5B45EDAF4", "524D9D6323724755C3A58301"); test_comp128v3("DC7AA07884338A1CA74A149C920B7113", "804DF3B4AF8F2EC1EFD23E30"); test_comp128v3("B49C7B29E5DC67EE200FC77742DDEE5E", "5A6B21FD6EF3D215C82D7033"); test_comp128v3("943410D203597F271EB914B045471F9A", "243BAB53856F7401082F722B"); test_comp128v3("A66F889E48C971C19BB1ACE8BA14C7D1", "53BC19A4C99196271B6DDCDC"); test_comp128v3("2C1987A822D55252A53F4C5165A9A6E0", "695CA9F5FE4FB59944E42198"); test_comp128v3("6E2A72A29D3E40CF17720A2EB2729F54", "277189F03E8DCE092840239B"); test_comp128v3("5756340ED21CCF06BD9CAB1B1B6B1F83", "F514DEB77B49396051473F97"); test_comp128v3("8F4D4E3BD46C79E859EA6C4B1707115A", "FA8DFF24FDDC28232DC7A86F"); test_comp128v3("6133F53D525646BAEE9303FD23A6A957", "4A1797DFA2F0382CE2C8A691"); test_comp128v3("E4079E18FBB564363AD12F51E06CE783", "367F320EA19755B931A57645"); test_comp128v3("7DE5A96E6249F511894B554941711A59", "B9D48BA0539A31A9532462A8"); test_comp128v3("AFD50CEBA82EC1FD6EE45FF2ABE23BBE", "3A1327E46668F8A9C4F703B0"); test_comp128v3("0106CFA6F81EAD4369327F7064B87D16", "3F432307ED0F3C057C5F2712"); test_comp128v3("CE3DB3EC969DAA637C83C99083F1F8A0", "7CED22DC3DC07E253C978C97"); test_comp128v3("C3392F85F96A21B777F33EFD11A832A9", "247BF07ADD6941713F40A327"); test_comp128v3("5A815EC556143C56EA7CB787305F8EC8", "BFF611BF4369FB38215FAF6A"); test_comp128v3("49ABB28EBD9106B5F204CC1AA2EEAE97", "0A9E54DD9C485068CCFD153F"); test_comp128v3("DEACE7DEE61E242671C0EF0E441C1B6B", "B6FD390C7404DAD7D4359416"); test_comp128v3("9A327503C873F7679DBC3271382093C7", "189B498F1D693458BD79489C"); test_comp128v3("6546D5569FDAA7E85217D87DC34C7E0C", "16FE55376BD68D78B6161E58"); test_comp128v3("0B695FEA192283985799C9CBD614C37A", "8D71BFD1C8A1559770C77689"); test_comp128v3("11E2A6E032B79DC02CCA50CDF257FEF1", "C8E97ED1703AE1CA0CFD14B4"); test_comp128v3("15C788FB019580F2EAEC04B3F074D96F", "F83FCEA965FE9D0A141EE058"); test_comp128v3("24463A9F5279C2F684EB2F17B9C0B793", "10E3C959293B21D9B4293BBC"); test_comp128v3("A5C0D212FD189818BED2B980CD771710", "21777B0F52239D696F7BEE2F"); test_comp128v3("6B38A49118E0A121DF29B8781E71C89F", "F922FE3B5C9A2826818B51E0"); test_comp128v3("100F07B6881EA8482EEAB29E695630D2", "A70F52D10C35A99831518DD0"); test_comp128v3("5478D20CB06C9D9EE3E5B159BAE6EFBC", "CBA83BF92463F9B14A6882AA"); test_comp128v3("2B1EAEED654AE3FDDC35BAA926D45B0F", "B2AE2D718F554D6855936C83"); test_comp128v3("827512AB9C1C73CCB8AE1A292233C563", "0B512886B3CED2AF94A54153"); test_comp128v3("312063D0754C824DA712B64D2A6B33A2", "8C3C7D5EAE0CC1BC2141AEF1"); test_comp128v3("C41B044B098F5D6773229480C84D66EF", "36A53378F71AA1014DDDE622"); test_comp128v3("06CA7A392F7FB360BE218ABE86020D80", "0D7C440E7D4D7C36077390E9"); test_comp128v3("E8F4DAFA1CF388ED27662B6F234BF3B2", "D755DE9D6A83D78A02EEBD2D"); test_comp128v3("7451425964131EA8FDEEDC3D51ECCF9E", "4FAB527C28B1F4BDB34158FC"); test_comp128v3("BA511CCBC0A07FFC45233241E1224340", "13683A5A03D2A1927F0ACDD8"); test_comp128v3("8CB04EA4DB020D5D4EE3812B38AB84CB", "26648E563AB2075E2B5C7281"); test_comp128v3("04A2631D325386F77579CDEAA967C37A", "C1DD35C987CA7918F4044197"); test_comp128v3("A5BE4CC4A516147408183FDEE9DA1D65", "696A0827A2FFA3C73A10404C"); test_comp128v3("36C43B5BCE7BECE6FB2DE03F4BE709E7", "FFD099DE6A2C089BED7D35D2"); test_comp128v3("35EA9D9C382CB460467F8B8D1145917F", "40D9401342FE480A8A009E2E"); test_comp128v3("E00D2EB361732C08E2C3125DB7D2E3F2", "491710E39159DBC5ADE0DBAB"); test_comp128v3("F587457CAEF8E5BAFBC129641BF8EC69", "CE15ACA01CD0077FDAA76A87"); test_comp128v3("BFE148C7255EAADCE3753A7CB3E1FDB2", "46BF49FEB914AB4ED667E4E0"); test_comp128v3("A8452A0CF7A80F80C4D4EB5C7E7969DC", "C04AB5C52A9B1C5964B0B86B"); test_comp128v3("BF289490E673C0B56DB6E768B3CC0A48", "856BD7044AF5997C29FC5138"); test_comp128v3("1DA9BF8EABE527222D4B7301F5F6E14A", "3D61A9F9DB5C452DD7C26F95"); test_comp128v3("036DBC1FC7537E77FA4CBDF33DFF927F", "44CC56A1DC3A5408286C3EBF"); test_comp128v3("A26A8816BC2735427354CD88742487B6", "BBCCC740C5571A4653805B58"); test_comp128v3("FB57CEE47FB84A4CF354FFB02BD7F27E", "CC74DB56925160F24B998789"); test_comp128v3("6B7D14B36BEFA7AFD23BED5C89274817", "7FE28DAB2C64E290FE8D8CD1"); test_comp128v3("783B2B4B5D240551B0D1C5073C86EAE3", "BF879E67B031772A214137FC"); test_comp128v3("A5C5D0E238A3C63E36ABE8CBB8D3A755", "C637492282DDADF2344C5F12"); test_comp128v3("F60AFBED2788482DCE9012B308F91018", "A6D07EDAD84C22F55D578795"); test_comp128v3("FF29BAAE83F0D4BDAEDF57439B240E2E", "C88A6075AE55B48C41413694"); test_comp128v3("BD2EE48803F736F741F7BC539143B6B7", "6AF648295D9D5BB30994F0DF"); test_comp128v3("8759E7150969DE74EA60E6DC6B0605FE", "DCECE445325ADB93295C208C"); test_comp128v3("E8A3873E185A8130171809B1C670E466", "A52DC25C7BA9A8B5D35A3447"); test_comp128v3("CFE9F56C387AF2EECB6C16BD73100E84", "9F0AAF87BBE52A0D82B0F79C"); test_comp128v3("504D313E64F204B950E1FCBA766E6A10", "2A6E346A487840A9FDC91C91"); test_comp128v3("BB973244B0AB827B73463C26EABEDFF4", "CAA52660E1D03128283903BB"); test_comp128v3("099D40CC50BECB4943D5417116A02A43", "184B3CD556245A75AC3B2E95"); test_comp128v3("6F936FB2AEF98A81E78E4D190DF9D876", "591FF4491319AE943B7E3F5A"); test_comp128v3("ED889EE0177981FB3054558A3D93F5D9", "C3A3682BE186AC971EAE5B96"); test_comp128v3("D610FF2C9B5E26A58C3852B1CF968675", "CAA5295895B7A4167F1FAE69"); test_comp128v3("60608D5017ADBEC3DCEC4F36DC2CCADB", "3850062836D6D9C342A5754F"); test_comp128v3("1607142E96C20A6D55887D473E1C1ABA", "57B3B068E892BCC25B81BAD0"); test_comp128v3("0173C6DB9751FC3246158C2F1082842F", "D24D9DE4DFF90F1862A202AB"); test_comp128v3("15A4DFEECD687B26B2398404FE651E26", "124DB27D43E78C9E1F22329B"); test_comp128v3("8C04FCE7248101CA8434190F8F3F18E7", "DEDA9836E027014878678B27"); test_comp128v3("A1F308C74C95344CDA356907739190DC", "34E78710601E8D992F9AF14F"); test_comp128v3("6CB50D780B92072B37F757ED6E63B0C6", "A2AA5D1A550999067BB69BA3"); test_comp128v3("5E2BFEC3FEE9ACE4CBF6B5441346F5DA", "18617E4E20754E060463F495"); test_comp128v3("B28D0F02AFCB8CC20235898B355CF3A1", "E1A687503828B8AF3C8EC33A"); test_comp128v3("E7A8045111133E9BD905248B3E3D9E8A", "98B3E202A3A5A3FA76358768"); test_comp128v3("CD33375D0823C4C5B6CEB0F6B4E39F8D", "92FB4EB73E56C32476604CDB"); test_comp128v3("4D8318B9642BAE26C791643DF0F53080", "BE8371F5FB7CEEA806AA292C"); test_comp128v3("9DABE054F9D33BCE9795748681EE640C", "CDEBCCD1A2CA52EE3907F9AB"); test_comp128v3("16AE48F67D14618AD12F93B7D5E3A74B", "29D03897577684C1CC2C1A60"); test_comp128v3("1D544B9110BAE6EBB3FB9BF625DC725D", "4309E6F048FF37D9D21E7EDC"); test_comp128v3("563E947E114D9A470A52F05745B59B58", "9EAE363902AD89A2EA744C7B"); test_comp128v3("0804E1F159B228C2B549965B0628B600", "7874B39866D29960E6A4EA9E"); test_comp128v3("AED48B489D0D2C9E81AC8D5E6A15084A", "45FAA83D1F868D1E1509A444"); test_comp128v3("FACBAF33A3409556CD177817673BBE7E", "396965C351C7668CD26E734C"); test_comp128v3("A9919EFE9BEEC2374E74FCA514442152", "9116234214E63C5C488B384B"); test_comp128v3("870EDD564454CB858A51E67D360D217E", "6A159AF76162DABBD6D0DF63"); test_comp128v3("E66FBF9271A50B84A5D7AB53E7373176", "30ACE630BED634AF6D1DD338"); test_comp128v3("1B561ED7F3EEC916D48BE068611A49FB", "47AE71282F48B4F5892F49F7"); test_comp128v3("400472731C0366CF0BE540DBFA745BD3", "12EFAB1FA8A873483C53F0CA"); test_comp128v3("DAE6DFAD532BABC40BB959C970D8814B", "40ECC9C908605D2AD7C04044"); test_comp128v3("61F1FDE5DA854053A07508662FD5198B", "56DE93DEBF9302DA18649DE2"); test_comp128v3("BFF8B0D12F533A3E3C9C46BB8B73D4E3", "8DB85E1EC6AF6F2720F44165"); test_comp128v3("172A3E21168D80ECFAD1E4E122C0B1A9", "60CD03545AA93CF3EE8B9A5E"); test_comp128v3("F304D41493FEC89AECE06C355269D24F", "BDA5728AF5174BFE8A1DE7A6"); test_comp128v3("7223C3B61097610DB9D86BE0E5C7425A", "EF3B366F60DB819EA4CE3CC8"); test_comp128v3("73D79A145D0F20AACCFCE879398A14D9", "6B90D4161C02C37583E93EC4"); test_comp128v3("D2A3297B864EA9FABB396211FB8D7DC5", "43BC011FDDB44128F061841D"); test_comp128v3("FBF84C03307C92DBF77E819748CA693A", "562EFBF4DB6078A949AFF824"); test_comp128v3("70D8437A9120D5F1EECC400CB8F009F7", "A3585AC211D316CA9BAF8349"); test_comp128v3("AD70A2CFA9289909520AC3E442066E95", "4DE9364411A3D22E18E05EA9"); test_comp128v3("200DF7FF1CA3C90B9CE7BA3E1622C243", "87C54F689FAF75861A864E36"); test_comp128v3("3558987AA0E60869CCD2AAB41000171D", "F88E5005DDA6A32A86B46323"); test_comp128v3("76C03334BEECF8488345EFE09AFEBA8F", "7A8B21E14808D8FDE12DEC82"); test_comp128v3("6C34C7E0F992FFD107CB3C21AFD8FCF2", "7223C34459BE33199E397C97"); test_comp128v3("F567B76CC9075EADFD5B1F5BB995DAD8", "3A4D7CAE9189475274EB3A26"); test_comp128v3("FE553460124B7240AD5927F199968901", "66628D72D37687E345B5B80A"); test_comp128v3("20A361BE827F77F8752324D0DF0191B7", "7BF2B29BD955EE9DF8A0A462"); test_comp128v3("89E668A5719753E733E483449D26FC35", "59E64E6FFA910A75BA6BBA60"); test_comp128v3("EC23E756AD6C5A8EB93FE43FD1743407", "296AD8FCB4BE13EB61403D22"); test_comp128v3("4C78D75FAC41A829171137A924C008FD", "B5B12D7F28E1C3CF0BBA182B"); test_comp128v3("1582B13A55A441793F24364F4E79D70A", "4FD6AA11A62B3DAEE7AD9B01"); test_comp128v3("F83F6CF2845E6049268A98937165CF46", "90C14A957BDC4877443E00E8"); test_comp128v3("89A39A6F6D0EFC34FB44B9D94E1FF998", "B42B60ABBBC80A57030893E0"); test_comp128v3("506069010FBBD8FB9B621E1E2499CFDB", "E143B413238339E5F1691BA7"); test_comp128v3("F05CDF287428D1F8A82F8D2B90AD2D45", "97F6CA366943F4B6CE9FF71B"); test_comp128v3("250C4E224C7EEED75B5C47E9B281F038", "22468D1F2D1F8BF1D39859FA"); test_comp128v3("D8C1F4CEDF1F26B67CE6AED5CF27346B", "652F8F8149F1819CA8C863CA"); test_comp128v3("821DCF113144A74A0696107A511BDA50", "3D8BB5E6B9C0F05E46D96D75"); test_comp128v3("21BB340C501240AC021FFAE616ADA466", "876909CD1586AC2DF5FC7AE6"); test_comp128v3("F877A1C9FD4DA1F3E375314677BD07CD", "F373090EBB599E4E9EF9F429"); test_comp128v3("26845DAB23924FD243CE4BA118AFC18F", "039F20C260E2A343D36182FF"); test_comp128v3("0C74932F7693F4D4F7E0418E41879F5B", "B7C468AD4CD5728DC6565CFA"); test_comp128v3("9AF335A2CC31464188BD38CF75937FA2", "1D1A99E64812A75CEF4596A9"); test_comp128v3("A9F5E25ECAA22CC54A2637CB8DC01FFF", "A756214C3895B523FB63A378"); test_comp128v3("42FB25801DC657140C86C1C7F18F2F01", "590D9B13514090C19B0000BB"); test_comp128v3("CFA06688034DC8F1A474C5CB230FF52B", "5937F4916B9D698E3FABE3DE"); test_comp128v3("C711B7789802A2E87BD9C892ADF37666", "B885298A5DBA44735E11AC17"); test_comp128v3("0754A2EB8464B56CD205F6956C88869D", "CE314473BF4B1E1B6C7BF179"); test_comp128v3("4EF0E8BE04D929C3AF6BF71852A72A30", "E14518550A295F9866046EFE"); test_comp128v3("E845124E430E41B9F7AAD910F93DC6C8", "B478A5A1128ABBE224610BC3"); test_comp128v3("7FB474511BD214140C90B521798743A9", "E8BE27301A6EF0E481A68DDC"); test_comp128v3("C727D9EFB0A70769934515C95015E3FD", "C33D3FBA7B6B71F7F5B1E18E"); test_comp128v3("64ED08124194B3590CAF7263A77E688E", "58FB6C2A5678A5FE49C6DF04"); test_comp128v3("3384D1E460CDBA2E9306042D35C2DAF8", "53249E60E0137005D6C0582F"); test_comp128v3("4E4E2DAA21826A569BEB3E89C3F75954", "0B51B269D2EF17CC2AC97F8C"); test_comp128v3("7783A5B5BBCCF29C2AD09070882EDDAA", "F0365CC0D5656AB7C9EEC1D0"); test_comp128v3("362BC8026D00F70FAC9406ED186655D4", "261C2E9B772917B953719AFC"); test_comp128v3("DA0E8368EEE2F2946AB1D284CFB58D75", "2AFF64A0A7796A645F8F6569"); test_comp128v3("1C3F461143F2E728E463F6AA04835328", "98F8FB6C03A8F04E5DA6BD80"); test_comp128v3("1BFD760F78B82698E764E2F009E83A10", "ADB4E618176A154B4077D28B"); test_comp128v3("BD4833A790171E25A3FBB40DFCA805CD", "BCA4151338D53087CE12FEA2"); test_comp128v3("012135788F5DE698BA7865313D838D1F", "B6E7203A49F125E0EF781021"); test_comp128v3("CE3713587AD50A75828DBD3B27C4227E", "F1E1EC13C4DD8F300801DFA0"); test_comp128v3("8CA1F5693F9485DA47665289F9B7994C", "820A5683642DC787C4E86A46"); test_comp128v3("412A89798D2ACDFFE18E5C1E4D7316A7", "D93057561BBC893843682759"); test_comp128v3("00CD5CE9424EC374FC60FB76B70EECBF", "88A74D19F682E1A255932C53"); test_comp128v3("038994C83ADEB2F74D8C8839F53437F0", "A3F15A3800700FBF4036A9B6"); test_comp128v3("5378C9DF92AAF716A3E3479FC93F9B6B", "A1FA256354C249210CE3F4A6"); test_comp128v3("3201667073288CAACF46F3BEBAE0367A", "345D24DCF43872214332792E"); test_comp128v3("7C88491C3A4AF5934C112299476F96F2", "DDA8299BAFAE19127326C0E4"); test_comp128v3("A09EA9002485129676578DBC2D29970E", "19E2856AA1822D8C382BFCE6"); test_comp128v3("0249D8409D94C899CB3211101F44E706", "34F666DF84C3F4861F031B70"); test_comp128v3("393735BBB4F295F78875118F2282FEFD", "1172696A8F3AD17FF4CDC32D"); test_comp128v3("880A34A01DB11B02983984409F474598", "78E946DE142861000EA71378"); test_comp128v3("8D2D885D4E8D0FEDF57329B9CA684481", "5C839ED7586932AF9267D4EE"); test_comp128v3("D9A319465292440E4EC40AD4311412BD", "34C4833EAB737BB515DC3E29"); test_comp128v3("03928DA8EE9E6EF58F8BDD25383BD746", "FBD0B65369592420F4AC13E7"); test_comp128v3("871B76BCCE398DA305C05583328F3D0B", "10070DBD7C735D468E99FEFC"); test_comp128v3("2D8329DFF082425E4865ED82BEB77D2B", "6891E87151FABC787888D502"); test_comp128v3("1AD6B061F1E920431D52B7C4D7A6814A", "1ABA71E6777298078DC34E9A"); test_comp128v3("6A3113748345C8D03408EC03C67AA757", "C073219B699FF4EFE6BD7F20"); test_comp128v3("E78DEA79071AE8EBB147A5BA7530F6C2", "7F90BBB9710C26C21B7B306D"); test_comp128v3("10A9BD1DF9BC3D57CE9878D0AFA3171C", "8BA97E65B5C543E81A0968E9"); test_comp128v3("691DA2346C6D0FDB814C07F9A243DF31", "14331E4947AD140A7C1A6ECB"); test_comp128v3("CE6961F7424C872568BB6FFA0D7C00EA", "05543EA7314FB4A0012243D9"); test_comp128v3("BEF85F199B780AEC2D7E75FECD14F54D", "DBED42B3388D1DCB8FCCE224"); test_comp128v3("B08AF472657A3742598FB1BC3E3DC702", "776AE1440D3ECC1771317076"); test_comp128v3("1F585700F880CE42F2C10AA2B2D9F743", "CB2B3444973841C5300DC71B"); test_comp128v3("25E7C4BF79991617715EA342FDD16E9F", "A4360B1247A7112B4525966F"); test_comp128v3("EE3964F72F1F85062AF2AE995B43A511", "672886691D327820EA6639D6"); test_comp128v3("0F4DA216B41A2ED1777CBA97B4674B1B", "B4A8EA97BED0613AF1C51AB8"); test_comp128v3("66A36F7EC04C45F3948403E381E3D5AA", "9782454E51F13175CE23C4C6"); test_comp128v3("E88D31F0EB83D24F7D26371129763FBC", "18DB2B166C64F39868EE8D97"); test_comp128v3("8C13DE138071D1F51FCB8D6FE388E038", "662291FFC942AF9D67AECE01"); test_comp128v3("F05DCF03504184FDA2616467DF5795A0", "BD52678F949A9FB2B389E5E2"); test_comp128v3("4F4F788BEC2EE583ACF059B0ADE014CC", "747EAF9274A438F5AC993B1A"); test_comp128v3("C37A8A7C38765AC31CDA16786D732F1E", "D3411AE9A8133D3577EB52E0"); test_comp128v3("E4B2DC6D7322CC108231C199330860B1", "0B8183CDB4774373E772B3AB"); test_comp128v3("642BA8B13B3A34684C6D526A5FD2D752", "902123CAFC2DCF346D63151B"); test_comp128v3("EDB994505213703314C42A325D55A40C", "83D68942220B5A33CD61FB1F"); test_comp128v3("4D1399046DA2D653405F1F1940E875C8", "4750475855BE7402E5D1C5C1"); test_comp128v3("5C435B4BC5E2F9651ECE8EE13D6E25A2", "D8876ABEFACB10BC3AC52E34"); test_comp128v3("8D9A5A7C9CBA7A07DCFAEA0E4DAB2725", "C58EB014A932C3116E65DE5D"); test_comp128v3("09A2B5AECF349CB2D56B7A39872E4163", "C1FC34A7A8D16531914737B4"); test_comp128v3("28EC2B7795CD5FD29D0D88A33D0E107C", "C4254A6DC983AC85BFDCC948"); test_comp128v3("3BD04EB44E9025675CEEA1EE80A4EFD6", "CD3F1B6EA3FDD0DE0EC87DC2"); test_comp128v3("B8FE9194D8EDD327E9D5EC30E09CE619", "C2D257CA005B12DAB1C57E99"); test_comp128v3("9FF0A2CE81526677BECE7093E03F40B7", "F639DA208C14F5D5E9B9155B"); test_comp128v3("D8523C03A2DE75F6421D0BC91F02359C", "4E3BD1349917A5406A619CF1"); test_comp128v3("860466FE29D625755A732EF72620BF12", "DB53D349D3D90BFD6C7B5097"); test_comp128v3("96DCD22792D5381A5B33392650D8FE83", "64C9D371D87E92B9BAFF2BFD"); test_comp128v3("4482BCC96F8D8E5FD6EE2A3A302316C2", "15D4F790CE455848506F6917"); test_comp128v3("A53C321889AD33DE74846729AD0482B5", "6819059E37B8BCB2CD3BB056"); test_comp128v3("CB6FBE7ACE4F1F336B7535F43CE3FCE3", "36FEB6CC91753307137675E5"); test_comp128v3("32323174AE66387A960E21E3E48A36C2", "7941EB9CFAAF2FCF2D9D4F9D"); test_comp128v3("A2F01BB704E0A56C51D985BC170C2516", "2DBFDD6D224B73B8E3BA0F93"); test_comp128v3("B2FD8D3C4135A5F53C9E9CB2E857C3B0", "783971383699EAAB8A969788"); test_comp128v3("FBFE997F54C815A9769C576DC5557918", "A6EFB070C41375FA9077AD0B"); test_comp128v3("A04DE27D214F2BAE0AED7427D2D57228", "74A3F90EF7E7E5F9535ACB23"); test_comp128v3("6E47EF035F98F3D57BC9C1CB574D6799", "FCF439F7297B000610E5BC95"); test_comp128v3("9F7C7310B4AE3FBEA22077FE53884CD4", "D2D20CA60E5D657DF4AC1A57"); test_comp128v3("2B5456E3EDE3A798519D70DA24FDC14E", "8786A30EB8DFDE39EF411441"); test_comp128v3("3EB8F29B8FFCE34CFE924A4C8B412BEB", "FBF694C68239868881135AB5"); test_comp128v3("9112F7C42E8F86ADC612FFEF62BCF031", "A9A0EC605E0B7D959D94F3C9"); test_comp128v3("3B9E8C729F6BD2A647103C87B50084FF", "C7C6B0CA4CF9067D70D50450"); test_comp128v3("721720697921EEF72389D70412DC9BD2", "2E3A7C1632E8038E1DF4710D"); test_comp128v3("F4B192A4752141D528FDFE57403D1D29", "7EC20B5FFD6D4F380EED6885"); test_comp128v3("96ABB2AD544F3B40B2302317A00AE6C9", "3AACD18243723B98834DE29E"); test_comp128v3("475BC853DB0993F15E4DDA017849EAA3", "E356B15F48DE8C2A841CF3DF"); test_comp128v3("DB6E877FDF36CD13D291111A2A52706A", "20685B9CA28B27D1B267808B"); test_comp128v3("AF844AFF605D6CA75D32C8D76911FA5F", "05FAD3B3B7DF158D73F68D46"); test_comp128v3("B7BD6D23A35ADAA7A24698768989CCBB", "2D4B6CE203755ED6DE54D1BD"); test_comp128v3("F22358724D39699DC5F3CE252E5CB388", "1362F31DED7766967CDF75FD"); test_comp128v3("8DEED8937A8635D058E1D3B2937C1F14", "FED61D4167F6721F3D42A96A"); test_comp128v3("C8F471A85DF24F8694B66679B8F3AA05", "0C53F26A0AA68A554F02BFDF"); test_comp128v3("FBFFD9900A7DEFA9FFAAAC445CEB9FC8", "3A77E657D27C1AB33CC527EF"); test_comp128v3("57D09B46F380628DC5529A22E6CBB191", "FF53F69D116A083581928053"); test_comp128v3("2EC3BC236DABD021D37F1777AADD9678", "AC7EB38011974A02ABEAB3F9"); test_comp128v3("B79834352EA64EB93D000257045EE658", "F9EAA0D8DC11FF29D6B516E9"); test_comp128v3("655B9375AC8864762424A4A0D47B7BD3", "4646C2ED5F21B2EE15379E4D"); test_comp128v3("6E022D6981D611DB9C6E1D71BC5F3829", "E9421D6DA7103E98C42BC348"); test_comp128v3("E82BC59288987CC4900809B3F93A258C", "765C26D3449490E464D3A68E"); test_comp128v3("D88E96E65B3E75EBEC71A167FC745022", "9CE53769A506228A186ADB5D"); test_comp128v3("DE5DEBB9E0A8676C9AFD507CC4584B13", "4011F610990EEA93D7CE122A"); test_comp128v3("4285D4CFB1769D1A24E50EA2E22E9897", "981AF6169C7B6C2F9D35EBBD"); test_comp128v3("662AA01C92C099B9F1E6202E196893FC", "A18AC40821D530216226AF89"); test_comp128v3("02AC385F2414C82B3471B647689AB849", "E94F1A9A2178C9FF103B0844"); test_comp128v3("0F51172514E1CD57F69EF867599C3DC8", "3982621C739ED323AAAA7C37"); test_comp128v3("F3525F3F535F4E6DF13BECC1D8FB1455", "F62AFDBE83135B71413D79D3"); test_comp128v3("F92792BC56E1E01FF0E02D970FAFFC4E", "7BA8768FC9DE99379058D8F7"); test_comp128v3("2B52F1DCB4966196D138C68DD54E5919", "E461FD2030A5BF5D6EE94D1D"); test_comp128v3("2A185CE97AFEBF1571FE5D75D3BD575C", "97C86A5C1573579F9C21F2A3"); test_comp128v3("B41B160321225CE25C4E33E040056747", "F571B0467D59F5B9CCF118D6"); test_comp128v3("F670778BBA33B3995EABDF9CE23E1F2F", "7EC8E7AEB2F5F754DBF3E2C8"); test_comp128v3("9F01509B4208F00FE9AC5F302E913FD0", "57EC36681223C3B6D4926342"); test_comp128v3("FA147D70166C6DDE3DF84175679D182B", "10BBA85D4FEAD1DD02D2A997"); test_comp128v3("CC0F7F90199AA8DC130452DD284E1472", "1A02ED61BFF438D4C5CA0141"); test_comp128v3("76ED3C6965EDE96C8D4E1E8ECD9F56DC", "6F3A549C817F60E6DAA3EDC9"); test_comp128v3("4B337D2FDC0E5FE89A8E552DC45CD4C5", "ECED242B4B6A1D78DE53A425"); test_comp128v3("C2E1E0773A8030D18A18B9D272425875", "BA7CB02898761CDC4873BFD3"); test_comp128v3("A0B7D58F803E71297F445D449F7D692C", "7D106558576E6F817F16B949"); test_comp128v3("F9E967729FA158B2BBADB67F03F24077", "6909F02053F635627288732B"); test_comp128v3("E11EE60D5050CE4C9D0B03410C5ECAE6", "71C3F54E1E05B0F7DAD5C400"); test_comp128v3("02F33A269A8DC9F28CA3C370A15E8844", "F1947CDB239461CA161485F5"); test_comp128v3("3EAB022CAE7D0FF5171CCF46470D2B1A", "EC68B69C5D6B56DB8009520A"); test_comp128v3("D8D4A183A00EE3AE6342CD895CC722FE", "1AA84577A5A893B64CE59077"); test_comp128v3("8C4AFE74025F7228BEC7F0F338143284", "7D0F91326ED356A09110688B"); test_comp128v3("0A42C179A055958B374DDB41CCB29EEE", "0D2943530809487742DFA541"); test_comp128v3("D7ED82A3EE21516F5B5E9AC6F7C461A5", "6366910A49AAAEA42C9030D7"); test_comp128v3("86D87077BC84F98E3937097EEAB02ED5", "2A9ABE810CF32B7A58CA3F3C"); test_comp128v3("880C564BBCEFC7442CE047715FF7D734", "0CFCFAC0132393C12C1604E3"); test_comp128v3("DC31B4989AD996F310386615015ACE5E", "5CC175EAB000EA51A1FFE224"); test_comp128v3("392104223345E1D18364CBEA4BC3BFD3", "E264A4072A07CD9B6680274E"); test_comp128v3("5F7D8CD69434C38C21342F0AA1BA95B2", "A6054BD785E94219C6B13676"); test_comp128v3("0583E23F330357A0ED6C19F4B1170687", "D4420333B9B73E98C9CDBEBD"); test_comp128v3("BEBA72E43E8B849A8AB7DE5E384F4FFF", "5C8E69DDEDACC0A2E2C53C49"); test_comp128v3("E924217A8D6DC3349F1227E479B75ED3", "EE4F62ED04137D5789593131"); test_comp128v3("D816518AD87D4EFCE7596BF5CB44F4E7", "05064CA57A799CC2BE53F4DA"); test_comp128v3("915D72694C6A437D7B0F426F09E052D6", "D424688D264C9C6C4DF27328"); test_comp128v3("A670F35E061E9939F3BCDC59FE3713BD", "6D01F2C7E14F078D905D0CC5"); test_comp128v3("5C4F891E7FA9033123808EA080057854", "CC073D07C363B207B01E7A92"); test_comp128v3("18179F59C553186751D81E19B525844B", "5A85AB9293AA28FF5FAB717E"); test_comp128v3("C2B28E2A5F646D7C1D8CD149FC5CFCBC", "C92B159FBE27D67E60BC4979"); test_comp128v3("B4359F02926FB5CFE5CA5A8D1CA4DD9A", "3F3BE16228D0E16613464E71"); test_comp128v3("DEE387A6D3DE2596552C22CC63DE4C51", "0C00FAC805524991C3279C9D"); test_comp128v3("D51C0E2C76902EF329A51E1F0D7BCCD2", "2B22C1C141C8048A35731F8F"); test_comp128v3("D5E486BEEB5F24C1DA0E8845A9B7A814", "EFC378D93A3973CC122E8D4B"); test_comp128v3("5854C608946E292D53C10BB4217FFCFE", "1C9F8CC7D50860B4B3A0D942"); test_comp128v3("B7AF5AC0E19E193F7D8E6F39A574735B", "548B1F53379D01046A6A56CC"); test_comp128v3("34E91F46730F97BB9FB5EB234FD1F8EA", "63AA742D508678B048EA9C39"); test_comp128v3("341BA1C17F4B3404873D4FAA11360CFE", "72FB0649FA0BF6BB586BD4D8"); test_comp128v3("9E3FB44A03A77C0CE5A61A6A1401D171", "6CC44D57F3A12BF4EFD1A8EE"); test_comp128v3("6F736586A0EEF3449DF0B60D94FDF437", "9CD22D8E908B8604E50AEB38"); test_comp128v3("3DD7BBAA9219A684937E01ED3C4BB765", "979F4315ADAEEFEB2FA425A8"); test_comp128v3("14DA6A52901E8307A0A670AE479F86CA", "2BCB1F00A750C44FD8796227"); test_comp128v3("0CCFC8E22D343F1F742109B298C70B6D", "8A2774F0FCA16945E112A963"); test_comp128v3("6EFFA300E7B42409B6C0AB2F7702DE10", "03775D043B0E74A09DD3C682"); test_comp128v3("6E32DD9C06363D3861E0CA8757F6F1E6", "7A93227B2D9D42E1E9BC575D"); test_comp128v3("EA9C1AE547A7A703FB9927071741BBDE", "E21A228CBE7A6CB571B048A3"); test_comp128v3("9C02C6007B9520A2C91AD461F0D036B0", "1152183D03228FA0824CB02B"); test_comp128v3("D05F647A9D52588A8080FABC23C8FBE2", "081E7E132E7FA762FEC9615A"); test_comp128v3("C69B4F862714E7DE0F164AFB86FA749A", "A6EA4793326C53FCDD068BFE"); test_comp128v3("8AAA18EA7C2BF9AA2CF2FEBC67A2D237", "A844FDC8F08B88FA76203076"); test_comp128v3("99EB982A955878D476D0DCF26F4F2549", "4CEFC03D8877AE5A4948132D"); test_comp128v3("E67E1D6202D1579CB6CE1019C356365C", "359F17EFCEBBAF1871BB23B0"); test_comp128v3("51E24C6EAE298E24032801B915284529", "A4D4A479C4A563C887CDAB23"); test_comp128v3("A680BBE906B789E8EABF3A2105F729BD", "003D877DB7245BD9494FAA4F"); test_comp128v3("B8C0B91B3E4D721C64AB693D3661F0A8", "E6BD97DF792D7247ED7470A6"); test_comp128v3("C209970974C5FB03F13CC37DC5AB14D9", "CE35FF0D37FA85538CE400E0"); test_comp128v3("AA776ED1EDA8F6365E7B6C309C2D14C0", "44363E12F52D707C3A603CFC"); test_comp128v3("63B00CC8C764D6349953A48FA7C9257A", "926B6CD0BA38598834ED10B0"); test_comp128v3("DCADDE2690C1A93C5E2726A62AAE2C7D", "FBE1015B5DA87469EFC57492"); test_comp128v3("37EAF2D85AFD72C0262AA7DCD7429557", "33EB97675FC486E25CA4015D"); test_comp128v3("AD2B37FCBFF34E2A3E298C4D0E889335", "F1FB1A310DE2FDF67C14D823"); test_comp128v3("21E5FC1E51A73A330400ACE4C4620506", "D5C7DEF1981B1E4777A03D0A"); test_comp128v3("5715725A11F18EAF719F33AD86259A07", "51A55915E4606D4BB5293F18"); test_comp128v3("C3F809C472F506B2B1E857DC01069423", "05206E64B7887F0DC6496ECE"); test_comp128v3("93F2E9C05B6A1C6BDC847F30C173335F", "59A69CA2FF24C63F4081368D"); test_comp128v3("BE45C95A8747E9AB6CA17343FCBE1EA9", "2584335E68BAE8EA10F0218E"); test_comp128v3("680BA0B4E836CB3878E5D0DEF1C2E896", "E62B658EEA7A6D7A43D59686"); test_comp128v3("2D3653DC920D22B620131542E79336C0", "F95FC2F730472BB634C51FE0"); test_comp128v3("C5519276EB1657216B1EC10FB4A93F56", "3C425689A01D4D8029631CBD"); test_comp128v3("20BCB9E98C1415A8EDF358D37539315B", "BE8FC33FE00171B5785F850D"); test_comp128v3("444B9544EE15034F9B8C15E25488BBDC", "192A693DB42C3C1A80AE4D59"); test_comp128v3("74838C600CAB9BED85163A7DEA529D7C", "45BE629E6266098B2637BABB"); test_comp128v3("BA67F869BE79821954A4C23546F5A620", "8B1D92C591984DF390B34396"); test_comp128v3("BAE1A926B0C4C0F224D15BF6480BBB57", "B47B558C9333834B06901440"); test_comp128v3("3C5A480FBEC0C7DB36FFE0A4D0714EFE", "6324B2C4EC3557EFBB6F0A3A"); test_comp128v3("79D99D9DAD7E91564058816E40216884", "AE065CF21B8617F8206DF134"); test_comp128v3("652F5B2A342B02A44BF35CE9BD99B1F1", "27530B5DFE9656356422E110"); test_comp128v3("298878C7092089346E99538E4B530732", "1E99C17F976097BF59727225"); test_comp128v3("FCE036213AA9AE0890D6B4C4BBE1B819", "2F406CE178B194BA4223F014"); test_comp128v3("7CFB133F107F8FAAF1105550EE7153B2", "E9219A1A70AF922F7F6335C0"); test_comp128v3("2E5CAE7355EC3D5082C3AC9D2D4C1D74", "111F2E0C0435C381F5F0443D"); test_comp128v3("A432251736F441174D9BF11302FC8A0B", "6D85327CBC7D05E5B498061D"); test_comp128v3("864C3D7719506623594FD357343FAB3A", "99E9C7CE8A73118896FDFA73"); test_comp128v3("8060C76EA57A22E835FC003E36FBE134", "5237739EF805E1A8B0175638"); test_comp128v3("39B499359679F3DF5F20EC816087037F", "EED3A9367C317D2C93F7350F"); test_comp128v3("3B125A61F19898D982A68D4747F3AB27", "E4565B4CD803A7E706F7648C"); test_comp128v3("59E0CA0FC910ED47CE9541E983256AB5", "86D49D08A5CF0A80AD3D5471"); test_comp128v3("1509F82B32C4D1CBD190289F2ABE2A4F", "181C407D8EB4F6228F3829D2"); test_comp128v3("A5A1461145641E786BEBD6A7216DE8DC", "94431F516F754C875F978737"); test_comp128v3("059B3DA9442AB954CAF99206B5BE89AD", "651C10359109860AF1718475"); test_comp128v3("AA9D00B5EE743F509F23AFB30D2DC459", "FDB81785C720C703A71500C9"); test_comp128v3("55D1E3F5EA6945E329135BE531AE5AA2", "EE17C77D4FFFCC83060A77A6"); test_comp128v3("6874E0DAA246D283EEBA23D56647A2D1", "E7018B195236562B25504B68"); test_comp128v3("B97E607938AB98651325079D9873FC2F", "7264AB0E73B77C3BCD9C59E2"); test_comp128v3("97242D0FFC2DDC9D58DCB99F47FDB54D", "FFA3866BD5962ED89FEA8F43"); test_comp128v3("7105A4274D2BB5F3964D306E0B84BC00", "362122F98251B36C8D265AE7"); test_comp128v3("85A013070F50C8F318EA9D2C297B32B8", "2DC9C0B8598E482E3419CADF"); test_comp128v3("7F9D1A3A528BF1503E0659C4599BAF4D", "426C7085EA709094FB9E9737"); test_comp128v3("E40D0BF9B1BDB25BB8CEF5F2C6E42EEE", "0A24F4C8561666FACEB3FA68"); test_comp128v3("99F0E31E63033B10C013EBBEB5A38F25", "D04FDC9CE5590DAEEC9D2071"); test_comp128v3("ACED4722C9A93AA8653984D1F748AAF3", "F24EA9777C8BA77D01531F9E"); test_comp128v3("C24AD9258F77CD859AAD2E9929D0F86B", "BCEDF6F871E3EA3157A11783"); test_comp128v3("C3F2BFEAD419E0677BCCC0BBBA0F0526", "B5677CC99B8ECE1CC3E6A8E5"); test_comp128v3("754FECEB3814DD16FCCB048240C569F8", "DEFDCB968FE746EF38F62121"); test_comp128v3("858D87A91898E0E017AE65A8E96E009B", "13239CEC1BBA2B7080B21A45"); test_comp128v3("5420B3F002F35796CD870C9EF308E305", "2AD04D7F4DEB18EBFC6380E2"); test_comp128v3("75B4876D6E2469EEAD26C4CBF4AA3A74", "BC66DF1BFE59D3EA4B51056E"); test_comp128v3("68BAC8C22D18BBAFA5746D8BAF3919D9", "E125668BA81A618FAD0A626E"); test_comp128v3("61A5CB8A7FE9124A50581DE1F88B4CE3", "4713490084F1061AC0D01E61"); test_comp128v3("48A114454ECCCD8F73B798AC77397379", "D69DC6F8710A5DDC1C097681"); test_comp128v3("7295FAE9C0FB3EF6DCCD703EA74BFA6C", "0EAC8CD8DC71849E3C8D11D7"); test_comp128v3("41EFD59DFD1E7A03F6608E0E7BD475C6", "9276BF8FD0EFE2CEC453E0CD"); test_comp128v3("C5D1CB28DEAFD2DE2D83F9670C193867", "F8C00545BEA262A59508A28F"); test_comp128v3("F1341091B0AAD50914EC51A0DA1E8DB6", "0C1CCCA9988F7891593B5B3D"); test_comp128v3("15B47603920EDDE3B34B7842547DDF58", "75E90A950B489CF1B3FF84E0"); test_comp128v3("AE9B3B9176219A4A8AC1AFEC7C152267", "54212EDF5C06DD53A5360B30"); test_comp128v3("640A58C1432588AA65E3B88E8A66326E", "1864A1790FA618FC9AB896C0"); test_comp128v3("BBDB0C7B620366A7E9F3BB5AD634CA0F", "A059B19B2641F578227C1F99"); test_comp128v3("FF31F65075488B3C9C4271D31087052A", "B81CD905D39B5FDA2E4A454E"); test_comp128v3("33D44783AD83F85CEC49E9710B8230E7", "68085BE1594AF4350BB72575"); test_comp128v3("DB70BDF5382D01D9F38D868298BBB2D4", "E67ABBB646268CA0DD275928"); test_comp128v3("1ABA1A66B8F22CF69576CE375FAB4EA0", "182F2FB770A7F636B2B97AAA"); test_comp128v3("2CCC1B002E74532B12BFCE0F75F080EF", "1F298C218193E0F3ED2959D6"); test_comp128v3("A2E879EB6F21E729463FF02A5367088F", "CCEA2AB95BC3214EAC9E627E"); test_comp128v3("5325A7BD8CD28B4C8009AFF45E545987", "5BF39891DE27F5C1D971D6A6"); test_comp128v3("E422ACCAFEBBA23C330372593E0EDCE5", "EFBD096CA871AE04B041A3FA"); test_comp128v3("AF4F883267B43FB627A6CED4D39D3297", "3F3DE74071C3A880C23A2B12"); test_comp128v3("FD49F0326311DC823D757F635197572E", "907E8D9EA29C29D0DD4FAB21"); test_comp128v3("FCE21FB08EF39CBE431CBE3023C4DB27", "73ACE1A839774DEA18A34375"); test_comp128v3("A0BAE1DF873FE7A7D9048D635F1F14EF", "F3074BBA4F277FBF5A7F91D9"); test_comp128v3("B37C38BB80328F3C97A977F2ED254174", "AC6039F5FC06E302415DDC31"); test_comp128v3("665CE0C255C84288F7A75A5F81926F6A", "55234AC615BB3356CD0E41A1"); test_comp128v3("8E7AA9F32253F42EF7F2D68EFBEB21AC", "04762BF970D1F2DECF22252D"); test_comp128v3("A1BE05726059C3F312787A379F585DE2", "094D1F7842C129B549BCEE85"); test_comp128v3("58B1B3DC7636E88130647FD67B68B08E", "8486978922E8D4D23A075885"); test_comp128v3("01DF6BC1C5CEE8C177C59CFFB4370F8E", "B71D112215E3D942CC8B156B"); test_comp128v3("E21294A89D9132C2DAA27BE40B719293", "00DBB154E82102ECAAE060A4"); test_comp128v3("35B192D8BF541EB6EE622972AF7A640C", "4137A9C7D970F4A79B0C0826"); test_comp128v3("457C3690BCC9955B543D2147A98B8BEF", "1E6860FC647A0AAB87131B5B"); test_comp128v3("7CBCAFB6658EDF3F504659B024251EFF", "D0BE0503E3D82D3545C0FF71"); test_comp128v3("247DA9A5A1C41600098D805C941BD8EF", "0B620D456A380440F69C42B0"); test_comp128v3("96C7ADF6EB63AE953A9AD69CFDEC2ECD", "7B0035A1982AEECC937A9463"); test_comp128v3("4998CF82AA9184C7AAC721A14EDE612A", "8B953D21C3805FCBDB26DC03"); test_comp128v3("6D6C2397F816501C1C2A54026A87A891", "5E4E4DA6DF8439447F40AB24"); test_comp128v3("9296C69A474A588CAA17E769F09E650E", "F2454421E40AF0469806ED3F"); test_comp128v3("B5E72F91519E8C1676DE816B80E8ADCD", "A3A9B91A7FC134B9D01551BD"); test_comp128v3("7BCBBFBE158DC5930C71CA5752DB2993", "20AEED525F372D089FA1A1C8"); test_comp128v3("9F5DAE81ABAFEB880B768EC43240DECC", "AD83E8DD85824241E62C971F"); test_comp128v3("9A0E44698E7CFA4A85C50490D45B5D52", "B10B5377EDB51812B6F29C8B"); test_comp128v3("57248389353A263554EB4D9980CCC158", "D1881105398CD380A6B43608"); test_comp128v3("276F117BDD45E2A3B9E8A034CCBFAD36", "31C05360EC269EDAEF5B60CF"); test_comp128v3("58B44C471348805E3472F3B3CFCDF804", "C6C8C30D56B10D1D9BB0C187"); test_comp128v3("7B6B3A243E90EE58132A0C4E648BA8D9", "741851B256F0927411A5E180"); test_comp128v3("48B2B680CDD3420DA07D8043834FFA25", "8A4DE29E3AD6AFFCA0B4D62D"); test_comp128v3("9AB7967D54AA825C370EB756206BD293", "3F9C0A891963C81E9C98532E"); test_comp128v3("05415E6C0388DBD47BA5B90075C80AD1", "FDFBB592A74BB2B7DC06605D"); test_comp128v3("A16149E3963E97DE8616C5B2D5C69BCD", "28A88DB95E7F29561D90AD81"); test_comp128v3("B440410CE552432A8CD1C8CFB44D4E17", "9A8DC4CD1584016C16C71170"); test_comp128v3("282F5FC1F1DB7045E95C4D2CEF993331", "278540F1D12DA5AACCCCA5F2"); test_comp128v3("69CB46C66FEC0563177C54A92389B55C", "266B6C7FEB0A70ED61325A02"); test_comp128v3("8A996783B3ED992261F42900F14EEDE0", "E09AC5B6CB1C5D8919A65CF1"); test_comp128v3("A0D7319B6F633463D5D2678D9535DE6A", "96CF081192B489B4942226CC"); test_comp128v3("A9163934E4B6F027E9D1B897089E5B8E", "7C33CF678EAC9D79A4B8DF71"); test_comp128v3("7FA59B5C3098D6A4EA607ACE8EBDFCF6", "09E20383EF90CD41BCFB9E3C"); test_comp128v3("545266A2AC95D9DC7523D9CC26811772", "76E1DD1B438A449674311F7F"); test_comp128v3("AD404E88DD3ABACDC4B15C02F41F9DE9", "2EFC6B2B406E29D1E947AF1C"); test_comp128v3("92CED35256204E31EC3549F58B15C7B0", "1E212698E114E761E5B663C5"); test_comp128v3("EB1F247E0D56A25EF5718ED36380EEBD", "01BBF677F47C754FF502EBAC"); test_comp128v3("32083C4DBFF7FE1FA500529BEC94F689", "587DE353144F1B3BA54447E0"); test_comp128v3("3C6742A6E91A429CC202BB838BB717B8", "CA7694D92FD84F2F45AA6613"); test_comp128v3("4C6A5D02313B182340FEECDB0A9CB3B2", "416B5C878D90B6481EF1DDBE"); test_comp128v3("04C814633D2B0BF3B03D3B2F5CFB9C43", "A9E3C68FD77D1B30D415180C"); test_comp128v3("E6908C7AEFB2819798F070BF57354A8C", "EEAE98FAF4225799B577D6B5"); test_comp128v3("D8B8B932852D90A70F245B3E01E0623E", "E978D99AE21FA544E0FF7750"); test_comp128v3("B2B1C9AA587FFAA004D2DE4743EB7DB6", "BEA2D1F69952283D80F4CBCC"); test_comp128v3("C891C1DDE6D0688136A82B79CB8DD541", "E6E6CBF549BAC1BCC230DC82"); test_comp128v3("E5619F847A4005AE32AA9751EB407CF8", "F9AA6BB21CE75E21372A5ABB"); test_comp128v3("E6FE23F42B4B5FE8B333A1428E7EC789", "A84BC66F58C540B8BDA0CC71"); test_comp128v3("0B784416AEABB2135791774CA7FF04FD", "9AB68C5F642C64C82567E299"); test_comp128v3("42AFC9A45375E548CE1EF6E49C907FF3", "A756D8B5D20EFF34C4C5D18E"); test_comp128v3("61334E0DB09793458FF1E7874BD5BB56", "FF1EB9D76FF1B2E49564419D"); test_comp128v3("471A3B7B06D76797970D836EF664519D", "C51C2ADB60D331AE5AA94282"); test_comp128v3("26D42F7E2DCB2C1FEB76511033C7D986", "A28858C9C4E7FA2CC6F99576"); test_comp128v3("8FA6CCDCD58890BCBEDBC29EE019A002", "B394C74C41F3FCA60AD2ED04"); test_comp128v3("FE6B666AA3EF9CBA6094007E3F1B4F24", "9246AE183103570DBB6E1AE3"); test_comp128v3("7A18F941E8B893C7D27D63F5F4E14985", "345E27AC263C7C0585F25D06"); test_comp128v3("C3E4A515BF1C087D1F3AEE498D3C9F4A", "B3A6BBEAB3459BD607310A3F"); test_comp128v3("DE2024BDD217B94AA602EB502C9FAF4A", "9104CB600FEDEFA6365F34D2"); test_comp128v3("61636EA7E19138D4BBEA213AD61543AE", "90992C68472147762B9FEEE8"); test_comp128v3("FAB3BFF88D340A5423E32C9596BC8B57", "7FA6B3679298BB21B282A350"); test_comp128v3("4D37D80ED1C7F445F73A66F59C11FCF0", "110889228DE97F9AFE93A1BE"); test_comp128v3("A6D10F7561451F50B85255AD5BD912D3", "668622414C3922D25727F3C7"); test_comp128v3("E6A2AC428310CAF276CB6035CB8EB9BD", "8F3E6B6B0E0485B69F011B6D"); test_comp128v3("68204E1B48BDB4F5D89F8A970B2CCACD", "D404A42390D66EB423F42554"); test_comp128v3("98CFEE98EDA627B7680EF7EDBB4CE14C", "A3B4D01986D64F0AD563F7EB"); test_comp128v3("D06325445759A1D117B09B2E8F4C94C2", "F5F33FCC60A8E84325C3CF5E"); test_comp128v3("92A8B5DC09AC530E8A5CF2F38AFF22F9", "2DDC1FB79A084C64EA4BE75A"); test_comp128v3("B5521DC9BCD55EC9CFB2158A120BAE06", "D30F832834176C330CC43514"); test_comp128v3("C14B9437622D990C05C3FFCB734DA52A", "D0CA95116EBF7A6261CCF2EB"); test_comp128v3("75E4718349BE6677212F218679338FD8", "837DC56CE8A0EB5D01903DB0"); test_comp128v3("937F3F6779B2D4EBD5B7A35E3DC89D57", "A86B1F588435C042D6C7642C"); test_comp128v3("352D597B542F025B4D18A42C9B50E426", "AF86E90EB92C60A7B0B942E5"); test_comp128v3("195505E0551B87975CCE1A27F0B1DE1A", "7960D8F66F1D52C8623223DF"); test_comp128v3("23AD7E24D5C114B17F629337C20B2267", "430761488833363E43CCF38D"); test_comp128v3("258C233904FE9E5DF8E21E6F5AE217F7", "5A03D16A770893B19751D022"); test_comp128v3("CB9F5D69C3189F895C6D541588F6F6B1", "AD173ABFA217584CF3E33648"); test_comp128v3("1D41DB0E40598D7AFD4500E3B3B1934A", "884D68E5EE36AA82FC277648"); test_comp128v3("A825B23D799E59BF6AC02ECEC986D308", "16731DAC8BC5B9FFE165452F"); test_comp128v3("5A02958E967EFB560ADFAA471FBE545E", "CD455E83E810D06DAC2DA40A"); test_comp128v3("E0BA11F212EFD783B1B2D7C1D94CFB02", "BB3D1F7CF901443C564AF018"); test_comp128v3("6D2B6B980D7A84ABC255FD36D78F73C9", "9A9EB3E89127EC8C59F2F219"); test_comp128v3("29F7F3DD366FD417FB27FF070C6C64D9", "90A88C2971EF2519A9286ED5"); test_comp128v3("398096054AAE11F1466EC25C31EB488B", "FE8C1282B3EC0BCC245DC0D4"); test_comp128v3("4727E5FA42CC306C94BF5BC60BA5D04B", "0979930740A16964D4E8DC9D"); test_comp128v3("00FA2643930D4CAB0B437D4538A9A5CC", "D7199ABE41A62FA9A21C940D"); test_comp128v3("4BA3FC019B73B6D50671AA008136D42F", "632173C95CFD3E845734F21A"); test_comp128v3("DCF66A88B4E29275FEAD88B7FA30432E", "CAFCCD143CAE517ECED695CE"); test_comp128v3("65945975D5C9C172830BA2C9E82FBCEE", "C124C3DB68DAC92C77D9D77B"); test_comp128v3("88F1F56B2114A44495DD4758A27DEE2D", "8AE73C7FE5A4D3AFBA04735C"); test_comp128v3("98D15F929E3C088915CF014704953670", "D40859F7D1B9B0BE218DF3A0"); test_comp128v3("2A5CE1BDCB03D4A716F94FA058AA4A8D", "3C52E2C038B420B1148FFE5A"); test_comp128v3("06E140F81908655D7EB5CAB5BA500EAB", "EB33656525860992973EC912"); test_comp128v3("CA5F889F82FF6D1BC8A37EE7F4CBCD8C", "5A05C0D894B353837EA89DCB"); test_comp128v3("18EF57BF7CE6A833096851B002085B85", "836B27AE1ACCFB7E70705D78"); test_comp128v3("03C91A567AA4F70F8FAE96090B1735EB", "6A9AFF2DEF414785C740FA96"); test_comp128v3("87A6F4595E81766503F3A69ED488CF30", "1CEDAC452A7E2A8F0C6F0898"); test_comp128v3("A6D2C603B63B9C2DB78C7603FBBA2E56", "4D87573C92C742AD679C5F47"); test_comp128v3("38F20E83B0B6B0ADECCD8AEEBFA0D699", "174B64864AB6D9ACB49E9974"); test_comp128v3("FAC8FECBB568C411B8A026E8101EAFCB", "DD1C6136805B948F8A752F88"); test_comp128v3("444530537C31B55D744B3ECDAE7351F8", "79452F3E1F9D9CAD82B87F8C"); test_comp128v3("857CC0770DCA36D9022EDAB273E28663", "83392240C1AF37AE62A628EF"); test_comp128v3("57E9FAD98FEDEE806AF218277C3C5D41", "7CCCC317E62BA74A9C76D519"); test_comp128v3("B5824EEB55DCD5A494FF1A90587A823B", "286597939FE17FB2632AC770"); test_comp128v3("2B279D4E24EB13A17401CACFEE936E04", "5218F1BB472334ED7E1638FA"); test_comp128v3("1EC44F30AD61EFB4E0DAF670C3B2AB70", "9996814A92FCDB239CE26937"); test_comp128v3("4FCF2FCBA29AB5869EA97742E1B84F36", "4C4AEBEFFF5F38B19E115879"); test_comp128v3("12690AD6A685FC5E12F9F2C8BC3B0EBC", "4E637991268BBECEB730DCB7"); test_comp128v3("D9F82CD33E3B58A7A12CCD5CE0263795", "60B29E63DDB9E1662CAB28AA"); test_comp128v3("9338545BE3BC0BEBB1147A60A901D050", "87A06D1628D7F632AA0B09FE"); test_comp128v3("430A9DDFFBFDCDB35CF25C2FBD5FE048", "3CFDE615E8709673BBF5F81D"); test_comp128v3("9142B3536CBC753D11927BE8106E06BF", "7AA594617CF61D637A708F6C"); test_comp128v3("3035ECECE2B602952946B25C768FFF52", "790963DE796E2AD0F5609414"); test_comp128v3("238844A8C5D4EE87E1E3DBB9730AF9E1", "EBADA4E0EB87ACAE64160331"); test_comp128v3("2EE2ECCD9C8353F0DDDB3DE1FB564845", "9639CD9CB0B49815D3CEBDA5"); test_comp128v3("4CCF70032015B8E5DC13EE6DF5D9766C", "4199007AFF50F079DD974CC9"); test_comp128v3("940EC0E6CC164B97CE7A620569BAF28D", "8F4FDA8480B3DB9879680E17"); test_comp128v3("E9AE188BDDE66B3F5854C3EF4B039A52", "E2C4FA22AE53E117546C6A9D"); test_comp128v3("77933DBD0A5BEC33112F9984918AE86B", "3BFF6F18700B52ECCE2C84C7"); test_comp128v3("18B1D51FAEAD1B22B013DEACB696F70C", "A60CA3558AF36179BB73000A"); test_comp128v3("AC8C43FFCA7B3BE74B75C4FE2161DDD2", "0A94334EA63F06450687F5A2"); test_comp128v3("A1F89ECC18A10A50802BDA9425C04646", "04D28F46E4CB41BF9A42A595"); test_comp128v3("E1EF29848E8859D78699F0BC084FFBBF", "FAAD7FD942FA72B031577F66"); test_comp128v3("1A6E390B309FFE210B5F08E60C9A38C6", "AEB7315860F4D0720BB9F041"); test_comp128v3("B574C2AA7D8F662B40A8B32502003397", "1DF4B893D380E8976DD338E3"); test_comp128v3("92D42585A0375621E0CE7624BC8A9EB0", "A5806E604AF5D548D7680484"); test_comp128v3("EDF884875394558E62621E8017BC4F2A", "4716D1CBC44A8B3EFEAF67A9"); test_comp128v3("B7E1B515FE3CDC08B58C5A9954DEAC7C", "4D639CF494DDFD648BB5AD8E"); test_comp128v3("7B7E1FD35769F032A5253E103D39C9F1", "ECE54620684332950785D8BE"); test_comp128v3("0679BCD97C8EEA6B4E3A6D9A6DE33835", "CF055324EBBAB90814F42FBF"); test_comp128v3("2DC3110C86D5C7E1D4D3D545EE24FC2C", "07938CAEBE1712F5AE1E3ACD"); test_comp128v3("AE1B1164BDBBEAAB83ABE0C419DDE545", "9A5C9BA8D8F26A7CF64AF101"); test_comp128v3("B0754B5A1DD4B70D6668EC8EB4B39C10", "52FB1BF1CE5FC6A231F0F05E"); test_comp128v3("EC45EBD7DD6E36E070B10B353CCF9D5C", "B729AF7EA1195B39374B58E7"); test_comp128v3("F61666A78A70D5FB48828AF5EF5AA2E7", "D27EC7914E016A7FCFF71C54"); test_comp128v3("50BD44366D78AF6A69F20093BBD5E0A3", "17A8D6BF04FD4962D8E160BE"); test_comp128v3("29C0FA93802FAB90949F502AF5E3F2FF", "A8061D733AE08D36228A60C6"); test_comp128v3("CDF57CEB499D1531687C8E6BEC30F2AD", "2FE50295265CB2EE759C723D"); test_comp128v3("CBE08B4E5F61E801F929658192748795", "6616AA22DB8D0F9B383553F7"); test_comp128v3("AF1E33D1EB2364CC5EE33ACB28D96BF1", "7A57FA9E969374C8E3C47BB3"); test_comp128v3("A836E1719AE7E54F8A98ECAF902F587C", "80938563139ED354B2C3FFA8"); test_comp128v3("39BEEBBC77D4CC9FB568B738E7D02F71", "34087FA85A9AAC1586847BE1"); test_comp128v3("EC546393DD23CF2F2862384E8C3B146B", "BB153B83A3908B4E64712AD1"); test_comp128v3("96BD017521C07684145B6F8C6A001308", "854845992A4DF09576663A15"); test_comp128v3("F60B146739D623E691FFE695283958A2", "D4E66CF06941040DCACA85CF"); test_comp128v3("881FF49FE99FF0FBA2ADB26C9E27276D", "0ACAF775BB38337A1E344200"); test_comp128v3("3714AF1C4056F4F1A0334428E70EAE31", "675C9D5C711980E4568EF851"); test_comp128v3("76ADE089A96DC337F6DF5AA2AD9B9379", "A4BB7572C86755C40F67668C"); test_comp128v3("FC2E67BF2FDB79D53509A1B712C3338F", "A08FDFE14B6377B9593425DC"); test_comp128v3("9B1BF9D0A2191C33F6D3119A057E9ECF", "A40A99883EC4D2F5E66F135F"); test_comp128v3("8D2D20EA10C7B5E377E110CC2C471298", "D771870E3342A158CF5BC565"); test_comp128v3("20DBA5BA5C7364DAF94894FA2E3133EB", "D5ED6537C0B3343D2E3DB575"); test_comp128v3("5B045A56105D5FB8E568F5A18107F339", "DB2DB2118ED8A75AEC9F17EE"); test_comp128v3("6A6EC373C993C65460F60D5C2F1E2C42", "AFC9C8DBB8D37070636E8A83"); test_comp128v3("78D42F93ACCE494C7C48F8B3FEB833BC", "56C4A07071992807A24873FD"); test_comp128v3("877087DBDE0778182AEDF1835188A17A", "A5AEBA67FD0C0053573562D1"); test_comp128v3("C5D36C7B00BC21E37EE7C8BD2344A2B1", "9B87A0E5CD0F142730217AAB"); test_comp128v3("2848480D8E779C0190C1FAE7572E7A4A", "8F1512191748CB0A91FDB205"); test_comp128v3("C3863CD1CBCB481366B8B1C1F6FF8530", "0766720770BE1247317F7B64"); test_comp128v3("BB71FAB97A20ED16A391A519A9EC193F", "486EC0A9E17EE343578837C0"); test_comp128v3("052E5A2C31CEF3E3695A1397853D45D5", "936CA82F9FE6A435C4EE4DE2"); test_comp128v3("7B6306FDF353E730770E66604F8466CF", "4121273261A8C8394BCE6FD5"); test_comp128v3("9A6FC4AFFD8C84F8818AF4AF49FDD53D", "5763599BEEC27160A6AF64BD"); test_comp128v3("D61D855CEBE64691EB99EB73E3758FA7", "E5D9845F2515E2BD741E912C"); test_comp128v3("6982C1E0C933006920542445974B0459", "AD5EBEEDACF13A80488D11FB"); test_comp128v3("8D36A5A66DD0059C76B2C0510EC41E25", "4AF22EF5C1DB27880E8F1B1F"); test_comp128v3("CD58C379E73A4923555FF88714E16CF4", "D30AE0937D8729472D0FC3C0"); test_comp128v3("E04F522692CBEE4B7F0315AD9C51D72D", "D96F380954E06B17783E0391"); test_comp128v3("BBE2C80970B92F6ADC1AE1525A197DAF", "9FCEBC453C0C2F496D2B7B0B"); test_comp128v3("D8CC3E92B9C35D22B2FBB94706521276", "BBF9E042F4E5EEDDC95EBA85"); test_comp128v3("30B4398C6A943E1FE0C3C512BD4BC788", "8503AC4FEB9FDF7CAB207F62"); test_comp128v3("42176E40D3F1446A15D9AF3BAEAA97D1", "FBB1A09D0DC843BAA4151147"); test_comp128v3("2D9269B04D899888C4DFD9D18A14EC62", "9319967214C5E69AF7CC83A5"); test_comp128v3("577A7773EDAF8B1A958F7786EAEF9777", "F32957EF68CF821FE3C4C844"); test_comp128v3("62BD892681623A3893599E5EDC068BF7", "52B4A34186CA802DC7650385"); test_comp128v3("A8FF2DED49942BCF15D92184DB625AEC", "2B7ECBDD13D65F645A1D71BC"); test_comp128v3("2A375F080809C959288ECBE28177B8FE", "A15C38E02FF392874C2477F3"); test_comp128v3("F24A400691C85CFA11801DB8358EDB28", "AED4274B44DA2CE47C646927"); test_comp128v3("2A605DD2D6CD2532034962B7B341C92F", "90C1FE26F8FE8BFAA6CCDBD4"); test_comp128v3("4A0EE729A82478AB404AF83758EABB77", "9FB249FB258CC32EA8C2CE1A"); test_comp128v3("77E87DCD362B3011042A2D50DA174118", "50FEB8FE43E76FBD9FA6DB41"); test_comp128v3("05099369CBEF98698783BA40403A34A2", "D6F60A72FB0E5B8B693A2191"); test_comp128v3("33817E9D63C8FE1DDE0025FDE7DB2FEC", "2CB474A91467A18E2A9CC760"); test_comp128v3("D237DCCFF06B1DB84E8D2BE3554FCAE9", "8501DE0D5ACF9E38D7363AFB"); test_comp128v3("377F98A0C49FB4150128986DCE4E7359", "25D8CAAA3EFEE5D7102D6039"); test_comp128v3("B00DBA3CC58C9C7ABF27447431B16300", "9221FB5259BE9C96B161459F"); test_comp128v3("20E5A41ACB1B3853D6A31C52DB5DD12E", "67928229F5693E291B98E732"); test_comp128v3("DF9CCAEC4EAEA10523ACF122D4224B0F", "D6912B17D1ED650EDBFA5DA6"); test_comp128v3("030C9B40FF53EDDB9097EACF7DE5030A", "931B4F1E46FD35AB1E5B0F58"); test_comp128v3("977A5F4C3C961719DB5EB52D0B38B1FB", "F8E13B7190E28876833BF22E"); test_comp128v3("D726AFD1E009A57B50C7751EBCF50A3A", "455B65855ACAB653892103D3"); test_comp128v3("0066B204988D12934052DD78451B3C38", "7A14D3B1095D6E7778043C48"); test_comp128v3("310344BE7D063F69CE10EEE11B3D3325", "D9D916E2A9E0EB1D884F8817"); test_comp128v3("BE1E399FDAC7463EC2AE1BE7CC281C15", "761BD212E485B0C0FB45CD19"); test_comp128v3("3BCA508E2BBE57BC3F0373052B3BC3D9", "8EFE95BCDA4BD9EDB5DF843C"); test_comp128v3("F2FDDB7EC0126349130702C354D9FF5C", "212B7A28EBB7D9F43B25FF93"); test_comp128v3("476582F5421F58306A3C57C8F6CDED9A", "1A591D59BCEA8D9B7D10A2B8"); test_comp128v3("AC58FDECC86B561D6A148B4074872BE5", "4BA6D4EFC468467C570168BE"); test_comp128v3("CFE2C743B197342B2C6E33058DCEADF5", "D683B57875B63428A0523913"); test_comp128v3("D0921D159B743A3E644DD3787FC767F6", "5729DB51AA3905FCF6D11CF5"); test_comp128v3("22A292888CA8C313B785F970B490C261", "7AB4DC3F26954848629108E5"); test_comp128v3("996E7453208FCE8F62601B741B187880", "129FA10FA3D67ECE7A49E8E2"); test_comp128v3("B3915B7F245EE8CA2613C5DBFA40B36B", "AC6CF8265CA6021E6D28F477"); test_comp128v3("4865C31CE54C3BB99E53591AC8979198", "2650D862DA70A85B9FB4503A"); test_comp128v3("FC16BFBF1FADDB8A3DED51D1ADD8C391", "F3CD6C256753896320EC4D9A"); test_comp128v3("1D27C082749B362A9E079528CE462144", "49BBBBF8AB5CFF59005105E9"); test_comp128v3("19308C8E873FE8D050BDDD797A029CCD", "476AF62908F1421B62E0E334"); test_comp128v3("1426F94934BB10E6B4EBAD64D2A54A2B", "02C6B814B58120E5F96B4107"); test_comp128v3("AF1E5C627A5533D575A8CECA5B1117BB", "9304DD11B97DB619F303A9C8"); test_comp128v3("58547645FA672D474940279877918D62", "BA40A424762D75DD60C52373"); test_comp128v3("3A56DCC21C13902FC625643FB25C987B", "57E685340A4A237414207C48"); test_comp128v3("B851E274F9E1E7545ADDEDE0DBA5D2BD", "AE14288C0BD0877319194E8A"); test_comp128v3("7627A495FF15D16F5DFC3BA784DCC243", "5772AAE27CAB453D3F6EC374"); test_comp128v3("34CCAF8288808B8281740C29BE6E71B7", "C8CC08C9299FC6A61C92B17A"); test_comp128v3("17795498DFB7074869664325E892F88E", "E0319808163DA661053AEE0D"); test_comp128v3("7A4338E4C6CAED253D81CA943F266933", "12FD13AE57F3492999AC0020"); test_comp128v3("9C70DA6910BEDF88184ED2CC8A630A64", "D13FA04CA1CEE854310D2469"); test_comp128v3("51C87035E8757BE513382458A614D592", "6643B6F05BD94565D7292AE0"); test_comp128v3("890B1AB6B918B6FB910A874F1774097E", "5E8292F81CFC8D81DEC5C71E"); test_comp128v3("093495E9E1A906FF8A02BE13F27EBCAC", "E4700F7002C7311ADFDD0AD3"); test_comp128v3("18534EA254A6E58AD279F2E75ECD1A2A", "F7CA1F8D9D942211356F15AD"); test_comp128v3("E1B25EA19D9FE6E6C075DD10D7178DA9", "DAF3C83EE49AA9C795967BA3"); test_comp128v3("3919CCE828CAA532B0D12734DE55C183", "F9FCA32B5411A7A5802D8953"); test_comp128v3("056CA833057E0B16285F753AD6FC4925", "D99B2F3AEDD8CB9229BBEC0D"); test_comp128v3("4E900241483908832374039E44B365DD", "CFCD7BF4E9C80C302555C5E6"); test_comp128v3("434E2A4081A6AAA495F6324A27A2BEE6", "546C2AE028F7324B31EAD660"); test_comp128v3("542BB3F650D3FD2197D651F2AA3B4CB8", "779CCC12B9A81203BF6E64E0"); test_comp128v3("85C002B4D3C22F62CA29B5A200016AEF", "DBFE7A507BF3581FDBC223B3"); test_comp128v3("A0D62D296D3DE757A2B97313CDFDBF28", "F6A883DBB001D5A06FA56A18"); test_comp128v3("758F8139CC9EDB9E1046D5D71AC6ACFB", "A11C1322F33B0C7147ACF210"); test_comp128v3("9854BB439AC95310546DDCD6AF8C9EE4", "75DFC039FC4A5AC6541750D7"); test_comp128v3("162B05A3D90E4F691CF4FDA964F9D08D", "BD38EC87D8FBA954372B5C2C"); test_comp128v3("3B870DC087FFF1CFE1FF0E7A3E5809FF", "D8926C4CE7F8A1B73803DEB8"); test_comp128v3("74DC9116A986A4C5623CCFFE99151940", "E384D58528BA6593998B47F8"); test_comp128v3("4F0AE2A1169E2C0CA32D76222F82AFDA", "D0D1CF2A17E32F33E0BC5D85"); test_comp128v3("6E401FA58FAFE8C370CDD180384503D4", "410C2B70E0AF9ED19AD1A655"); test_comp128v3("B600D795E20D5072AAEC3F12FED9831C", "8B195E4EDDFFBEA4FD2078CD"); test_comp128v3("84F03AD81AC842234924AEACBFD9E007", "C4841B6926907A9ACE38F692"); test_comp128v3("A9BB6DD52A6131A6324B2200A94A4C37", "6135DD345339D952D055778D"); test_comp128v3("8C76285968EE5CE3C5BC053389320472", "483B0678643EC1B079587398"); test_comp128v3("85CE181363376F133C27395F546810A6", "9942C4C95A9803060EF4E9C5"); test_comp128v3("0EF7F03976FD4F35877D81E51773F788", "E07E11CCD12DECB4ECFD52F4"); test_comp128v3("9E051CE8CD789364F3D4514664637125", "37016F051186B84AB6C0017C"); test_comp128v3("3B969219C864C6EBC0E28E998E723EFD", "06FDA82FD788237C82EFC648"); test_comp128v3("941D4CA3583D84AD49E8B36043A26B3B", "711A06F246EFD1CB875555EC"); test_comp128v3("42452AB4BBA8816B642A7A2B2CB32746", "4963261C47864C816D91D757"); test_comp128v3("EFDC72ECFC87BAF583836B28CD402AEB", "8FFA4E3012A7358F04C87DDC"); test_comp128v3("6A4A1F77C1F84217AD574B06BADA8D8D", "7C615CB1B0C19C1B5108B2D0"); test_comp128v3("612B7A4023CE381E2DA777DB5670EF19", "0F3C0B32C0CFA19A1E371569"); test_comp128v3("F2FE60B316249CFC37FC844738B6776D", "094BD3DFB26EE7787C67A768"); test_comp128v3("F16427D34518986545D6C34CE3DE0F25", "E61CAA8E90A56C33E64B314E"); test_comp128v3("8CC551123C8D0E5F06CE4CCC4C5CD2D6", "4A12AC7EC803DE9CCE4F5BD2"); test_comp128v3("0CAF4FCF0116F09688F70DB5BAC4FCF7", "7BB52ED93C9753BCD663B3CB"); test_comp128v3("570A449DE9CF697AD0F857BA3B4ED10A", "F4D5A26EB7CFE1DB99F235B9"); test_comp128v3("E7492BB6C9641FDC442007FADEE378C2", "47BB8A378D5FD4618BAAD3FC"); test_comp128v3("4BF5A78BEB85E6EE45587980AA8997D1", "856007831C77BAEED3FB74B7"); test_comp128v3("C467D3AA1E425853300115E2852A2C22", "79D0A87198628A4DD0715A81"); test_comp128v3("166E20A0937416E40585C4725A2639A9", "E60A8BF1DE5F8FEF98FCAD74"); test_comp128v3("FF60302DDA32C9B15B810FDFF066C3A8", "066BACD0B62EE1F2DFF67E5C"); test_comp128v3("0CF0CC0B334C5FD676AA5ABD29DB391C", "A8C1FF13A05A3C2AFADF07B7"); test_comp128v3("3C6F0B6C1F4145802A9B4DAFF7595919", "B21DEA3B36A10ADE95DFDA5E"); test_comp128v3("59E49926C0291B47EBAF1E08B8FC2E6A", "29CB0AAFC26985131F99553C"); test_comp128v3("24A9969D2A435B8E8B8F425AAA089862", "6358ED4DD9679D461F39870B"); test_comp128v3("CEBF9A54B65EE526C4F5962B744BC8E9", "CFBD78D18CE385CD80AE77D4"); test_comp128v3("766D7612E2649493E42E8729651E3915", "B543D6DF887B827AFE7A49C4"); test_comp128v3("8D9C226BDE5412DD4CDC3B82143A831D", "18C01DD500E50DC6BF028E93"); test_comp128v3("AC89833635DA629FB52A15D4603AED87", "5865D89B38AC00FC8277D3AF"); test_comp128v3("75C5B6B1886B874B64A935443CB167A4", "B71CEE0596FBA77B740403C9"); test_comp128v3("A154255A2AFF8779A6E0396E3FAE226D", "CCD287D9A032F1D350D3EDEC"); test_comp128v3("3493696275EBF6764767DD177EBA8F68", "9D0F44BEFE7C57E7A342E055"); test_comp128v3("48B4168389CC95EF4EA289767AA4CBDF", "77DC39A48A7C1944B4AFD3AD"); test_comp128v3("9F1EE8FC7A3886E78B1A21338B64837D", "419BC21C840CA23E21C29EC9"); test_comp128v3("B1EA444B1E3E7A548FDDA7FF64D2E602", "4977A7DE73FD33B1D97D58FE"); test_comp128v3("68F8AE28016004B285403B23E021BB2B", "CA330C40333234E36F460166"); test_comp128v3("7F4F1775A34BE9711260B1F977E152EC", "7BC572ED3D38FFBA5488732D"); test_comp128v3("3901121148BCE143BD22B87CB212EF88", "5D3212E5298C99705B7AC932"); test_comp128v3("DC61F4169C4BC0B3C54437F622977834", "D4506840401DF6554635D857"); test_comp128v3("5A051696E25C5B4852D48331780441CB", "BA534B6CA3EBAF8CF044826F"); test_comp128v3("841B5804EAB0F86CDE97139A2C6B180F", "278FBF615BF11F7FE79EAF23"); test_comp128v3("F1488D2026FE51161531EB1966D59BAC", "A3CDC43DD0FAC00FD463B36E"); test_comp128v3("13913340133F4E555B5BFFADDB325BF2", "6E07FFB5F8E5DFE6FF97BC2E"); test_comp128v3("9111742825025FF594EFF2F058048BF2", "BD0896C75C8E80FD28C0C583"); test_comp128v3("BD1CB3D3F4E34057AF30029403CA3E54", "3EDF25FC2A8068B3EB1DB74C"); test_comp128v3("C054E12BAFDF2452238738A499CC283D", "B7212228FAD9A97F935AB498"); test_comp128v3("F52B105E60C0CB09793BC8CD7AEEE955", "323153E1E96C18F34AC12A74"); test_comp128v3("2FD896ADBD204A8EC457DD6AE4446DCA", "78DA31F53A33D4BEA3F7A1E6"); test_comp128v3("03C67D7FB9F90F3B8416249FEDE60208", "3C625218B4B96297314882EC"); test_comp128v3("FD0C8286A0EC5A245BBCCED275905A72", "FAD0C4B702D988EF237D72DB"); test_comp128v3("A93176818C64D31AE11DB44936738D59", "665AE6D0A8E0E0FCF7FE4204"); test_comp128v3("7801F9B77C8EA23586FF132942D674A9", "C6E394BDAEEB9F1797D9A6EA"); test_comp128v3("4FA3B9E855330723B00CE8E3B7FD0F2D", "6C615F2A5E6E5BB3E3F4E496"); test_comp128v3("F3AAEFD08C1A3711672606593FBB55DC", "916E07B07A2BECC7D73C5C7B"); test_comp128v3("A8A2B83CC57E49E38B3116FB032AB7C2", "A4264C771ABF08AB7B9BF457"); test_comp128v3("B169A6F3D14D65AB435A411580C72BE0", "91EC0B1EBCB8F77237ECE573"); test_comp128v3("0D43F218A959414940569C4F80B5F9F1", "A2FCC9A9F23685617EA963CE"); test_comp128v3("AD5A7D67CBA2D953A064FA3F0C7B426E", "9D2F2AF67DF7016C4078365B"); test_comp128v3("A1F412EC7A762D4D86B524DB41A03CC5", "B46893F2D972D64E0C56595D"); test_comp128v3("4FEE01CAB67DB39FE9E67E62D1EAF9FC", "B84477FBD1FD9217E64D21F5"); test_comp128v3("2C557D29B854DC9F895D95B3D9A71C83", "5FC57C5E4DDD05E6D4125643"); test_comp128v3("78BAA554D65A8E13CC4AE5F244FDFA65", "05C6EC3E2A5CB9469981D932"); test_comp128v3("8026E9D238C5ABB9F3453D505358379B", "0A1D29C53AC98815E7A834A5"); test_comp128v3("3960A9455AAB630A81F8379CB8254B81", "3ED3FD6E9716E4CD21F791D9"); test_comp128v3("E9D5E86088C4798AAE8EC68FF93B8B6A", "1BCD6B153FBB8C98DCFE2BB8"); test_comp128v3("E27A3FAEE5ED88DB4184F48A312A955A", "C0A83F05F7F5D8718FE8A810"); test_comp128v3("3ABF186BF4A9EE6DE4BDD164A9EB3CEA", "0B85C14D123EB7FBEB22BC64"); test_comp128v3("2B4FEDF3F46178F97BBCB8DC4B6A7AEF", "9B47E11E0BA56D68AE00ABD6"); test_comp128v3("DF9DB131DD1927D7523C996A8CCC7956", "AFA7F27615E6AABD484B3750"); test_comp128v3("3151C7838F64ACE68776C88E23DC26F1", "7A18AA3641D5750C64165993"); test_comp128v3("AFACB079853517002305BA6CB7D7876F", "09851BB26A04A16B792B284C"); test_comp128v3("620666076D2D088AD2F61F4B4DEC00DB", "63D6377AE4D5077130A3D5FD"); test_comp128v3("81181C3B04A2FB3E90A2ECB3A09DF44F", "CB46C3E3ADC28DA7A9F12CBE"); test_comp128v3("9B485FC8D34D39B84D619B0FA0B91DF1", "0366966D53CB7BDE4BF394C1"); test_comp128v3("DE43D7FFAF046211613FAA0672781DDE", "653E1665CF9679361F428A57"); test_comp128v3("78C2DFA82F4520991FD3507A142AABD2", "4FBE0E4158E403B8D8AA3269"); test_comp128v3("6E11061572E6C5755F897773043DB7D4", "7E654E600CEAFE38C6337807"); test_comp128v3("3FB2EF05BD30839A1F11EEA2BC4C3ACC", "CAF07B3BBA6A5CD6757F0C5E"); test_comp128v3("6D2557CB6B0ABDAC48E074C3CF3EEA55", "909EEF3E8014ABD0C3141328"); test_comp128v3("ED451E3A8E2618FAE17CF8C9C544EA12", "D43D8B75C4370BD8DA08289E"); test_comp128v3("1A7EF4A0F0C6F89DFA9020D69425B363", "BFA698344E2EC5C260D3A3CA"); test_comp128v3("39FF53E64F2B4B8E445981EC8B04DE8F", "926623ACF4217A7D4DE94C15"); test_comp128v3("7FB4E8BCE634F763D8FE2CD361057705", "3001951E5CDE9C320AABCBA6"); test_comp128v3("744DE4EF2893C68A63D340FE40E2C1CE", "420982D52145AB5A54B5EADE"); test_comp128v3("C8ECBED4C9D3639D3F1E5F4F34F682A1", "24865C4F6792107A3365B515"); test_comp128v3("D00ED1D1D9CF74C256AB08C8621FA824", "665B7FA2B71AEB7C2DFF7724"); test_comp128v3("83745C78C893B3F786C728DEF6A48A57", "6872F7917E0FEF0F22D5EA61"); test_comp128v3("7FBB66885417679E5970AE6B72C5BE4A", "84E39E1931E4CA690551F64D"); test_comp128v3("AF9D46D06E926C66835517B0155C8ECD", "150A4C90CF275DA03AA0A342"); test_comp128v3("236722E40A7FE9AF48008DA60028B057", "28C9E79D9B3B9433BA2C8AE8"); test_comp128v3("374143C31BF20F54B0300181515ECCEF", "028C56AE7D60DB723E0A4983"); test_comp128v3("A84BEB57AB7EFA419243F145D7B81623", "4E00F03F82C080039BD00EF5"); test_comp128v3("078CFA828AAC69931B30F86E3451B81E", "AE382636F9F94C763B8070A8"); test_comp128v3("1F9476007304E4C4A3C1F14A27CB2EEA", "476B9D26577B59CB3E1C3C16"); test_comp128v3("9DBAF388DAE2EB27B76759C2CCACBE8B", "263C08E1450EAEDF987EDCE7"); test_comp128v3("9C2BF5F3EFC3DEA34B4EC3F633280B03", "41B8D9727A5E8469EDB661F0"); test_comp128v3("AC7D476A7CD104A4C4B152FE82899E39", "A3B40003A9FD7894C517C8E3"); test_comp128v3("178B204DF182A0560778E5B90D1EE170", "E77BF4BCA640C82BD2665C9B"); test_comp128v3("9AEF8B5694B926BC4A55DED5259163A2", "55A731EF32A6CA3E833F50B4"); test_comp128v3("6B8D7482E2E1A96F6B776DBE6F7E5661", "CC7E378D8C7AF20E8FCFA0A2"); test_comp128v3("2AC44A80A5FE1003DD24AA4C6864BC61", "EF7AAE0833F9C3888473EC56"); test_comp128v3("C260EAC6C71D7B7D35C5BE1CCB34212A", "716DC830699958E175632ABD"); test_comp128v3("5946CA2D7232076E2DBEC266E794D1A4", "338283CB5CD91817A465438B"); test_comp128v3("210EAA6C850967ACCF3325487329849F", "29D05D8C1655A641764CEAF0"); test_comp128v3("393C4E5602EF6301890D599C7766CA53", "85EF9BF7A469BF0005D5AB2D"); test_comp128v3("5ADE7491694A75A5CE0959996473031A", "78A42538B39E12BED126DD63"); test_comp128v3("670A72D60EC6606E4E798EBB1D4AEA45", "3EB374EDE44B276A0B05F01D"); test_comp128v3("23916D367C0E00CF13B2BA5849293281", "3BCEC47F6FF94E05EB2756D7"); test_comp128v3("3D37E626165E8CFDA7F3503EE96D78AF", "7DB25F0039486AA1C17DEEBC"); test_comp128v3("730EC7F1A1334D7A9FA161ECB8FE839D", "4DFCF9EDFE4056DAFBFDEDCC"); test_comp128v3("BF7A806A9945108833CB3C136032A33D", "68CB935008CA401B29CDFE2C"); test_comp128v3("148D1ADC6281ED1ECEEB4C61A5D5974B", "6611E49991CA4CCC35CA4BBC"); test_comp128v3("5D941E9A26889E243CC7BA63356B8769", "B2F69A7288ED0794C8D8F3BB"); test_comp128v3("52176D9C830733245657C8866CB3FD34", "07D624B781E45E1AB2885D43"); test_comp128v3("B6677C3BD296A6AF1E3D08CB803D9F29", "6DA6E7C25EEFC908BC9CF2ED"); test_comp128v3("35BC2C889362E17CC46F1DC4C69DE5AE", "9B632BFDC79828C2EAE40ACD"); test_comp128v3("A8FE6732AC535DB718039CBB7FA0B0A1", "CED6008EBFC492271022666F"); test_comp128v3("9CCB764C102F715FA8F6A77B9C681A29", "E63D04D6553D5A1272EC93FF"); test_comp128v3("F430C9CCD6AC2C5448DFC20AEAC26B78", "A268C88D3C4AAC5C3C40B971"); test_comp128v3("CD2A3D23F134BCD78AD204776884DF1D", "09C2DF4686D239A55FECF7C2"); test_comp128v3("C6CA21CE9B44435BB2DA88DD15B1E1A6", "23E6DAB765FA303816298AC7"); test_comp128v3("7F7A993FDE676BE6063A83BCCA2F4CA7", "A0C1BD80DBE3CDDF9157ED84"); test_comp128v3("84D99115B8B2820C52546580B5A71517", "EF30D73FC9C7712BC1F4E834"); test_comp128v3("8F104901412EE55AD48EC8A70C0D920A", "F9758C4357FCDDCFF5D7878C"); test_comp128v3("B6E970122768A8B60D6ECF3590256587", "449F36D3222CBF4262F7052F"); test_comp128v3("9456329E3E51725FF4306C1BF1FC84C5", "47E7AF27620A89B4F646FB8D"); test_comp128v3("A320F5AB902AB2DA39A19819EC3DAE64", "F6BBE92D2F19DEE5593071F4"); test_comp128v3("ACF41B8B0345BB7C0611BAA2F78CE719", "1FBC5EAEE93BD973D6AA6F6F"); test_comp128v3("511765C125FBEE4D4E5BA1B339214FEA", "6B1389AE53C01A07793BE30D"); test_comp128v3("960398E8F44A9F26F51694BDE61DE6E8", "1EBE854FECE2AB72356FF067"); test_comp128v3("43172468FF0E5D538B047E89D9FAF128", "968204B1B443C19E2E861632"); test_comp128v3("EA690D56A40B0E4810CAA7C3F48ACD9B", "2B46CF65F8EE650856000B67"); test_comp128v3("CB2728D1C06FA1653325838B6786ED19", "C757A09F686434C6D2AF8FBB"); test_comp128v3("4ED8EFD706EC13D2D8ADB73E922BDB96", "F188C54E0B4FADFEEEDA9E98"); test_comp128v3("DE4E12D86637EC29DAF8366BE5A17078", "927929B7DC0C251D1B2BB0B7"); test_comp128v3("2E241E86614830FC4AFBC8A9B994BC11", "37D46D82906800EA6197658F"); test_comp128v3("FE24429FB354D1BE7F27D1220D7801EF", "A3D69BDD8EB8C24A6A19BC09"); test_comp128v3("C1C835697AF1C95B77CB5D09ECBF402F", "684525124857136F39ADE1DD"); test_comp128v3("AE8AD487E7DD8B1D8CABB82C4B3183CB", "EDED085BBF5084415D4E0205"); test_comp128v3("70BD0DB40F3D976E4907DCBA3E40FEE1", "EFE6A6F67370348AE283ECCF"); test_comp128v3("5B1072BA60B98806208700F6BBCB338F", "4C374E86BEBA04BAF4A2E11C"); test_comp128v3("536C7B7A6D773299B839416916D78B6D", "1F09C90AD9654A66EDEC86C2"); test_comp128v3("024FF49B1CA2AC1056801B2B9FF0BDF1", "3CB058ECA6914AD9AB4E58F9"); test_comp128v3("3F73F9C6FD5F80388DE7CD0D447E34C0", "9D4EEE0478937DC2D7EFAC6E"); test_comp128v3("56C45BC9B76D6D98B674258314ABD03F", "9307A97F0128F4A8A062C418"); test_comp128v3("6EEDC53430538D16AEE578433AC82C87", "6BFE03DA1A89F83253866536"); test_comp128v3("15526C4A50256C554663A59AFA31E799", "33F71C34F358D46AD9618212"); test_comp128v3("31FFA6A17A8A7537CCF03A58E7F32A28", "52F193B5D4238BCBA227B4C7"); test_comp128v3("C7EEE147EE90896BD4375E96B11D467D", "712F540C52D2B47177F8087D"); test_comp128v3("218825B240C7C411F7A140834E3C6439", "7659E16CBE00DBEE2C89CA18"); test_comp128v3("8A61111A6B336543BF5ECCEB68F7D15C", "2E895DC6ED3A9C291F169FA4"); test_comp128v3("05582B24643BF38B4F0B95044533098F", "484D01822E1106E27D0DF3C4"); test_comp128v3("A209067823F038C99E35FBBCA3BDE95A", "0DCD65DF9F21B41F6A1E5C8E"); test_comp128v3("62EE981575D6A3250E3BA64DC029F078", "C7A87C7FA9F0372FE89BF1C4"); test_comp128v3("062C73ABD56F61A3A42DAEE0F8EFD09D", "F5660C6FFCDA6350574A887B"); test_comp128v3("2F4F96A5A4062B572F7BBE311ECF6492", "3F67F5A619A34CE49DC41D7E"); test_comp128v3("B360977080E47CD9B3ED66ED22091F4C", "23822C6017E8BA385838AD0A"); test_comp128v3("E6E7FBD7603CD52AC2EC8CE72041DD0A", "F76BC82C5FF94A2F0B411FD0"); test_comp128v3("F53314B138E7CCAFB2F7131185918AEA", "7788F96D0ACEA0A2645D52A7"); test_comp128v3("D6B452B402B2851C2012C30029645FBA", "311CA2C5A76C27E8C7A58685"); test_comp128v3("AFDAF1BA6EF954C620B12F16762B35AE", "189701B5967B2D96457EC247"); test_comp128v3("642D3D046F4F1EBD0C775B06592CDB27", "D054138B51038F23BD0171D8"); test_comp128v3("CDB86368F91E71CE0FC34A70F6FD65C2", "74D76011A1912FA279AD1CC8"); test_comp128v3("9FEDAF38BA24A6C82BBA68DA95B71176", "7FE4FCFBF42536F899ED7360"); test_comp128v3("FEF1D522E84CB983A5090B4151780376", "1489794A18B7CBD81B3E6E57"); test_comp128v3("861435ACCB03FB2D1BFCA69782DC469D", "4B4D585395AD649C89FE6139"); test_comp128v3("995AD2985AD4E5A1F8802B90B7F8ECA2", "993C1F391FBAEDF577E144E0"); test_comp128v3("742263AF7482CE41D4EF0B03A3AD9B7E", "ADB826CAEF2799A7A88DC866"); test_comp128v3("479337DF35E98C67741FAD81CF3365B9", "FCD508F6FC1ABBE522E0B691"); test_comp128v3("C7B92DE42D76D646284A2C2039F584B7", "C541B2C7568E4817DFF95FAB"); test_comp128v3("D0C3DDC48EC35DA67B369A9DE0CCFFCC", "8AA975041C6BD0DA7586DF38"); test_comp128v3("599B31C8C3852C1C23DD19A7ACD55C11", "07D62DF63575B19B89DA071F"); test_comp128v3("0F269A0EA3E8EFEF5C3C52D43D002225", "A34DB9124623C3271061BAA6"); test_comp128v3("6AD44C3FDCF70DD91323CC62CFB02D4E", "D17293017E5E46B0F054C9F7"); test_comp128v3("BFE112DD9EBF7E917C561F72750B8912", "8825188DA2B08941B8089631"); test_comp128v3("B87A63D3DCAB1BAA9A185650D52F98BA", "781E9ED97865B4EAEB24F256"); test_comp128v3("15F374A04F54435C4986C3CEADE49078", "3803413B6A6B7D342431E19B"); test_comp128v3("9597CEB0C4C1DE570C408C732C4AAEFF", "FFFCC0E7778720989F5D207B"); test_comp128v3("9EA551BB7EEAB54B2CB0B7D5A4B46984", "12670590F5569DF36255182B"); test_comp128v3("E0A14DD9D10CA02AAB400CDD6D4CC8DF", "3B8D453873F43514FBFD6E61"); test_comp128v3("6D7D889972B4415B2C4BB94147A8B0C3", "9AB50F45E012C1C13DC0C123"); test_comp128v3("BD143BC0B969AB78A4C77AEF75DF577B", "46BA7B2AACE2BE1EAE041BAA"); test_comp128v3("AFA3644F3A1B2E3C949409CD1A9FD9EE", "4784E15B8459FC8C31BC5A09"); test_comp128v3("F68A2E4BC14FF93B6E8AED6603681E44", "08C81D5D7ACCE6421A3A83C8"); test_comp128v3("8958835A3177994FD9E234A64E8DD402", "4AB52CC30160C00D0CB8149E"); test_comp128v3("FC5A744BCC4C3A64731A56568F1824CD", "7691B4A850CC3F520A27CAED"); test_comp128v3("A0BC0414793FD7CD91C4901ED37CEFEE", "50387A5169C4BEF0DFABA169"); test_comp128v3("CECD899ECEEA771F48B27ADAE869A0FA", "AC47989CED4D7ECC858B3316"); test_comp128v3("227AE04F7CD994EC2C5028C28EB91C60", "2CF79C8535D09CBE08C99BC4"); test_comp128v3("E1EFAED76F88CB13D36D6FA9341E6F72", "D0939986C168DF7A71F8692B"); test_comp128v3("0BB39C124E6009D6F192BE076CE0B912", "07ECF9210CD0BA6CB7D0A331"); test_comp128v3("66B94A56E5ED1CA6D8D13B716C826EFD", "AB586CD5C53D9B44AC9CF827"); test_comp128v3("74BA0547304F65181E09067ADE3944A8", "1BC03458AF15F80379307560"); test_comp128v3("E5BDCE39E73DED6127D19DC0D2B07509", "2AA15E6321BC75606E161299"); test_comp128v3("5FA9FB4F33B5931DFD0B608D3673CD8B", "2EF8B7A870C0B16E2A2E0771"); test_comp128v3("591C4B52914FD9906794F9B437297932", "57D657E1F9EE91A75BAA281C"); test_comp128v3("88A97F10BDFC5900CE69B02CD4927D5F", "49B141185B37C4BFE45770A6"); test_comp128v3("B699392799957BDD5F7578F616FE052D", "E9FAD536A1195A43049FD4FB"); test_comp128v3("CFDE65C7EEAB7B468E5EB53B59560E56", "EBDD36D040A23090DD2B7C8C"); test_comp128v3("7DD1288548E9EE0F59C7AB71ED93F458", "570BEE5B5B720E1C71CC4188"); test_comp128v3("F8A953657955164A202FD05D61ADDF18", "ADB41A9171F3B9EE64CDA252"); test_comp128v3("8496897F262DB3DA5B1492BD910EE533", "A116D7BB8E39511500CBB3C9"); test_comp128v3("3143866A1BFF58CB64A6AAD00A8B98A0", "88A823BB6C87C33D326F89E5"); test_comp128v3("BF2FA6BFFB949B5EF935D4A70A1AD000", "059EB7C4093E12A3D67AF43B"); test_comp128v3("22D61E9979690AE0F70A2097CF75617A", "4CEA847615CAAB386441D6D3"); test_comp128v3("8BA2EEFC875953E279A0BBFF3E40D8EF", "EEB30C7A21BF5CFF8C016E76"); test_comp128v3("0054786656570CAF86CDA3E2658CBC4A", "A8D77AF4C0D2968BF86F7EBC"); test_comp128v3("CD789413163D67BEB5A8B4B2041AC8C3", "1E8EFC2C49634EF3D3A76663"); test_comp128v3("D5C2E3294F8E3F7EDDB41D8038E8AE4F", "DFB267CA183A97071445EBCD"); test_comp128v3("4547BAA42EC0A2AAA5E4A51A40F508C7", "D726295FDD1F03CC38C0D510"); test_comp128v3("30675DADA51DD427E74150DAA3D08E47", "D98EC07B336FF2636664CEDA"); test_comp128v3("E2049EFBF5A6B4DBFC1D827F7B6F4F79", "8A5E61E9C25DABB569047DBB"); test_comp128v3("4DAEAD6EFAFC7B2D5B7599CB572D966A", "3C7F91652C4F50ECAE96E31C"); test_comp128v3("421C75DAE44E53BF661EC64716C3315F", "B7C0ECAAD15C95E5A230B0BC"); test_comp128v3("3DB086094870B8992C429827A41388CE", "932A80FADA930997A72E765B"); test_comp128v3("1AED87BACBA9546CEFD7577149B343CF", "37496BAE7BAB169805B8A404"); test_comp128v3("1D4C0838314C7B2E969CA01BE86628E8", "FDA25570F91EE000DF4A462D"); test_comp128v3("E2CFEBC9DF035EED50D61010B97114DF", "507A0EE2F0E2377E9617D79A"); test_comp128v3("B7C41BDBCB3DC0D686FB4F2A246F1EBE", "9A8B1932BE12002117B66C60"); test_comp128v3("7F3C6DDFAAB07F2655835AD684C1EAF8", "84F746F4206E7B9D70DCE400"); test_comp128v3("4BE5B436297DD7EFE78F43B50E9D83AE", "6A7351376DEBD0C0045287F5"); test_comp128v3("810A6D4C4BCC9B442FCC2D17302ABD52", "BE8DFEFD0040A9171DFD1028"); test_comp128v3("70BCCBE54FA56D6C760B2A30F12DFE3C", "D066D8EAE15177FB53E82557"); test_comp128v3("685C7FF8585763703654DA98CA2ADD46", "E3AA8A4EEFF90AE719F7C9A1"); test_comp128v3("70EF0027B67540B854A5678C2FFDC575", "58B0CE51BE4916D04D84D042"); test_comp128v3("AA3ED4404B99F8C48BC481239F470693", "C7184C0EC24D703FE74FE294"); test_comp128v3("9E5EF17C6BEAA0E6B65199ED365DDAB3", "9EE43A1ED3DBFD931A317464"); test_comp128v3("2E9403A2E3000BA5C4E0D27C2EC64E4C", "813E15EBE8D0B17B5823E4BE"); test_comp128v3("0173781A0EB21F505712948BE75B8458", "1E3CA90D5A9E8A6464C8E36D"); test_comp128v3("3C088C10489234D6328F501EDBC98C00", "9D7C6B66C8630DC1F3F5312D"); test_comp128v3("A6EE02A9C039B2D0FC48FD8A345F1892", "0A822A5118F13C0B8734AF15"); test_comp128v3("BE8C4CBEAF4F1F1A9A3695894F9F2A99", "CE0FCD1BC48933F43895E002"); test_comp128v3("128432DC7C3146F692965FB0D0A3308D", "770FB50A91CC47B0382B00E0"); test_comp128v3("C88C48F3C4D3EC720F01BF9C6A65265C", "8F6DF2A2261D41952A2C382A"); test_comp128v3("B971116F61EB90F27D34457F37AD39D9", "2D33847A8C32844FDD307D51"); test_comp128v3("227551D0863419A949DD59F36CC83C5D", "829E83017A42C31FB195868D"); test_comp128v3("03234F66F921406777377B0DF204A6DE", "C43127089A8D4F7250387C9D"); test_comp128v3("D88DF421C6DE2284789ED5D54F45F03D", "3495E7918C60F295548DB637"); test_comp128v3("C3FA2BC662694EEBEE4781F73CBB18C4", "F691B5C42013705DE1C5AE33"); test_comp128v3("2DDA9DD56CF3E1FA5BC6B9118623A733", "469BDF4A8704D356A9A14B2F"); test_comp128v3("C6A403AE0667AF03479DA7D96BC52127", "4996261F8E580F85B515239D"); test_comp128v3("9B2F270E808E89FE9FBF7910D371A99A", "7904A08879D7368E8D28AB78"); test_comp128v3("346CF33E3A1CB2ECCBB0293B22718FE0", "4A381CFA638346B0AF50A973"); test_comp128v3("9DBDF0D1632047D2E033CD99585D4EF4", "8D52FC66020546B7F4C0AA7D"); test_comp128v3("C0C92335F55CAFA75C8F32ECC8A8382E", "AAFF1231A4FF3657E2A2F050"); test_comp128v3("F98D69B9C10B8180D8CCEB6F8512EFC8", "ECBAA8308702D56AB4B8042E"); test_comp128v3("D18711BD38803D6528A0ACC4A2D13F44", "FE40B2A809ED1B288A23EFED"); test_comp128v3("B45EB08D49D283FE8D5CD8736CFBB0BA", "2DC3543A5FC96E5D77C6A8C6"); test_comp128v3("E5A19F4E5A95B41A6B467A4BB61FAF6D", "FDDE48FF9ABA9E1772A997F1"); test_comp128v3("DEC185B0F7E9E915A245BCDFAA61B9EE", "B4E8B8317BB9B202C1216D39"); test_comp128v3("7CF79DD706BB5431EA0505C5A9FD02DD", "B25B155F0F33758DA3E34314"); test_comp128v3("D7FB84CABA49F0DCDA6CF9BD8DC7706E", "47350A6046056A36028A17EB"); test_comp128v3("1552B8D34DA464D3306574792B48A3D9", "E941C62F7B37C94DA75700C7"); test_comp128v3("BC7D021BCD887C891AD03F35E0A15BAF", "BE126713B56707925500E8FB"); test_comp128v3("9134777CBFF4D521A3BFA344F7A0B437", "FDBDA66AC1D9ADAD8755DFE7"); test_comp128v3("7C5F8F8A6BCE5887333A3CAD627717E6", "31F970EEF6CD0EB0A2CF4916"); test_comp128v3("DF8CB343C509A1AD4F2EA7115FCCDB06", "693FC7E51AEE34C25011590B"); test_comp128v3("A91315BD7A33231FD527B607D84FC46E", "1830B3DC10BDF6965B2C9228"); test_comp128v3("AD2E8EE1869FF0D47F58E63BC83318BE", "5FC704AB4A24BB4EE7D4E116"); test_comp128v3("6DEAB545F792A82A34768E828C52895D", "2C2573C04319579657238F81"); test_comp128v3("002EE13DDD62BB00513824409FCC1E3C", "714637C12FBC3464601C51B9"); test_comp128v3("1B7F26BDA08083628375BEF7D154A39D", "70863DE4A86FFCBEB170F92D"); test_comp128v3("50260BE0CBC61779A3974F29E8F759AA", "0977C5842CB846C0F9B7C801"); test_comp128v3("7CBE72435D57253A7F17B38DBDC91592", "FE586E3BB775847DD4D53896"); test_comp128v3("AD828B0B2057DC6664ED791C5D07B0F0", "2DA0521A402DB87C23EAA90F"); test_comp128v3("4D15F0C7057F79CA95112266A0EC2372", "C8E87F6A99CD77F1E6C52BDB"); test_comp128v3("2603498B980BB24F3CE98CDCE7132DEC", "3D120C7D2A0626188F107796"); test_comp128v3("7D3F0B786427ACCC65036320D6A87E0D", "90EB68AFCE491FF4FC1B935C"); test_comp128v3("CEF85C041E9B497E104280592B4E20A5", "D7088EDD39E9441B42FA5425"); test_comp128v3("28423C88529A282FEB63168012C7C9CA", "8541314D155ED02F6180F8C4"); test_comp128v3("3D00F94DF50F19EB4FABAEABCFD6D2D0", "CBDC0CBBA58873A08254EBBB"); test_comp128v3("2770701DD269B0577098A27093E4CBEA", "D433D01C27279F7871B8CFBE"); test_comp128v3("A6196F4218B78A98B4744669E89272A4", "6D9355226838299D764B92FD"); test_comp128v3("C2746977B2BDFC92BFBD7B108E31888F", "13349E628F46C2871403E8BD"); test_comp128v3("EEC4692F450CC83E13E2B71D59B3A3A6", "8373F07A23AA5A770F09475E"); test_comp128v3("C6016D020BC4C0FFB135065862E023E1", "C65DD883032F002242741716"); test_comp128v3("7EC7083310F0065F3B96E8472A3B13D1", "1023CB8704E3F094C4254B40"); test_comp128v3("CF0F35022BD195F113924F7FE3170685", "F91BEF9CEBB0E62E17D189F1"); test_comp128v3("13B6AB833AA9181A0D0589C321E1D641", "B2393292651E5C5FAC10EEA2"); test_comp128v3("78E0922DFDD9664537CA30DE31608273", "2BB017C76BEC45864CE8B5DA"); test_comp128v3("92D6133ADB98116A1A8D5233C0D82798", "A638E5FB37FD99BDAE373B67"); test_comp128v3("E63DD02079DC3B5E6EB39366B31C321B", "6F90B73C6489D8259C852982"); test_comp128v3("18A08A6C828005707749B55A9B372486", "D5EB1D8073248AD37B8F239D"); test_comp128v3("D27A6AFDA367D22533F4A41A08E728D6", "C0C1517C33F09E91EAC4779A"); test_comp128v3("9DB473E798478DA64AF4ED702F4BC9F2", "3022D423A1C7629E82A5A24E"); test_comp128v3("D9B45909B8FB54224223494EEA6E2E30", "032151B6AB3213BB7DDFD85D"); test_comp128v3("8031C87FC0B6482EE41FDAE9452ECB50", "B6944CB7B74EAB9C478F1C52"); test_comp128v3("D75BDA282D511A8FDA9D2A668C9C8DAF", "2C95EC0DD014289FFB35213F"); test_comp128v3("CEF23CA86FACB24BC87C7A6F8E3B1C88", "18A4524E271A78D2DE5E7499"); test_comp128v3("FF647B9CC719F05B6FC23549E39B1357", "0C865AB29E48C3C797B021D6"); test_comp128v3("27F8DA6420EAD9D9577BB23FBC8505DF", "C3C5015754C0AFA87182B618"); test_comp128v3("FF8354FB4DF2F4DCAD9810E6D6BE2701", "82C123077C870F46B60D29BD"); test_comp128v3("036E56648887DBAA96FFA30E83D8AD82", "B8DCA4880CAD8DB0E51FD51C"); test_comp128v3("86E00865EFAA86388A9005B52F005717", "9241332752219C7BB5A5D3CD"); test_comp128v3("C9629A55E7894963AD2EEC2468FE7DBB", "E1136ACDFFD9517A555B0F27"); test_comp128v3("E09B731993F83B3F5B3870A764951ABE", "9BF47B15A4384C738487A24B"); test_comp128v3("2378E55D7C30DFC2977F4B1DC4F9F5E8", "48EB70F03A3CCB87CD82A158"); test_comp128v3("6184EA341945118E0646CBA5D58384B4", "77675FA63C5BD4A41AC05381"); test_comp128v3("3BFDF98A86FD921FB72992EC14F7BB45", "F538CEDE6978E01EAACBB45B"); test_comp128v3("4456A5A28E7312B13115462951C92AFC", "72A818CF0DC16D1CF61AAB95"); test_comp128v3("FDD3E2786D4F2CFD9480D66573BCFC9C", "2841008AEF343BB28D103C8D"); test_comp128v3("6DD6D61B2289A9410CFD9714CCECD4C8", "F59AB2D1B59D311242978F3F"); test_comp128v3("F167C9441706277D09DDCDD7F7E52A64", "126858DE4A4F5819448B7834"); test_comp128v3("980FF11DA5B60F457D8AD89A85974578", "379707AA8D96C5D76FA14C03"); test_comp128v3("70AAC833D245454AE89349A7446EE506", "5D79BA0B6FFD9631AF1CB4C7"); test_comp128v3("927181350C74438DE3B9E8098A617E9B", "F763FBD8F7F514F54B18A736"); test_comp128v3("CE2FC9FE401D5EEDE241E70202A918BB", "6D93D8C321F8333F0AEA5D7D"); test_comp128v3("D0052E34EB985536E784846E9328E485", "4AE323A62EEF67882838712A"); test_comp128v3("F0258B4E9282FCC224A3D6CD3B03CBD9", "91BE1348792DB54DB4CE8DED"); test_comp128v3("D14CDA342B363D1D643CA37F69459559", "9B5263789B687DA01AD2408F"); test_comp128v3("3D1B125A5450D9985C1FEB85BBC60A50", "36BEC97C697AA64958AC0150"); test_comp128v3("89F95D62593A08D9C22E451C7FB8C5BA", "D57A8B45E9EAA8AB3A514325"); test_comp128v3("5B1FD080E4A66D7057CFADEF6FED9146", "1BCDA51E01D6A6BD34B4447F"); test_comp128v3("AFA91C8D534BF172752D8A5A1E5ABF18", "1171B4FE506D4A96CDDEC1A3"); test_comp128v3("928BCB824199B826E519EADA61A4C251", "EB2F19E5D73C846D8C714D3E"); test_comp128v3("F53692F3FB3982697190167751544A29", "7AD742D7360FA6C3D379B2B7"); test_comp128v3("35E981691DFC03F3594134BBB7666048", "E80AAB73B59F47BF4EE3ACA2"); test_comp128v3("E1C591DED46E4151A2758DEE40739154", "46FE19CD4CFFB3A2A20C2DED"); test_comp128v3("FDDF6A5BB92A46D2E62A4D892D62DA3E", "91A0067C6F79DCDD23291EB7"); test_comp128v3("2D57BDF8192CA18115B7D8475314A217", "9714FD544700667211D17C04"); test_comp128v3("9B362EB227A8F692CBCAAE2A35126D4A", "6A9616C6A43C428F826A47F7"); test_comp128v3("AFA7288D0D58EADB8C9786402A6E64D8", "997C242B49CB8E562D648454"); test_comp128v3("DC52408D8CA5204B565EDC5A0FC1F62A", "EE9CB66B77A507D22BF44138"); test_comp128v3("FCF61DE9035595B243208EC41671C65A", "2FF077600EA3FA5A0D767078"); test_comp128v3("9A583D3FDD3982A15876623AA3030000", "19A047A8A76A867653D4911C"); test_comp128v3("2DCFD0800243D3D0D713A0FE4B1C41C9", "C64FC0E63E24B5990B10208E"); test_comp128v3("72213EB6319FADAE5FFE5DBD4FD3F03E", "8BB15C1A20487D9F61DCBB2A"); test_comp128v3("2895AC8B667126F52244CEC0831D2C11", "179EC0708E768B387560A03E"); test_comp128v3("7091C9ABA945FF32B3CD9818DE84AEE5", "2107211D6BC2DC36418D81EC"); test_comp128v3("219370CB0713962417F979DED2342075", "BF57FBAF865A8BADEA9AABE7"); test_comp128v3("F836740255B8ECB99BA7AEE2CE8C53BB", "A3A5D83BA6B66D2EC39B4035"); test_comp128v3("E31DC4205A93AC332F96CA5E7BC9FCA7", "73483A4971376226A7D00F88"); test_comp128v3("A5128C271F394B9FF303E9D169451808", "E189B4B86326531262853FCB"); test_comp128v3("7F8AB312C58E36A6FF9B80D9FA784D4F", "77BEDAC4AF6EBD00DDCA8663"); test_comp128v3("DD6FAC2574A4358D54BF123B5B2E4D47", "29D903403B472B3968A0D511"); test_comp128v3("85CAE759E11DCD58B02C500C539C1FD2", "EA65F4805AED4E612A71FB27"); return 0; } libosmocore-0.9.0/tests/comp128/comp128_test.ok000066400000000000000000000240521261607044000211560ustar00rootroot00000000000000COMP128v2 support: 2 COMP128v3 support: 3 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK 0 OK libosmocore-0.9.0/tests/conv/000077500000000000000000000000001261607044000161445ustar00rootroot00000000000000libosmocore-0.9.0/tests/conv/conv_test.c000066400000000000000000000366771261607044000203370ustar00rootroot00000000000000#include #include #include #include #include #include #include #define MAX_LEN_BITS 512 #define MAX_LEN_BYTES (512/8) /* ------------------------------------------------------------------------ */ /* Test codes */ /* ------------------------------------------------------------------------ */ /* GSM xCCH -> Non-recursive code, flushed, not punctured */ static const uint8_t conv_gsm_xcch_next_output[][2] = { { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, }; static const uint8_t conv_gsm_xcch_next_state[][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, }; static const struct osmo_conv_code conv_gsm_xcch = { .N = 2, .K = 5, .len = 224, .term = CONV_TERM_FLUSH, .next_output = conv_gsm_xcch_next_output, .next_state = conv_gsm_xcch_next_state, }; /* GSM TCH/AFS 7.95 -> Recursive code, flushed, with puncturing */ static const uint8_t conv_gsm_tch_afs_7_95_next_output[][2] = { { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, }; static const uint8_t conv_gsm_tch_afs_7_95_next_state[][2] = { { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 }, { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 }, { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 }, { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 }, { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 }, { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 }, { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 }, { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 }, { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 }, { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 }, { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 }, { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 }, { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 }, { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 }, { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 }, }; static const uint8_t conv_gsm_tch_afs_7_95_next_term_output[] = { 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0, 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4, 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7, 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3, }; static const uint8_t conv_gsm_tch_afs_7_95_next_term_state[] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, }; static int conv_gsm_tch_afs_7_95_puncture[] = { 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310, 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367, 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415, 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463, 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505, 506, 508, 509, 511, 512, -1, /* end */ }; static const struct osmo_conv_code conv_gsm_tch_afs_7_95 = { .N = 3, .K = 7, .len = 165, .term = CONV_TERM_FLUSH, .next_output = conv_gsm_tch_afs_7_95_next_output, .next_state = conv_gsm_tch_afs_7_95_next_state, .next_term_output = conv_gsm_tch_afs_7_95_next_term_output, .next_term_state = conv_gsm_tch_afs_7_95_next_term_state, .puncture = conv_gsm_tch_afs_7_95_puncture, }; /* GMR-1 TCH3 Speech -> Non recursive code, tail-biting, punctured */ static const uint8_t conv_gmr1_tch3_speech_next_output[][2] = { { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, }; static const uint8_t conv_gmr1_tch3_speech_next_state[][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, }; static const int conv_gmr1_tch3_speech_puncture[] = { 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95, -1, /* end */ }; static const struct osmo_conv_code conv_gmr1_tch3_speech = { .N = 2, .K = 7, .len = 48, .term = CONV_TERM_TAIL_BITING, .next_output = conv_gmr1_tch3_speech_next_output, .next_state = conv_gmr1_tch3_speech_next_state, .puncture = conv_gmr1_tch3_speech_puncture, }; /* WiMax FCH -> Non recursive code, tail-biting, non-punctured */ static const uint8_t conv_wimax_fch_next_output[][2] = { { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 3, 0 }, { 1, 2 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, { 2, 1 }, { 0, 3 }, { 1, 2 }, { 3, 0 }, { 1, 2 }, { 3, 0 }, { 2, 1 }, { 0, 3 }, }; static const uint8_t conv_wimax_fch_next_state[][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, }; static const struct osmo_conv_code conv_wimax_fch = { .N = 2, .K = 7, .len = 48, .term = CONV_TERM_TAIL_BITING, .next_output = conv_wimax_fch_next_output, .next_state = conv_wimax_fch_next_state, }; /* Random code -> Non recursive code, direct truncation, non-punctured */ static const struct osmo_conv_code conv_trunc = { .N = 2, .K = 5, .len = 224, .term = CONV_TERM_TRUNCATION, .next_output = conv_gsm_xcch_next_output, .next_state = conv_gsm_xcch_next_state, }; /* ------------------------------------------------------------------------ */ /* Test vectors */ /* ------------------------------------------------------------------------ */ struct conv_test_vector { const char *name; const struct osmo_conv_code *code; int in_len; int out_len; int has_vec; pbit_t vec_in[MAX_LEN_BYTES]; pbit_t vec_out[MAX_LEN_BYTES]; }; static const struct conv_test_vector tests[] = { { .name = "GSM xCCH (non-recursive, flushed, not punctured)", .code = &conv_gsm_xcch, .in_len = 224, .out_len = 456, .has_vec = 1, .vec_in = { 0xf3, 0x1d, 0xb4, 0x0c, 0x4d, 0x1d, 0x9d, 0xae, 0xc0, 0x0a, 0x42, 0x57, 0x13, 0x60, 0x80, 0x96, 0xef, 0x23, 0x7e, 0x4c, 0x1d, 0x96, 0x24, 0x19, 0x17, 0xf2, 0x44, 0x99 }, .vec_out = { 0xe9, 0x4d, 0x70, 0xab, 0xa2, 0x87, 0xf0, 0xe7, 0x04, 0x14, 0x7c, 0xab, 0xaf, 0x6b, 0xa1, 0x16, 0xeb, 0x30, 0x00, 0xde, 0xc8, 0xfd, 0x0b, 0x85, 0x80, 0x41, 0x4a, 0xcc, 0xd3, 0xc0, 0xd0, 0xb6, 0x26, 0xe5, 0x4e, 0x32, 0x49, 0x69, 0x38, 0x17, 0x33, 0xab, 0xaf, 0xb6, 0xc1, 0x08, 0xf3, 0x9f, 0x8c, 0x75, 0x6a, 0x4e, 0x08, 0xc4, 0x20, 0x5f, 0x8f }, }, { .name = "GSM TCH/AFS 7.95 (recursive, flushed, punctured)", .code = &conv_gsm_tch_afs_7_95, .in_len = 165, .out_len = 448, .has_vec = 1, .vec_in = { 0x87, 0x66, 0xc3, 0x58, 0x09, 0xd4, 0x06, 0x59, 0x10, 0xbf, 0x6b, 0x7f, 0xc8, 0xed, 0x72, 0xaa, 0xc1, 0x3d, 0xf3, 0x1e, 0xb0 }, .vec_out = { 0x92, 0xbc, 0xde, 0xa0, 0xde, 0xbe, 0x01, 0x2f, 0xbe, 0xe4, 0x61, 0x32, 0x4d, 0x4f, 0xdc, 0x41, 0x43, 0x0d, 0x15, 0xe0, 0x23, 0xdd, 0x18, 0x91, 0xe5, 0x36, 0x2d, 0xb7, 0xd9, 0x78, 0xb8, 0xb1, 0xb7, 0xcb, 0x2f, 0xc0, 0x52, 0x8f, 0xe2, 0x8c, 0x6f, 0xa6, 0x79, 0x88, 0xed, 0x0c, 0x2e, 0x9e, 0xa1, 0x5f, 0x45, 0x4a, 0xfb, 0xe6, 0x5a, 0x9c }, }, { .name = "GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured)", .code = &conv_gmr1_tch3_speech, .in_len = 48, .out_len = 72, .has_vec = 1, .vec_in = { 0x4d, 0xcb, 0xfc, 0x72, 0xf4, 0x8c }, .vec_out = { 0xc0, 0x86, 0x63, 0x4b, 0x8b, 0xd4, 0x6a, 0x76, 0xb2 }, }, { .name = "WiMax FCH (non-recursive, tail-biting, not punctured)", .code = &conv_wimax_fch, .in_len = 48, .out_len = 96, .has_vec = 1, .vec_in = { 0xfc, 0xa0, 0xa0, 0xfc, 0xa0, 0xa0 }, .vec_out = { 0x19, 0x42, 0x8a, 0xed, 0x21, 0xed, 0x19, 0x42, 0x8a, 0xed, 0x21, 0xed }, }, { .name = "??? (non-recursive, direct truncation, not punctured)", .code = &conv_trunc, .in_len = 224, .out_len = 448, .has_vec = 1, .vec_in = { 0xe5, 0xe0, 0x85, 0x7e, 0xf7, 0x08, 0x19, 0x5a, 0xb9, 0xad, 0x82, 0x37, 0x98, 0x8b, 0x26, 0xb9, 0x81, 0x26, 0x9c, 0x75, 0xaf, 0xf3, 0xcb, 0x07, 0xac, 0x63, 0xe2, 0x9c, }, .vec_out = { 0xea, 0x3b, 0x55, 0x0c, 0xd3, 0xf7, 0x85, 0x69, 0xe5, 0x79, 0x83, 0xd3, 0xc3, 0x9f, 0xb8, 0x61, 0x21, 0x63, 0x51, 0x18, 0xac, 0xcd, 0x32, 0x49, 0x53, 0x5c, 0x13, 0x1d, 0xbe, 0x05, 0x11, 0x63, 0x5c, 0xc3, 0x42, 0x05, 0x1c, 0x68, 0x0a, 0xb4, 0x61, 0x15, 0xaa, 0x4d, 0x94, 0xed, 0xb3, 0x3a, 0x5d, 0x1b, 0x09, 0xc2, 0x99, 0x01, 0xec, 0x68 }, }, { /* end */ }, }; /* ------------------------------------------------------------------------ */ /* Main */ /* ------------------------------------------------------------------------ */ static void fill_random(ubit_t *b, int n) { int i; for (i=0; iname; tst++) { int i,l; /* Test name */ printf("[+] Testing: %s\n", tst->name); /* Check length */ l = osmo_conv_get_input_length(tst->code, 0); printf("[.] Input length : ret = %3d exp = %3d -> %s\n", l, tst->in_len, l == tst->in_len ? "OK" : "Bad !"); if (l != tst->in_len) { fprintf(stderr, "[!] Failure for input length computation\n"); return -1; } l = osmo_conv_get_output_length(tst->code, 0); printf("[.] Output length : ret = %3d exp = %3d -> %s\n", l, tst->out_len, l == tst->out_len ? "OK" : "Bad !"); if (l != tst->out_len) { fprintf(stderr, "[!] Failure for output length computation\n"); return -1; } /* Check pre-computed vector */ if (tst->has_vec) { printf("[.] Pre computed vector checks:\n"); printf("[..] Encoding: "); osmo_pbit2ubit(bu0, tst->vec_in, tst->in_len); l = osmo_conv_encode(tst->code, bu0, bu1); if (l != tst->out_len) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed encoding length check\n"); return -1; } osmo_pbit2ubit(bu0, tst->vec_out, tst->out_len); if (memcmp(bu0, bu1, tst->out_len)) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed encoding: Results don't match\n"); return -1; }; printf("OK\n"); printf("[..] Decoding: "); ubit_to_sbit(bs, bu0, l); l = osmo_conv_decode(tst->code, bs, bu1); if (l != 0) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed decoding: non-zero path (%d)\n", l); return -1; } osmo_pbit2ubit(bu0, tst->vec_in, tst->in_len); if (memcmp(bu0, bu1, tst->in_len)) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed decoding: Results don't match\n"); return -1; } printf("OK\n"); } /* Check random vector */ printf("[.] Random vector checks:\n"); for (i=0; i<3; i++) { printf("[..] Encoding / Decoding cycle : "); fill_random(bu0, tst->in_len); l = osmo_conv_encode(tst->code, bu0, bu1); if (l != tst->out_len) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed encoding length check\n"); return -1; } ubit_to_sbit(bs, bu1, l); l = osmo_conv_decode(tst->code, bs, bu1); if (l != 0) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed decoding: non-zero path (%d)\n", l); return -1; } if (memcmp(bu0, bu1, tst->in_len)) { printf("ERROR !\n"); fprintf(stderr, "[!] Failed decoding: Results don't match\n"); return -1; } printf("OK\n"); } /* Spacing */ printf("\n"); } free(bs); free(bu1); free(bu0); return 0; } libosmocore-0.9.0/tests/conv/conv_test.ok000066400000000000000000000034261261607044000205100ustar00rootroot00000000000000[+] Testing: GSM xCCH (non-recursive, flushed, not punctured) [.] Input length : ret = 224 exp = 224 -> OK [.] Output length : ret = 456 exp = 456 -> OK [.] Pre computed vector checks: [..] Encoding: OK [..] Decoding: OK [.] Random vector checks: [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [+] Testing: GSM TCH/AFS 7.95 (recursive, flushed, punctured) [.] Input length : ret = 165 exp = 165 -> OK [.] Output length : ret = 448 exp = 448 -> OK [.] Pre computed vector checks: [..] Encoding: OK [..] Decoding: OK [.] Random vector checks: [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [+] Testing: GMR-1 TCH3 Speech (non-recursive, tail-biting, punctured) [.] Input length : ret = 48 exp = 48 -> OK [.] Output length : ret = 72 exp = 72 -> OK [.] Pre computed vector checks: [..] Encoding: OK [..] Decoding: OK [.] Random vector checks: [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [+] Testing: WiMax FCH (non-recursive, tail-biting, not punctured) [.] Input length : ret = 48 exp = 48 -> OK [.] Output length : ret = 96 exp = 96 -> OK [.] Pre computed vector checks: [..] Encoding: OK [..] Decoding: OK [.] Random vector checks: [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [+] Testing: ??? (non-recursive, direct truncation, not punctured) [.] Input length : ret = 224 exp = 224 -> OK [.] Output length : ret = 448 exp = 448 -> OK [.] Pre computed vector checks: [..] Encoding: OK [..] Decoding: OK [.] Random vector checks: [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK [..] Encoding / Decoding cycle : OK libosmocore-0.9.0/tests/fr/000077500000000000000000000000001261607044000156065ustar00rootroot00000000000000libosmocore-0.9.0/tests/fr/fr_test.c000066400000000000000000000035341261607044000174250ustar00rootroot00000000000000/* * (C) 2012 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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #define _GNU_SOURCE #include #include #include #include #include #include #include int (*real_socket)(int, int, int); static int GR_SOCKET = -1; static void resolve_real(void) { if (real_socket) return; real_socket = dlsym(RTLD_NEXT, "socket"); } int socket(int domain, int type, int protocol) { int fd; resolve_real(); if (domain != AF_INET || type != SOCK_RAW || protocol != IPPROTO_GRE) return (*real_socket)(domain, type, protocol); /* Now call socket with a normal UDP/IP socket and assign to GR_SOCKET */ fd = (*real_socket)(domain, SOCK_DGRAM, IPPROTO_UDP); GR_SOCKET = fd; return fd; } void bssgp_prim_cb() { } static const struct log_info log_info = {}; int main(int argc, char **argv) { int rc; struct gprs_ns_inst *nsi; log_init(&log_info, NULL); nsi = gprs_ns_instantiate(NULL, NULL); nsi->frgre.enabled = 1; rc = gprs_ns_frgre_listen(nsi); printf("Result: %s\n", rc == GR_SOCKET ? "PASSED" : "FAILED"); return rc == GR_SOCKET ? EXIT_SUCCESS : EXIT_FAILURE; } libosmocore-0.9.0/tests/fr/fr_test.err000066400000000000000000000000001261607044000177540ustar00rootroot00000000000000libosmocore-0.9.0/tests/fr/fr_test.ok000066400000000000000000000000171261607044000176050ustar00rootroot00000000000000Result: PASSED libosmocore-0.9.0/tests/gb/000077500000000000000000000000001261607044000155675ustar00rootroot00000000000000libosmocore-0.9.0/tests/gb/bssgp_fc_test.c000066400000000000000000000102451261607044000205620ustar00rootroot00000000000000/* test routines for BSSGP flow control implementation in libosmogb * (C) 2012 by Harald Welte */ #include #include #include #include #include #include #include #include #include #include #include static unsigned long in_ctr = 1; static struct timeval tv_start; int get_centisec_diff(void) { struct timeval tv; struct timeval now; gettimeofday(&now, NULL); timersub(&now, &tv_start, &tv); return tv.tv_sec * 100 + tv.tv_usec/10000; } /* round to deciseconds to make sure test output is always consistent */ int round_decisec(int csec_in) { int tmp = csec_in / 10; return tmp * 10; } static int fc_out_cb(struct bssgp_flow_control *fc, struct msgb *msg, uint32_t llc_pdu_len, void *priv) { unsigned int csecs = get_centisec_diff(); csecs = round_decisec(csecs); printf("%u: FC OUT Nr %lu\n", csecs, (unsigned long) msg->cb[0]); msgb_free(msg); return 0; } static int fc_in(struct bssgp_flow_control *fc, unsigned int pdu_len) { struct msgb *msg; unsigned int csecs = get_centisec_diff(); csecs = round_decisec(csecs); msg = msgb_alloc(1, "fc test"); msg->cb[0] = in_ctr++; printf("%u: FC IN Nr %lu\n", csecs, msg->cb[0]); bssgp_fc_in(fc, msg, pdu_len, NULL); return 0; } static void test_fc(uint32_t bucket_size_max, uint32_t bucket_leak_rate, uint32_t max_queue_depth, uint32_t pdu_len, uint32_t pdu_count) { struct bssgp_flow_control *fc = talloc_zero(NULL, struct bssgp_flow_control); int i; bssgp_fc_init(fc, bucket_size_max, bucket_leak_rate, max_queue_depth, fc_out_cb); gettimeofday(&tv_start, NULL); for (i = 0; i < pdu_count; i++) { fc_in(fc, pdu_len); osmo_timers_check(); osmo_timers_prepare(); osmo_timers_update(); } while (1) { usleep(100000); osmo_timers_check(); osmo_timers_prepare(); osmo_timers_update(); if (llist_empty(&fc->queue)) break; } } static void help(void) { printf(" -h --help This help message\n"); printf(" -s --bucket-size-max N Maximum size of bucket in octets\n"); printf(" -r --bucket-leak-rate N Bucket leak rate in octets/sec\n"); printf(" -d --max-queue-depth N Maximum length of pending PDU queue (msgs)\n"); printf(" -l --pdu-length N Length of each PDU in octets\n"); } int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { return -1; } static struct log_info info = {}; int main(int argc, char **argv) { uint32_t bucket_size_max = 100; /* octets */ uint32_t bucket_leak_rate = 100; /* octets / second */ uint32_t max_queue_depth = 5; /* messages */ uint32_t pdu_length = 10; /* octets */ uint32_t pdu_count = 20; /* messages */ int c; static const struct option long_options[] = { { "bucket-size-max", 1, 0, 's' }, { "bucket-leak-rate", 1, 0, 'r' }, { "max-queue-depth", 1, 0, 'd' }, { "pdu-length", 1, 0, 'l' }, { "pdu-count", 1, 0, 'c' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; osmo_init_logging(&info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); while ((c = getopt_long(argc, argv, "s:r:d:l:c:", long_options, NULL)) != -1) { switch (c) { case 's': bucket_size_max = atoi(optarg); break; case 'r': bucket_leak_rate = atoi(optarg); break; case 'd': max_queue_depth = atoi(optarg); break; case 'l': pdu_length = atoi(optarg); break; case 'c': pdu_count = atoi(optarg); break; case 'h': help(); exit(EXIT_SUCCESS); break; default: exit(EXIT_FAILURE); } } /* bucket leak rate less than 100 not supported! */ if (bucket_leak_rate < 100) { fprintf(stderr, "Bucket leak rate < 100 not supported!\n"); exit(EXIT_FAILURE); } printf("===== BSSGP flow-control test START\n"); printf("size-max=%u oct, leak-rate=%u oct/s, " "queue-len=%u msgs, pdu_len=%u oct, pdu_cnt=%u\n\n", bucket_size_max, bucket_leak_rate, max_queue_depth, pdu_length, pdu_count); test_fc(bucket_size_max, bucket_leak_rate, max_queue_depth, pdu_length, pdu_count); printf("===== BSSGP flow-control test END\n\n"); exit(EXIT_SUCCESS); } libosmocore-0.9.0/tests/gb/bssgp_fc_tests.err000066400000000000000000000024241261607044000213130ustar00rootroot00000000000000Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! Single PDU (size=1000) is larger than maximum bucket size (100)! libosmocore-0.9.0/tests/gb/bssgp_fc_tests.ok000066400000000000000000000050321261607044000211320ustar00rootroot00000000000000===== BSSGP flow-control test START size-max=100 oct, leak-rate=100 oct/s, queue-len=5 msgs, pdu_len=10 oct, pdu_cnt=20 0: FC IN Nr 1 0: FC OUT Nr 1 0: FC IN Nr 2 0: FC OUT Nr 2 0: FC IN Nr 3 0: FC OUT Nr 3 0: FC IN Nr 4 0: FC OUT Nr 4 0: FC IN Nr 5 0: FC OUT Nr 5 0: FC IN Nr 6 0: FC OUT Nr 6 0: FC IN Nr 7 0: FC OUT Nr 7 0: FC IN Nr 8 0: FC OUT Nr 8 0: FC IN Nr 9 0: FC OUT Nr 9 0: FC IN Nr 10 0: FC OUT Nr 10 0: FC IN Nr 11 0: FC IN Nr 12 0: FC IN Nr 13 0: FC IN Nr 14 0: FC IN Nr 15 0: FC IN Nr 16 0: FC IN Nr 17 0: FC IN Nr 18 0: FC IN Nr 19 0: FC IN Nr 20 10: FC OUT Nr 11 20: FC OUT Nr 12 30: FC OUT Nr 13 40: FC OUT Nr 14 50: FC OUT Nr 15 ===== BSSGP flow-control test END ===== BSSGP flow-control test START size-max=100 oct, leak-rate=100 oct/s, queue-len=100 msgs, pdu_len=10 oct, pdu_cnt=20 0: FC IN Nr 1 0: FC OUT Nr 1 0: FC IN Nr 2 0: FC OUT Nr 2 0: FC IN Nr 3 0: FC OUT Nr 3 0: FC IN Nr 4 0: FC OUT Nr 4 0: FC IN Nr 5 0: FC OUT Nr 5 0: FC IN Nr 6 0: FC OUT Nr 6 0: FC IN Nr 7 0: FC OUT Nr 7 0: FC IN Nr 8 0: FC OUT Nr 8 0: FC IN Nr 9 0: FC OUT Nr 9 0: FC IN Nr 10 0: FC OUT Nr 10 0: FC IN Nr 11 0: FC IN Nr 12 0: FC IN Nr 13 0: FC IN Nr 14 0: FC IN Nr 15 0: FC IN Nr 16 0: FC IN Nr 17 0: FC IN Nr 18 0: FC IN Nr 19 0: FC IN Nr 20 10: FC OUT Nr 11 20: FC OUT Nr 12 30: FC OUT Nr 13 40: FC OUT Nr 14 50: FC OUT Nr 15 60: FC OUT Nr 16 70: FC OUT Nr 17 80: FC OUT Nr 18 90: FC OUT Nr 19 100: FC OUT Nr 20 ===== BSSGP flow-control test END ===== BSSGP flow-control test START size-max=100 oct, leak-rate=100 oct/s, queue-len=5 msgs, pdu_len=1000 oct, pdu_cnt=20 0: FC IN Nr 1 0: FC IN Nr 2 0: FC IN Nr 3 0: FC IN Nr 4 0: FC IN Nr 5 0: FC IN Nr 6 0: FC IN Nr 7 0: FC IN Nr 8 0: FC IN Nr 9 0: FC IN Nr 10 0: FC IN Nr 11 0: FC IN Nr 12 0: FC IN Nr 13 0: FC IN Nr 14 0: FC IN Nr 15 0: FC IN Nr 16 0: FC IN Nr 17 0: FC IN Nr 18 0: FC IN Nr 19 0: FC IN Nr 20 ===== BSSGP flow-control test END ===== BSSGP flow-control test START size-max=100 oct, leak-rate=100 oct/s, queue-len=5 msgs, pdu_len=10 oct, pdu_cnt=20 0: FC IN Nr 1 0: FC OUT Nr 1 0: FC IN Nr 2 0: FC OUT Nr 2 0: FC IN Nr 3 0: FC OUT Nr 3 0: FC IN Nr 4 0: FC OUT Nr 4 0: FC IN Nr 5 0: FC OUT Nr 5 0: FC IN Nr 6 0: FC OUT Nr 6 0: FC IN Nr 7 0: FC OUT Nr 7 0: FC IN Nr 8 0: FC OUT Nr 8 0: FC IN Nr 9 0: FC OUT Nr 9 0: FC IN Nr 10 0: FC OUT Nr 10 0: FC IN Nr 11 0: FC IN Nr 12 0: FC IN Nr 13 0: FC IN Nr 14 0: FC IN Nr 15 0: FC IN Nr 16 0: FC IN Nr 17 0: FC IN Nr 18 0: FC IN Nr 19 0: FC IN Nr 20 10: FC OUT Nr 11 20: FC OUT Nr 12 30: FC OUT Nr 13 40: FC OUT Nr 14 50: FC OUT Nr 15 ===== BSSGP flow-control test END libosmocore-0.9.0/tests/gb/bssgp_fc_tests.sh000077500000000000000000000003711261607044000211370ustar00rootroot00000000000000#!/bin/sh T=$1/bssgp_fc_test # default test (1 second, insufficient queue depth) $T # default test (1 second, sufficient queue depth) $T -d 100 # test with PDU too large for bucket max $T -l 1000 # test with 100 byte PDUs (10 second) $T -s 100 libosmocore-0.9.0/tests/gb/gprs_bssgp_test.c000066400000000000000000000164051261607044000211510ustar00rootroot00000000000000/* Test routines for the BSSGP implementation in libosmogb * * (C) 2014 by sysmocom s.f.m.c. GmbH * Author: Jacob Erlbeck * * Skeleton based on bssgp_fc_test.c * (C) 2012 by Harald Welte */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BSS_NSEI 0x0b55 static struct osmo_prim_hdr last_oph = {0}; /* override */ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { typedef ssize_t (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static sendto_t real_sendto = NULL; uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); if (!real_sendto) real_sendto = dlsym(RTLD_NEXT, "sendto"); fprintf(stderr, "MESSAGE to 0x%08x, msg length %d\n%s\n", dest_host, len, osmo_hexdump(buf, len)); return len; } /* override */ int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { fprintf(stderr, "CALLBACK, event %d, msg length %d, bvci 0x%04x\n%s\n\n", event, msgb_bssgp_len(msg), bvci, osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg))); return 0; } struct msgb *last_ns_tx_msg = NULL; /* override */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { msgb_free(last_ns_tx_msg); last_ns_tx_msg = msg; return msgb_length(msg); } int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { printf("BSSGP primitive, SAP %d, prim = %d, op = %d, msg = %s\n", oph->sap, oph->primitive, oph->operation, msgb_hexdump(oph->msg)); last_oph.sap = oph->sap; last_oph.primitive = oph->primitive; last_oph.operation = oph->operation; last_oph.msg = NULL; return -1; } static void msgb_bssgp_send_and_free(struct msgb *msg) { msgb_nsei(msg) = BSS_NSEI; bssgp_rcvmsg(msg); msgb_free(msg); } static void send_bssgp_supend(enum bssgp_pdu_type pdu_type, uint32_t tlli) { struct msgb *msg = bssgp_msgb_alloc(); uint32_t tlli_be = htonl(tlli); uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; msgb_v_put(msg, pdu_type); msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]); msgb_bssgp_send_and_free(msg); } static void send_bssgp_resume(enum bssgp_pdu_type pdu_type, uint32_t tlli) { struct msgb *msg = bssgp_msgb_alloc(); uint32_t tlli_be = htonl(tlli); uint8_t rai[] = {0x0f, 0xf1, 0x80, 0x20, 0x37, 0x00}; uint8_t suspend_ref = 1; msgb_v_put(msg, pdu_type); msgb_tvlv_put(msg, BSSGP_IE_TLLI, sizeof(tlli_be), (uint8_t *)&tlli_be); msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, sizeof(rai), &rai[0]); msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); msgb_bssgp_send_and_free(msg); } static void test_bssgp_suspend_resume(void) { const uint32_t tlli = 0xf0123456; printf("----- %s START\n", __func__); memset(&last_oph, 0, sizeof(last_oph)); send_bssgp_supend(BSSGP_PDUT_SUSPEND, tlli); OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_SUSPEND); send_bssgp_resume(BSSGP_PDUT_RESUME, tlli); OSMO_ASSERT(last_oph.primitive == PRIM_BSSGP_GMM_RESUME); printf("----- %s END\n", __func__); } static void send_bssgp_status(enum gprs_bssgp_cause cause, uint16_t *bvci) { struct msgb *msg = bssgp_msgb_alloc(); uint8_t cause_ = cause; msgb_v_put(msg, BSSGP_PDUT_STATUS); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause_); if (bvci) { uint16_t bvci_ = htons(*bvci); msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &bvci_); } msgb_bssgp_send_and_free(msg); } static void test_bssgp_status(void) { uint16_t bvci; printf("----- %s START\n", __func__); send_bssgp_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL); OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS); /* Enforce prim != PRIM_NM_STATUS */ last_oph.primitive = PRIM_NM_LLC_DISCARDED; bvci = 1234; send_bssgp_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci); OSMO_ASSERT(last_oph.primitive == PRIM_NM_STATUS); printf("----- %s END\n", __func__); } static void test_bssgp_bad_reset() { struct msgb *msg; uint16_t bvci_be = htons(2); uint8_t cause = BSSGP_CAUSE_OML_INTERV; printf("----- %s START\n", __func__); msg = bssgp_msgb_alloc(); msgb_v_put(msg, BSSGP_PDUT_BVC_RESET); msgb_tvlv_put(msg, BSSGP_IE_BVCI, sizeof(bvci_be), (uint8_t *)&bvci_be); msgb_tvlv_put(msg, BSSGP_IE_CAUSE, sizeof(cause), &cause); msgb_bvci(msg) = 0xbad; msgb_bssgp_send_and_free(msg); printf("----- %s END\n", __func__); } static void test_bssgp_flow_control_bvc(void) { struct bssgp_bvc_ctx bctx = { .nsei = 0x1234, .bvci = 0x5678, }; const uint8_t tag = 42; const uint32_t bmax = 0x1022 * 100; const uint32_t rate = 0xc040 / 8 * 100; const uint32_t bmax_ms = bmax / 2; const uint32_t rate_ms = rate / 2; uint8_t ratio = 0x78; uint32_t qdelay = 0x1144 * 10; int rc; static uint8_t expected_simple_msg[] = { 0x26, 0x1e, 0x81, 0x2a, /* tag */ 0x05, 0x82, 0x10, 0x22, /* Bmax */ 0x03, 0x82, 0xc0, 0x40, /* R */ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */ 0x1c, 0x82, 0x60, 0x20, /* R_MS */ }; static uint8_t expected_ext_msg[] = { 0x26, 0x1e, 0x81, 0x2a, /* tag */ 0x05, 0x82, 0x10, 0x22, /* Bmax */ 0x03, 0x82, 0xc0, 0x40, /* R */ 0x01, 0x82, 0x08, 0x11, /* Bmax_MS */ 0x1c, 0x82, 0x60, 0x20, /* R_MS */ 0x3c, 0x81, 0x78, /* ratio */ 0x06, 0x82, 0x11, 0x44, /* Qdelay */ }; printf("----- %s START\n", __func__); rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms, NULL, NULL); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_ns_tx_msg != NULL); printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg)); OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_simple_msg)); OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg), expected_simple_msg, sizeof(expected_simple_msg))); rc = bssgp_tx_fc_bvc(&bctx, tag, bmax, rate, bmax_ms, rate_ms, &ratio, &qdelay); OSMO_ASSERT(rc >= 0); OSMO_ASSERT(last_ns_tx_msg != NULL); printf("Got message: %s\n", msgb_hexdump(last_ns_tx_msg)); OSMO_ASSERT(msgb_length(last_ns_tx_msg) == sizeof(expected_ext_msg)); OSMO_ASSERT(0 == memcmp(msgb_data(last_ns_tx_msg), expected_ext_msg, sizeof(expected_ext_msg))); msgb_free(last_ns_tx_msg); last_ns_tx_msg = NULL; printf("----- %s END\n", __func__); } static struct log_info info = {}; int main(int argc, char **argv) { struct sockaddr_in bss_peer= {0}; osmo_init_logging(&info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); bssgp_nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); bss_peer.sin_family = AF_INET; bss_peer.sin_port = htons(32000); bss_peer.sin_addr.s_addr = htonl(0x7f0000ff); gprs_ns_nsip_connect(bssgp_nsi, &bss_peer, BSS_NSEI, BSS_NSEI+1); printf("===== BSSGP test START\n"); test_bssgp_suspend_resume(); test_bssgp_status(); test_bssgp_bad_reset(); test_bssgp_flow_control_bvc(); printf("===== BSSGP test END\n\n"); exit(EXIT_SUCCESS); } libosmocore-0.9.0/tests/gb/gprs_bssgp_test.ok000066400000000000000000000015211261607044000213310ustar00rootroot00000000000000===== BSSGP test START ----- test_bssgp_suspend_resume START BSSGP primitive, SAP 16777219, prim = 3, op = 0, msg = 0b 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00 BSSGP primitive, SAP 16777219, prim = 4, op = 0, msg = 0e 1f 84 f0 12 34 56 1b 86 0f f1 80 20 37 00 1d 81 01 ----- test_bssgp_suspend_resume END ----- test_bssgp_status START BSSGP primitive, SAP 16777221, prim = 11, op = 2, msg = 41 07 81 27 BSSGP primitive, SAP 16777221, prim = 11, op = 2, msg = 41 07 81 05 04 82 04 d2 ----- test_bssgp_status END ----- test_bssgp_bad_reset START ----- test_bssgp_bad_reset END ----- test_bssgp_flow_control_bvc START Got message: 26 1e 81 2a 05 82 10 22 03 82 c0 40 01 82 08 11 1c 82 60 20 Got message: 26 1e 81 2a 05 82 10 22 03 82 c0 40 01 82 08 11 1c 82 60 20 3c 81 78 06 82 11 44 ----- test_bssgp_flow_control_bvc END ===== BSSGP test END libosmocore-0.9.0/tests/gb/gprs_ns_test.c000066400000000000000000000634261261607044000204600ustar00rootroot00000000000000/* test routines for NS connection handling * (C) 2013 by sysmocom s.f.m.c. GmbH * Author: Jacob Erlbeck */ #undef _GNU_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define REMOTE_BSS_ADDR 0x01020304 #define REMOTE_SGSN_ADDR 0x05060708 #define SGSN_NSEI 0x0100 static int sent_pdu_type = 0; static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len); static void send_ns_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, enum ns_cause cause, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET, */ unsigned char msg[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = cause; msg[6] = nsvci / 256; msg[7] = nsvci % 256; msg[10] = nsei / 256; msg[11] = nsei % 256; gprs_process_message(nsi, "RESET", src_addr, msg, sizeof(msg)); } static void send_ns_reset_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { /* GPRS Network Service, PDU type: NS_RESET_ACK, */ unsigned char msg[9] = { 0x03, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; msg[3] = nsvci / 256; msg[4] = nsvci % 256; msg[7] = nsei / 256; msg[8] = nsei % 256; gprs_process_message(nsi, "RESET_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_alive(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE */ unsigned char msg[1] = { 0x0a }; gprs_process_message(nsi, "ALIVE", src_addr, msg, sizeof(msg)); } static void send_ns_alive_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_ALIVE_ACK */ unsigned char msg[1] = { 0x0b }; gprs_process_message(nsi, "ALIVE_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK */ unsigned char msg[1] = { 0x06 }; gprs_process_message(nsi, "UNBLOCK", src_addr, msg, sizeof(msg)); } static void send_ns_unblock_ack(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr) { /* GPRS Network Service, PDU type: NS_UNBLOCK_ACK */ unsigned char msg[1] = { 0x07 }; gprs_process_message(nsi, "UNBLOCK_ACK", src_addr, msg, sizeof(msg)); } static void send_ns_unitdata(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsbvci, const unsigned char *bssgp_msg, size_t bssgp_msg_size) { /* GPRS Network Service, PDU type: NS_UNITDATA */ unsigned char msg[4096] = { 0x00, 0x00, 0x00, 0x00 }; OSMO_ASSERT(bssgp_msg_size <= sizeof(msg) - 4); msg[2] = nsbvci / 256; msg[3] = nsbvci % 256; memcpy(msg + 4, bssgp_msg, bssgp_msg_size); gprs_process_message(nsi, "UNITDATA", src_addr, msg, bssgp_msg_size + 4); } static void send_bssgp_reset(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 * BSSGP RESET */ unsigned char msg[22] = { 0x22, 0x04, 0x82, 0x4a, 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x10, 0x00 }; msg[3] = bvci / 256; msg[4] = bvci % 256; send_ns_unitdata(nsi, src_addr, 0, msg, sizeof(msg)); } static void setup_ns(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t nsvci, uint16_t nsei) { printf("Setup NS-VC: remote 0x%08x:%d, " "NSVCI 0x%04x(%d), NSEI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), nsvci, nsvci, nsei, nsei); send_ns_reset(nsi, src_addr, NS_CAUSE_OM_INTERVENTION, nsvci, nsei); send_ns_alive(nsi, src_addr); send_ns_unblock(nsi, src_addr); send_ns_alive_ack(nsi, src_addr); } static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) __attribute__((__unused__)); static void setup_bssgp(struct gprs_ns_inst *nsi, struct sockaddr_in *src_addr, uint16_t bvci) { printf("Setup BSSGP: remote 0x%08x:%d, " "BVCI 0x%04x(%d)\n\n", ntohl(src_addr->sin_addr.s_addr), ntohs(src_addr->sin_port), bvci, bvci); send_bssgp_reset(nsi, src_addr, bvci); } /* GPRS Network Service, PDU type: NS_RESET, * Cause: O&M intervention, NS VCI: 0x1122, NSEI 0x1122 */ static const unsigned char gprs_ns_reset[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; /* GPRS Network Service, PDU type: NS_RESET, * Cause: O&M intervention, NS VCI: 0x3344, NSEI 0x1122 */ static const unsigned char gprs_ns_reset_vci2[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x33, 0x44, 0x04, 0x82, 0x11, 0x22 }; /* GPRS Network Service, PDU type: NS_RESET, * Cause: O&M intervention, NS VCI: 0x1122, NSEI 0x3344 */ static const unsigned char gprs_ns_reset_nsei2[12] = { 0x02, 0x00, 0x81, 0x01, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x33, 0x44 }; /* GPRS Network Service, PDU type: NS_ALIVE */ static const unsigned char gprs_ns_alive[1] = { 0x0a }; /* GPRS Network Service, PDU type: NS_STATUS, * Cause: PDU not compatible with the protocol state * PDU: NS_ALIVE */ static const unsigned char gprs_ns_status_invalid_alive[7] = { 0x08, 0x00, 0x81, 0x0a, 0x02, 0x81, 0x0a }; /* GPRS Network Service, PDU type: NS_ALIVE_ACK */ static const unsigned char gprs_ns_alive_ack[1] = { 0x0b }; /* GPRS Network Service, PDU type: NS_UNBLOCK */ static const unsigned char gprs_ns_unblock[1] = { 0x06 }; /* GPRS Network Service, PDU type: NS_STATUS, * Cause: PDU not compatible with the protocol state * PDU: NS_RESET_ACK, NS VCI: 0x1122, NSEI 0x1122 */ static const unsigned char gprs_ns_status_invalid_reset_ack[15] = { 0x08, 0x00, 0x81, 0x0a, 0x02, 0x89, 0x03, 0x01, 0x82, 0x11, 0x22, 0x04, 0x82, 0x11, 0x22 }; /* GPRS Network Service, PDU type: NS_UNITDATA, BVCI 0 */ static const unsigned char gprs_bssgp_reset[22] = { 0x00, 0x00, 0x00, 0x00, 0x22, 0x04, 0x82, 0x4a, 0x2e, 0x07, 0x81, 0x08, 0x08, 0x88, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x10, 0x00 }; int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, struct sockaddr_in *saddr, enum gprs_ns_ll ll); /* override */ int gprs_ns_callback(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci) { printf("CALLBACK, event %d, msg length %d, bvci 0x%04x\n%s\n\n", event, msgb_bssgp_len(msg), bvci, osmo_hexdump(msgb_bssgph(msg), msgb_bssgp_len(msg))); return 0; } /* override */ ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { typedef ssize_t (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); static sendto_t real_sendto = NULL; uint32_t dest_host = htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr); if (!real_sendto) real_sendto = dlsym(RTLD_NEXT, "sendto"); sent_pdu_type = len > 0 ? ((uint8_t *)buf)[0] : -1; if (dest_host == REMOTE_BSS_ADDR) printf("MESSAGE to BSS, msg length %d\n%s\n\n", len, osmo_hexdump(buf, len)); else if (dest_host == REMOTE_SGSN_ADDR) printf("MESSAGE to SGSN, msg length %d\n%s\n\n", len, osmo_hexdump(buf, len)); else return real_sendto(sockfd, buf, len, flags, dest_addr, addrlen); return len; } /* override */ int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) { typedef int (*gprs_ns_sendmsg_t)(struct gprs_ns_inst *nsi, struct msgb *msg); static gprs_ns_sendmsg_t real_gprs_ns_sendmsg = NULL; uint16_t bvci = msgb_bvci(msg); uint16_t nsei = msgb_nsei(msg); unsigned char *buf = msg->data; size_t len = msg->len; if (!real_gprs_ns_sendmsg) real_gprs_ns_sendmsg = dlsym(RTLD_NEXT, "gprs_ns_sendmsg"); if (nsei == SGSN_NSEI) printf("NS UNITDATA MESSAGE to SGSN, BVCI 0x%04x, msg length %d\n%s\n\n", bvci, len, osmo_hexdump(buf, len)); else printf("NS UNITDATA MESSAGE to BSS, BVCI 0x%04x, msg length %d\n%s\n\n", bvci, len, osmo_hexdump(buf, len)); return real_gprs_ns_sendmsg(nsi, msg); } static void dump_rate_ctr_group(FILE *stream, const char *prefix, struct rate_ctr_group *ctrg) { unsigned int i; for (i = 0; i < ctrg->desc->num_ctr; i++) { struct rate_ctr *ctr = &ctrg->ctr[i]; if (ctr->current && !strchr(ctrg->desc->ctr_desc[i].name, '.')) fprintf(stream, " %s%s: %llu%s", prefix, ctrg->desc->ctr_desc[i].description, (long long)ctr->current, "\n"); }; } /* Signal handler for signals from NS layer */ static int test_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ns_signal_data *nssd = signal_data; if (subsys != SS_L_NS) return 0; switch (signal) { case S_NS_RESET: printf("==> got signal NS_RESET, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_ALIVE_EXP: printf("==> got signal NS_ALIVE_EXP, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_BLOCK: printf("==> got signal NS_BLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_UNBLOCK: printf("==> got signal NS_UNBLOCK, NS-VC 0x%04x/%s\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; case S_NS_REPLACED: printf("==> got signal NS_REPLACED: 0x%04x/%s", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); printf(" -> 0x%04x/%s\n", nssd->old_nsvc->nsvci, gprs_ns_ll_str(nssd->old_nsvc)); break; case S_NS_MISMATCH: printf("==> got signal NS_MISMATCH: 0x%04x/%s pdu=%d, ie=%d\n", nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc), nssd->pdu_type, nssd->ie_type); break; default: printf("==> got signal %d, NS-VC 0x%04x/%s\n", signal, nssd->nsvc->nsvci, gprs_ns_ll_str(nssd->nsvc)); break; } return 0; } static int gprs_process_message(struct gprs_ns_inst *nsi, const char *text, struct sockaddr_in *peer, const unsigned char* data, size_t data_len) { struct msgb *msg; int ret; if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) { fprintf(stderr, "message too long: %d\n", data_len); return -1; } msg = gprs_ns_msgb_alloc(); memmove(msg->data, data, data_len); msg->l2h = msg->data; msgb_put(msg, data_len); printf("PROCESSING %s from 0x%08x:%d\n%s\n\n", text, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), osmo_hexdump(data, data_len)); ret = gprs_ns_rcvmsg(nsi, msg, peer, GPRS_NS_LL_UDP); printf("result (%s) = %d\n\n", text, ret); msgb_free(msg); return ret; } static int gprs_send_message(struct gprs_ns_inst *nsi, const char *text, uint16_t nsei, uint16_t bvci, const unsigned char* data, size_t data_len) { struct msgb *msg; int ret; if (data_len > NS_ALLOC_SIZE - NS_ALLOC_HEADROOM) { fprintf(stderr, "message too long: %d\n", data_len); return -1; } msg = gprs_ns_msgb_alloc(); memmove(msg->data, data, data_len); msg->l2h = msg->data; msgb_put(msg, data_len); msgb_nsei(msg) = nsei; msgb_bvci(msg) = bvci; printf("SENDING %s to NSEI 0x%04x, BVCI 0x%04x\n", text, nsei, bvci); ret = gprs_ns_sendmsg(nsi, msg); printf("result (%s) = %d\n\n", text, ret); return ret; } static void gprs_dump_nsi(struct gprs_ns_inst *nsi) { struct gprs_nsvc *nsvc; printf("Current NS-VCIs:\n"); llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { struct sockaddr_in *peer = &(nsvc->ip.bts_addr); printf(" VCI 0x%04x, NSEI 0x%04x, peer 0x%08x:%d%s%s%s\n", nsvc->nsvci, nsvc->nsei, ntohl(peer->sin_addr.s_addr), ntohs(peer->sin_port), nsvc->state & NSE_S_BLOCKED ? ", blocked" : "", nsvc->state & NSE_S_ALIVE ? "" : ", dead", nsvc->nsvci_is_valid ? "" : ", invalid VCI" ); dump_rate_ctr_group(stdout, " ", nsvc->ctrg); } printf("\n"); } static int expire_nsvc_timer(struct gprs_nsvc *nsvc) { int rc; if (!osmo_timer_pending(&nsvc->timer)) return -1; rc = nsvc->timer_mode; osmo_timer_del(&nsvc->timer); nsvc->timer.cb(nsvc->timer.data); return rc; } static void test_nsvc() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer[1] = {{0},}; struct gprs_nsvc *nsvc; int i; peer[0].sin_family = AF_INET; peer[0].sin_port = htons(1111); peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); for (i=0; i<4; ++i) { printf("--- Create via RESET (round %d) ---\n\n", i); send_ns_reset(nsi, &peer[0], NS_CAUSE_OM_INTERVENTION, 0x1001, 0x1000); gprs_dump_nsi(nsi); printf("--- Delete nsvc object (round %d)---\n\n", i); nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001); OSMO_ASSERT(nsvc != NULL); gprs_nsvc_delete(nsvc); gprs_dump_nsi(nsi); } gprs_ns_destroy(nsi); nsi = NULL; printf("--- Process timers ---\n\n"); /* wait for rate_ctr_timer expiry */ usleep(1100000); /* ensure termination */ alarm(2); osmo_timers_update(); alarm(0); } static void test_ignored_messages() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer[1] = {{0},}; peer[0].sin_family = AF_INET; peer[0].sin_port = htons(1111); peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); printf("--- Send unexpected NS STATUS (should not be answered)---\n\n"); /* Do not respond, see 3GPP TS 08.16, 7.5.1 */ gprs_process_message(nsi, "STATUS", &peer[0], gprs_ns_status_invalid_alive, sizeof(gprs_ns_status_invalid_alive)); printf("--- Send unexpected NS ALIVE ACK (should not be answered)---\n\n"); /* Ignore this, see 3GPP TS 08.16, 7.4.1 */ send_ns_alive_ack(nsi, &peer[0]); printf("--- Send unexpected NS RESET ACK (should not be answered)---\n\n"); /* Ignore this, see 3GPP TS 08.16, 7.3.1 */ send_ns_reset_ack(nsi, &peer[0], 0xe001, 0xe000); gprs_ns_destroy(nsi); nsi = NULL; } static void test_bss_port_changes() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer[4] = {{0},}; peer[0].sin_family = AF_INET; peer[0].sin_port = htons(1111); peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[1].sin_family = AF_INET; peer[1].sin_port = htons(2222); peer[1].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[2].sin_family = AF_INET; peer[2].sin_port = htons(3333); peer[2].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[3].sin_family = AF_INET; peer[3].sin_port = htons(4444); peer[3].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); printf("--- Setup, send BSSGP RESET ---\n\n"); setup_ns(nsi, &peer[0], 0x1122, 0x1122); gprs_dump_nsi(nsi); gprs_process_message(nsi, "BSSGP RESET", &peer[0], gprs_bssgp_reset, sizeof(gprs_bssgp_reset)); printf("--- Peer port changes, RESET, message remains unchanged ---\n\n"); send_ns_reset(nsi, &peer[1], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122); gprs_dump_nsi(nsi); printf("--- Peer port changes, RESET, VCI changes ---\n\n"); send_ns_reset(nsi, &peer[2], NS_CAUSE_OM_INTERVENTION, 0x3344, 0x1122); gprs_dump_nsi(nsi); printf("--- Peer port changes, RESET, NSEI changes ---\n\n"); send_ns_reset(nsi, &peer[3], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x3344); gprs_dump_nsi(nsi); printf("--- Peer port 3333, RESET, VCI is changed back ---\n\n"); send_ns_reset(nsi, &peer[2], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122); gprs_dump_nsi(nsi); printf("--- Peer port 4444, RESET, NSEI is changed back ---\n\n"); send_ns_reset(nsi, &peer[3], NS_CAUSE_OM_INTERVENTION, 0x1122, 0x1122); gprs_dump_nsi(nsi); gprs_ns_destroy(nsi); nsi = NULL; } static void test_bss_reset_ack() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in peer[4] = {{0},}; struct gprs_nsvc *nsvc; struct sockaddr_in *nse[4]; int rc; peer[0].sin_family = AF_INET; peer[0].sin_port = htons(1111); peer[0].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[1].sin_family = AF_INET; peer[1].sin_port = htons(2222); peer[1].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[2].sin_family = AF_INET; peer[2].sin_port = htons(3333); peer[2].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); peer[3].sin_family = AF_INET; peer[3].sin_port = htons(4444); peer[3].sin_addr.s_addr = htonl(REMOTE_BSS_ADDR); nse[0] = &peer[0]; nse[1] = &peer[1]; printf("--- Setup VC 1 BSS -> SGSN ---\n\n"); setup_ns(nsi, nse[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); printf("--- Setup VC 2 BSS -> SGSN ---\n\n"); setup_ns(nsi, nse[1], 0x2001, 0x2000); gprs_dump_nsi(nsi); printf("--- Setup VC 1 SGSN -> BSS ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); printf("--- Exchange NSEI 1 + 2 links ---\n\n"); nse[1] = &peer[0]; nse[0] = &peer[1]; printf("--- Setup VC 2 SGSN -> BSS (hits NSEI 1) ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x2001); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); printf("--- Setup VC 2 SGSN -> BSS (hits NSEI 2) ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x2001); rc = gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); OSMO_ASSERT(rc < 0); printf("--- Setup VC 1 SGSN -> BSS (hits NSEI 1) ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); printf("--- Setup VC 2 BSS -> SGSN ---\n\n"); setup_ns(nsi, nse[1], 0x2001, 0x2000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 3rd paragraph. */ /* This is not rejected because the NSEI has been * assigned dynamically and not by configuration. * This is not strictly spec conformant. */ printf("--- RESET with invalid NSEI, BSS -> SGSN ---\n\n"); send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION, 0x1001, 0xf000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 2nd paragraph. */ /* This is not rejected because the NSEI has been * assigned dynamically and not by configuration. * This is not strictly spec conformant. */ printf("--- RESET with invalid NSVCI, BSS -> SGSN ---\n\n"); send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION, 0xf001, 0x1000); gprs_dump_nsi(nsi); printf("--- RESET with old NSEI, NSVCI, BSS -> SGSN ---\n\n"); send_ns_reset(nsi, nse[0], NS_CAUSE_OM_INTERVENTION, 0x1001, 0x1000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 5th paragraph. */ printf("--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---\n\n"); send_ns_reset_ack(nsi, nse[0], 0x1001, 0x1000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */ printf("--- RESET_ACK with invalid NSEI, BSS -> SGSN ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, nse[0], 0x1001, 0xf000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */ printf("--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, 0x1001); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, nse[0], 0xf001, 0x1000); gprs_dump_nsi(nsi); gprs_ns_destroy(nsi); nsi = NULL; } static void test_sgsn_reset() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0}; struct gprs_nsvc *nsvc; sgsn_peer.sin_family = AF_INET; sgsn_peer.sin_port = htons(32000); sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR); gprs_dump_nsi(nsi); printf("--- Setup SGSN connection, BSS -> SGSN ---\n\n"); gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI); send_ns_alive_ack(nsi, &sgsn_peer); send_ns_unblock_ack(nsi, &sgsn_peer); gprs_dump_nsi(nsi); printf("--- RESET, SGSN -> BSS ---\n\n"); send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION, SGSN_NSEI+1, SGSN_NSEI); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 3rd paragraph. */ printf("--- RESET with invalid NSEI, SGSN -> BSS ---\n\n"); send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION, SGSN_NSEI+1, 0xf000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 2nd paragraph. */ printf("--- RESET with invalid NSVCI, SGSN -> BSS ---\n\n"); send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION, 0xf001, SGSN_NSEI); gprs_dump_nsi(nsi); printf("--- RESET, SGSN -> BSS ---\n\n"); send_ns_reset(nsi, &sgsn_peer, NS_CAUSE_OM_INTERVENTION, SGSN_NSEI+1, SGSN_NSEI); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 5th paragraph. */ printf("--- Unexpected RESET_ACK VC 1, BSS -> SGSN ---\n\n"); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */ printf("--- RESET_ACK with invalid NSEI, BSS -> SGSN ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, 0xe000); gprs_dump_nsi(nsi); /* Test 3GPP TS 08.16, 7.3.1, 4th paragraph. */ printf("--- RESET_ACK with invalid NSVCI, BSS -> SGSN ---\n\n"); nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1); gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); send_ns_reset_ack(nsi, &sgsn_peer, 0xe001, SGSN_NSEI); gprs_dump_nsi(nsi); gprs_ns_destroy(nsi); nsi = NULL; } static void test_sgsn_reset_invalid_state() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0}; struct gprs_nsvc *nsvc; int retry; uint8_t dummy_sdu[] = {0x01, 0x02, 0x03, 0x04}; sgsn_peer.sin_family = AF_INET; sgsn_peer.sin_port = htons(32000); sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR); gprs_dump_nsi(nsi); printf("=== %s ===\n", __func__); printf("--- Setup SGSN connection, BSS -> SGSN ---\n\n"); gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1); OSMO_ASSERT(sent_pdu_type == NS_PDUT_RESET); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI); OSMO_ASSERT(sent_pdu_type == NS_PDUT_ALIVE); send_ns_alive_ack(nsi, &sgsn_peer); OSMO_ASSERT(sent_pdu_type == NS_PDUT_UNBLOCK); send_ns_unblock_ack(nsi, &sgsn_peer); gprs_dump_nsi(nsi); nsvc = gprs_nsvc_by_nsvci(nsi, SGSN_NSEI+1); OSMO_ASSERT(nsvc->state == NSE_S_ALIVE); OSMO_ASSERT(nsvc->remote_state == NSE_S_ALIVE); printf("--- Time out local test procedure ---\n\n"); OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_TEST); OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_ALIVE); for (retry = 1; retry <= nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]; ++retry) OSMO_ASSERT(expire_nsvc_timer(nsvc) == NSVC_TIMER_TNS_ALIVE); OSMO_ASSERT(nsvc->state == NSE_S_BLOCKED); printf("--- Remote test procedure continues ---\n\n"); send_ns_alive(nsi, &sgsn_peer); OSMO_ASSERT(sent_pdu_type == NS_PDUT_RESET); printf("--- Don't send a NS_RESET_ACK message (pretend it is lost) ---\n\n"); sent_pdu_type = -1; send_ns_alive(nsi, &sgsn_peer); OSMO_ASSERT(sent_pdu_type == -1); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI); OSMO_ASSERT(sent_pdu_type == NS_PDUT_ALIVE); send_ns_alive_ack(nsi, &sgsn_peer); OSMO_ASSERT(nsvc->state == (NSE_S_ALIVE | NSE_S_BLOCKED)); OSMO_ASSERT(nsvc->remote_state == (NSE_S_ALIVE | NSE_S_BLOCKED)); OSMO_ASSERT(sent_pdu_type == NS_PDUT_UNBLOCK); send_ns_unblock_ack(nsi, &sgsn_peer); OSMO_ASSERT(nsvc->state == NSE_S_ALIVE); OSMO_ASSERT(nsvc->remote_state == NSE_S_ALIVE); send_ns_unitdata(nsi, &sgsn_peer, 0x1234, dummy_sdu, sizeof(dummy_sdu)); gprs_ns_destroy(nsi); nsi = NULL; } static void test_sgsn_output() { struct gprs_ns_inst *nsi = gprs_ns_instantiate(gprs_ns_callback, NULL); struct sockaddr_in sgsn_peer= {0}; sgsn_peer.sin_family = AF_INET; sgsn_peer.sin_port = htons(32000); sgsn_peer.sin_addr.s_addr = htonl(REMOTE_SGSN_ADDR); gprs_dump_nsi(nsi); printf("--- Send message to SGSN ---\n\n"); gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0, gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4); printf("--- Setup dead connection to SGSN ---\n\n"); gprs_ns_nsip_connect(nsi, &sgsn_peer, SGSN_NSEI, SGSN_NSEI+1); gprs_dump_nsi(nsi); printf("--- Send message to SGSN ---\n\n"); gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0, gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4); printf("--- Make connection to SGSN alive ---\n\n"); send_ns_reset_ack(nsi, &sgsn_peer, SGSN_NSEI+1, SGSN_NSEI); send_ns_alive_ack(nsi, &sgsn_peer); gprs_dump_nsi(nsi); printf("--- Send message to SGSN ---\n\n"); gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0, gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4); printf("--- Unblock connection to SGSN ---\n\n"); send_ns_unblock_ack(nsi, &sgsn_peer); send_ns_alive(nsi, &sgsn_peer); gprs_dump_nsi(nsi); printf("--- Send message to SGSN ---\n\n"); gprs_send_message(nsi, "BSSGP RESET", SGSN_NSEI, 0, gprs_bssgp_reset+4, sizeof(gprs_bssgp_reset)-4); printf("--- Send empty message with BVCI to SGSN ---\n\n"); gprs_send_message(nsi, "[empty]", SGSN_NSEI, 0x0102, gprs_bssgp_reset, 0); gprs_ns_destroy(nsi); nsi = NULL; } int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { return -1; } static struct log_info info = {}; int main(int argc, char **argv) { osmo_init_logging(&info); log_set_use_color(osmo_stderr_target, 0); log_set_print_filename(osmo_stderr_target, 0); osmo_signal_register_handler(SS_L_NS, &test_signal, NULL); log_set_print_filename(osmo_stderr_target, 0); log_set_log_level(osmo_stderr_target, LOGL_INFO); setlinebuf(stdout); printf("===== NS protocol test START\n"); test_nsvc(); test_ignored_messages(); test_bss_port_changes(); test_bss_reset_ack(); test_sgsn_reset(); test_sgsn_reset_invalid_state(); test_sgsn_output(); printf("===== NS protocol test END\n\n"); exit(EXIT_SUCCESS); } libosmocore-0.9.0/tests/gb/gprs_ns_test.ok000066400000000000000000000505051261607044000206410ustar00rootroot00000000000000===== NS protocol test START --- Create via RESET (round 0) --- PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked --- Delete nsvc object (round 0)--- Current NS-VCIs: --- Create via RESET (round 1) --- PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked --- Delete nsvc object (round 1)--- Current NS-VCIs: --- Create via RESET (round 2) --- PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked --- Delete nsvc object (round 2)--- Current NS-VCIs: --- Create via RESET (round 3) --- PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked --- Delete nsvc object (round 3)--- Current NS-VCIs: --- Process timers --- --- Send unexpected NS STATUS (should not be answered)--- PROCESSING STATUS from 0x01020304:1111 08 00 81 0a 02 81 0a result (STATUS) = 0 --- Send unexpected NS ALIVE ACK (should not be answered)--- PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 --- Send unexpected NS RESET ACK (should not be answered)--- PROCESSING RESET_ACK from 0x01020304:1111 03 01 82 e0 01 04 82 e0 00 result (RESET_ACK) = 0 --- Setup, send BSSGP RESET --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1122(4386), NSEI 0x1122(4386) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 11 22 04 82 11 22 ==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 11 22 04 82 11 22 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1122/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1122, NSEI 0x1122, peer 0x01020304:1111 PROCESSING BSSGP RESET from 0x01020304:1111 00 00 00 00 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 CALLBACK, event 0, msg length 18, bvci 0x0000 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 result (BSSGP RESET) = 0 --- Peer port changes, RESET, message remains unchanged --- PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 11 22 04 82 11 22 ==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:2222 MESSAGE to BSS, msg length 9 03 01 82 11 22 04 82 11 22 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x1122, NSEI 0x1122, peer 0x01020304:2222, blocked --- Peer port changes, RESET, VCI changes --- PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 33 44 04 82 11 22 ==> got signal NS_RESET, NS-VC 0x3344/1.2.3.4:3333 MESSAGE to BSS, msg length 9 03 01 82 33 44 04 82 11 22 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x3344, NSEI 0x1122, peer 0x01020304:3333, blocked VCI 0x1122, NSEI 0x1122, peer 0x01020304:2222, blocked --- Peer port changes, RESET, NSEI changes --- PROCESSING RESET from 0x01020304:4444 02 00 81 01 01 82 11 22 04 82 33 44 ==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:4444 MESSAGE to BSS, msg length 9 03 01 82 11 22 04 82 33 44 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x3344, NSEI 0x1122, peer 0x01020304:3333, blocked VCI 0x1122, NSEI 0x3344, peer 0x01020304:4444, blocked NS-VC changed NSEI count : 1 --- Peer port 3333, RESET, VCI is changed back --- PROCESSING RESET from 0x01020304:3333 02 00 81 01 01 82 11 22 04 82 11 22 ==> got signal NS_REPLACED: 0x1122/1.2.3.4:4444 -> 0x3344/1.2.3.4:3333 ==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:3333 MESSAGE to BSS, msg length 9 03 01 82 11 22 04 82 11 22 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x3344, NSEI 0x1122, peer 0x00000000:0, blocked VCI 0x1122, NSEI 0x1122, peer 0x01020304:3333, blocked NS-VC replaced other count: 1 NS-VC changed NSEI count : 2 --- Peer port 4444, RESET, NSEI is changed back --- PROCESSING RESET from 0x01020304:4444 02 00 81 01 01 82 11 22 04 82 11 22 ==> got signal NS_RESET, NS-VC 0x1122/1.2.3.4:4444 MESSAGE to BSS, msg length 9 03 01 82 11 22 04 82 11 22 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x3344, NSEI 0x1122, peer 0x00000000:0, blocked VCI 0x1122, NSEI 0x1122, peer 0x01020304:4444, blocked NS-VC replaced other count: 1 NS-VC changed NSEI count : 2 --- Setup VC 1 BSS -> SGSN --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x1001(4097), NSEI 0x1000(4096) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x1001/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 --- Setup VC 2 BSS -> SGSN --- Setup NS-VC: remote 0x01020304:2222, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:2222 0a MESSAGE to BSS, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:2222 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:2222 MESSAGE to BSS, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:2222 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111 --- Setup VC 1 SGSN -> BSS --- MESSAGE to BSS, msg length 12 02 00 81 01 01 82 10 01 04 82 10 00 PROCESSING RESET_ACK from 0x01020304:1111 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET_ACK) = 1 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:2222 VCI 0x1001, NSEI 0x1000, peer 0x01020304:1111, blocked NS-VC Block count : 1 --- Exchange NSEI 1 + 2 links --- --- Setup VC 2 SGSN -> BSS (hits NSEI 1) --- MESSAGE to BSS, msg length 12 02 00 81 01 01 82 20 01 04 82 20 00 PROCESSING RESET_ACK from 0x01020304:2222 03 01 82 10 01 04 82 10 00 ==> got signal NS_REPLACED: 0x1001/1.2.3.4:1111 -> 0x2001/1.2.3.4:2222 MESSAGE to BSS, msg length 1 0a result (RESET_ACK) = 1 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x00000000:0, blocked, dead VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 2 NS-VC replaced other count: 1 --- Setup VC 2 SGSN -> BSS (hits NSEI 2) --- --- Setup VC 1 SGSN -> BSS (hits NSEI 1) --- MESSAGE to BSS, msg length 12 02 00 81 01 01 82 10 01 04 82 10 00 PROCESSING RESET_ACK from 0x01020304:2222 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET_ACK) = 1 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x00000000:0, blocked, dead VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 3 NS-VC replaced other count: 1 --- Setup VC 2 BSS -> SGSN --- Setup NS-VC: remote 0x01020304:1111, NSVCI 0x2001(8193), NSEI 0x2000(8192) PROCESSING RESET from 0x01020304:1111 02 00 81 01 01 82 20 01 04 82 20 00 ==> got signal NS_RESET, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS, msg length 9 03 01 82 20 01 04 82 20 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 PROCESSING ALIVE from 0x01020304:1111 0a MESSAGE to BSS, msg length 1 0b result (ALIVE) = 1 PROCESSING UNBLOCK from 0x01020304:1111 06 ==> got signal NS_UNBLOCK, NS-VC 0x2001/1.2.3.4:1111 MESSAGE to BSS, msg length 1 07 result (UNBLOCK) = 1 PROCESSING ALIVE_ACK from 0x01020304:1111 0b result (ALIVE_ACK) = 0 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 3 NS-VC replaced other count: 1 --- RESET with invalid NSEI, BSS -> SGSN --- PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 10 01 04 82 f0 00 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:2222 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 f0 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0xf000, peer 0x01020304:2222, blocked NS-VC Block count : 3 NS-VC replaced other count: 1 NS-VC changed NSEI count : 1 --- RESET with invalid NSVCI, BSS -> SGSN --- PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 f0 01 04 82 10 00 ==> got signal NS_REPLACED: 0xf001/0.0.0.0:0 -> 0x1001/1.2.3.4:2222 ==> got signal NS_RESET, NS-VC 0xf001/1.2.3.4:2222 MESSAGE to BSS, msg length 9 03 01 82 f0 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0xf001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC replaced other count: 1 VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0xf000, peer 0x00000000:0, blocked NS-VC Block count : 3 NS-VC replaced other count: 1 NS-VC changed NSEI count : 1 --- RESET with old NSEI, NSVCI, BSS -> SGSN --- PROCESSING RESET from 0x01020304:2222 02 00 81 01 01 82 10 01 04 82 10 00 ==> got signal NS_REPLACED: 0x1001/0.0.0.0:0 -> 0xf001/1.2.3.4:2222 ==> got signal NS_RESET, NS-VC 0x1001/1.2.3.4:2222 MESSAGE to BSS, msg length 9 03 01 82 10 01 04 82 10 00 MESSAGE to BSS, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked NS-VC replaced other count: 1 VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 3 NS-VC replaced other count: 2 NS-VC changed NSEI count : 2 --- Unexpected RESET_ACK VC 1, BSS -> SGSN --- PROCESSING RESET_ACK from 0x01020304:2222 03 01 82 10 01 04 82 10 00 result (RESET_ACK) = 0 Current NS-VCIs: VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked NS-VC replaced other count: 1 VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 3 NS-VC replaced other count: 2 NS-VC changed NSEI count : 2 --- RESET_ACK with invalid NSEI, BSS -> SGSN --- MESSAGE to BSS, msg length 12 02 00 81 01 01 82 10 01 04 82 10 00 PROCESSING RESET_ACK from 0x01020304:2222 03 01 82 10 01 04 82 f0 00 MESSAGE to BSS, msg length 1 0a result (RESET_ACK) = 1 Current NS-VCIs: VCI 0xf001, NSEI 0x1000, peer 0x00000000:0, blocked NS-VC replaced other count: 1 VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0xf000, peer 0x01020304:2222, blocked NS-VC Block count : 4 NS-VC replaced other count: 2 NS-VC changed NSEI count : 3 --- RESET_ACK with invalid NSVCI, BSS -> SGSN --- MESSAGE to BSS, msg length 12 02 00 81 01 01 82 10 01 04 82 f0 00 PROCESSING RESET_ACK from 0x01020304:2222 03 01 82 f0 01 04 82 10 00 ==> got signal NS_REPLACED: 0xf001/0.0.0.0:0 -> 0x1001/1.2.3.4:2222 MESSAGE to BSS, msg length 1 0a result (RESET_ACK) = 1 Current NS-VCIs: VCI 0xf001, NSEI 0x1000, peer 0x01020304:2222, blocked NS-VC Block count : 1 NS-VC replaced other count: 2 VCI 0x2001, NSEI 0x2000, peer 0x01020304:1111 VCI 0x1001, NSEI 0xf000, peer 0x00000000:0, blocked, dead NS-VC Block count : 4 NS-VC replaced other count: 2 NS-VC changed NSEI count : 3 Current NS-VCIs: --- Setup SGSN connection, BSS -> SGSN --- MESSAGE to SGSN, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- RESET, SGSN -> BSS --- PROCESSING RESET from 0x05060708:32000 02 00 81 01 01 82 01 01 04 82 01 00 ==> got signal NS_RESET, NS-VC 0x0101/5.6.7.8:32000 MESSAGE to SGSN, msg length 9 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 --- RESET with invalid NSEI, SGSN -> BSS --- PROCESSING RESET from 0x05060708:32000 02 00 81 01 01 82 01 01 04 82 f0 00 ==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=2, ie=4 MESSAGE to SGSN, msg length 9 03 01 82 01 01 04 82 01 00 result (RESET) = 0 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 NSEI was invalid count : 1 --- RESET with invalid NSVCI, SGSN -> BSS --- PROCESSING RESET from 0x05060708:32000 02 00 81 01 01 82 f0 01 04 82 01 00 ==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=2, ie=1 MESSAGE to SGSN, msg length 9 03 01 82 01 01 04 82 01 00 result (RESET) = 0 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 NS-VCI was invalid count : 1 NSEI was invalid count : 1 --- RESET, SGSN -> BSS --- PROCESSING RESET from 0x05060708:32000 02 00 81 01 01 82 01 01 04 82 01 00 ==> got signal NS_RESET, NS-VC 0x0101/5.6.7.8:32000 MESSAGE to SGSN, msg length 9 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET) = 9 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 NS-VCI was invalid count : 1 NSEI was invalid count : 1 --- Unexpected RESET_ACK VC 1, BSS -> SGSN --- PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 result (RESET_ACK) = 0 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 NS-VCI was invalid count : 1 NSEI was invalid count : 1 --- RESET_ACK with invalid NSEI, BSS -> SGSN --- MESSAGE to SGSN, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 e0 00 ==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=3, ie=4 result (RESET_ACK) = -22 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead NS-VC Block count : 1 NS-VCI was invalid count : 1 NSEI was invalid count : 2 --- RESET_ACK with invalid NSVCI, BSS -> SGSN --- MESSAGE to SGSN, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 e0 01 04 82 01 00 ==> got signal NS_MISMATCH: 0x0101/5.6.7.8:32000 pdu=3, ie=1 result (RESET_ACK) = -22 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead NS-VC Block count : 1 NS-VCI was invalid count : 2 NSEI was invalid count : 2 Current NS-VCIs: === test_sgsn_reset_invalid_state === --- Setup SGSN connection, BSS -> SGSN --- MESSAGE to SGSN, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Time out local test procedure --- MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a MESSAGE to SGSN, msg length 1 0a ==> got signal NS_ALIVE_EXP, NS-VC 0x0101/5.6.7.8:32000 ==> got signal NS_BLOCK, NS-VC 0x0101/5.6.7.8:32000 --- Remote test procedure continues --- PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN, msg length 12 02 00 81 0a 01 82 01 01 04 82 01 00 result (ALIVE) = 12 --- Don't send a NS_RESET_ACK message (pretend it is lost) --- PROCESSING ALIVE from 0x05060708:32000 0a result (ALIVE) = 0 PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN, msg length 1 06 result (ALIVE_ACK) = 1 PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING UNITDATA from 0x05060708:32000 00 00 12 34 01 02 03 04 CALLBACK, event 0, msg length 4, bvci 0x1234 01 02 03 04 result (UNITDATA) = 0 Current NS-VCIs: --- Send message to SGSN --- SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 result (BSSGP RESET) = -22 --- Setup dead connection to SGSN --- MESSAGE to SGSN, msg length 12 02 00 81 01 01 82 01 01 04 82 01 00 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked, dead --- Send message to SGSN --- SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 result (BSSGP RESET) = -16 --- Make connection to SGSN alive --- PROCESSING RESET_ACK from 0x05060708:32000 03 01 82 01 01 04 82 01 00 MESSAGE to SGSN, msg length 1 0a result (RESET_ACK) = 1 PROCESSING ALIVE_ACK from 0x05060708:32000 0b MESSAGE to SGSN, msg length 1 06 result (ALIVE_ACK) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000, blocked NS-VC Block count : 1 --- Send message to SGSN --- SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 result (BSSGP RESET) = -16 --- Unblock connection to SGSN --- PROCESSING UNBLOCK_ACK from 0x05060708:32000 07 ==> got signal NS_UNBLOCK, NS-VC 0x0101/5.6.7.8:32000 result (UNBLOCK_ACK) = 0 PROCESSING ALIVE from 0x05060708:32000 0a MESSAGE to SGSN, msg length 1 0b result (ALIVE) = 1 Current NS-VCIs: VCI 0x0101, NSEI 0x0100, peer 0x05060708:32000 NS-VC Block count : 1 --- Send message to SGSN --- SENDING BSSGP RESET to NSEI 0x0100, BVCI 0x0000 NS UNITDATA MESSAGE to SGSN, BVCI 0x0000, msg length 18 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 MESSAGE to SGSN, msg length 22 00 00 00 00 22 04 82 4a 2e 07 81 08 08 88 10 20 30 40 50 60 10 00 result (BSSGP RESET) = 22 --- Send empty message with BVCI to SGSN --- SENDING [empty] to NSEI 0x0100, BVCI 0x0102 NS UNITDATA MESSAGE to SGSN, BVCI 0x0102, msg length 0 MESSAGE to SGSN, msg length 4 00 00 01 02 result ([empty]) = 4 ===== NS protocol test END libosmocore-0.9.0/tests/gsm0408/000077500000000000000000000000001261607044000163015ustar00rootroot00000000000000libosmocore-0.9.0/tests/gsm0408/gsm0408_test.c000066400000000000000000000101641261607044000206100ustar00rootroot00000000000000/* * (C) 2012 by Harald Welte * 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 #include #include #include #include #include #include #include #include #include static const uint8_t csd_9600_v110_lv[] = { 0x07, 0xa1, 0xb8, 0x89, 0x21, 0x15, 0x63, 0x80 }; static const struct gsm_mncc_bearer_cap bcap_csd_9600_v110 = { .transfer = GSM48_BCAP_ITCAP_UNR_DIG_INF, .mode = GSM48_BCAP_TMOD_CIRCUIT, .coding = GSM48_BCAP_CODING_GSM_STD, .radio = GSM48_BCAP_RRQ_FR_ONLY, .speech_ver[0]= -1, .data = { .rate_adaption = GSM48_BCAP_RA_V110_X30, .sig_access = GSM48_BCAP_SA_I440_I450, .async = 1, .nr_stop_bits = 1, .nr_data_bits = 8, .user_rate = GSM48_BCAP_UR_9600, .parity = GSM48_BCAP_PAR_NONE, .interm_rate = GSM48_BCAP_IR_16k, .transp = GSM48_BCAP_TR_TRANSP, .modem_type = GSM48_BCAP_MT_NONE, }, }; static const uint8_t speech_all_lv[] = { 0x06, 0x60, 0x04, 0x02, 0x00, 0x05, 0x81 }; static const struct gsm_mncc_bearer_cap bcap_speech_all = { .transfer = GSM48_BCAP_ITCAP_SPEECH, .mode = GSM48_BCAP_TMOD_CIRCUIT, .coding = GSM48_BCAP_CODING_GSM_STD, .radio = GSM48_BCAP_RRQ_DUAL_FR, .speech_ver = { 4, 2, 0, 5, 1, -1, }, }; struct bcap_test { const uint8_t *lv; const struct gsm_mncc_bearer_cap *bc; const char *name; }; static const struct bcap_test bcap_tests[] = { { csd_9600_v110_lv, &bcap_csd_9600_v110, "CSD 9600/V.110/transparent" }, { speech_all_lv, &bcap_speech_all, "Speech, all codecs" }, }; static int test_bearer_cap() { struct gsm_mncc_bearer_cap bc; int i, rc; for (i = 0; i < ARRAY_SIZE(bcap_tests); i++) { struct msgb *msg = msgb_alloc(100, "test"); int lv_len; memset(&bc, 0, sizeof(bc)); /* test decoding */ rc = gsm48_decode_bearer_cap(&bc, bcap_tests[i].lv); if (rc < 0) { fprintf(stderr, "Error decoding %s\n", bcap_tests[i].name); return rc; } if (memcmp(&bc, bcap_tests[i].bc, sizeof(bc))) { fprintf(stderr, "Incorrect decoded result of %s:\n", bcap_tests[i].name); fprintf(stderr, " should: %s\n", osmo_hexdump((uint8_t *) bcap_tests[i].bc, sizeof(bc))); fprintf(stderr, " is: %s\n", osmo_hexdump((uint8_t *) &bc, sizeof(bc))); return -1; } /* also test re-encode? */ rc = gsm48_encode_bearer_cap(msg, 1, &bc); if (rc < 0) { fprintf(stderr, "Error encoding %s\n", bcap_tests[i].name); return rc; } lv_len = bcap_tests[i].lv[0]+1; if (memcmp(msg->data, bcap_tests[i].lv, lv_len)) { fprintf(stderr, "Incorrect encoded result of %s:\n", bcap_tests[i].name); fprintf(stderr, " should: %s\n", osmo_hexdump(bcap_tests[i].lv, lv_len)); fprintf(stderr, " is: %s\n", osmo_hexdump(msg->data, msg->len)); return -1; } printf("Test `%s' passed\n", bcap_tests[i].name); msgb_free(msg); } return 0; } static void test_mid_from_tmsi(void) { static const uint8_t res[] = { 0x17, 0x05, 0xf4, 0xaa, 0xbb, 0xcc, 0xdd }; uint32_t tmsi = 0xAABBCCDD; uint8_t buf[3 + sizeof(uint32_t)]; printf("Simple TMSI encoding test...."); memset(&buf, 0xFE, sizeof(buf)); gsm48_generate_mid_from_tmsi(buf, tmsi); OSMO_ASSERT(memcmp(buf, res, sizeof(res)) == 0); printf("passed\n"); } int main(int argc, char **argv) { test_bearer_cap(); test_mid_from_tmsi(); return EXIT_SUCCESS; } libosmocore-0.9.0/tests/gsm0408/gsm0408_test.ok000066400000000000000000000001561261607044000207770ustar00rootroot00000000000000Test `CSD 9600/V.110/transparent' passed Test `Speech, all codecs' passed Simple TMSI encoding test....passed libosmocore-0.9.0/tests/gsm0808/000077500000000000000000000000001261607044000163055ustar00rootroot00000000000000libosmocore-0.9.0/tests/gsm0808/gsm0808_test.c000066400000000000000000000155731261607044000206310ustar00rootroot00000000000000/* * (C) 2012 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 #include #include #define VERIFY(msg, data, len) \ if (msgb_l3len(msg) != len) { \ printf("%s:%d Length don't match: %d vs. %d. %s\n", \ __func__, __LINE__, msgb_l3len(msg), len, \ osmo_hexdump(msg->l3h, msgb_l3len(msg))); \ abort(); \ } else if (memcmp(msg->l3h, data, len) != 0) { \ printf("%s:%d didn't match: got: %s\n", \ __func__, __LINE__, \ osmo_hexdump(msg->l3h, msgb_l3len(msg))); \ abort(); \ } static void test_create_layer3(void) { static const uint8_t res[] = { 0x00, 0x0e, 0x57, 0x05, 0x08, 0x00, 0x77, 0x62, 0x83, 0x33, 0x66, 0x44, 0x88, 0x17, 0x01, 0x23 }; struct msgb *msg, *in_msg; printf("Testing creating Layer3\n"); in_msg = msgb_alloc_headroom(512, 128, "foo"); in_msg->l3h = in_msg->data; msgb_v_put(in_msg, 0x23); msg = gsm0808_create_layer3(in_msg, 0x1122, 0x2244, 0x3366, 0x4488); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); msgb_free(in_msg); } static void test_create_reset() { static const uint8_t res[] = { 0x00, 0x04, 0x30, 0x04, 0x01, 0x20 }; struct msgb *msg; printf("Testing creating Reset\n"); msg = gsm0808_create_reset(); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_clear_command() { static const uint8_t res[] = { 0x20, 0x04, 0x01, 0x23 }; struct msgb *msg; printf("Testing creating Clear Command\n"); msg = gsm0808_create_clear_command(0x23); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_clear_complete() { static const uint8_t res[] = { 0x00, 0x01, 0x21 }; struct msgb *msg; printf("Testing creating Clear Complete\n"); msg = gsm0808_create_clear_complete(); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_cipher_complete() { static const uint8_t res1[] = { 0x00, 0x08, 0x55, 0x20, 0x03, 0x23, 0x42, 0x21, 0x2c, 0x04 }; static const uint8_t res2[] = { 0x00, 0x03, 0x55, 0x2c, 0x04}; struct msgb *l3, *msg; printf("Testing creating Cipher Complete\n"); l3 = msgb_alloc_headroom(512, 128, "l3h"); l3->l3h = l3->data; msgb_v_put(l3, 0x23); msgb_v_put(l3, 0x42); msgb_v_put(l3, 0x21); /* with l3 data */ msg = gsm0808_create_cipher_complete(l3, 4); VERIFY(msg, res1, ARRAY_SIZE(res1)); msgb_free(msg); /* with l3 data but short */ l3->len -= 1; l3->tail -= 1; msg = gsm0808_create_cipher_complete(l3, 4); VERIFY(msg, res2, ARRAY_SIZE(res2)); msgb_free(msg); /* without l3 data */ msg = gsm0808_create_cipher_complete(NULL, 4); VERIFY(msg, res2, ARRAY_SIZE(res2)); msgb_free(msg); msgb_free(l3); } static void test_create_cipher_reject() { static const uint8_t res[] = { 0x00, 0x02, 0x59, 0x23 }; struct msgb *msg; printf("Testing creating Cipher Reject\n"); msg = gsm0808_create_cipher_reject(0x23); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_cm_u() { static const uint8_t res[] = { 0x00, 0x07, 0x54, 0x12, 0x01, 0x23, 0x13, 0x01, 0x42 }; static const uint8_t res2o[] = { 0x00, 0x04, 0x54, 0x12, 0x01, 0x23 }; struct msgb *msg; const uint8_t cm2 = 0x23; const uint8_t cm3 = 0x42; printf("Testing creating CM U\n"); msg = gsm0808_create_classmark_update(&cm2, 1, &cm3, 1); VERIFY(msg, res, ARRAY_SIZE(res)); msg = gsm0808_create_classmark_update(&cm2, 1, NULL, 0); VERIFY(msg, res2o, ARRAY_SIZE(res2o)); msgb_free(msg); } static void test_create_sapi_reject() { static const uint8_t res[] = { 0x00, 0x03, 0x25, 0x03, 0x25 }; struct msgb *msg; printf("Testing creating SAPI Reject\n"); msg = gsm0808_create_sapi_reject(3); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_ass_compl() { static const uint8_t res1[] = { 0x00, 0x09, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c, 0x11, 0x40, 0x22 }; static const uint8_t res2[] = { 0x00, 0x07, 0x02, 0x15, 0x23, 0x21, 0x42, 0x2c, 0x11}; struct msgb *msg; printf("Testing creating Assignment Complete\n"); msg = gsm0808_create_assignment_completed(0x23, 0x42, 0x11, 0x22); VERIFY(msg, res1, ARRAY_SIZE(res1)); msgb_free(msg); msg = gsm0808_create_assignment_completed(0x23, 0x42, 0x11, 0); VERIFY(msg, res2, ARRAY_SIZE(res2)); msgb_free(msg); } static void test_create_ass_fail() { static const uint8_t res1[] = { 0x00, 0x04, 0x03, 0x04, 0x01, 0x23 }; static const uint8_t res2[] = { 0x00, 0x06, 0x03, 0x04, 0x01, 0x23, 0x15, 0x02}; uint8_t rr_res = 2; struct msgb *msg; printf("Testing creating Assignment Failure\n"); msg = gsm0808_create_assignment_failure(0x23, NULL); VERIFY(msg, res1, ARRAY_SIZE(res1)); msgb_free(msg); msg = gsm0808_create_assignment_failure(0x23, &rr_res); VERIFY(msg, res2, ARRAY_SIZE(res2)); msgb_free(msg); } static void test_create_clear_rqst() { static const uint8_t res[] = { 0x00, 0x04, 0x22, 0x04, 0x01, 0x23 }; struct msgb *msg; printf("Testing creating Clear Request\n"); msg = gsm0808_create_clear_rqst(0x23); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); } static void test_create_dtap() { static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 }; struct msgb *msg, *l3; printf("Testing creating DTAP\n"); l3 = msgb_alloc_headroom(512, 128, "test"); l3->l3h = l3->data; msgb_v_put(l3, 0x23); msgb_v_put(l3, 0x42); msg = gsm0808_create_dtap(l3, 0x3); VERIFY(msg, res, ARRAY_SIZE(res)); msgb_free(msg); msgb_free(l3); } static void test_prepend_dtap() { static const uint8_t res[] = { 0x01, 0x03, 0x02, 0x23, 0x42 }; struct msgb *in_msg; printf("Testing prepend DTAP\n"); in_msg = msgb_alloc_headroom(512, 128, "test"); msgb_v_put(in_msg, 0x23); msgb_v_put(in_msg, 0x42); gsm0808_prepend_dtap_header(in_msg, 0x3); in_msg->l3h = in_msg->data; VERIFY(in_msg, res, ARRAY_SIZE(res)); msgb_free(in_msg); } int main(int argc, char **argv) { printf("Testing generation of GSM0808 messages\n"); test_create_layer3(); test_create_reset(); test_create_clear_command(); test_create_clear_complete(); test_create_cipher_complete(); test_create_cipher_reject(); test_create_cm_u(); test_create_sapi_reject(); test_create_ass_compl(); test_create_ass_fail(); test_create_clear_rqst(); test_create_dtap(); test_prepend_dtap(); printf("Done\n"); return EXIT_SUCCESS; } libosmocore-0.9.0/tests/gsm0808/gsm0808_test.ok000066400000000000000000000006401261607044000210050ustar00rootroot00000000000000Testing generation of GSM0808 messages Testing creating Layer3 Testing creating Reset Testing creating Clear Command Testing creating Clear Complete Testing creating Cipher Complete Testing creating Cipher Reject Testing creating CM U Testing creating SAPI Reject Testing creating Assignment Complete Testing creating Assignment Failure Testing creating Clear Request Testing creating DTAP Testing prepend DTAP Done libosmocore-0.9.0/tests/kasumi/000077500000000000000000000000001261607044000164705ustar00rootroot00000000000000libosmocore-0.9.0/tests/kasumi/kasumi_test.c000066400000000000000000000234751261607044000211770ustar00rootroot00000000000000#include #include #include #include #include #include #include #include /* Test vectors are taken from TS 135 202 */ inline int _compare_mem(uint8_t * x, uint8_t * y, size_t len) { if (0 != memcmp(x, y, len)) { printf ("X: %s\t", osmo_hexdump_nospc(x, len)); printf ("Y: %s\n", osmo_hexdump_nospc(y, len)); return 0; } return 1; } inline static void test_expansion(uint8_t * test_key, uint16_t * _KLi1, uint16_t * _KLi2, uint16_t * _KOi1, uint16_t * _KOi2, uint16_t * _KOi3, uint16_t * _KIi1, uint16_t * _KIi2, uint16_t * _KIi3, uint16_t * _KLi1_r, uint16_t * _KLi2_r, uint16_t * _KOi1_r, uint16_t * _KOi2_r, uint16_t * _KOi3_r, uint16_t * _KIi1_r, uint16_t * _KIi2_r, uint16_t * _KIi3_r) { _kasumi_key_expand(test_key, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3); int passed = 1; passed = _compare_mem((uint8_t *)_KLi1, (uint8_t *)_KLi1_r, 16); passed = _compare_mem((uint8_t *)_KLi2, (uint8_t *)_KLi2_r, 16); passed = _compare_mem((uint8_t *)_KOi1, (uint8_t *)_KOi1_r, 16); passed = _compare_mem((uint8_t *)_KOi2, (uint8_t *)_KOi2_r, 16); passed = _compare_mem((uint8_t *)_KOi3, (uint8_t *)_KOi3_r, 16); passed = _compare_mem((uint8_t *)_KIi1, (uint8_t *)_KIi1_r, 16); passed = _compare_mem((uint8_t *)_KIi2, (uint8_t *)_KIi2_r, 16); passed = _compare_mem((uint8_t *)_KIi3, (uint8_t *)_KIi3_r, 16); printf(passed ? " OK. " : "FAILED!"); } int main(int argc, char **argv) { uint16_t _KLi1[8], _KLi2[8], _KOi1[8], _KOi2[8], _KOi3[8], _KIi1[8], _KIi2[8], _KIi3[8], _KLi1_r[8], _KLi2_r[8], _KOi1_r[8], _KOi2_r[8], _KOi3_r[8], _KIi1_r[8], _KIi2_r[8], _KIi3_r[8]; printf("testing KASUMI key expansion and encryption (ETSI TS 135 203):\n"); printf("KASUMI Test Set 1..."); uint8_t _test_key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xB3, 0x00, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48}; _KLi1_r[0] = 0x57AC; _KLi1_r[1] = 0x8B3E; _KLi1_r[2] = 0x058B; _KLi1_r[3] = 0x6601; _KLi1_r[4] = 0x2A59; _KLi1_r[5] = 0x9220; _KLi1_r[6] = 0x9102; _KLi1_r[7] = 0xFE91; _KLi2_r[0] = 0x0B6E; _KLi2_r[1] = 0x7EEF; _KLi2_r[2] = 0x6BF0; _KLi2_r[3] = 0xF388; _KLi2_r[4] = 0x3ED5; _KLi2_r[5] = 0xCD58; _KLi2_r[6] = 0x2AF5; _KLi2_r[7] = 0x00F8; _KOi1_r[0] = 0xB3E8; _KOi1_r[1] = 0x58B0; _KOi1_r[2] = 0x6016; _KOi1_r[3] = 0xA592; _KOi1_r[4] = 0x2209; _KOi1_r[5] = 0x1029; _KOi1_r[6] = 0xE91F; _KOi1_r[7] = 0x7AC5; _KOi2_r[0] = 0x1049; _KOi2_r[1] = 0x8148; _KOi2_r[2] = 0x48FF; _KOi2_r[3] = 0xD62B; _KOi2_r[4] = 0x9F45; _KOi2_r[5] = 0xC582; _KOi2_r[6] = 0x00B3; _KOi2_r[7] = 0x2C95; _KOi3_r[0] = 0x2910; _KOi3_r[1] = 0x1FE9; _KOi3_r[2] = 0xC57A; _KOi3_r[3] = 0xE8B3; _KOi3_r[4] = 0xB058; _KOi3_r[5] = 0x1660; _KOi3_r[6] = 0x92A5; _KOi3_r[7] = 0x0922; _KIi1_r[0] = 0x6BF0; _KIi1_r[1] = 0xF388; _KIi1_r[2] = 0x3ED5; _KIi1_r[3] = 0xCD58; _KIi1_r[4] = 0x2AF5; _KIi1_r[5] = 0x00F8; _KIi1_r[6] = 0x0B6E; _KIi1_r[7] = 0x7EEF; _KIi2_r[0] = 0x7EEF; _KIi2_r[1] = 0x6BF0; _KIi2_r[2] = 0xF388; _KIi2_r[3] = 0x3ED5; _KIi2_r[4] = 0xCD58; _KIi2_r[5] = 0x2AF5; _KIi2_r[6] = 0x00F8; _KIi2_r[7] = 0x0B6E; _KIi3_r[0] = 0xCD58; _KIi3_r[1] = 0x2AF5; _KIi3_r[2] = 0x00F8; _KIi3_r[3] = 0x0B6E; _KIi3_r[4] = 0x7EEF; _KIi3_r[5] = 0x6BF0; _KIi3_r[6] = 0xF388; _KIi3_r[7] = 0x3ED5; test_expansion(_test_key1, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r); if (0xDF1F9B251C0BF45F == _kasumi(0xEA024714AD5C4D84, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3)) printf("OK."); else printf("FAILED!"); printf("\nKASUMI Test Set 2..."); uint8_t _test_key2[] = {0x8C, 0xE3, 0x3E, 0x2C, 0xC3, 0xC0, 0xB5, 0xFC, 0x1F, 0x3D, 0xE8, 0xA6, 0xDC, 0x66, 0xB1, 0xF3}; _KLi1_r[0] = 0x19C7; _KLi1_r[1] = 0x7C58; _KLi1_r[2] = 0x8781; _KLi1_r[3] = 0x6BF9; _KLi1_r[4] = 0x3E7A; _KLi1_r[5] = 0xD14D; _KLi1_r[6] = 0xB8CD; _KLi1_r[7] = 0x63E7; _KLi2_r[0] = 0x4A6B; _KLi2_r[1] = 0x7813; _KLi2_r[2] = 0xE1E1; _KLi2_r[3] = 0x523E; _KLi2_r[4] = 0xAA32; _KLi2_r[5] = 0x83E3; _KLi2_r[6] = 0x8DC0; _KLi2_r[7] = 0x7B4B; _KOi1_r[0] = 0xC587; _KOi1_r[1] = 0x7818; _KOi1_r[2] = 0xBF96; _KOi1_r[3] = 0xE7A3; _KOi1_r[4] = 0x14DD; _KOi1_r[5] = 0x8CDB; _KOi1_r[6] = 0x3E76; _KOi1_r[7] = 0x9C71; _KOi2_r[0] = 0xA6E8; _KOi2_r[1] = 0x66DC; _KOi2_r[2] = 0xF3B1; _KOi2_r[3] = 0xE38C; _KOi2_r[4] = 0x2C3E; _KOi2_r[5] = 0xC0C3; _KOi2_r[6] = 0xFCB5; _KOi2_r[7] = 0x3D1F; _KOi3_r[0] = 0xDB8C; _KOi3_r[1] = 0x763E; _KOi3_r[2] = 0x719C; _KOi3_r[3] = 0x87C5; _KOi3_r[4] = 0x1878; _KOi3_r[5] = 0x96BF; _KOi3_r[6] = 0xA3E7; _KOi3_r[7] = 0xDD14; _KIi1_r[0] = 0xE1E1; _KIi1_r[1] = 0x523E; _KIi1_r[2] = 0xAA32; _KIi1_r[3] = 0x83E3; _KIi1_r[4] = 0x8DC0; _KIi1_r[5] = 0x7B4B; _KIi1_r[6] = 0x4A6B; _KIi1_r[7] = 0x7813; _KIi2_r[0] = 0x7813; _KIi2_r[1] = 0xE1E1; _KIi2_r[2] = 0x523E; _KIi2_r[3] = 0xAA32; _KIi2_r[4] = 0x83E3; _KIi2_r[5] = 0x8DC0; _KIi2_r[6] = 0x7B4B; _KIi2_r[7] = 0x4A6B; _KIi3_r[0] = 0x83E3; _KIi3_r[1] = 0x8DC0; _KIi3_r[2] = 0x7B4B; _KIi3_r[3] = 0x4A6B; _KIi3_r[4] = 0x7813; _KIi3_r[5] = 0xE1E1; _KIi3_r[6] = 0x523E; _KIi3_r[7] = 0xAA32; test_expansion(_test_key2, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r); if (0xDE551988CEB2F9B7 == _kasumi(0xD3C5D592327FB11C, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3)) printf("OK."); else printf("FAILED!"); printf("\nKASUMI Test Set 3..."); uint8_t _test_key3[] = {0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1, 0xA8, 0xFF, 0x86, 0x67, 0xB1, 0x71, 0x40, 0x13}; _KLi1_r[0] = 0x806A; _KLi1_r[1] = 0x8CD1; _KLi1_r[2] = 0x15F0; _KLi1_r[3] = 0x8DA3; _KLi1_r[4] = 0x51FF; _KLi1_r[5] = 0x0CCF; _KLi1_r[6] = 0x62E3; _KLi1_r[7] = 0x8026; _KLi2_r[0] = 0x8353; _KLi2_r[1] = 0x0B3E; _KLi2_r[2] = 0x5623; _KLi2_r[3] = 0x3CFF; _KLi2_r[4] = 0xC725; _KLi2_r[5] = 0x7203; _KLi2_r[6] = 0x4116; _KLi2_r[7] = 0x830F; _KOi1_r[0] = 0xCD18; _KOi1_r[1] = 0x5F01; _KOi1_r[2] = 0xDA38; _KOi1_r[3] = 0x1FF5; _KOi1_r[4] = 0xCCF0; _KOi1_r[5] = 0x2E36; _KOi1_r[6] = 0x0268; _KOi1_r[7] = 0x06A8; _KOi2_r[0] = 0x6786; _KOi2_r[1] = 0x71B1; _KOi2_r[2] = 0x1340; _KOi2_r[3] = 0x3540; _KOi2_r[4] = 0x68C6; _KOi2_r[5] = 0xF80A; _KOi2_r[6] = 0xD1C6; _KOi2_r[7] = 0xFFA8; _KOi3_r[0] = 0x362E; _KOi3_r[1] = 0x6802; _KOi3_r[2] = 0xA806; _KOi3_r[3] = 0x18CD; _KOi3_r[4] = 0x015F; _KOi3_r[5] = 0x38DA; _KOi3_r[6] = 0xF51F; _KOi3_r[7] = 0xF0CC; _KIi1_r[0] = 0x5623; _KIi1_r[1] = 0x3CFF; _KIi1_r[2] = 0xC725; _KIi1_r[3] = 0x7203; _KIi1_r[4] = 0x4116; _KIi1_r[5] = 0x830F; _KIi1_r[6] = 0x8353; _KIi1_r[7] = 0x0B3E; _KIi2_r[0] = 0x0B3E; _KIi2_r[1] = 0x5623; _KIi2_r[2] = 0x3CFF; _KIi2_r[3] = 0xC725; _KIi2_r[4] = 0x7203; _KIi2_r[5] = 0x4116; _KIi2_r[6] = 0x830F; _KIi2_r[7] = 0x8353; _KIi3_r[0] = 0x7203; _KIi3_r[1] = 0x4116; _KIi3_r[2] = 0x830F; _KIi3_r[3] = 0x8353; _KIi3_r[4] = 0x0B3E; _KIi3_r[5] = 0x5623; _KIi3_r[6] = 0x3CFF; _KIi3_r[7] = 0xC725; test_expansion(_test_key3, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3, _KLi1_r, _KLi2_r, _KOi1_r, _KOi2_r, _KOi3_r, _KIi1_r, _KIi2_r, _KIi3_r); if (0x4592B0E78690F71B == _kasumi(0x62A540981BA6F9B7, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3)) printf("OK."); else printf("FAILED!"); printf("\nKASUMI Test Set 4..."); uint8_t _test_key4[] = {0x3A, 0x3B, 0x39, 0xB5, 0xC3, 0xF2, 0x37, 0x6D, 0x69, 0xF7, 0xD5, 0x46, 0xE5, 0xF8, 0x5D, 0x43}; uint64_t I4 = 0xCA49C1C75771AB0B, i; _kasumi_key_expand(_test_key4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3); for (i = 0; i < 50; i++) I4 = _kasumi(I4, _KLi1, _KLi2, _KOi1, _KOi2, _KOi3, _KIi1, _KIi2, _KIi3); if (0x738BAD4C4A690802 == I4) printf(" OK.\n"); else printf("FAILED!"); uint8_t gamma[32]; uint8_t _Key1[] = {0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00, 0x2B, 0xD6, 0x45, 0x9F, 0x82, 0xC5, 0xBC, 0x00}, _gamma1[] = {0x88, 0x9E, 0xEA, 0xAF, 0x9E, 0xD1, 0xBA, 0x1A, 0xBB, 0xD8, 0x43, 0x62, 0x32, 0xE4, 0x57, 0x28, 0xD0, 0x1A, 0xA8, 0x91, 0x33, 0xDA, 0x73, 0xC1, 0x1E, 0xAB, 0x68, 0xB7, 0xD8, 0x9B, 0xC8, 0x41}; _kasumi_kgcore(0xF, 0, 0x0024F20F, 0, _Key1, gamma, 228); printf ("KGCORE Test Set 1: %d\n", _compare_mem(gamma, _gamma1, 32)); uint8_t _Key2[] = {0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48, 0x95, 0x2C, 0x49, 0x10, 0x48, 0x81, 0xFF, 0x48}, _gamma2[] = {0xFB, 0x4D, 0x5F, 0xBC, 0xEE, 0x13, 0xA3, 0x33, 0x89, 0x28, 0x56, 0x86, 0xE9, 0xA5, 0xC9, 0x42, 0x40, 0xDE, 0x38, 0x15, 0x01, 0x15, 0xF1, 0x5F, 0x8D, 0x9D, 0x98, 0xB9, 0x1A, 0x94, 0xB2, 0x96}; _kasumi_kgcore(0xF, 0, 0x00061272, 0, _Key2, gamma, 228); printf ("KGCORE Test Set 2: %d\n", _compare_mem(gamma, _gamma2, 32)); uint8_t _Key3[] = {0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A, 0xEF, 0xA8, 0xB2, 0x22, 0x9E, 0x72, 0x0C, 0x2A}, _gamma3[] = {0x0E, 0x40, 0x15, 0x75, 0x5A, 0x33, 0x64, 0x69, 0xC3, 0xDD, 0x86, 0x80, 0xE3, 0x03, 0x5B, 0xC4, 0x19, 0xA7, 0x8A, 0xD3, 0x86, 0x2C, 0x10, 0x90, 0xC6, 0x8A, 0x39, 0x1F, 0xE8, 0xA6, 0xAD, 0xEB}; _kasumi_kgcore(0xF, 0, 0x0033FD3F, 0, _Key3, gamma, 228); printf ("KGCORE Test Set 3: %d\n", _compare_mem(gamma, _gamma3, 32)); uint8_t _Key4[] = {0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D, 0x51, 0x20, 0x4E, 0xA5, 0x5A, 0xCB, 0x1D, 0x64, 0x4C, 0x0D}, _gamma4[] = {0xE0, 0x95, 0x30, 0x6A, 0xD5, 0x08, 0x6E, 0x2E, 0xAC, 0x7F, 0x31, 0x07, 0xDE, 0x4F, 0xA2, 0x2D, 0xC1, 0xDF, 0xC9, 0x7D, 0x5B, 0xC5, 0x66, 0x1D, 0xD6, 0x09, 0x6F, 0x47, 0x6A, 0xED, 0xC6, 0x4B}; _kasumi_kgcore(0xF, 0, 0x00156B26, 0, _Key4, gamma, 228); printf ("KGCORE Test Set 4: %d\n", _compare_mem(gamma, _gamma4, 32)); uint8_t _Key5[] = {0xD3, 0xC5, 0xD5, 0x92, 0x32, 0x7F, 0xB1, 0x1C, 0x40, 0x35, 0xC6, 0x68, 0x0A, 0xF8, 0xC6, 0xD1}, _gamma5[] = {0xDC, 0xE6, 0x43, 0x62, 0xAB, 0x5F, 0x89, 0xC1, 0x1E, 0xF0, 0xB3, 0x05, 0x16, 0x65, 0x70, 0xF4, 0x88, 0x9D, 0x55, 0x11, 0xE9, 0xE3, 0x57, 0x5D, 0x06, 0x2B, 0x5C, 0xED, 0x60, 0x39, 0x50, 0x6A}; _kasumi_kgcore(0xF, 0, 0x000A59B4, 0, _Key5, gamma, 228); printf ("KGCORE Test Set 5: %d\n", _compare_mem(gamma, _gamma5, 32)); return 0; } libosmocore-0.9.0/tests/kasumi/kasumi_test.ok000066400000000000000000000004301261607044000213500ustar00rootroot00000000000000testing KASUMI key expansion and encryption (ETSI TS 135 203): KASUMI Test Set 1... OK. OK. KASUMI Test Set 2... OK. OK. KASUMI Test Set 3... OK. OK. KASUMI Test Set 4... OK. KGCORE Test Set 1: 1 KGCORE Test Set 2: 1 KGCORE Test Set 3: 1 KGCORE Test Set 4: 1 KGCORE Test Set 5: 1 libosmocore-0.9.0/tests/lapd/000077500000000000000000000000001261607044000161175ustar00rootroot00000000000000libosmocore-0.9.0/tests/lapd/lapd_test.c000066400000000000000000000524101261607044000202440ustar00rootroot00000000000000/* * (C) 2011 by Holger Hans Peter Freyther * (C) 2011 by On-Waves * (C) 2014 by Daniel Willmann * 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 #include #include #include #include #include #include #define CHECK_RC(rc) \ if (rc != 0) { \ printf("Operation failed rc=%d on %s:%d\n", rc, __FILE__, __LINE__); \ abort(); \ } static struct log_info info = {}; static int dummy_l1_header_len = 0; struct lapdm_polling_state { struct lapdm_channel *bts; int bts_read; struct lapdm_channel *ms; int ms_read; }; static struct msgb *msgb_from_array(const uint8_t *data, int len) { struct msgb *msg = msgb_alloc_headroom(4096, 128, "data"); msg->l3h = msgb_put(msg, len); memcpy(msg->l3h, data, len); return msg; } /* * Test data is below... */ static const uint8_t cm[] = { 0x05, 0x24, 0x31, 0x03, 0x50, 0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, }; static const uint8_t ua[] = { 0x01, 0x73, 0x41, 0x05, 0x24, 0x31, 0x03, 0x50, 0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2b, 0x2b, 0x2b, 0x2b }; static const uint8_t mm[] = { 0x00, 0x0c, 0x00, 0x03, 0x01, 0x01, 0x20, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x05, 0x04, 0x0d }; static const uint8_t dummy1[] = { 0xab, 0x03, 0x30, 0x60, 0x06, }; static const uint8_t rel_req[] = { 0x02, 0x07, 0x01, 0x0a, 0x02, 0x40, 0x14, 0x01 }; static const uint8_t est_req_sdcch_sapi3[] = { 0x02, 0x04, 0x01, 0x20, 0x02, 0x03 }; static const uint8_t est_req_sacch_sapi3[] = { 0x02, 0x04, 0x01, 0x0b, 0x02, 0x43 }; static struct msgb *create_cm_serv_req(void) { struct msgb *msg; msg = msgb_from_array(cm, sizeof(cm)); rsl_rll_push_l3(msg, RSL_MT_EST_REQ, 0, 0, 1); msgb_push(msg, dummy_l1_header_len); return msg; } static struct msgb *create_mm_id_req(void) { struct msgb *msg; msg = msgb_from_array(mm, sizeof(mm)); msg->l2h = msg->data + 3; OSMO_ASSERT(msgb_l2len(msg) == 12); msg->l3h = msg->l2h + 6; OSMO_ASSERT(msgb_l3len(msg) == 6); msgb_push(msg, dummy_l1_header_len); return msg; } static struct msgb *create_empty_msg(void) { struct msgb *msg; msg = msgb_from_array(NULL, 0); OSMO_ASSERT(msgb_l3len(msg) == 0); rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1); msgb_push(msg, dummy_l1_header_len); return msg; } static struct msgb *create_dummy_data_req(void) { struct msgb *msg; msg = msgb_from_array(dummy1, sizeof(dummy1)); rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, 0, 0, 1); msgb_push(msg, dummy_l1_header_len); return msg; } static struct msgb *create_rel_req(void) { struct msgb *msg; msg = msgb_from_array(rel_req, sizeof(rel_req)); msg->l2h = msg->data; msgb_push(msg, dummy_l1_header_len); msg->l3h = msg->l2h + sizeof(struct abis_rsl_rll_hdr); return msg; } static struct msgb *create_est_req(const uint8_t *est_req, size_t est_req_size) { struct msgb *msg; msg = msgb_from_array(est_req, est_req_size); msg->l2h = msg->data; msgb_push(msg, dummy_l1_header_len); msg->l3h = msg->l2h + sizeof(struct abis_rsl_rll_hdr); return msg; } static int send(struct msgb *in_msg, struct lapdm_channel *chan) { struct osmo_phsap_prim pp; struct msgb *msg; int rc; msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind"); osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, msg); /* copy over actual MAC block */ msg->l2h = msgb_put(msg, msgb_l2len(in_msg)); memcpy(msg->l2h, in_msg->l2h, msgb_l2len(in_msg)); /* LAPDm requires those... */ pp.u.data.chan_nr = 0; pp.u.data.link_id = 0; /* feed into the LAPDm code of libosmogsm */ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch); OSMO_ASSERT(rc == 0 || rc == -EBUSY); return 0; } /* Receive from L1 */ static int send_buf(const uint8_t *buf, size_t len, struct lapdm_channel *chan) { struct osmo_phsap_prim pp; struct msgb *msg; int rc; msg = msgb_from_array(buf, len); msg->l2h = msg->l3h; msg->l3h = NULL; osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, msg); /* LAPDm requires those... */ pp.u.data.chan_nr = 0; pp.u.data.link_id = 0; /* feed into the LAPDm code of libosmogsm */ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch); OSMO_ASSERT(rc == 0 || rc == -EBUSY); return 0; } /* Receive from L3 */ static int enqueue_buf(const uint8_t *buf, size_t len, int sapi, struct lapdm_channel *chan) { struct osmo_dlsap_prim dp; struct msgb *msg; int rc; struct lapdm_datalink *dl; dl = lapdm_datalink_for_sapi(&chan->lapdm_dcch, sapi); OSMO_ASSERT(dl); msg = msgb_from_array(buf, len); osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg); rc = lapd_recv_dlsap(&dp, &dl->dl.lctx); OSMO_ASSERT(rc == 0 || rc == -EBUSY); return 0; } static int send_sabm(struct lapdm_channel *chan, int sapi, const uint8_t *data, size_t len) { struct osmo_phsap_prim pp; struct msgb *msg; int rc; OSMO_ASSERT(sapi == 0 || sapi == 3); msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind"); osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, msg); /* copy over actual MAC block */ msg->l2h = msgb_put(msg, 3 + len); msg->l2h[0] = 0x01 | (sapi << 2); msg->l2h[1] = 0x3f; msg->l2h[2] = 0x01 | (len << 2); if (len > 0) memcpy(msg->l2h + 3, data, len); /* LAPDm requires those... */ pp.u.data.chan_nr = 0; pp.u.data.link_id = 0; /* feed into the LAPDm code of libosmogsm */ rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch); OSMO_ASSERT(rc == 0 || rc == -EBUSY); return 0; } /* * I get called from the LAPDm code when something was sent my way... */ static int bts_to_ms_tx_cb(struct msgb *in_msg, struct lapdm_entity *le, void *_ctx) { struct lapdm_polling_state *state = _ctx; printf("%s: MS->BTS(us) message %d\n", __func__, msgb_length(in_msg)); if (state->bts_read == 0) { printf("BTS: Verifying CM request.\n"); OSMO_ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(cm)); OSMO_ASSERT(memcmp(in_msg->l3h, cm, ARRAY_SIZE(cm)) == 0); } else if (state->bts_read == 1) { printf("BTS: Verifying dummy message.\n"); OSMO_ASSERT(msgb_l3len(in_msg) == ARRAY_SIZE(dummy1)); OSMO_ASSERT(memcmp(in_msg->l3h, dummy1, ARRAY_SIZE(dummy1)) == 0); } else { printf("BTS: Do not know to verify: %d\n", state->bts_read); } state->bts_read += 1; msgb_free(in_msg); return 0; } static int ms_to_bts_l1_cb(struct osmo_prim_hdr *oph, void *_ctx) { int rc; struct lapdm_polling_state *state = _ctx; printf("%s: MS(us) -> BTS prim message\n", __func__); /* i stuff it into the LAPDm channel of the BTS */ rc = send(oph->msg, state->bts); msgb_free(oph->msg); return rc; } static int dequeue_prim(struct lapdm_entity *le, struct osmo_phsap_prim *pp, const char *queue_name) { int rc; int l2_header_len; int l3_len = 0; /* Take message from queue */ rc = lapdm_phsap_dequeue_prim(le, pp); fprintf(stderr, "dequeue: got rc %d: %s\n", rc, rc <= 0 ? strerror(-rc) : "-"); if (rc < 0) return rc; l2_header_len = msgb_l2len(pp->oph.msg); if (msgb_l3(pp->oph.msg)) { l3_len = msgb_l3len(pp->oph.msg); l2_header_len -= l3_len; } else fprintf(stderr, "MSGB: L3 is undefined\n"); if (l2_header_len < 0 || l2_header_len > pp->oph.msg->data_len) { fprintf(stderr, "MSGB inconsistent: data = %p, l2 = %p, l3 = %p, tail = %p\n", pp->oph.msg->data, pp->oph.msg->l2h, pp->oph.msg->l3h, pp->oph.msg->tail); l2_header_len = -1; } printf("Took message from %s queue: " "L2 header size %d, L3 size %d, " "SAP %#x, %d/%d, Link 0x%02x\n", queue_name, l2_header_len, l3_len, pp->oph.sap, pp->oph.primitive, pp->oph.operation, pp->u.data.link_id); printf("Message: %s\n", msgb_hexdump(pp->oph.msg)); return rc; } static int ms_to_bts_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *_ctx) { struct lapdm_polling_state *state = _ctx; printf("%s: BTS->MS(us) message %d\n", __func__, msgb_length(msg)); if (state->ms_read == 0) { struct abis_rsl_rll_hdr hdr; printf("MS: Verifying incoming primitive.\n"); OSMO_ASSERT(msg->len == sizeof(struct abis_rsl_rll_hdr) + 3); /* verify the header */ memset(&hdr, 0, sizeof(hdr)); rsl_init_rll_hdr(&hdr, RSL_MT_EST_CONF); hdr.c.msg_discr |= ABIS_RSL_MDISC_TRANSP; OSMO_ASSERT(memcmp(msg->data, &hdr, sizeof(hdr)) == 0); /* Verify the added RSL_IE_L3_INFO but we have a bug here */ OSMO_ASSERT(msg->data[6] == RSL_IE_L3_INFO); #warning "RSL_IE_L3_INFO 16 bit length is wrong" /* This should be okay but it is actually 0x0, 0x9c on ia-32 */ /* OSMO_ASSERT(msg->data[7] == 0x0 && msg->data[8] == 0x0); */ } else if (state->ms_read == 1) { printf("MS: Verifying incoming MM message: %d\n", msgb_l3len(msg)); OSMO_ASSERT(msgb_l3len(msg) == 3); OSMO_ASSERT(memcmp(msg->l3h, &mm[12], msgb_l3len(msg)) == 0); } else { printf("MS: Do not know to verify: %d\n", state->ms_read); } state->ms_read += 1; msgb_free(msg); return 0; } static void test_lapdm_polling() { printf("I do some very simple LAPDm test.\n"); int rc; struct lapdm_polling_state test_state; struct osmo_phsap_prim pp; /* Configure LAPDm on both sides */ struct lapdm_channel bts_to_ms_channel; struct lapdm_channel ms_to_bts_channel; memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel)); memset(&ms_to_bts_channel, 0, sizeof(ms_to_bts_channel)); memset(&test_state, 0, sizeof(test_state)); test_state.bts = &bts_to_ms_channel; test_state.ms = &ms_to_bts_channel; /* BTS to MS in polling mode */ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS); lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY); lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state); lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state); /* MS to BTS in direct mode */ lapdm_channel_init(&ms_to_bts_channel, LAPDM_MODE_MS); lapdm_channel_set_l1(&ms_to_bts_channel, ms_to_bts_l1_cb, &test_state); lapdm_channel_set_l3(&ms_to_bts_channel, ms_to_bts_tx_cb, &test_state); /* * We try to send messages from the MS to the BTS to the MS.. */ /* 1. Start with MS -> BTS, BTS should have a pending message */ printf("Establishing link.\n"); lapdm_rslms_recvmsg(create_cm_serv_req(), &ms_to_bts_channel); /* 2. Poll on the BTS for sending out a confirmation */ printf("\nConfirming\n"); OSMO_ASSERT(test_state.bts_read == 1); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(pp.oph.msg->data == pp.oph.msg->l2h); send(pp.oph.msg, &ms_to_bts_channel); msgb_free(pp.oph.msg); OSMO_ASSERT(test_state.ms_read == 1); /* 3. Send some data to the MS */ printf("\nSending back to MS\n"); lapdm_rslms_recvmsg(create_mm_id_req(), &bts_to_ms_channel); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); send(pp.oph.msg, &ms_to_bts_channel); msgb_free(pp.oph.msg); OSMO_ASSERT(test_state.ms_read == 2); /* verify that there is nothing more to poll */ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); OSMO_ASSERT(rc < 0); /* 3. And back to the BTS */ printf("\nSending back to BTS\n"); OSMO_ASSERT(test_state.ms_read == 2); lapdm_rslms_recvmsg(create_dummy_data_req(), &ms_to_bts_channel); /* 4. And back to the MS, but let's move data/l2h apart */ OSMO_ASSERT(test_state.bts_read == 2); OSMO_ASSERT(test_state.ms_read == 2); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); send(pp.oph.msg, &ms_to_bts_channel); OSMO_ASSERT(test_state.ms_read == 2); msgb_free(pp.oph.msg); /* verify that there is nothing more to poll */ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); OSMO_ASSERT(rc < 0); rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH"); OSMO_ASSERT(rc < 0); /* check sending an empty L3 message fails */ rc = lapdm_rslms_recvmsg(create_empty_msg(), &bts_to_ms_channel); OSMO_ASSERT(rc == -1); OSMO_ASSERT(test_state.ms_read == 2); /* clean up */ lapdm_channel_exit(&bts_to_ms_channel); lapdm_channel_exit(&ms_to_bts_channel); /* Check if exit is idempotent */ lapdm_channel_exit(&bts_to_ms_channel); lapdm_channel_exit(&ms_to_bts_channel); } static void test_lapdm_contention_resolution() { printf("I test contention resultion by having two mobiles collide and " "first mobile repeating SABM.\n"); int rc; struct lapdm_polling_state test_state; struct osmo_phsap_prim pp; uint8_t *cm2; /* Configure LAPDm on both sides */ struct lapdm_channel bts_to_ms_channel; memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel)); memset(&test_state, 0, sizeof(test_state)); test_state.bts = &bts_to_ms_channel; /* BTS to MS in polling mode */ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS); lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY); lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state); lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state); /* Send SABM MS 1, we must get UA */ send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm)); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0); /* Send SABM MS 2, we must get nothing, due to collision */ cm2 = malloc(sizeof(cm)); memcpy(cm2, cm, sizeof(cm)); cm2[0] += 1; send_sabm(&bts_to_ms_channel, 0, cm2, sizeof(cm2)); free(cm2); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); OSMO_ASSERT(rc == -ENODEV); /* Send SABM MS 1 again, we must get UA gain */ send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm)); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0); /* clean up */ lapdm_channel_exit(&bts_to_ms_channel); /* idempotent */ lapdm_channel_exit(&bts_to_ms_channel); } static void test_lapdm_early_release() { printf("I test RF channel release of an unestablished channel.\n"); int rc; struct lapdm_polling_state test_state; /* Configure LAPDm on both sides */ struct lapdm_channel bts_to_ms_channel; memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel)); memset(&test_state, 0, sizeof(test_state)); test_state.bts = &bts_to_ms_channel; /* BTS to MS in polling mode */ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS); lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY); lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state); lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state); /* Send the release request */ rc = lapdm_rslms_recvmsg(create_rel_req(), &bts_to_ms_channel); OSMO_ASSERT(rc == -EINVAL); /* clean up */ lapdm_channel_exit(&bts_to_ms_channel); /* Check if exit is idempotent */ lapdm_channel_exit(&bts_to_ms_channel); } static void lapdm_establish(const uint8_t *est_req, size_t est_req_size) { int rc; struct lapdm_polling_state test_state; struct osmo_phsap_prim pp; struct msgb *msg; /* Configure LAPDm on both sides */ struct lapdm_channel bts_to_ms_channel; memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel)); memset(&test_state, 0, sizeof(test_state)); test_state.bts = &bts_to_ms_channel; /* BTS to MS in polling mode */ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS); lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY); lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state); lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state); /* Send the release request */ msg = create_est_req(est_req, est_req_size); rc = lapdm_rslms_recvmsg(msg, &bts_to_ms_channel); fprintf(stderr, "recvmsg: got rc %d: %s\n", rc, rc <= 0 ? strerror(-rc) : "???"); OSMO_ASSERT(rc == 0); /* Take message from queue */ rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); if (rc < 0) rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH"); CHECK_RC(rc); OSMO_ASSERT(pp.oph.msg->data == msgb_l2(pp.oph.msg)); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); OSMO_ASSERT(rc < 0); rc = dequeue_prim(&bts_to_ms_channel.lapdm_acch, &pp, "ACCH"); OSMO_ASSERT(rc < 0); /* clean up */ lapdm_channel_exit(&bts_to_ms_channel); /* idempotent */ lapdm_channel_exit(&bts_to_ms_channel); } static void test_lapdm_establishment() { printf("I test RF channel establishment.\n"); printf("Testing SAPI3/SDCCH\n"); lapdm_establish(est_req_sdcch_sapi3, sizeof(est_req_sdcch_sapi3)); printf("Testing SAPI3/SACCH\n"); lapdm_establish(est_req_sacch_sapi3, sizeof(est_req_sacch_sapi3)); } const uint8_t cm_chg[] = { 0x01, 0x00, 0x49, 0x06, 0x16, 0x03, 0x33, 0x59, 0xa6, 0x20, 0x0a, 0x20, 0x04, 0x04, 0x2f, 0x65, 0x23, 0x02, 0x00, 0x24, 0x04 }; const uint8_t cm_chg_ack[] = { 0x01, 0x21, 0x01 }; const uint8_t gprs_susp[] = { 0x01, 0x02, 0x35, 0x06, 0x34, 0xe3, 0xd4, 0xd2, 0x6f, 0x09, 0xf1, 0x07, 0x00, 0x01, 0x00, 0x02 }; const uint8_t cipher_cmd[] = { 0x06, 0x35, 0x01 }; /* The cipher command we send to the MS after updating our N(R) */ const uint8_t cipher_cmd_out[] = { 0x03, 0x40, 0x0d, 0x06, 0x35, 0x01 }; uint8_t cipher_compl[] = { 0x01, 0x24, 0x09, 0x06, 0x32 }; uint8_t cipher_compl_ack[] = { 0x01, 0x61, 0x01 }; static const uint8_t ua_sms[] = { 0x0d, 0x73, 0x01 }; const uint8_t cp_data_1[] = { 0x0d, 0x00, 0x53, 0x59, 0x01, 0x5c, 0x00, 0x4c, 0x00, 0x06, 0x91, 0x86, 0x77, 0x07, 0x00, 0xf9, 0x51, 0x11, 0x76, 0x05, 0x81, 0x29, 0x32 }; const uint8_t cp_data_1_ack[] = { 0x0d, 0x21, 0x01 }; static int bts_to_ms_dummy_tx_cb(struct msgb *in_msg, struct lapdm_entity *le, void *_ctx) { printf("%s: MS->BTS(us) message %d\n", __func__, msgb_length(in_msg)); msgb_free(in_msg); return 0; } static void dump_queue(struct llist_head *head) { struct msgb* msg; printf("\nDumping queue:\n"); llist_for_each_entry(msg, head, list) printf("%s\n", msgb_hexdump(msg)); printf("\n"); } static void test_lapdm_desync() { printf("I test if desync problems exist in LAPDm\n"); int rc; struct osmo_phsap_prim pp; /* Configure LAPDm on both sides */ struct lapdm_channel bts_to_ms_channel; memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel)); /* BTS to MS in polling mode */ lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS); lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY); lapdm_channel_set_l1(&bts_to_ms_channel, NULL, NULL); lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_dummy_tx_cb, NULL); struct lapdm_datalink *dl = lapdm_datalink_for_sapi(&bts_to_ms_channel.lapdm_dcch, 0); dl->mctx.dl = dl; dl->dl.lctx.dl = &dl->dl; /* Send SABM MS 1, we must get UA */ printf("\nEstablishing SAPI=0\n"); send_sabm(&bts_to_ms_channel, 0, cm, sizeof(cm)); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0); printf("\nSending Classmark Change\n"); send_buf(cm_chg, sizeof(cm_chg), &bts_to_ms_channel); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cm_chg_ack, ARRAY_SIZE(cm_chg_ack)) == 0); printf("\nEnqueueing Ciphering Mode Command\n"); enqueue_buf(cipher_cmd, sizeof(cipher_cmd), 0, &bts_to_ms_channel); dump_queue(&dl->dl.tx_queue); printf("\nSending GPRS Suspend Request\n"); send_buf(gprs_susp, sizeof(gprs_susp), &bts_to_ms_channel); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cipher_cmd_out, ARRAY_SIZE(cipher_cmd_out)) == 0); printf("\nSending Cipher Mode Complete\n"); send_buf(cipher_compl, sizeof(cipher_compl), &bts_to_ms_channel); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cipher_compl_ack, ARRAY_SIZE(cipher_compl_ack)) == 0); printf("\nEstablishing SAPI=3\n"); send_sabm(&bts_to_ms_channel, 3, NULL, 0); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua_sms, ARRAY_SIZE(ua_sms)) == 0); printf("\nSending CP-DATA\n"); send_buf(cp_data_1, sizeof(cp_data_1), &bts_to_ms_channel); dump_queue(&dl->dl.tx_queue); rc = dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp, "DCCH"); CHECK_RC(rc); OSMO_ASSERT(memcmp(pp.oph.msg->l2h, cp_data_1_ack, ARRAY_SIZE(cp_data_1_ack)) == 0); /* clean up */ lapdm_channel_exit(&bts_to_ms_channel); /* Check if exit is idempotent */ lapdm_channel_exit(&bts_to_ms_channel); } int main(int argc, char **argv) { osmo_init_logging(&info); /* Prevent the test from segfaulting */ dummy_l1_header_len = 0; test_lapdm_polling(); dummy_l1_header_len = 3; test_lapdm_early_release(); test_lapdm_contention_resolution(); test_lapdm_establishment(); test_lapdm_desync(); printf("Success.\n"); return 0; } libosmocore-0.9.0/tests/lapd/lapd_test.ok000066400000000000000000000075271261607044000204440ustar00rootroot00000000000000I do some very simple LAPDm test. Establishing link. ms_to_bts_l1_cb: MS(us) -> BTS prim message bts_to_ms_tx_cb: MS->BTS(us) message 25 BTS: Verifying CM request. Confirming Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b ms_to_bts_tx_cb: BTS->MS(us) message 9 MS: Verifying incoming primitive. Sending back to MS Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 03 00 0d [L3]> 05 04 0d 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b ms_to_bts_tx_cb: BTS->MS(us) message 12 MS: Verifying incoming MM message: 3 ms_to_bts_l1_cb: MS(us) -> BTS prim message Sending back to BTS ms_to_bts_l1_cb: MS(us) -> BTS prim message bts_to_ms_tx_cb: MS->BTS(us) message 14 BTS: Verifying dummy message. Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b I test RF channel release of an unestablished channel. I test contention resultion by having two mobiles collide and first mobile repeating SABM. bts_to_ms_tx_cb: MS->BTS(us) message 25 BTS: Verifying CM request. Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b I test RF channel establishment. Testing SAPI3/SDCCH Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03 Message: [L2]> 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Testing SAPI3/SACCH Took message from ACCH queue: L2 header size 5, L3 size 18, SAP 0x1000000, 0/0, Link 0x43 Message: [L2]> 00 00 0f 3f 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b I test if desync problems exist in LAPDm Establishing SAPI=0 bts_to_ms_dummy_tx_cb: MS->BTS(us) message 25 Dumping queue: 00 00 17 [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 73 41 [L3]> 05 24 31 03 50 18 93 08 29 47 80 00 00 00 00 80 2b 2b 2b 2b Sending Classmark Change bts_to_ms_dummy_tx_cb: MS->BTS(us) message 27 Dumping queue: 00 00 17 [L2]> 01 21 01 Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Enqueueing Ciphering Mode Command Dumping queue: 00 00 17 [L2]> 03 20 0d [L3]> 06 35 01 Sending GPRS Suspend Request bts_to_ms_dummy_tx_cb: MS->BTS(us) message 22 Dumping queue: 00 00 17 [L2]> 03 40 0d [L3]> 06 35 01 Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 03 40 0d [L3]> 06 35 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Sending Cipher Mode Complete bts_to_ms_dummy_tx_cb: MS->BTS(us) message 11 Dumping queue: 00 00 17 [L2]> 01 61 01 Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x00 Message: [L2]> 01 61 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Establishing SAPI=3 bts_to_ms_dummy_tx_cb: MS->BTS(us) message 6 Dumping queue: Took message from DCCH queue: L2 header size 3, L3 size 20, SAP 0x1000000, 0/0, Link 0x03 Message: [L2]> 0d 73 01 [L3]> 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Sending CP-DATA Dumping queue: Took message from DCCH queue: L2 header size 23, L3 size 0, SAP 0x1000000, 0/0, Link 0x03 Message: [L2]> 0d 21 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b Success. libosmocore-0.9.0/tests/logging/000077500000000000000000000000001261607044000166255ustar00rootroot00000000000000libosmocore-0.9.0/tests/logging/logging_test.c000066400000000000000000000047101261607044000214600ustar00rootroot00000000000000/* simple test for the debug interface */ /* * (C) 2008, 2009 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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include enum { DRLL, DCC, DMM, }; static int filter_called = 0; static const struct log_info_cat default_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = NULL, .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; static int test_filter(const struct log_context *ctx, struct log_target *target) { filter_called += 1; /* omit everything */ return 0; } const struct log_info log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), .filter_fn = test_filter, }; int main(int argc, char **argv) { struct log_target *stderr_target; log_init(&log_info, NULL); stderr_target = log_target_create_stderr(); log_add_target(stderr_target); log_set_all_filter(stderr_target, 1); log_set_print_filename(stderr_target, 0); log_parse_category_mask(stderr_target, "DRLL:DCC"); log_parse_category_mask(stderr_target, "DRLL"); DEBUGP(DCC, "You should not see this\n"); log_parse_category_mask(stderr_target, "DRLL:DCC"); DEBUGP(DRLL, "You should see this\n"); DEBUGP(DCC, "You should see this\n"); DEBUGP(DMM, "You should not see this\n"); OSMO_ASSERT(filter_called == 0); log_set_all_filter(stderr_target, 0); DEBUGP(DRLL, "You should not see this and filter is called\n"); OSMO_ASSERT(filter_called == 1); return 0; } libosmocore-0.9.0/tests/logging/logging_test.err000066400000000000000000000001001261607044000220130ustar00rootroot00000000000000You should see this You should see this libosmocore-0.9.0/tests/logging/logging_test.ok000066400000000000000000000000001261607044000216330ustar00rootroot00000000000000libosmocore-0.9.0/tests/loggingrb/000077500000000000000000000000001261607044000171515ustar00rootroot00000000000000libosmocore-0.9.0/tests/loggingrb/logging_test.err000066400000000000000000000001001261607044000223370ustar00rootroot00000000000000You should see this You should see this libosmocore-0.9.0/tests/loggingrb/logging_test.ok000066400000000000000000000000001261607044000221570ustar00rootroot00000000000000libosmocore-0.9.0/tests/loggingrb/loggingrb_test.c000066400000000000000000000045641261607044000223370ustar00rootroot00000000000000/* simple test for the debug interface */ /* * (C) 2008, 2009 by Holger Hans Peter Freyther * (C) 2012-2013 by Katerina Barone-Adesi * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include enum { DRLL, DCC, DMM, }; static const struct log_info_cat default_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = NULL, .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; int main(int argc, char **argv) { struct log_target *ringbuf_target; log_init(&log_info, NULL); ringbuf_target = log_target_create_rb(0x1000); log_add_target(ringbuf_target); log_set_all_filter(ringbuf_target, 1); log_set_print_filename(ringbuf_target, 0); log_parse_category_mask(ringbuf_target, "DRLL:DCC"); log_parse_category_mask(ringbuf_target, "DRLL"); DEBUGP(DCC, "You should not see this\n"); log_parse_category_mask(ringbuf_target, "DRLL:DCC"); DEBUGP(DRLL, "You should see this\n"); DEBUGP(DCC, "You should see this\n"); DEBUGP(DMM, "You should not see this\n"); fprintf(stderr, "%s", log_target_rb_get(ringbuf_target, 0)); fprintf(stderr, "%s", log_target_rb_get(ringbuf_target, 1)); OSMO_ASSERT(!log_target_rb_get(ringbuf_target, 2)); return 0; } libosmocore-0.9.0/tests/msgfile/000077500000000000000000000000001261607044000166255ustar00rootroot00000000000000libosmocore-0.9.0/tests/msgfile/msgconfig.cfg000066400000000000000000000000441261607044000212600ustar00rootroot00000000000000# This is a comment *:*::Hello Welt libosmocore-0.9.0/tests/msgfile/msgfile_test.c000066400000000000000000000027061261607044000214630ustar00rootroot00000000000000/* * (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 #include #include static void dump_entries(struct osmo_config_list *entries) { struct osmo_config_entry *entry; if (!entries) { fprintf(stderr, "Failed to parse the file\n"); return; } llist_for_each_entry(entry, &entries->entry, list) { printf("Entry '%s:%s:%s:%s'\n", entry->mcc, entry->mnc, entry->option, entry->text); } } int main(int argc, char **argv) { struct osmo_config_list *entries; /* todo use msgfile_test.c.in and replace the path */ entries = osmo_config_list_parse(NULL, "msgconfig.cfg"); dump_entries(entries); talloc_free(entries); return 0; } libosmocore-0.9.0/tests/msgfile/msgfile_test.ok000066400000000000000000000000301261607044000216360ustar00rootroot00000000000000Entry '*:*::Hello Welt' libosmocore-0.9.0/tests/sms/000077500000000000000000000000001261607044000160015ustar00rootroot00000000000000libosmocore-0.9.0/tests/sms/sms_test.c000066400000000000000000000337221261607044000200150ustar00rootroot00000000000000/* * (C) 2008 by Daniel Willmann * (C) 2010 by Nico Golde * 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 #include #include #include #include #include #include #include #include #include struct log_info fake_log_info = {}; struct test_case { const uint8_t *input; const uint16_t input_length; const uint8_t *expected; const uint16_t expected_octet_length; const uint16_t expected_septet_length; const uint8_t ud_hdr_ind; }; static const char simple_text[] = "test text"; #define simple_septet_length 9 static const uint8_t simple_enc[] = { 0xf4, 0xf2, 0x9c, 0x0e, 0xa2, 0x97, 0xf1, 0x74 }; static const char escape_text[] = "!$ a more#^- complicated test@@?_%! case"; #define escape_septet_length 41 /* note: the ^ counts as two, because it is a extension character */ static const uint8_t escape_enc[] = { 0x21, 0x01, 0x28, 0x0c, 0x6a, 0xbf, 0xe5, 0xe5, 0xd1, 0x86, 0xd2, 0x02, 0x8d, 0xdf, 0x6d, 0x38, 0x3b, 0x3d, 0x0e, 0xd3, 0xcb, 0x64, 0x10, 0xbd, 0x3c, 0xa7, 0x03, 0x00, 0xbf, 0x48, 0x29, 0x04, 0x1a, 0x87, 0xe7, 0x65, }; static const char enhanced_text[] = "enhanced ^ {][} test |+~ ^ test"; #define enhanced_septet_length 39 /* note: the characters { } [ ] ^ | ~ count as two (each of them), because they are extension characters */ static const uint8_t enhanced_enc[] = { 0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D, 0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05, 0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4, 0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0x01, }; static const char enhancedV2_text[] = "enhanced ^ {][} test |+~ ^ tests"; #define enhancedV2_septet_length 40 /* note: number of octets are equal to the enhanced_text! */ static const uint8_t enhancedV2_enc[] = { 0x65, 0x37, 0x3A, 0xEC, 0x1E, 0x97, 0xC9, 0xA0, 0x0D, 0x05, 0xB4, 0x41, 0x6D, 0x7C, 0x1B, 0xDE, 0x26, 0x05, 0xA2, 0x97, 0xE7, 0x74, 0xD0, 0x06, 0xB8, 0xDA, 0xF4, 0x40, 0x1B, 0x0A, 0x88, 0x5E, 0x9E, 0xD3, 0xE7, }; static const char concatenated_text[] = "this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. " "this is a testmessage. this is a testmessage. cut here .....: this is a second testmessage. end here."; static const char splitted_text_part1[] = "this is a testmessage. this is a testmessage. this is a testmessage. this is a testmessage. " "this is a testmessage. this is a testmessage. cut here .....:"; #define concatenated_part1_septet_length_with_header 160 #define concatenated_part1_septet_length 153 static const uint8_t concatenated_part1_enc[] = { 0x05, 0x00, 0x03, 0x6f, 0x02, 0x01, 0xe8, 0xe8, 0xf4, 0x1c, 0x94, 0x9e, 0x83, 0xc2, 0x20, 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3, 0xf0, 0xb9, 0xec, 0x02, 0xd1, 0xd1, 0xe9, 0x39, 0x28, 0x3d, 0x07, 0x85, 0x41, 0xf4, 0xf2, 0x9c, 0xde, 0x2e, 0xcf, 0xe7, 0xe1, 0x73, 0xd9, 0x05, 0xa2, 0xa3, 0xd3, 0x73, 0x50, 0x7a, 0x0e, 0x0a, 0x83, 0xe8, 0xe5, 0x39, 0xbd, 0x5d, 0x9e, 0xcf, 0xc3, 0xe7, 0xb2, 0x0b, 0x44, 0x47, 0xa7, 0xe7, 0xa0, 0xf4, 0x1c, 0x14, 0x06, 0xd1, 0xcb, 0x73, 0x7a, 0xbb, 0x3c, 0x9f, 0x87, 0xcf, 0x65, 0x17, 0x88, 0x8e, 0x4e, 0xcf, 0x41, 0xe9, 0x39, 0x28, 0x0c, 0xa2, 0x97, 0xe7, 0xf4, 0x76, 0x79, 0x3e, 0x0f, 0x9f, 0xcb, 0x2e, 0x10, 0x1d, 0x9d, 0x9e, 0x83, 0xd2, 0x73, 0x50, 0x18, 0x44, 0x2f, 0xcf, 0xe9, 0xed, 0xf2, 0x7c, 0x1e, 0x3e, 0x97, 0x5d, 0xa0, 0x71, 0x9d, 0x0e, 0x42, 0x97, 0xe5, 0x65, 0x90, 0xcb, 0xe5, 0x72, 0xb9, 0x74, }; static const char splitted_text_part2[] = " this is a second testmessage. end here."; #define concatenated_part2_septet_length_with_header 47 #define concatenated_part2_septet_length 40 static const uint8_t concatenated_part2_enc[] = { 0x05, 0x00, 0x03, 0x6f, 0x02, 0x02, 0x40, 0x74, 0x74, 0x7a, 0x0e, 0x4a, 0xcf, 0x41, 0x61, 0xd0, 0xbc, 0x3c, 0x7e, 0xbb, 0xc9, 0x20, 0x7a, 0x79, 0x4e, 0x6f, 0x97, 0xe7, 0xf3, 0xf0, 0xb9, 0xec, 0x02, 0x95, 0xdd, 0x64, 0x10, 0xba, 0x2c, 0x2f, 0xbb, 0x00, }; static const struct test_case test_multiple_encode[] = { { .input = (const uint8_t *) concatenated_text, .expected = concatenated_part1_enc, .expected_octet_length = sizeof(concatenated_part1_enc), .expected_septet_length = concatenated_part1_septet_length, .ud_hdr_ind = 1, }, { .input = (const uint8_t *) concatenated_text, .expected = concatenated_part2_enc, .expected_octet_length = sizeof(concatenated_part2_enc), .expected_septet_length = concatenated_part2_septet_length, .ud_hdr_ind = 1, }, }; static const struct test_case test_encode[] = { { .input = (const uint8_t *) simple_text, .expected = simple_enc, .expected_octet_length = sizeof(simple_enc), .expected_septet_length = simple_septet_length, .ud_hdr_ind = 0, }, { .input = (const uint8_t *) escape_text, .expected = escape_enc, .expected_octet_length = sizeof(escape_enc), .expected_septet_length = escape_septet_length, .ud_hdr_ind = 0, }, { .input = (const uint8_t *) enhanced_text, .expected = enhanced_enc, .expected_octet_length = sizeof(enhanced_enc), .expected_septet_length = enhanced_septet_length, .ud_hdr_ind = 0, }, { .input = (const uint8_t *) enhancedV2_text, .expected = enhancedV2_enc, .expected_octet_length = sizeof(enhancedV2_enc), .expected_septet_length = enhancedV2_septet_length, .ud_hdr_ind = 0, }, }; static const struct test_case test_decode[] = { { .input = simple_enc, .input_length = sizeof(simple_enc), .expected = (const uint8_t *) simple_text, .expected_septet_length = simple_septet_length, .ud_hdr_ind = 0, }, { .input = escape_enc, .input_length = sizeof(escape_enc), .expected = (const uint8_t *) escape_text, .expected_septet_length = escape_septet_length, .ud_hdr_ind = 0, }, { .input = enhanced_enc, .input_length = sizeof(enhanced_enc), .expected = (const uint8_t *) enhanced_text, .expected_septet_length = enhanced_septet_length, .ud_hdr_ind = 0, }, { .input = enhancedV2_enc, .input_length = sizeof(enhancedV2_enc), .expected = (const uint8_t *) enhancedV2_text, .expected_septet_length = enhancedV2_septet_length, .ud_hdr_ind = 0, }, { .input = concatenated_part1_enc, .input_length = sizeof(concatenated_part1_enc), .expected = (const uint8_t *) splitted_text_part1, .expected_septet_length = concatenated_part1_septet_length_with_header, .ud_hdr_ind = 1, }, { .input = concatenated_part2_enc, .input_length = sizeof(concatenated_part2_enc), .expected = (const uint8_t *) splitted_text_part2, .expected_septet_length = concatenated_part2_septet_length_with_header, .ud_hdr_ind = 1, }, }; static void test_octet_return() { char out[256]; int oct, septets; printf("Encoding some tests and printing number of septets/octets\n"); septets = gsm_7bit_encode_n((uint8_t *) out, sizeof(out), "test1234", &oct); printf("SEPTETS: %d OCTETS: %d\n", septets, oct); printf("Done\n"); } static void test_gen_oa(void) { uint8_t oa[12]; int len; printf("Testing gsm340_gen_oa\n"); /* first try... */ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_UNKNOWN, GSM340_PLAN_ISDN, "12345678901234567891"); OSMO_ASSERT(len == 12); printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len)); len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_NATIONAL, GSM340_PLAN_ISDN, "12345678901234567891"); OSMO_ASSERT(len == 12); printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len)); /* long input.. will fail and just prints the header*/ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_INTERNATIONAL, GSM340_PLAN_ISDN, "123456789123456789120"); OSMO_ASSERT(len == 2); printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len)); /* try the alpha numeric encoding */ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_ALPHA_NUMERIC, GSM340_PLAN_UNKNOWN, "OpenBSC"); OSMO_ASSERT(len == 9); printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len)); /* long alpha numeric text */ len = gsm340_gen_oa(oa, ARRAY_SIZE(oa), GSM340_TYPE_ALPHA_NUMERIC, GSM340_PLAN_UNKNOWN, "OpenBSCabcdefghijklm"); OSMO_ASSERT(len == 12); printf("Result: len(%d) data(%s)\n", len, osmo_hexdump(oa, len)); } int main(int argc, char** argv) { printf("SMS testing\n"); uint8_t i; uint16_t buffer_size; uint8_t octet_length; int octets_written; uint8_t computed_octet_length; uint8_t septet_length; uint8_t coded[256]; uint8_t tmp[160]; uint8_t septet_data[256]; int nchars; char result[256]; /* Fake logging. */ osmo_init_logging(&fake_log_info); /* test 7-bit encoding */ for (i = 0; i < ARRAY_SIZE(test_encode); ++i) { /* Test legacy function (return value only) */ septet_length = gsm_7bit_encode(coded, (const char *) test_encode[i].input); printf("Legacy encode case %d: " "septet length %d (expected %d)\n" , i , septet_length, test_encode[i].expected_septet_length ); OSMO_ASSERT (septet_length == test_encode[i].expected_septet_length); /* Test new function */ memset(coded, 0x42, sizeof(coded)); septet_length = gsm_7bit_encode_n(coded, sizeof(coded), (const char *) test_encode[i].input, &octets_written); computed_octet_length = gsm_get_octet_len(septet_length); printf("Encode case %d: " "Octet length %d (expected %d, computed %d), " "septet length %d (expected %d)\n" , i , octets_written, test_encode[i].expected_octet_length, computed_octet_length , septet_length, test_encode[i].expected_septet_length ); OSMO_ASSERT (octets_written == test_encode[i].expected_octet_length); OSMO_ASSERT (octets_written == computed_octet_length); OSMO_ASSERT (memcmp(coded, test_encode[i].expected, octets_written) == 0); OSMO_ASSERT (septet_length == test_encode[i].expected_septet_length); /* check buffer limiting */ memset(coded, 0xaa, sizeof(coded)); for (buffer_size = 0; buffer_size < test_encode[i].expected_octet_length + 1 && buffer_size < sizeof(coded) - 1; ++buffer_size) { gsm_7bit_encode_n(coded, buffer_size, (const char *) test_encode[i].input, &octets_written); OSMO_ASSERT(octets_written <= buffer_size); OSMO_ASSERT(coded[buffer_size] == 0xaa); } } /* Test: encode multiple SMS */ int number_of_septets = gsm_septet_encode(septet_data, (const char *) test_multiple_encode[0].input); (void) number_of_septets; /* SMS part 1 */ memset(tmp, 0x42, sizeof(tmp)); memset(coded, 0x42, sizeof(coded)); memcpy(tmp, septet_data, concatenated_part1_septet_length); /* In our case: test_multiple_decode[0].ud_hdr_ind equals number of padding bits*/ octet_length = gsm_septets2octets(coded, tmp, concatenated_part1_septet_length, test_multiple_encode[0].ud_hdr_ind); /* copy header */ memset(tmp, 0x42, sizeof(tmp)); int udh_length = test_multiple_encode[0].expected[0] + 1; memcpy(tmp, test_multiple_encode[0].expected, udh_length); memcpy(tmp + udh_length, coded, octet_length); memset(coded, 0x42, sizeof(coded)); memcpy(coded, tmp, octet_length + 6); OSMO_ASSERT(memcmp(coded, test_multiple_encode[0].expected, octet_length) == 0); /* SMS part 2 */ memset(tmp, 0x42, sizeof(tmp)); memset(coded, 0x42, sizeof(coded)); memcpy(tmp, septet_data + concatenated_part1_septet_length, concatenated_part2_septet_length); /* In our case: test_multiple_decode[1].ud_hdr_ind equals number of padding bits*/ octet_length = gsm_septets2octets(coded, tmp, concatenated_part2_septet_length, test_multiple_encode[1].ud_hdr_ind); /* copy header */ memset(tmp, 0x42, sizeof(tmp)); udh_length = test_multiple_encode[1].expected[0] + 1; memcpy(tmp, test_multiple_encode[1].expected, udh_length); memcpy(tmp + udh_length, coded, octet_length); memset(coded, 0x42, sizeof(coded)); memcpy(coded, tmp, octet_length + 6); OSMO_ASSERT(memcmp(coded, test_multiple_encode[1].expected, octet_length) == 0); /* test 7-bit decoding */ for (i = 0; i < ARRAY_SIZE(test_decode); ++i) { /* Test legacy function (return value only) */ if (!test_decode[i].ud_hdr_ind) { nchars = gsm_7bit_decode(result, test_decode[i].input, test_decode[i].expected_septet_length); printf("Legacy decode case %d: " "return value %d (expected %d)\n", i, nchars, test_decode[i].expected_septet_length); } /* Test new function */ memset(result, 0x42, sizeof(result)); nchars = gsm_7bit_decode_n_hdr(result, sizeof(result), test_decode[i].input, test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind); printf("Decode case %d: return value %d (expected %d)\n", i, nchars, strlen(result)); OSMO_ASSERT(strcmp(result, (const char *) test_decode[i].expected) == 0); OSMO_ASSERT(nchars == strlen(result)); /* check buffer limiting */ memset(result, 0xaa, sizeof(result)); for (buffer_size = 1; buffer_size < test_decode[i].expected_septet_length + 1 && buffer_size < sizeof(result) - 1; ++buffer_size) { nchars = gsm_7bit_decode_n_hdr(result, buffer_size, test_decode[i].input, test_decode[i].expected_septet_length, test_decode[i].ud_hdr_ind); OSMO_ASSERT(nchars <= buffer_size); OSMO_ASSERT(result[buffer_size] == (char)0xaa); OSMO_ASSERT(result[nchars] == '\0'); } } test_octet_return(); test_gen_oa(); printf("OK\n"); return 0; } libosmocore-0.9.0/tests/sms/sms_test.ok000066400000000000000000000026101261607044000201740ustar00rootroot00000000000000SMS testing Legacy encode case 0: septet length 9 (expected 9) Encode case 0: Octet length 8 (expected 8, computed 8), septet length 9 (expected 9) Legacy encode case 1: septet length 41 (expected 41) Encode case 1: Octet length 36 (expected 36, computed 36), septet length 41 (expected 41) Legacy encode case 2: septet length 39 (expected 39) Encode case 2: Octet length 35 (expected 35, computed 35), septet length 39 (expected 39) Legacy encode case 3: septet length 40 (expected 40) Encode case 3: Octet length 35 (expected 35, computed 35), septet length 40 (expected 40) Legacy decode case 0: return value 9 (expected 9) Decode case 0: return value 9 (expected 9) Legacy decode case 1: return value 41 (expected 41) Decode case 1: return value 40 (expected 40) Legacy decode case 2: return value 39 (expected 39) Decode case 2: return value 31 (expected 31) Legacy decode case 3: return value 40 (expected 40) Decode case 3: return value 32 (expected 32) Decode case 4: return value 153 (expected 153) Decode case 5: return value 40 (expected 40) Encoding some tests and printing number of septets/octets SEPTETS: 8 OCTETS: 7 Done Testing gsm340_gen_oa Result: len(12) data(14 81 21 43 65 87 09 21 43 65 87 19 ) Result: len(12) data(14 a1 21 43 65 87 09 21 43 65 87 19 ) Result: len(2) data(00 91 ) Result: len(9) data(0e d0 4f 78 d9 2d 9c 0e 01 ) Result: len(12) data(14 d0 4f 78 d9 2d 9c 0e c3 e2 31 19 ) OK libosmocore-0.9.0/tests/smscb/000077500000000000000000000000001261607044000163065ustar00rootroot00000000000000libosmocore-0.9.0/tests/smscb/gsm0341_test.c000066400000000000000000000042201261607044000206050ustar00rootroot00000000000000/* * (C) 2014 by Harald Welte * 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 #include #include #include #include #include #include #include struct gsm341_ms_message *gen_msg_from_text(uint16_t msg_id, const char *text) { struct gsm341_ms_message *cbmsg; int text_len = strlen(text); /* assuming default GSM alphabet, the encoded payload cannot be * longer than the input text */ uint8_t payload[text_len]; int payload_octets; srand(time(NULL)); gsm_7bit_encode_n(payload, sizeof(payload), text, &payload_octets); //cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x0f, 1, 1, payload, payload_octets); cbmsg = gsm0341_build_msg(NULL, 0, rand(), 0, msg_id, 0x00, 1, 1, payload, payload_octets); printf("%s\n", osmo_hexdump_nospc((uint8_t *)cbmsg, sizeof(*cbmsg)+payload_octets)); return cbmsg; } int main(int argc, char **argv) { uint16_t msg_id = GSM341_MSGID_ETWS_CMAS_MONTHLY_TEST; char *text = "Mahlzeit!"; char tbuf[GSM341_MAX_CHARS+1]; if (argc > 1) msg_id = atoi(argv[1]); if (argc > 2) text = argv[2]; strncpy(tbuf, text, GSM341_MAX_CHARS); if (strlen(text) < GSM341_MAX_CHARS) memset(tbuf+strlen(text), GSM341_7BIT_PADDING, sizeof(tbuf)-strlen(text)); tbuf[GSM341_MAX_CHARS] = 0; gen_msg_from_text(msg_id, tbuf); return EXIT_SUCCESS; } libosmocore-0.9.0/tests/smscb/smscb_test.c000066400000000000000000000025761261607044000206320ustar00rootroot00000000000000/* * (C) 2010 Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include static uint8_t smscb_msg[] = { 0x40, 0x10, 0x05, 0x0d, 0x01, 0x11 }; int main(int argc, char **argv) { struct gsm341_ms_message *msg; msg = (struct gsm341_ms_message *) smscb_msg; printf("(srl) GS: %d MSG_CODE: %d UPDATE: %d\n", msg->serial.gs, GSM341_MSG_CODE(msg), msg->serial.update); printf("(msg) msg_id: %d\n", htons(msg->msg_id)); printf("(dcs) group: %d language: %d\n", msg->dcs.language, msg->dcs.group); printf("(pge) page total: %d current: %d\n", msg->page.total, msg->page.current); return 0; } libosmocore-0.9.0/tests/smscb/smscb_test.ok000066400000000000000000000001571261607044000210120ustar00rootroot00000000000000(srl) GS: 1 MSG_CODE: 1 UPDATE: 0 (msg) msg_id: 1293 (dcs) group: 1 language: 0 (pge) page total: 1 current: 1 libosmocore-0.9.0/tests/stats/000077500000000000000000000000001261607044000163355ustar00rootroot00000000000000libosmocore-0.9.0/tests/stats/stats_test.c000066400000000000000000000142661261607044000207070ustar00rootroot00000000000000/* tests for statistics */ /* * (C) 2015 Sysmocom s.m.f.c. GmbH * * 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 #include #include #include static void stat_test(void) { enum test_items { TEST_A_ITEM, TEST_B_ITEM, }; static const struct osmo_stat_item_desc item_description[] = { { "item.a", "The A value", "ma", 4, -1 }, { "item.b", "The B value", "kb", 7, -1 }, }; static const struct osmo_stat_item_group_desc statg_desc = { .group_name_prefix = "test.one", .group_description = "Test number 1", .num_items = ARRAY_SIZE(item_description), .item_desc = item_description, }; struct osmo_stat_item_group *statg = osmo_stat_item_group_alloc(NULL, &statg_desc, 0); struct osmo_stat_item_group *sgrp2; const struct osmo_stat_item *sitem1, *sitem2; int rc; int32_t value; int32_t rd_a = 0; int32_t rd_b = 0; int i; OSMO_ASSERT(statg != NULL); sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0); OSMO_ASSERT(sgrp2 == statg); sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 1); OSMO_ASSERT(sgrp2 == NULL); sgrp2 = osmo_stat_item_get_group_by_name_idx("test.two", 0); OSMO_ASSERT(sgrp2 == NULL); sitem1 = osmo_stat_item_get_by_name(statg, "item.c"); OSMO_ASSERT(sitem1 == NULL); sitem1 = osmo_stat_item_get_by_name(statg, "item.a"); OSMO_ASSERT(sitem1 != NULL); OSMO_ASSERT(sitem1 == statg->items[TEST_A_ITEM]); sitem2 = osmo_stat_item_get_by_name(statg, "item.b"); OSMO_ASSERT(sitem2 != NULL); OSMO_ASSERT(sitem2 != sitem1); OSMO_ASSERT(sitem2 == statg->items[TEST_B_ITEM]); value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]); OSMO_ASSERT(value == -1); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc == 0); osmo_stat_item_set(statg->items[TEST_A_ITEM], 1); value = osmo_stat_item_get_last(statg->items[TEST_A_ITEM]); OSMO_ASSERT(value == 1); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc == 0); for (i = 2; i <= 32; i++) { osmo_stat_item_set(statg->items[TEST_A_ITEM], i); osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == i); rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1000 + i); } /* Keep 2 in FIFO */ osmo_stat_item_set(statg->items[TEST_A_ITEM], 33); osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + 33); for (i = 34; i <= 64; i++) { osmo_stat_item_set(statg->items[TEST_A_ITEM], i); osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == i-1); rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1000 + i-1); } rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 64); rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1000 + 64); /* Overrun FIFOs */ for (i = 65; i <= 96; i++) { osmo_stat_item_set(statg->items[TEST_A_ITEM], i); osmo_stat_item_set(statg->items[TEST_B_ITEM], 1000 + i); } rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 93); for (i = 94; i <= 96; i++) { rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == i); } rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1000 + 90); for (i = 91; i <= 96; i++) { rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 1000 + i); } /* Test Discard (single item) */ osmo_stat_item_set(statg->items[TEST_A_ITEM], 97); rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a); OSMO_ASSERT(rc > 0); rc = osmo_stat_item_discard(statg->items[TEST_A_ITEM], &rd_a); OSMO_ASSERT(rc == 0); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc == 0); osmo_stat_item_set(statg->items[TEST_A_ITEM], 98); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc > 0); OSMO_ASSERT(value == 98); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc == 0); /* Test Discard (all items) */ osmo_stat_item_set(statg->items[TEST_A_ITEM], 99); osmo_stat_item_set(statg->items[TEST_A_ITEM], 100); osmo_stat_item_set(statg->items[TEST_A_ITEM], 101); osmo_stat_item_set(statg->items[TEST_B_ITEM], 99); osmo_stat_item_set(statg->items[TEST_B_ITEM], 100); rc = osmo_stat_item_discard_all(&rd_a); rc = osmo_stat_item_discard_all(&rd_b); rc = osmo_stat_item_get_next(statg->items[TEST_A_ITEM], &rd_a, &value); OSMO_ASSERT(rc == 0); rc = osmo_stat_item_get_next(statg->items[TEST_B_ITEM], &rd_b, &value); OSMO_ASSERT(rc == 0); osmo_stat_item_group_free(statg); sgrp2 = osmo_stat_item_get_group_by_name_idx("test.one", 0); OSMO_ASSERT(sgrp2 == NULL); } int main(int argc, char **argv) { static const struct log_info log_info = {}; log_init(&log_info, NULL); osmo_stat_item_init(NULL); stat_test(); return 0; } libosmocore-0.9.0/tests/stats/stats_test.ok000066400000000000000000000000001261607044000210530ustar00rootroot00000000000000libosmocore-0.9.0/tests/strrb/000077500000000000000000000000001261607044000163335ustar00rootroot00000000000000libosmocore-0.9.0/tests/strrb/strrb_test.c000066400000000000000000000140651261607044000207000ustar00rootroot00000000000000/* (C) 2012-2013 by Katerina Barone-Adesi * All Rights Reserved * * This program is iree 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include struct osmo_strrb *rb0, *rb1, *rb2, *rb3, *rb4, *rb5; #define STR0 "hello" #define STR1 "a" #define STR2 "world" #define STR3 "sky" #define STR4 "moon" #define TESTSIZE 3 void init_rbs(void) { rb0 = osmo_strrb_create(NULL, TESTSIZE); rb1 = osmo_strrb_create(NULL, TESTSIZE); osmo_strrb_add(rb1, STR0); rb2 = osmo_strrb_create(NULL, TESTSIZE); osmo_strrb_add(rb2, STR0); osmo_strrb_add(rb2, STR1); rb3 = osmo_strrb_create(NULL, TESTSIZE); osmo_strrb_add(rb3, STR0); osmo_strrb_add(rb3, STR1); osmo_strrb_add(rb3, STR2); rb4 = osmo_strrb_create(NULL, TESTSIZE); osmo_strrb_add(rb4, STR0); osmo_strrb_add(rb4, STR1); osmo_strrb_add(rb4, STR2); osmo_strrb_add(rb4, STR3); rb5 = osmo_strrb_create(NULL, TESTSIZE); osmo_strrb_add(rb5, STR0); osmo_strrb_add(rb5, STR1); osmo_strrb_add(rb5, STR2); osmo_strrb_add(rb5, STR3); osmo_strrb_add(rb5, STR4); } void free_rbs(void) { talloc_free(rb0); talloc_free(rb1); talloc_free(rb2); talloc_free(rb3); talloc_free(rb4); talloc_free(rb5); } void test_offset_valid(void) { OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb1, 0)); OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb1, 1)); OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb1, 2)); OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb3, 0)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb3, 1)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb3, 2)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb4, 0)); OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb4, 1)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb4, 2)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb5, 0)); OSMO_ASSERT(_osmo_strrb_is_bufindex_valid(rb5, 1)); OSMO_ASSERT(!_osmo_strrb_is_bufindex_valid(rb5, 2)); } void test_elems(void) { OSMO_ASSERT(osmo_strrb_elements(rb0) == 0); OSMO_ASSERT(osmo_strrb_elements(rb1) == 1); OSMO_ASSERT(osmo_strrb_elements(rb2) == 2); OSMO_ASSERT(osmo_strrb_elements(rb3) == 2); } void test_getn(void) { OSMO_ASSERT(!osmo_strrb_get_nth(rb0, 0)); OSMO_ASSERT(!strcmp(STR0, osmo_strrb_get_nth(rb2, 0))); OSMO_ASSERT(!strcmp(STR1, osmo_strrb_get_nth(rb2, 1))); OSMO_ASSERT(!strcmp(STR1, osmo_strrb_get_nth(rb3, 0))); OSMO_ASSERT(!strcmp(STR2, osmo_strrb_get_nth(rb3, 1))); OSMO_ASSERT(!osmo_strrb_get_nth(rb3, 2)); } void test_getn_wrap(void) { OSMO_ASSERT(!strcmp(STR2, osmo_strrb_get_nth(rb4, 0))); OSMO_ASSERT(!strcmp(STR3, osmo_strrb_get_nth(rb4, 1))); OSMO_ASSERT(!strcmp(STR3, osmo_strrb_get_nth(rb5, 0))); OSMO_ASSERT(!strcmp(STR4, osmo_strrb_get_nth(rb5, 1))); } void test_add(void) { struct osmo_strrb *rb = osmo_strrb_create(NULL, 4); OSMO_ASSERT(rb->start == 0); OSMO_ASSERT(rb->end == 0); osmo_strrb_add(rb, "a"); osmo_strrb_add(rb, "b"); osmo_strrb_add(rb, "c"); OSMO_ASSERT(rb->start == 0); OSMO_ASSERT(rb->end == 3); OSMO_ASSERT(osmo_strrb_elements(rb) == 3); osmo_strrb_add(rb, "d"); OSMO_ASSERT(rb->start == 1); OSMO_ASSERT(rb->end == 0); OSMO_ASSERT(osmo_strrb_elements(rb) == 3); OSMO_ASSERT(!strcmp("b", osmo_strrb_get_nth(rb, 0))); OSMO_ASSERT(!strcmp("c", osmo_strrb_get_nth(rb, 1))); OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 2))); osmo_strrb_add(rb, "e"); OSMO_ASSERT(rb->start == 2); OSMO_ASSERT(rb->end == 1); OSMO_ASSERT(!strcmp("c", osmo_strrb_get_nth(rb, 0))); OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 1))); OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 2))); osmo_strrb_add(rb, "f"); OSMO_ASSERT(rb->start == 3); OSMO_ASSERT(rb->end == 2); OSMO_ASSERT(!strcmp("d", osmo_strrb_get_nth(rb, 0))); OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 1))); OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 2))); osmo_strrb_add(rb, "g"); OSMO_ASSERT(rb->start == 0); OSMO_ASSERT(rb->end == 3); OSMO_ASSERT(!strcmp("e", osmo_strrb_get_nth(rb, 0))); OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 1))); OSMO_ASSERT(!strcmp("g", osmo_strrb_get_nth(rb, 2))); osmo_strrb_add(rb, "h"); OSMO_ASSERT(rb->start == 1); OSMO_ASSERT(rb->end == 0); OSMO_ASSERT(!strcmp("f", osmo_strrb_get_nth(rb, 0))); OSMO_ASSERT(!strcmp("g", osmo_strrb_get_nth(rb, 1))); OSMO_ASSERT(!strcmp("h", osmo_strrb_get_nth(rb, 2))); talloc_free(rb); } void test_long_msg(void) { struct osmo_strrb *rb = osmo_strrb_create(NULL, 2); int test_size = RB_MAX_MESSAGE_SIZE + 7; char *tests1, *tests2; const char *rb_content; int i; tests1 = malloc(test_size); tests2 = malloc(test_size); /* Be certain allocating memory worked before continuing */ OSMO_ASSERT(tests1); OSMO_ASSERT(tests2); for (i = 0; i < RB_MAX_MESSAGE_SIZE; i += 2) { tests1[i] = 'a'; tests1[i + 1] = 'b'; } tests1[i] = '\0'; osmo_strrb_add(rb, tests1); strcpy(tests2, tests1); /* Verify that no stale data from test1 is lingering... */ bzero(tests1, test_size); free(tests1); rb_content = osmo_strrb_get_nth(rb, 0); OSMO_ASSERT(!strncmp(tests2, rb_content, RB_MAX_MESSAGE_SIZE - 1)); OSMO_ASSERT(!rb_content[RB_MAX_MESSAGE_SIZE - 1]); OSMO_ASSERT(strlen(rb_content) == RB_MAX_MESSAGE_SIZE - 1); free(tests2); talloc_free(rb); } int main(int argc, char **argv) { init_rbs(); test_offset_valid(); test_elems(); test_getn(); test_getn_wrap(); test_add(); test_long_msg(); printf("All tests passed\n"); free_rbs(); return 0; } libosmocore-0.9.0/tests/strrb/strrb_test.ok000066400000000000000000000000211261607044000210520ustar00rootroot00000000000000All tests passed libosmocore-0.9.0/tests/testsuite.at000066400000000000000000000106111261607044000175550ustar00rootroot00000000000000AT_INIT AT_BANNER([Regression tests.]) # todo.. create one macro for it AT_SETUP([a5]) AT_KEYWORDS([a5]) cat $abs_srcdir/a5/a5_test.ok > expout AT_CHECK([$abs_top_builddir/tests/a5/a5_test], [0], [expout]) AT_CLEANUP AT_SETUP([kasumi]) AT_KEYWORDS([kasumi]) cat $abs_srcdir/kasumi/kasumi_test.ok > expout AT_CHECK([$abs_top_builddir/tests/kasumi/kasumi_test], [0], [expout]) AT_CLEANUP AT_SETUP([bits]) AT_KEYWORDS([bits]) cat $abs_srcdir/bits/bitrev_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bits/bitrev_test], [0], [expout]) AT_CLEANUP AT_SETUP([conv]) AT_KEYWORDS([conv]) cat $abs_srcdir/conv/conv_test.ok > expout AT_CHECK([$abs_top_builddir/tests/conv/conv_test], [0], [expout]) AT_CLEANUP if ENABLE_MSGFILE AT_SETUP([msgfile]) AT_KEYWORDS([msgfile]) cp $abs_srcdir/msgfile/msgconfig.cfg . cat $abs_srcdir/msgfile/msgfile_test.ok > expout AT_CHECK([$abs_top_builddir/tests/msgfile/msgfile_test], [0], [expout]) AT_CLEANUP endif AT_SETUP([sms]) AT_KEYWORDS([sms]) cat $abs_srcdir/sms/sms_test.ok > expout AT_CHECK([$abs_top_builddir/tests/sms/sms_test], [0], [expout]) AT_CLEANUP AT_SETUP([smscb]) AT_KEYWORDS([smscb]) cat $abs_srcdir/smscb/smscb_test.ok > expout AT_CHECK([$abs_top_builddir/tests/smscb/smscb_test], [0], [expout]) AT_CLEANUP AT_SETUP([ussd]) AT_KEYWORDS([ussd]) cat $abs_srcdir/ussd/ussd_test.ok > expout AT_CHECK([$abs_top_builddir/tests/ussd/ussd_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([auth]) AT_KEYWORDS([auth]) cat $abs_srcdir/auth/milenage_test.ok > expout AT_CHECK([$abs_top_builddir/tests/auth/milenage_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([comp128]) AT_KEYWORDS([comp128]) cat $abs_srcdir/comp128/comp128_test.ok > expout AT_CHECK([$abs_top_builddir/tests/comp128/comp128_test], [0], [expout]) AT_CLEANUP AT_SETUP([lapd]) AT_KEYWORDS([lapd]) cat $abs_srcdir/lapd/lapd_test.ok > expout AT_CHECK([$abs_top_builddir/tests/lapd/lapd_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([gsm0808]) AT_KEYWORDS([gsm0808]) cat $abs_srcdir/gsm0808/gsm0808_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gsm0808/gsm0808_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([gsm0408]) AT_KEYWORDS([gsm0408]) cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([logging]) AT_KEYWORDS([logging]) cat $abs_srcdir/logging/logging_test.ok > expout cat $abs_srcdir/logging/logging_test.err > experr AT_CHECK([$abs_top_builddir/tests/logging/logging_test], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([fr]) AT_KEYWORDS([fr]) cat $abs_srcdir/fr/fr_test.ok > expout cat $abs_srcdir/fr/fr_test.err > experr AT_CHECK([$abs_top_builddir/tests/fr/fr_test], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([loggingrb]) AT_KEYWORDS([loggingrb]) cat $abs_srcdir/loggingrb/logging_test.ok > expout cat $abs_srcdir/loggingrb/logging_test.err > experr AT_CHECK([$abs_top_builddir/tests/loggingrb/loggingrb_test], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([strrb]) AT_KEYWORDS([strrb]) cat $abs_srcdir/strrb/strrb_test.ok > expout AT_CHECK([$abs_top_builddir/tests/strrb/strrb_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([vty]) AT_KEYWORDS([vty]) cat $abs_srcdir/vty/vty_test.ok > expout AT_CHECK([$abs_top_builddir/tests/vty/vty_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([gprs-bssgp]) AT_KEYWORDS([gprs-bssgp]) cat $abs_srcdir/gb/gprs_bssgp_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gb/gprs_bssgp_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([gprs-ns]) AT_KEYWORDS([gprs-ns]) cat $abs_srcdir/gb/gprs_ns_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gb/gprs_ns_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([utils]) AT_KEYWORDS([utils]) cat $abs_srcdir/utils/utils_test.ok > expout AT_CHECK([$abs_top_builddir/tests/utils/utils_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([stats]) AT_KEYWORDS([stats]) cat $abs_srcdir/stats/stats_test.ok > expout AT_CHECK([$abs_top_builddir/tests/stats/stats_test], [0], [expout], [ignore]) AT_CLEANUP AT_SETUP([bssgp-fc]) AT_KEYWORDS([bssgp-fc]) cat $abs_srcdir/gb/bssgp_fc_tests.ok > expout cat $abs_srcdir/gb/bssgp_fc_tests.err > experr AT_CHECK([$abs_top_srcdir/tests/gb/bssgp_fc_tests.sh $abs_top_builddir/tests/gb], [0], [expout], [experr]) AT_CLEANUP AT_SETUP([timer]) AT_KEYWORDS([timer]) cat $abs_srcdir/timer/timer_test.ok > expout AT_CHECK([$abs_top_builddir/tests/timer/timer_test -s 5], [0], [expout], [ignore]) AT_CLEANUP libosmocore-0.9.0/tests/timer/000077500000000000000000000000001261607044000163175ustar00rootroot00000000000000libosmocore-0.9.0/tests/timer/timer_test.c000066400000000000000000000121201261607044000206360ustar00rootroot00000000000000/* * (C) 2008 by Holger Hans Peter Freyther * (C) 2011 by Harald Welte * All Rights Reserved * * Authors: Holger Hans Peter Freyther * Pablo Neira Ayuso * * 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 #include #include #include #include #include #include #include #include #include "../config.h" static void main_timer_fired(void *data); static void secondary_timer_fired(void *data); static unsigned int main_timer_step = 0; static struct osmo_timer_list main_timer = { .cb = main_timer_fired, .data = &main_timer_step, }; static LLIST_HEAD(timer_test_list); struct test_timer { struct llist_head head; struct osmo_timer_list timer; struct timeval start; struct timeval stop; }; /* number of test steps. We add fact(steps) timers in the whole test. */ #define MAIN_TIMER_NSTEPS 16 /* time between two steps, in secs. */ #define TIME_BETWEEN_STEPS 1 /* timer imprecision that we accept for this test: 10 milliseconds. */ #define TIMER_PRES_SECS 0 #define TIMER_PRES_USECS 20000 static int timer_nsteps = MAIN_TIMER_NSTEPS; static unsigned int expired_timers = 0; static unsigned int total_timers = 0; static unsigned int too_late = 0; static void main_timer_fired(void *data) { unsigned int *step = data; unsigned int add_in_this_step; int i; if (*step == timer_nsteps) { fprintf(stderr, "Main timer has finished, please, " "wait a bit for the final report.\n"); return; } /* add 2^step pair of timers per step. */ add_in_this_step = (1 << *step); for (i=0; istart, NULL); v->timer.cb = secondary_timer_fired; v->timer.data = v; unsigned int seconds = (random() % 10) + 1; v->stop.tv_sec = v->start.tv_sec + seconds; osmo_timer_schedule(&v->timer, seconds, 0); llist_add(&v->head, &timer_test_list); } fprintf(stderr, "added %d timers in step %u (expired=%u)\n", add_in_this_step, *step, expired_timers); total_timers += add_in_this_step; osmo_timer_schedule(&main_timer, TIME_BETWEEN_STEPS, 0); (*step)++; } static void secondary_timer_fired(void *data) { struct test_timer *v = data, *this, *tmp; struct timeval current, res, precision = { 1, 0 }; gettimeofday(¤t, NULL); timersub(¤t, &v->stop, &res); if (timercmp(&res, &precision, >)) { fprintf(stderr, "ERROR: timer %p has expired too late!\n", &v->timer); too_late++; } llist_del(&v->head); talloc_free(data); expired_timers++; if (expired_timers == total_timers) { fprintf(stdout, "test over: added=%u expired=%u too_late=%u \n", total_timers, expired_timers, too_late); exit(EXIT_SUCCESS); } /* randomly (10%) deletion of timers. */ llist_for_each_entry_safe(this, tmp, &timer_test_list, head) { if ((random() % 100) < 10) { osmo_timer_del(&this->timer); llist_del(&this->head); talloc_free(this); expired_timers++; } } } static void alarm_handler(int signum) { fprintf(stderr, "ERROR: We took too long to run the timer test, " "something seems broken, aborting.\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int c; if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("cannot register signal handler"); exit(EXIT_FAILURE); } while ((c = getopt_long(argc, argv, "s:", NULL, NULL)) != -1) { switch(c) { case 's': timer_nsteps = atoi(optarg); if (timer_nsteps <= 0) { fprintf(stderr, "%s: steps must be > 0\n", argv[0]); exit(EXIT_FAILURE); } break; default: exit(EXIT_FAILURE); } } fprintf(stdout, "Running timer test for %u steps, accepting " "imprecision of %u.%.6u seconds\n", timer_nsteps, TIMER_PRES_SECS, TIMER_PRES_USECS); osmo_timer_schedule(&main_timer, 1, 0); /* if the test takes too long, we may consider that the timer scheduler * has hung. We set some maximum wait time which is the double of the * maximum timeout randomly set (10 seconds, worst case) plus the * number of steps (since some of them are reset each step). */ alarm(2 * (10 + timer_nsteps)); #ifdef HAVE_SYS_SELECT_H while (1) { osmo_select_main(0); } #else fprintf(stdout, "Select not supported on this platform!\n"); #endif } libosmocore-0.9.0/tests/timer/timer_test.ok000066400000000000000000000001651261607044000210330ustar00rootroot00000000000000Running timer test for 5 steps, accepting imprecision of 0.020000 seconds test over: added=31 expired=31 too_late=0 libosmocore-0.9.0/tests/ussd/000077500000000000000000000000001261607044000161555ustar00rootroot00000000000000libosmocore-0.9.0/tests/ussd/ussd_test.c000066400000000000000000000116021261607044000203360ustar00rootroot00000000000000/* * (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 #include #include #include #include #include #include static const uint8_t ussd_request[] = { 0x0b, 0x7b, 0x1c, 0x15, 0xa1, 0x13, 0x02, 0x01, 0x03, 0x02, 0x01, 0x3b, 0x30, 0x0b, 0x04, 0x01, 0x0f, 0x04, 0x06, 0x2a, 0xd5, 0x4c, 0x16, 0x1b, 0x01, 0x7f, 0x01, 0x00 }; static int parse_ussd(const uint8_t *_data, int len) { uint8_t *data; int rc; struct ss_request req; struct gsm48_hdr *hdr; data = malloc(len); memcpy(data, _data, len); hdr = (struct gsm48_hdr *) &data[0]; rc = gsm0480_decode_ss_request(hdr, len, &req); free(data); return rc; } static int parse_mangle_ussd(const uint8_t *_data, int len) { uint8_t *data; int rc; struct ss_request req; struct gsm48_hdr *hdr; data = malloc(len); memcpy(data, _data, len); hdr = (struct gsm48_hdr *) &data[0]; hdr->data[1] = len - sizeof(*hdr) - 2; rc = gsm0480_decode_ss_request(hdr, len, &req); free(data); return rc; } struct log_info info = {}; static void test_7bit_ussd(const char *text, const char *encoded_hex, const char *appended_after_decode) { uint8_t coded[256]; char decoded[256]; int octets_written; int buffer_size; int nchars; printf("original = %s\n", osmo_hexdump((uint8_t *)text, strlen(text))); gsm_7bit_encode_n_ussd(coded, sizeof(coded), text, &octets_written); printf("encoded = %s\n", osmo_hexdump(coded, octets_written)); OSMO_ASSERT(strcmp(encoded_hex, osmo_hexdump_nospc(coded, octets_written)) == 0); gsm_7bit_decode_n_ussd(decoded, sizeof(decoded), coded, octets_written * 8 / 7); octets_written = strlen(decoded); printf("decoded = %s\n\n", osmo_hexdump((uint8_t *)decoded, octets_written)); OSMO_ASSERT(strncmp(text, decoded, strlen(text)) == 0); OSMO_ASSERT(strcmp(appended_after_decode, decoded + strlen(text)) == 0); /* check buffer limiting */ memset(decoded, 0xaa, sizeof(decoded)); for (buffer_size = 1; buffer_size < sizeof(decoded) - 1; ++buffer_size) { nchars = gsm_7bit_decode_n_ussd(decoded, buffer_size, coded, octets_written * 8 / 7); OSMO_ASSERT(nchars <= buffer_size); OSMO_ASSERT(decoded[buffer_size] == (char)0xaa); OSMO_ASSERT(decoded[nchars] == '\0'); } memset(coded, 0xaa, sizeof(coded)); for (buffer_size = 0; buffer_size < sizeof(coded) - 1; ++buffer_size) { gsm_7bit_encode_n_ussd(coded, buffer_size, text, &octets_written); OSMO_ASSERT(octets_written <= buffer_size); OSMO_ASSERT(coded[buffer_size] == 0xaa); } } int main(int argc, char **argv) { struct ss_request req; const int size = sizeof(ussd_request); int i; struct msgb *msg; osmo_init_logging(&info); gsm0480_decode_ss_request((struct gsm48_hdr *) ussd_request, size, &req); printf("Tested if it still works. Text was: %s\n", req.ussd_text); printf("Testing parsing a USSD request and truncated versions\n"); for (i = size; i > sizeof(struct gsm48_hdr); --i) { int rc = parse_ussd(&ussd_request[0], i); printf("Result for %d is %d\n", rc, i); } printf("Mangling the container now\n"); for (i = size; i > sizeof(struct gsm48_hdr) + 2; --i) { int rc = parse_mangle_ussd(&ussd_request[0], i); printf("Result for %d is %d\n", rc, i); } printf(" case test for 7 bit encode\n"); test_7bit_ussd("01234567", "b0986c46abd96e", ""); test_7bit_ussd("0123456", "b0986c46abd91a", ""); test_7bit_ussd("01234567\r", "b0986c46abd96e0d", ""); /* The appended \r is compliant to GSM 03.38 section 6.1.2.3.1: */ test_7bit_ussd("0123456\r", "b0986c46abd91a0d", "\r"); test_7bit_ussd("012345\r", "b0986c46ab351a", ""); printf("Checking GSM 04.80 USSD message generation.\n"); test_7bit_ussd("", "", ""); msg = gsm0480_create_unstructuredSS_Notify (0x00, ""); printf ("Created unstructuredSS_Notify (0x00): %s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg))); msgb_free (msg); test_7bit_ussd("forty-two", "e6b79c9e6fd1ef6f", ""); msg = gsm0480_create_unstructuredSS_Notify (0x42, "forty-two"); printf ("Created unstructuredSS_Notify (0x42): %s\n", osmo_hexdump(msgb_data(msg), msgb_length(msg))); msgb_free (msg); return 0; } libosmocore-0.9.0/tests/ussd/ussd_test.ok000066400000000000000000000036631261607044000205350ustar00rootroot00000000000000Tested if it still works. Text was: **321# Testing parsing a USSD request and truncated versions Result for 1 is 28 Result for 1 is 27 Result for 1 is 26 Result for 1 is 25 Result for 0 is 24 Result for 0 is 23 Result for 0 is 22 Result for 0 is 21 Result for 0 is 20 Result for 0 is 19 Result for 0 is 18 Result for 0 is 17 Result for 0 is 16 Result for 0 is 15 Result for 0 is 14 Result for 0 is 13 Result for 0 is 12 Result for 0 is 11 Result for 0 is 10 Result for 0 is 9 Result for 0 is 8 Result for 0 is 7 Result for 0 is 6 Result for 0 is 5 Result for 0 is 4 Result for 0 is 3 Mangling the container now Result for 0 is 28 Result for 0 is 27 Result for 1 is 26 Result for 1 is 25 Result for 0 is 24 Result for 0 is 23 Result for 0 is 22 Result for 0 is 21 Result for 0 is 20 Result for 0 is 19 Result for 0 is 18 Result for 0 is 17 Result for 0 is 16 Result for 0 is 15 Result for 0 is 14 Result for 0 is 13 Result for 0 is 12 Result for 0 is 11 Result for 0 is 10 Result for 0 is 9 Result for 0 is 8 Result for 0 is 7 Result for 0 is 6 Result for 1 is 5 case test for 7 bit encode original = 30 31 32 33 34 35 36 37 encoded = b0 98 6c 46 ab d9 6e decoded = 30 31 32 33 34 35 36 37 original = 30 31 32 33 34 35 36 encoded = b0 98 6c 46 ab d9 1a decoded = 30 31 32 33 34 35 36 original = 30 31 32 33 34 35 36 37 0d encoded = b0 98 6c 46 ab d9 6e 0d decoded = 30 31 32 33 34 35 36 37 0d original = 30 31 32 33 34 35 36 0d encoded = b0 98 6c 46 ab d9 1a 0d decoded = 30 31 32 33 34 35 36 0d 0d original = 30 31 32 33 34 35 0d encoded = b0 98 6c 46 ab 35 1a decoded = 30 31 32 33 34 35 0d Checking GSM 04.80 USSD message generation. original = encoded = decoded = Created unstructuredSS_Notify (0x00): 30 08 04 01 0f 04 00 04 01 00 original = 66 6f 72 74 79 2d 74 77 6f encoded = e6 b7 9c 9e 6f d1 ef 6f decoded = 66 6f 72 74 79 2d 74 77 6f Created unstructuredSS_Notify (0x42): 30 10 04 01 0f 04 08 e6 b7 9c 9e 6f d1 ef 6f 04 01 42 libosmocore-0.9.0/tests/utils/000077500000000000000000000000001261607044000163375ustar00rootroot00000000000000libosmocore-0.9.0/tests/utils/utils_test.c000066400000000000000000000053641261607044000207120ustar00rootroot00000000000000/* tests for utilities of libmsomcore */ /* * (C) 2014 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 #include #include #include static void hexdump_test(void) { uint8_t data[4098]; int i; for (i = 0; i < ARRAY_SIZE(data); ++i) data[i] = i & 0xff; printf("Plain dump\n"); printf("%s\n", osmo_hexdump(data, 4)); printf("Corner case\n"); printf("%s\n", osmo_hexdump(data, ARRAY_SIZE(data))); printf("%s\n", osmo_hexdump_nospc(data, ARRAY_SIZE(data))); } static void test_idtag_parsing(void) { struct tlv_parsed tvp; int rc; static uint8_t data[] = { 0x01, 0x08, 0x01, 0x07, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x01, 0x01, 0x00, 0x11, 0x23, 0x4e, 0x6a, 0x28, 0xd2, 0xa2, 0x53, 0x3a, 0x2a, 0x82, 0xa7, 0x7a, 0xef, 0x29, 0xd4, 0x44, 0x30, 0x11, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; rc = ipa_ccm_idtag_parse_off(&tvp, data, sizeof(data), 1); OSMO_ASSERT(rc == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 8)); OSMO_ASSERT(TLVP_LEN(&tvp, 8) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 7)); OSMO_ASSERT(TLVP_LEN(&tvp, 7) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 2)); OSMO_ASSERT(TLVP_LEN(&tvp, 2) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 3)); OSMO_ASSERT(TLVP_LEN(&tvp, 3) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 4)); OSMO_ASSERT(TLVP_LEN(&tvp, 4) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 5)); OSMO_ASSERT(TLVP_LEN(&tvp, 5) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 1)); OSMO_ASSERT(TLVP_LEN(&tvp, 1) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 0)); OSMO_ASSERT(TLVP_LEN(&tvp, 0) == 0); OSMO_ASSERT(TLVP_PRESENT(&tvp, 0x23)); OSMO_ASSERT(TLVP_LEN(&tvp, 0x23) == 16); OSMO_ASSERT(TLVP_PRESENT(&tvp, 0x24)); OSMO_ASSERT(TLVP_LEN(&tvp, 0x24) == 16); OSMO_ASSERT(!TLVP_PRESENT(&tvp, 0x25)); } int main(int argc, char **argv) { static const struct log_info log_info = {}; log_init(&log_info, NULL); hexdump_test(); test_idtag_parsing(); return 0; } libosmocore-0.9.0/tests/utils/utils_test.ok000066400000000000000000000200431261607044000210700ustar00rootroot00000000000000Plain dump 00 01 02 03 Corner case 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe libosmocore-0.9.0/tests/vty/000077500000000000000000000000001261607044000160215ustar00rootroot00000000000000libosmocore-0.9.0/tests/vty/vty_test.c000066400000000000000000000134001261607044000200440ustar00rootroot00000000000000/* (C) 2013 by Jacob Erlbeck * All Rights Reserved * * This program is iree 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static enum event last_vty_connection_event = -1; static void test_cmd_string_from_valstr(void) { char *cmd; const struct value_string printf_seq_vs[] = { { .value = 42, .str = "[foo%s%s%s%s%s]"}, { .value = 43, .str = "[bar%s%s%s%s%s]"}, { .value = 0, .str = NULL} }; printf("Going to test vty_cmd_string_from_valstr()\n"); /* check against character strings that could break printf */ cmd = vty_cmd_string_from_valstr (NULL, printf_seq_vs, "[prefix%s%s%s%s%s]", "[sep%s%s%s%s%s]", "[end%s%s%s%s%s]", 1); printf ("Tested with %%s-strings, resulting cmd = '%s'\n", cmd); talloc_free (cmd); } static int do_vty_command(struct vty *vty, const char *cmd) { vector vline; int ret; printf("Going to execute '%s'\n", cmd); vline = cmd_make_strvec(cmd); ret = cmd_execute_command(vline, vty, NULL, 0); cmd_free_strvec(vline); printf("Returned: %d, Current node: %d '%s'\n", ret, vty->node, cmd_prompt(vty->node)); return ret; } /* Handle the events from telnet_interface.c */ static int vty_event_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *_signal_data) { struct vty_signal_data *signal_data; if (subsys != SS_L_VTY) return 0; if (signal != S_VTY_EVENT) return 0; signal_data = _signal_data; last_vty_connection_event = signal_data->event; fprintf(stderr, "Got VTY event: %d\n", signal_data->event); return 0; } static void test_node_tree_structure(void) { struct vty_app_info vty_info = { .name = "VtyTest", .version = 0, .go_parent_cb = NULL, .is_config_node = NULL, }; const struct log_info_cat default_categories[] = {}; const struct log_info log_info = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; struct vty *vty; int sock[2]; printf("Going to test VTY node tree structure\n"); /* Fake logging. */ osmo_init_logging(&log_info); vty_init(&vty_info); logging_vty_add_cmds(&log_info); /* Fake connection. */ socketpair(AF_UNIX, SOCK_STREAM, 0, sock); vty = vty_create(sock[0], NULL); OSMO_ASSERT(vty != NULL); OSMO_ASSERT(do_vty_command(vty, "enable") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CFG_LOG_NODE); OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CFG_LOG_NODE); OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS); OSMO_ASSERT(vty->node == VTY_NODE); OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS); OSMO_ASSERT(vty->node == VTY_NODE); OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); /* Check searching the parents nodes for matching commands. */ OSMO_ASSERT(do_vty_command(vty, "configure terminal") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CONFIG_NODE); OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CFG_LOG_NODE); OSMO_ASSERT(do_vty_command(vty, "line vty") == CMD_SUCCESS); OSMO_ASSERT(vty->node == VTY_NODE); OSMO_ASSERT(do_vty_command(vty, "log stderr") == CMD_SUCCESS); OSMO_ASSERT(vty->node == CFG_LOG_NODE); OSMO_ASSERT(do_vty_command(vty, "end") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); /* Check for final 'exit' (connection close). */ OSMO_ASSERT(do_vty_command(vty, "exit") == CMD_SUCCESS); OSMO_ASSERT(vty->node == ENABLE_NODE); OSMO_ASSERT(vty->status == VTY_CLOSE); vty_close(vty); OSMO_ASSERT(last_vty_connection_event == VTY_CLOSED); } int main(int argc, char **argv) { osmo_signal_register_handler(SS_L_VTY, vty_event_cb, NULL); test_cmd_string_from_valstr(); test_node_tree_structure(); printf("All tests passed\n"); return 0; } libosmocore-0.9.0/tests/vty/vty_test.ok000066400000000000000000000032761261607044000202450ustar00rootroot00000000000000Going to test vty_cmd_string_from_valstr() Tested with %s-strings, resulting cmd = '[prefix%s%s%s%s%s][foo%s%s%s%s%s][sep%s%s%s%s%s][bar%s%s%s%s%s][end%s%s%s%s%s]' Going to test VTY node tree structure Going to execute 'enable' Returned: 0, Current node: 3 '%s# ' Going to execute 'configure terminal' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'exit' Returned: 0, Current node: 3 '%s# ' Going to execute 'configure terminal' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'end' Returned: 0, Current node: 3 '%s# ' Going to execute 'configure terminal' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'log stderr' Returned: 0, Current node: 7 '%s(config-log)# ' Going to execute 'exit' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'log stderr' Returned: 0, Current node: 7 '%s(config-log)# ' Going to execute 'end' Returned: 0, Current node: 3 '%s# ' Going to execute 'configure terminal' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'line vty' Returned: 0, Current node: 9 '%s(config-line)# ' Going to execute 'exit' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'line vty' Returned: 0, Current node: 9 '%s(config-line)# ' Going to execute 'end' Returned: 0, Current node: 3 '%s# ' Going to execute 'configure terminal' Returned: 0, Current node: 4 '%s(config)# ' Going to execute 'log stderr' Returned: 0, Current node: 7 '%s(config-log)# ' Going to execute 'line vty' Returned: 0, Current node: 9 '%s(config-line)# ' Going to execute 'log stderr' Returned: 0, Current node: 7 '%s(config-log)# ' Going to execute 'end' Returned: 0, Current node: 3 '%s# ' Going to execute 'exit' Returned: 0, Current node: 3 '%s# ' All tests passed libosmocore-0.9.0/utils/000077500000000000000000000000001261607044000151755ustar00rootroot00000000000000libosmocore-0.9.0/utils/Makefile.am000066400000000000000000000012561261607044000172350ustar00rootroot00000000000000if ENABLE_UTILITIES AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include AM_CFLAGS = -Wall bin_PROGRAMS = osmo-arfcn osmo-auc-gen osmo_arfcn_SOURCES = osmo-arfcn.c osmo_arfcn_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la osmo_auc_gen_SOURCES = osmo-auc-gen.c osmo_auc_gen_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la if ENABLE_PCSC noinst_PROGRAMS = osmo-sim-test osmo_sim_test_SOURCES = osmo-sim-test.c osmo_sim_test_LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la $(top_builddir)/src/sim/libosmosim.la $(PCSC_LIBS) osmo_sim_test_CFLAGS = $(PCSC_CFLAGS) endif endif libosmocore-0.9.0/utils/gen_website_doc_tree.sh000077500000000000000000000004271261607044000216760ustar00rootroot00000000000000#!/bin/bash TOPDIR=`pwd` INDIR="$TOPDIR/doc" OUTDIR=/tmp/doxywww GITREV=`./git-version-gen .tarball-version` [ -f "$OUTDIR" ] || mkdir "$OUTDIR" for MOD in core gsm vty codec; do TGTDIR="$OUTDIR/libosmo$MOD/$GITREV" mkdir -p "$TGTDIR" cp -R "$INDIR/$MOD/"* "$TGTDIR/" done libosmocore-0.9.0/utils/osmo-arfcn.c000066400000000000000000000056171261607044000174160ustar00rootroot00000000000000/* Utility program for ARFCN / frequency calculations */ /* * (C) 2011 by Harald Welte * * 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 #include #include #include #include #include enum program_mode { MODE_NONE, MODE_A2F, MODE_F2A, }; static int arfcn2freq(int arfcn) { uint16_t freq10u, freq10d; if (arfcn < 0 || arfcn > 0xffff) { fprintf(stderr, "Invalid ARFCN %d\n", arfcn); return -EINVAL; } freq10u = gsm_arfcn2freq10(arfcn, 1); freq10d = gsm_arfcn2freq10(arfcn, 0); if (freq10u == 0xffff || freq10d == 0xffff) { fprintf(stderr, "Error during conversion of ARFCN %d\n", arfcn); return -EINVAL; } printf("ARFCN %4d: Uplink %4u.%1u MHz / Downlink %4u.%1u MHz\n", arfcn & ~ARFCN_FLAG_MASK, freq10u/10, freq10u%10, freq10d/10, freq10d%10); return 0; } static int freq2arfcn(int freq10, int uplink) { uint16_t arfcn; if (uplink != 0 && uplink != 1) { fprintf(stderr, "Need to specify uplink or downlink\n"); return -EINVAL; } arfcn = gsm_freq102arfcn(freq10, uplink); if (arfcn == 0xffff) { fprintf(stderr, "Unable to find matching ARFCN\n"); return -EINVAL; } printf("%s: ARFCN %4d\n", gsm_band_name(gsm_arfcn2band(arfcn)), arfcn & ~ARFCN_FLAG_MASK); return 0; } static void help(const char *progname) { printf("Usage: %s [-h] [-p] [-a arfcn] [-f freq] [-u|-d]\n", progname); } int main(int argc, char **argv) { int arfcn, freq, pcs = 0, uplink = -1; int opt; char *param; enum program_mode mode = MODE_NONE; while ((opt = getopt(argc, argv, "pa:f:ud")) != -1) { switch (opt) { case 'p': pcs = 1; break; case 'a': mode = MODE_A2F; param = optarg; break; case 'f': mode = MODE_F2A; param = optarg; break; case 'u': uplink = 1; break; case 'd': uplink = 0; break; case 'h': help(argv[0]); exit(0); break; default: break; } } switch (mode) { case MODE_NONE: help(argv[0]); exit(2); break; case MODE_A2F: arfcn = atoi(param); if (pcs) arfcn |= ARFCN_PCS; arfcn2freq(arfcn); break; case MODE_F2A: freq = (int)(atof(param) * 10.0f); freq2arfcn(freq, uplink); break; } exit(0); } libosmocore-0.9.0/utils/osmo-auc-gen.c000066400000000000000000000146411261607044000176410ustar00rootroot00000000000000/* GSM/GPRS/3G authentication testing tool */ /* (C) 2010-2012 by Harald Welte * * 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 #include #include #include #include #include #include #include #include #include #include static void dump_triplets_dat(struct osmo_auth_vector *vec) { if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "triplets.dat doesn't support UMTS!\n"); return; } printf("imsi,"); printf("%s,", osmo_hexdump_nospc(vec->rand, sizeof(vec->rand))); printf("%s,", osmo_hexdump_nospc(vec->sres, sizeof(vec->sres))); printf("%s\n", osmo_hexdump_nospc(vec->kc, sizeof(vec->kc))); } static void dump_auth_vec(struct osmo_auth_vector *vec) { printf("RAND:\t%s\n", osmo_hexdump(vec->rand, sizeof(vec->rand))); if (vec->auth_types & OSMO_AUTH_TYPE_UMTS) { printf("AUTN:\t%s\n", osmo_hexdump(vec->autn, sizeof(vec->autn))); printf("IK:\t%s\n", osmo_hexdump(vec->ik, sizeof(vec->ik))); printf("CK:\t%s\n", osmo_hexdump(vec->ck, sizeof(vec->ck))); printf("RES:\t%s\n", osmo_hexdump(vec->res, vec->res_len)); } if (vec->auth_types & OSMO_AUTH_TYPE_GSM) { printf("SRES:\t%s\n", osmo_hexdump(vec->sres, sizeof(vec->sres))); printf("Kc:\t%s\n", osmo_hexdump(vec->kc, sizeof(vec->kc))); } } static struct osmo_sub_auth_data test_aud = { .type = OSMO_AUTH_TYPE_NONE, .algo = OSMO_AUTH_ALG_NONE, }; static void help() { printf( "-2 --2g\tUse 2G (GSM) authentication\n" "-3 --3g\tUse 3G (UMTS) authentication\n" "-a --algorithm\tSpecify name of the algorithm\n" "-k --key\tSpecify Ki / K\n" "-o --opc\tSpecify OPC (only for 3G)\n" "-O --op\tSpecify OP (only for 3G)\n" "-f --amf\tSpecify AMF (only for 3G)\n" "-s --sqn\tSpecify SQN (only for 3G)\n" "-A --auts\tSpecify AUTS (only for 3G)\n" "-r --rand\tSpecify random value\n" "-I --ipsec\tOutput in triplets.dat format for strongswan\n"); } int main(int argc, char **argv) { struct osmo_auth_vector _vec; struct osmo_auth_vector *vec = &_vec; uint8_t _rand[16], _auts[16]; int rc, option_index; int rand_is_set = 0; int auts_is_set = 0; int fmt_triplets_dat = 0; printf("osmo-auc-gen (C) 2011-2012 by Harald Welte\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); memset(_auts, 0, sizeof(_auts)); while (1) { int c; unsigned long ul; static struct option long_options[] = { { "2g", 0, 0, '2' }, { "3g", 0, 0, '3' }, { "algorithm", 1, 0, 'a' }, { "key", 1, 0, 'k' }, { "opc", 1, 0, 'o' }, { "op", 1, 0, 'O' }, { "amf", 1, 0, 'f' }, { "sqn", 1, 0, 's' }, { "rand", 1, 0, 'r' }, { "auts", 1, 0, 'A' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; rc = 0; c = getopt_long(argc, argv, "23a:k:o:f:s:r:hO:A:I", long_options, &option_index); if (c == -1) break; switch (c) { case '2': test_aud.type = OSMO_AUTH_TYPE_GSM; break; case '3': test_aud.type = OSMO_AUTH_TYPE_UMTS; break; case 'a': rc = osmo_auth_alg_parse(optarg); if (rc < 0) break; test_aud.algo = rc; break; case 'k': switch (test_aud.type) { case OSMO_AUTH_TYPE_GSM: rc = osmo_hexparse(optarg, test_aud.u.gsm.ki, sizeof(test_aud.u.gsm.ki)); break; case OSMO_AUTH_TYPE_UMTS: rc = osmo_hexparse(optarg, test_aud.u.umts.k, sizeof(test_aud.u.umts.k)); break; default: fprintf(stderr, "please specify 2g/3g first!\n"); } break; case 'o': if (test_aud.type != OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "Only UMTS has OPC\n"); exit(2); } rc = osmo_hexparse(optarg, test_aud.u.umts.opc, sizeof(test_aud.u.umts.opc)); test_aud.u.umts.opc_is_op = 0; break; case 'O': if (test_aud.type != OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "Only UMTS has OP\n"); exit(2); } rc = osmo_hexparse(optarg, test_aud.u.umts.opc, sizeof(test_aud.u.umts.opc)); test_aud.u.umts.opc_is_op = 1; break; case 'A': if (test_aud.type != OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "Only UMTS has AUTS\n"); exit(2); } rc = osmo_hexparse(optarg, _auts, sizeof(_auts)); auts_is_set = 1; break; case 'f': if (test_aud.type != OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "Only UMTS has AMF\n"); exit(2); } rc = osmo_hexparse(optarg, test_aud.u.umts.amf, sizeof(test_aud.u.umts.amf)); break; case 's': if (test_aud.type != OSMO_AUTH_TYPE_UMTS) { fprintf(stderr, "Only UMTS has SQN\n"); exit(2); } ul = strtoul(optarg, 0, 10); test_aud.u.umts.sqn = ul; break; case 'r': rc = osmo_hexparse(optarg, _rand, sizeof(_rand)); rand_is_set = 1; break; case 'I': fmt_triplets_dat = 1; break; case 'h': help(); exit(0); default: help(); exit(1); } if (rc < 0) { fprintf(stderr, "Error parsing argument of option `%c'\n", c); exit(2); } } if (!rand_is_set) { int i; printf("WARNING: We're using really weak random numbers!\n\n"); srand(time(NULL)); for (i = 0; i < 4; ++i) { uint32_t r; r = rand(); memcpy(&_rand[i*4], &r, 4); } } if (test_aud.type == OSMO_AUTH_TYPE_NONE || test_aud.algo == OSMO_AUTH_ALG_NONE) { help(); exit(2); } memset(vec, 0, sizeof(*vec)); if (!auts_is_set) rc = osmo_auth_gen_vec(vec, &test_aud, _rand); else rc = osmo_auth_gen_vec_auts(vec, &test_aud, _auts, _rand, _rand); if (rc < 0) { if (!auts_is_set) fprintf(stderr, "error generating auth vector\n"); else fprintf(stderr, "AUTS from MS seems incorrect\n"); exit(1); } if (fmt_triplets_dat) dump_triplets_dat(vec); else dump_auth_vec(vec); if (auts_is_set) printf("AUTS success: SEQ.MS = %" PRIu64 "\n", test_aud.u.umts.sqn); exit(0); } libosmocore-0.9.0/utils/osmo-sim-test.c000066400000000000000000000226661261607044000200750ustar00rootroot00000000000000/* libosmosim test application - currently simply dumps a USIM */ /* (C) 2012 by Harald Welte * 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 #include #include #include #include #include #include #include /* FIXME: this needs to be moved to card_fs_uicc.c */ /* 11.1.1 */ static struct msgb *_select_file(struct osim_chan_hdl *st, uint8_t p1, uint8_t p2, const uint8_t *data, uint8_t data_len) { struct msgb *msg, *resp; char *dst; msg = osim_new_apdumsg(0x00, 0xA4, p1, p2, data_len, 256); dst = msgb_put(msg, data_len); memcpy(dst, data, data_len); osim_transceive_apdu(st, msg); return msg; } /* 11.1.1 */ static struct msgb *select_adf(struct osim_chan_hdl *st, const uint8_t *adf, uint8_t adf_len) { int sw; return _select_file(st, 0x04, 0x04, adf,adf_len); } /* 11.1.1 */ static struct msgb *select_file(struct osim_chan_hdl *st, uint16_t fid) { uint16_t cfid = htons(fid); return _select_file(st, 0x00, 0x04, (uint8_t *)&cfid, 2); } /* 11.1.9 */ static int verify_pin(struct osim_chan_hdl *st, uint8_t pin_nr, uint8_t *pin) { struct msgb *msg; char *pindst; int sw; if (strlen(pin) > 8) return -EINVAL; msg = osim_new_apdumsg(0x00, 0x20, 0x00, pin_nr, 8, 0); pindst = msgb_put(msg, 8); memset(pindst, 0xFF, 8); strncpy(pindst, pin, strlen(pin)); return osim_transceive_apdu(st, msg); } /* 11.1.5 */ static struct msgb *read_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr, uint16_t rec_size) { struct msgb *msg; msg = osim_new_apdumsg(0x00, 0xB2, rec_nr, 0x04, 0, rec_size); osim_transceive_apdu(st, msg); return msg; } /* 11.1.6 */ static struct msgb *update_record_nr(struct osim_chan_hdl *st, uint8_t rec_nr, const uint8_t *data, uint16_t rec_size) { struct msgb *msg; uint8_t *cur; msg = osim_new_apdumsg(0x00, 0xDC, rec_nr, 0x04, rec_size, 0); cur = msgb_put(msg, rec_size); memcpy(cur, data, rec_size); osim_transceive_apdu(st, msg); return msg; } /* 11.1.3 */ static struct msgb *read_binary(struct osim_chan_hdl *st, uint16_t offset, uint16_t len) { struct msgb *msg; if (offset > 0x7fff || len > 256) return NULL; msg = osim_new_apdumsg(0x00, 0xB0, offset >> 8, offset & 0xff, 0, len & 0xff); osim_transceive_apdu(st, msg); return msg; } /* 11.1.4 */ static struct msgb *update_binary(struct osim_chan_hdl *st, uint16_t offset, const uint8_t *data, uint16_t len) { struct msgb *msg; uint8_t *cur; if (offset > 0x7fff || len > 256) return NULL; msg = osim_new_apdumsg(0x00, 0xD6, offset >> 8, offset & 0xff, len & 0xff, 0); cur = msgb_put(msg, len); memcpy(cur, data, len); osim_transceive_apdu(st, msg); return msg; } static int dump_fcp_template(struct tlv_parsed *tp) { int i; for (i = 0; i < ARRAY_SIZE(tp->lv); i++) { if (TLVP_PRESENT(tp, i)) printf("Tag 0x%02x (%s): %s\n", i, get_value_string(ts102221_fcp_vals, i), osmo_hexdump(TLVP_VAL(tp, i), TLVP_LEN(tp, i))); } return 0; } static int dump_fcp_template_msg(struct msgb *msg) { struct tlv_parsed tp; int rc; rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); if (rc < 0) return rc; return dump_fcp_template(&tp); } struct osim_fcp_fd_decoded { enum osim_file_type type; enum osim_ef_type ef_type; uint16_t rec_len; uint8_t num_rec; }; static const enum osim_file_type iso2ftype[8] = { [0] = TYPE_EF, [1] = TYPE_EF_INT, [7] = TYPE_DF, }; static const enum osim_ef_type iso2eftype[8] = { [1] = EF_TYPE_TRANSP, [2] = EF_TYPE_RECORD_FIXED, [6] = EF_TYPE_RECORD_CYCLIC, }; static int osim_fcp_fd_decode(struct osim_fcp_fd_decoded *ofd, const uint8_t *fcp, int fcp_len) { memset(ofd, 0, sizeof(*ofd)); if (fcp_len != 2 && fcp_len != 5) return -EINVAL; ofd->type = iso2ftype[(fcp[0] >> 3) & 7]; if (ofd->type != TYPE_DF) ofd->ef_type = iso2eftype[fcp[0] & 7]; if (fcp[1] != 0x21) return -EINVAL; if (fcp_len >= 5) { ofd->rec_len = ntohs(*(uint16_t *)(fcp+2)); ofd->num_rec = fcp[4]; } return 0; } extern struct osim_card_profile *osim_cprof_usim(void *ctx); static struct msgb *try_select_adf_usim(struct osim_chan_hdl *st) { struct tlv_parsed tp; struct osim_fcp_fd_decoded ofd; struct msgb *msg, *msg2; uint8_t *cur; int rc, i; msg = select_file(st, 0x2f00); rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); if (rc < 0) return NULL; dump_fcp_template(&tp); if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) || TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 5) { msgb_free(msg); return NULL; } rc = osim_fcp_fd_decode(&ofd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC), TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC)); if (rc < 0) { msgb_free(msg); return NULL; } if (ofd.type != TYPE_EF || ofd.ef_type != EF_TYPE_RECORD_FIXED) { msgb_free(msg); return NULL; } msgb_free(msg); printf("ofd rec_len = %u, num_rec = %u\n", ofd.rec_len, ofd.num_rec); for (i = 0; i < ofd.num_rec; i++) { msg = read_record_nr(st, i+1, ofd.rec_len); if (!msg) return NULL; cur = msgb_apdu_de(msg); if (msgb_apdu_le(msg) < 5) { msgb_free(msg); return NULL; } if (cur[0] != 0x61 || cur[1] < 0x03 || cur[1] > 0x7f || cur[2] != 0x4F || cur[3] < 0x01 || cur[3] > 0x10) { msgb_free(msg); return NULL; } /* FIXME: actually check if it is an AID that we support, or * iterate until we find one that we support */ msg2 = select_adf(st, cur+4, cur[3]); /* attach the USIM profile, FIXME: do this based on AID match */ st->card->prof = osim_cprof_usim(st->card); st->cwd = osim_file_find_name(st->card->prof->mf, "ADF.USIM"); msgb_free(msg); return msg2; } return NULL; } static int dump_file(struct osim_chan_hdl *chan, uint16_t fid) { struct tlv_parsed tp; struct osim_fcp_fd_decoded ffdd; struct msgb *msg, *rmsg; int rc, i, offset; msg = select_file(chan, fid); if (!msg) { printf("Unable to select file\n"); return -EIO; } printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); if (msgb_apdu_sw(msg) != 0x9000) { printf("status 0x%04x selecting file\n", msgb_apdu_sw(msg)); goto out; } rc = tlv_parse(&tp, &ts102221_fcp_tlv_def, msgb_apdu_de(msg)+2, msgb_apdu_le(msg)-2, 0, 0); if (rc < 0) { printf("Unable to parse FCP\n"); goto out; } if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_DESC) || TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC) < 2) { printf("No file descriptor present ?!?\n"); goto out; } rc = osim_fcp_fd_decode(&ffdd, TLVP_VAL(&tp, UICC_FCP_T_FILE_DESC), TLVP_LEN(&tp, UICC_FCP_T_FILE_DESC)); if (rc < 0) { printf("Unable to decode File Descriptor\n"); goto out; } if (ffdd.type != TYPE_EF) { printf("File Type != EF\n"); goto out; } printf("EF type: %u\n", ffdd.ef_type); switch (ffdd.ef_type) { case EF_TYPE_RECORD_FIXED: for (i = 0; i < ffdd.num_rec; i++) { rmsg = read_record_nr(chan, i+1, ffdd.rec_len); if (!msg) return -EIO; printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); printf("Rec %03u: %s\n", i+1, osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg))); } break; case EF_TYPE_TRANSP: if (!TLVP_PRESENT(&tp, UICC_FCP_T_FILE_SIZE)) goto out; i = ntohs(*(uint16_t *)TLVP_VAL(&tp, UICC_FCP_T_FILE_SIZE)); printf("File size: %d bytes\n", i); for (offset = 0; offset < i-1; ) { uint16_t remain_len = i - offset; uint16_t read_len = OSMO_MIN(remain_len, 256); rmsg = read_binary(chan, offset, read_len); if (!rmsg) return -EIO; offset += read_len; printf("Content: %s\n", osmo_hexdump(msgb_apdu_de(rmsg), msgb_apdu_le(rmsg))); } break; default: goto out; } out: msgb_free(msg); return -EINVAL; } int main(int argc, char **argv) { struct osim_reader_hdl *reader; struct osim_card_hdl *card; struct osim_chan_hdl *chan; struct msgb *msg; int rc; reader = osim_reader_open(OSIM_READER_DRV_PCSC, 0, "", NULL); if (!reader) exit(1); card = osim_card_open(reader, OSIM_PROTO_T0); if (!card) exit(2); chan = llist_entry(card->channels.next, struct osim_chan_hdl, list); if (!chan) exit(3); msg = try_select_adf_usim(chan); if (!msg || msgb_apdu_sw(msg) != 0x9000) exit(4); dump_fcp_template_msg(msg); msgb_free(msg); msg = select_file(chan, 0x6fc5); dump_fcp_template_msg(msg); printf("SW: %s\n", osim_print_sw(chan->card, msgb_apdu_sw(msg))); msgb_free(msg); verify_pin(chan, 1, "1653"); msg = select_file(chan, 0x6f06); dump_fcp_template_msg(msg); msgb_free(msg); { struct osim_file_desc *ofd; llist_for_each_entry(ofd, &chan->cwd->child_list, list) { struct msgb *m; printf("\n\n================ %s (%s) ==================\n", ofd->short_name, ofd->long_name); m = select_file(chan, ofd->fid); dump_fcp_template_msg(m); msgb_free(m); dump_file(chan, ofd->fid); } } exit(0); }