pax_global_header00006660000000000000000000000064126173606240014521gustar00rootroot0000000000000052 comment=86fc3c8787a9c29f566a44969d528a604ee7e11f libosmo-abis-0.3.2+git86fc3c8/000077500000000000000000000000001261736062400156675ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/.gitignore000066400000000000000000000007421261736062400176620ustar00rootroot00000000000000Makefile Makefile.in .deps .libs *.o *.lo *.la *.pc aclocal.m4 m4/*.m4 autom4te.cache config.h* config.sub config.log config.status config.guess configure compile depcomp missing ltmain.sh install-sh stamp-h1 libtool #libosmo-abis-* tests/*_test .tarball-version .version .dirstamp # tests tests/atconfig tests/package.m4 tests/testsuite tests/testsuite.log tests/subchan_demux/.dirstamp tests/subchan_demux/subchan_demux_test tests/ipa_recv/ipa_recv_test # vi/vim files *.sw? libosmo-abis-0.3.2+git86fc3c8/AUTHORS000066400000000000000000000001721261736062400167370ustar00rootroot00000000000000Harald Welte Holger Hans Peter Freyther Pablo Neira Ayuso libosmo-abis-0.3.2+git86fc3c8/COPYING000066400000000000000000001033301261736062400167220ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. 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 them 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. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state 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 Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . libosmo-abis-0.3.2+git86fc3c8/Makefile.am000066400000000000000000000005361261736062400177270ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include SUBDIRS = include src tests pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libosmoabis.pc libosmotrau.pc BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version libosmo-abis-0.3.2+git86fc3c8/TODO-RELEASE000066400000000000000000000000611261736062400174320ustar00rootroot00000000000000#library what description / commit summary line libosmo-abis-0.3.2+git86fc3c8/configure.ac000066400000000000000000000026201261736062400201550ustar00rootroot00000000000000AC_INIT([libosmo-abis], 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_CC AC_PROG_INSTALL LT_INIT([pic-only]) AC_CONFIG_MACRO_DIR([m4]) # The following test is taken from WebKit's webkit.m4 saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden " AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], [ AC_MSG_RESULT([yes]) SYMBOL_VISIBILITY="-fvisibility=hidden"], AC_MSG_RESULT([no])) CFLAGS="$saved_CFLAGS" AC_SUBST(SYMBOL_VISIBILITY) dnl Generate the output AM_CONFIG_HEADER(config.h) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.10) PKG_CHECK_MODULES([ORTP], [ortp >= 0.13.1], [ PKG_CHECK_MODULES([ORTP_VERSION], [ortp >= 0.21], [AC_DEFINE(HAVE_ORTP_021, 1, [libortp >= 0.21])], [AC_DEFINE(HAVE_ORTP_021, 0, [libortp < 0.21])]) ] ) AC_CHECK_HEADERS(dahdi/user.h,,AC_MSG_WARN(DAHDI input driver will not be built)) AC_OUTPUT( libosmoabis.pc libosmotrau.pc include/Makefile src/Makefile tests/Makefile Makefile) libosmo-abis-0.3.2+git86fc3c8/debian/000077500000000000000000000000001261736062400171115ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/debian/changelog000066400000000000000000000027661261736062400207760ustar00rootroot00000000000000libosmo-abis (0.3.2) UNRELEASED; urgency=medium * Bump so version to re-link libosmovty -- Holger Hans Peter Freyther Tue, 03 Nov 2015 09:42:01 +0100 libosmo-abis (0.3.1) unstable; urgency=medium * New upstream release of libosmo-abis -- Holger Hans Peter Freyther Sun, 18 Jan 2015 19:26:40 +0100 libosmo-abis (0.3.0) unstable; urgency=medium * New upstream release of libosmo-abis -- Holger Hans Peter Freyther Mon, 08 Sep 2014 07:29:39 +0200 libosmo-abis (0.2.0) unstable; urgency=medium * New upstream release of libosmo-abis -- Holger Hans Peter Freyther Mon, 08 Sep 2014 07:29:19 +0200 libosmo-abis (0.1.6) unstable; urgency=medium * New upstream release of libosmo-abis -- Holger Hans Peter Freyther Mon, 20 Jan 2014 10:33:36 +0100 libosmo-abis (0.1.5+git1) unstable; urgency=low * Rebuild 0.1.5 with the right libosmocore depedency -- Holger Hans Peter Freyther Thu, 12 Dec 2013 11:47:01 +0100 libosmo-abis (0.1.4) unstable; urgency=low * New upstream release -- Holger Hans Peter Freyther Tue, 06 Nov 2012 13:33:45 +0100 libosmo-abis (0.1.3+git3-1) precise; urgency=low * Fix version issue. -- Eric Butler Tue, 14 Aug 2012 20:50:50 -0700 libosmo-abis (0.1.3+git3) precise; urgency=low Created new Ubuntu package. -- Eric Butler Tue, 14 Aug 2012 13:00:18 -0700 libosmo-abis-0.3.2+git86fc3c8/debian/compat000066400000000000000000000000021261736062400203070ustar00rootroot000000000000009 libosmo-abis-0.3.2+git86fc3c8/debian/control000066400000000000000000000030001261736062400205050ustar00rootroot00000000000000Source: libosmo-abis Section: libs Priority: optional Maintainer: Harald Welte Build-Depends: debhelper (>= 9), autotools-dev, autoconf, automake, libtool, dh-autoreconf, libdpkg-perl, git, libosmocore-dev (>= 0.6.4), pkg-config, libortp-dev Standards-Version: 3.9.6 Homepage: http://openbsc.osmocom.org/trac/wiki/libosmo-abis Vcs-Git: git://git.osmocom.org/libosmo-abis.git Vcs-Browser: http://git.osmocom.org/gitweb?p=libosmo-abis.git;a=summary Package: libosmo-abis Section: oldlibs Architecture: any Depends: libosmoabis5 (= ${binary:Version}), libosmotrau1 (= ${binary:Version}), ${misc:Depends} Multi-Arch: same Description: Legacy package for libosmo-abis libosmo-abis is an empty package helping in the transition to one package per DSO. Package: libosmoabis5 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: GSM A-bis handling Package: libosmotrau1 Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Multi-Arch: same Description: GSM trau handling Package: libosmo-abis-dev Section: libdevel Architecture: any Depends: ${misc:Depends}, libosmotrau1 (= ${binary:Version}), libosmoabis5 (= ${binary:Version}) Multi-Arch: same Description: Development headers for A-bis interface. Package: libosmo-abis-dbg Section: debug Architecture: any Priority: extra Depends: libosmoabis5 (= ${binary:Version}), libosmotrau1 (= ${binary:Version}), ${misc:Depends} Multi-Arch: same Description: Debug symbols for A-bis interface. libosmo-abis-0.3.2+git86fc3c8/debian/libosmo-abis-dev.install000066400000000000000000000001311261736062400236300ustar00rootroot00000000000000usr/include/* usr/lib/*/lib*.a usr/lib/*/lib*.so usr/lib/*/lib*.la usr/lib/*/pkgconfig/* libosmo-abis-0.3.2+git86fc3c8/debian/libosmoabis5.install000066400000000000000000000000331261736062400230650ustar00rootroot00000000000000usr/lib/*/libosmoabis.so.* libosmo-abis-0.3.2+git86fc3c8/debian/libosmotrau1.install000066400000000000000000000000331261736062400231160ustar00rootroot00000000000000usr/lib/*/libosmotrau.so.* libosmo-abis-0.3.2+git86fc3c8/debian/rules000077500000000000000000000007001261736062400201660ustar00rootroot00000000000000#!/usr/bin/make -f 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 DH_VERBOSE=1 export DEB_BUILD_HARDENING=1 %: dh $@ --with autoreconf --fail-missing override_dh_strip: dh_strip --dbg-package=libosmo-abis-dbg override_dh_autoreconf: echo $(VERSION) > .tarball-version dh_autoreconf libosmo-abis-0.3.2+git86fc3c8/debian/source/000077500000000000000000000000001261736062400204115ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/debian/source/format000066400000000000000000000000151261736062400216200ustar00rootroot000000000000003.0 (native) libosmo-abis-0.3.2+git86fc3c8/git-version-gen000077500000000000000000000125001261736062400206300ustar00rootroot00000000000000#!/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: libosmo-abis-0.3.2+git86fc3c8/include/000077500000000000000000000000001261736062400173125ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/include/Makefile.am000066400000000000000000000004661261736062400213540ustar00rootroot00000000000000noinst_HEADERS=mISDNif.h internal.h nobase_include_HEADERS = osmocom/abis/ipa.h osmocom/abis/trau_frame.h \ osmocom/abis/ipa_proxy.h osmocom/abis/ipaccess.h osmocom/abis/abis.h \ osmocom/abis/subchan_demux.h osmocom/abis/e1_input.h \ osmocom/abis/lapd.h osmocom/abis/lapd_pcap.h osmocom/trau/osmo_ortp.h libosmo-abis-0.3.2+git86fc3c8/include/internal.h000066400000000000000000000010541261736062400212770ustar00rootroot00000000000000#ifndef _INTERNAL_H_ #define _INTERNAL_H_ #include struct osmo_fd; struct e1inp_sign_link; struct e1inp_ts; /* talloc context for libosmo-abis. */ extern void *libosmo_abis_ctx; /* use libosmo_abis_init, this is only for internal use. */ void e1inp_init(void); /* ipaccess.c requires these functions defined here */ struct msgb; struct msgb *ipa_msg_alloc(int headroom); /* * helper for internal drivers, not public */ void e1inp_close_socket(struct e1inp_ts *ts, struct e1inp_sign_link *sign_link, struct osmo_fd *bfd); #endif libosmo-abis-0.3.2+git86fc3c8/include/mISDNif.h000066400000000000000000000232701261736062400207200ustar00rootroot00000000000000/* * * Author Karsten Keil * * Copyright 2008 by Karsten Keil * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE * version 2.1 as published by the Free Software Foundation. * * This code 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. * */ #ifndef mISDNIF_H #define mISDNIF_H #include #ifdef linux #include #include #include #else #include #include #include #endif /* * ABI Version 32 bit * * <8 bit> Major version * - changed if any interface become backwards incompatible * * <8 bit> Minor version * - changed if any interface is extended but backwards compatible * * <16 bit> Release number * - should be incremented on every checkin */ #define MISDN_MAJOR_VERSION 1 #define MISDN_MINOR_VERSION 1 #define MISDN_RELEASE 20 /* primitives for information exchange * generell format * <16 bit 0 > * <8 bit command> * BIT 8 = 1 LAYER private * BIT 7 = 1 answer * BIT 6 = 1 DATA * <8 bit target layer mask> * * Layer = 00 is reserved for general commands Layer = 01 L2 -> HW Layer = 02 HW -> L2 Layer = 04 L3 -> L2 Layer = 08 L2 -> L3 * Layer = FF is reserved for broadcast commands */ #define MISDN_CMDMASK 0xff00 #define MISDN_LAYERMASK 0x00ff /* generell commands */ #define OPEN_CHANNEL 0x0100 #define CLOSE_CHANNEL 0x0200 #define CONTROL_CHANNEL 0x0300 #define CHECK_DATA 0x0400 /* layer 2 -> layer 1 */ #define PH_ACTIVATE_REQ 0x0101 #define PH_DEACTIVATE_REQ 0x0201 #define PH_DATA_REQ 0x2001 #define MPH_ACTIVATE_REQ 0x0501 #define MPH_DEACTIVATE_REQ 0x0601 #define MPH_INFORMATION_REQ 0x0701 #define PH_CONTROL_REQ 0x0801 /* layer 1 -> layer 2 */ #define PH_ACTIVATE_IND 0x0102 #define PH_ACTIVATE_CNF 0x4102 #define PH_DEACTIVATE_IND 0x0202 #define PH_DEACTIVATE_CNF 0x4202 #define PH_DATA_IND 0x2002 #define PH_DATA_E_IND 0x3002 #define MPH_ACTIVATE_IND 0x0502 #define MPH_DEACTIVATE_IND 0x0602 #define MPH_INFORMATION_IND 0x0702 #define PH_DATA_CNF 0x6002 #define PH_CONTROL_IND 0x0802 #define PH_CONTROL_CNF 0x4802 /* layer 3 -> layer 2 */ #define DL_ESTABLISH_REQ 0x1004 #define DL_RELEASE_REQ 0x1104 #define DL_DATA_REQ 0x3004 #define DL_UNITDATA_REQ 0x3104 #define DL_INFORMATION_REQ 0x0004 /* layer 2 -> layer 3 */ #define DL_ESTABLISH_IND 0x1008 #define DL_ESTABLISH_CNF 0x5008 #define DL_RELEASE_IND 0x1108 #define DL_RELEASE_CNF 0x5108 #define DL_DATA_IND 0x3008 #define DL_UNITDATA_IND 0x3108 #define DL_INFORMATION_IND 0x0008 /* intern layer 2 managment */ #define MDL_ASSIGN_REQ 0x1804 #define MDL_ASSIGN_IND 0x1904 #define MDL_REMOVE_REQ 0x1A04 #define MDL_REMOVE_IND 0x1B04 #define MDL_STATUS_UP_IND 0x1C04 #define MDL_STATUS_DOWN_IND 0x1D04 #define MDL_STATUS_UI_IND 0x1E04 #define MDL_ERROR_IND 0x1F04 #define MDL_ERROR_RSP 0x5F04 /* DL_INFORMATION_IND types */ #define DL_INFO_L2_CONNECT 0x0001 #define DL_INFO_L2_REMOVED 0x0002 /* PH_CONTROL types */ /* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ #define DTMF_TONE_VAL 0x2000 #define DTMF_TONE_MASK 0x007F #define DTMF_TONE_START 0x2100 #define DTMF_TONE_STOP 0x2200 #define DTMF_HFC_COEF 0x4000 #define DSP_CONF_JOIN 0x2403 #define DSP_CONF_SPLIT 0x2404 #define DSP_RECEIVE_OFF 0x2405 #define DSP_RECEIVE_ON 0x2406 #define DSP_ECHO_ON 0x2407 #define DSP_ECHO_OFF 0x2408 #define DSP_MIX_ON 0x2409 #define DSP_MIX_OFF 0x240a #define DSP_DELAY 0x240b #define DSP_JITTER 0x240c #define DSP_TXDATA_ON 0x240d #define DSP_TXDATA_OFF 0x240e #define DSP_TX_DEJITTER 0x240f #define DSP_TX_DEJ_OFF 0x2410 #define DSP_TONE_PATT_ON 0x2411 #define DSP_TONE_PATT_OFF 0x2412 #define DSP_VOL_CHANGE_TX 0x2413 #define DSP_VOL_CHANGE_RX 0x2414 #define DSP_BF_ENABLE_KEY 0x2415 #define DSP_BF_DISABLE 0x2416 #define DSP_BF_ACCEPT 0x2416 #define DSP_BF_REJECT 0x2417 #define DSP_PIPELINE_CFG 0x2418 #define HFC_VOL_CHANGE_TX 0x2601 #define HFC_VOL_CHANGE_RX 0x2602 #define HFC_SPL_LOOP_ON 0x2603 #define HFC_SPL_LOOP_OFF 0x2604 /* DSP_TONE_PATT_ON parameter */ #define TONE_OFF 0x0000 #define TONE_GERMAN_DIALTONE 0x0001 #define TONE_GERMAN_OLDDIALTONE 0x0002 #define TONE_AMERICAN_DIALTONE 0x0003 #define TONE_GERMAN_DIALPBX 0x0004 #define TONE_GERMAN_OLDDIALPBX 0x0005 #define TONE_AMERICAN_DIALPBX 0x0006 #define TONE_GERMAN_RINGING 0x0007 #define TONE_GERMAN_OLDRINGING 0x0008 #define TONE_AMERICAN_RINGPBX 0x000b #define TONE_GERMAN_RINGPBX 0x000c #define TONE_GERMAN_OLDRINGPBX 0x000d #define TONE_AMERICAN_RINGING 0x000e #define TONE_GERMAN_BUSY 0x000f #define TONE_GERMAN_OLDBUSY 0x0010 #define TONE_AMERICAN_BUSY 0x0011 #define TONE_GERMAN_HANGUP 0x0012 #define TONE_GERMAN_OLDHANGUP 0x0013 #define TONE_AMERICAN_HANGUP 0x0014 #define TONE_SPECIAL_INFO 0x0015 #define TONE_GERMAN_GASSENBESETZT 0x0016 #define TONE_GERMAN_AUFSCHALTTON 0x0016 /* MPH_INFORMATION_IND */ #define L1_SIGNAL_LOS_OFF 0x0010 #define L1_SIGNAL_LOS_ON 0x0011 #define L1_SIGNAL_AIS_OFF 0x0012 #define L1_SIGNAL_AIS_ON 0x0013 #define L1_SIGNAL_RDI_OFF 0x0014 #define L1_SIGNAL_RDI_ON 0x0015 #define L1_SIGNAL_SLIP_RX 0x0020 #define L1_SIGNAL_SLIP_TX 0x0021 /* * protocol ids * D channel 1-31 * B channel 33 - 63 */ #define ISDN_P_NONE 0 #define ISDN_P_BASE 0 #define ISDN_P_TE_S0 0x01 #define ISDN_P_NT_S0 0x02 #define ISDN_P_TE_E1 0x03 #define ISDN_P_NT_E1 0x04 #define ISDN_P_TE_UP0 0x05 #define ISDN_P_NT_UP0 0x06 #define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) #define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) #define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) #define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) #define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) #define ISDN_P_LAPD_TE 0x10 #define ISDN_P_LAPD_NT 0x11 #define ISDN_P_B_MASK 0x1f #define ISDN_P_B_START 0x20 #define ISDN_P_B_RAW 0x21 #define ISDN_P_B_HDLC 0x22 #define ISDN_P_B_X75SLP 0x23 #define ISDN_P_B_L2DTMF 0x24 #define ISDN_P_B_L2DSP 0x25 #define ISDN_P_B_L2DSPHDLC 0x26 #define OPTION_L2_PMX 1 #define OPTION_L2_PTP 2 #define OPTION_L2_FIXEDTEI 3 #define OPTION_L2_CLEANUP 4 /* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ #define MISDN_MAX_IDLEN 20 struct mISDNhead { unsigned int prim; unsigned int id; } __attribute__((packed)); #define MISDN_HEADER_LEN sizeof(struct mISDNhead) #define MAX_DATA_SIZE 2048 #define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) #define MAX_DFRAME_LEN 260 #define MISDN_ID_ADDR_MASK 0xFFFF #define MISDN_ID_TEI_MASK 0xFF00 #define MISDN_ID_SAPI_MASK 0x00FF #define MISDN_ID_TEI_ANY 0x7F00 #define MISDN_ID_ANY 0xFFFF #define MISDN_ID_NONE 0xFFFE #define GROUP_TEI 127 #define TEI_SAPI 63 #define CTRL_SAPI 0 #define MISDN_MAX_CHANNEL 127 #define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) #define SOL_MISDN 0 struct sockaddr_mISDN { sa_family_t family; unsigned char dev; unsigned char channel; unsigned char sapi; unsigned char tei; }; struct mISDNversion { unsigned char major; unsigned char minor; unsigned short release; }; #define MAX_DEVICE_ID 63 struct mISDN_devinfo { u_int id; u_int Dprotocols; u_int Bprotocols; u_int protocol; u_char channelmap[MISDN_CHMAP_SIZE]; u_int nrbchan; char name[MISDN_MAX_IDLEN]; }; struct mISDN_devrename { u_int id; char name[MISDN_MAX_IDLEN]; }; struct ph_info_ch { int32_t protocol; int64_t Flags; }; struct ph_info_dch { struct ph_info_ch ch; int16_t state; int16_t num_bch; }; struct ph_info { struct ph_info_dch dch; struct ph_info_ch bch[]; }; /* timer device ioctl */ #define IMADDTIMER _IOR('I', 64, int) #define IMDELTIMER _IOR('I', 65, int) /* socket ioctls */ #define IMGETVERSION _IOR('I', 66, int) #define IMGETCOUNT _IOR('I', 67, int) #define IMGETDEVINFO _IOR('I', 68, int) #define IMCTRLREQ _IOR('I', 69, int) #define IMCLEAR_L2 _IOR('I', 70, int) #define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) static inline int test_channelmap(u_int nr, u_char *map) { if (nr <= MISDN_MAX_CHANNEL) return map[nr >> 3] & (1 << (nr & 7)); else return 0; } static inline void set_channelmap(u_int nr, u_char *map) { map[nr >> 3] |= (1 << (nr & 7)); } static inline void clear_channelmap(u_int nr, u_char *map) { map[nr >> 3] &= ~(1 << (nr & 7)); } /* CONTROL_CHANNEL parameters */ #define MISDN_CTRL_GETOP 0x0000 #define MISDN_CTRL_LOOP 0x0001 #define MISDN_CTRL_CONNECT 0x0002 #define MISDN_CTRL_DISCONNECT 0x0004 #define MISDN_CTRL_PCMCONNECT 0x0010 #define MISDN_CTRL_PCMDISCONNECT 0x0020 #define MISDN_CTRL_SETPEER 0x0040 #define MISDN_CTRL_UNSETPEER 0x0080 #define MISDN_CTRL_RX_OFF 0x0100 #define MISDN_CTRL_FILL_EMPTY 0x0200 #define MISDN_CTRL_GETPEER 0x0400 #define MISDN_CTRL_HW_FEATURES_OP 0x2000 #define MISDN_CTRL_HW_FEATURES 0x2001 #define MISDN_CTRL_HFC_OP 0x4000 #define MISDN_CTRL_HFC_PCM_CONN 0x4001 #define MISDN_CTRL_HFC_PCM_DISC 0x4002 #define MISDN_CTRL_HFC_CONF_JOIN 0x4003 #define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 #define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 #define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 #define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 #define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 /* socket options */ #define MISDN_TIME_STAMP 0x0001 struct mISDN_ctrl_req { int op; int channel; int p1; int p2; }; /* muxer options */ #define MISDN_OPT_ALL 1 #define MISDN_OPT_TEIMGR 2 #endif /* mISDNIF_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/000077500000000000000000000000001261736062400207665ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/000077500000000000000000000000001261736062400217045ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/abis.h000066400000000000000000000001301261736062400227650ustar00rootroot00000000000000#ifndef _OSMO_ABIS_H_ #define _OSMO_ABIS_H_ void libosmo_abis_init(void *ctx); #endif libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/e1_input.h000066400000000000000000000165721261736062400236140ustar00rootroot00000000000000#ifndef _E1_INPUT_H #define _E1_INPUT_H #include #include #include #include #include #include #include #include #define NUM_E1_TS 32 #define E1INP_USE_DEFAULT (-1) enum e1inp_sign_type { E1INP_SIGN_NONE, E1INP_SIGN_OML, E1INP_SIGN_RSL, E1INP_SIGN_OSMO, /* IPA CCM OSMO sub-type */ }; const char *e1inp_signtype_name(enum e1inp_sign_type tp); enum e1inp_ctr { E1I_CTR_HDLC_ABORT, E1I_CTR_HDLC_BADFCS, E1I_CTR_HDLC_OVERR, E1I_CTR_ALARM, E1I_CTR_REMOVED, }; struct e1inp_ts; struct vty; struct e1inp_sign_link { /* list of signalling links */ struct llist_head list; /* to which timeslot do we belong? */ struct e1inp_ts *ts; enum e1inp_sign_type type; /* trx for msg->trx of received msgs */ struct gsm_bts_trx *trx; /* msgb queue of to-be-transmitted msgs */ struct llist_head tx_list; /* SAPI and TEI on the E1 TS */ uint8_t sapi; uint8_t tei; union { struct { uint8_t channel; } misdn; } driver; }; enum e1inp_ts_type { E1INP_TS_TYPE_NONE, E1INP_TS_TYPE_SIGN, E1INP_TS_TYPE_TRAU, }; const char *e1inp_tstype_name(enum e1inp_ts_type tp); /* A timeslot in the E1 interface */ struct e1inp_ts { enum e1inp_ts_type type; int num; /* to which line do we belong ? */ struct e1inp_line *line; /* LAPD instance, if any */ struct lapd_instance *lapd; union { struct { /* list of all signalling links on this TS */ struct llist_head sign_links; /* delay for the queue */ int delay; /* timer when to dequeue next frame */ struct osmo_timer_list tx_timer; } sign; struct { /* subchannel demuxer for frames from E1 */ struct subch_demux demux; /* subchannel muxer for frames to E1 */ struct subch_mux mux; } trau; }; union { struct { /* mISDN driver has one fd for each ts */ struct osmo_fd fd; } misdn; struct { /* ip.access driver has one fd for each ts */ struct osmo_fd fd; } ipaccess; struct { /* DAHDI driver has one fd for each ts */ struct osmo_fd fd; } dahdi; struct { struct osmo_fd fd; } rs232; } driver; struct msgb *pending_msg; }; struct gsm_e1_subslot { /* Number of E1 link */ uint8_t e1_nr; /* Number of E1 TS inside E1 link */ uint8_t e1_ts; /* Sub-slot within the E1 TS, 0xff if full TS */ uint8_t e1_ts_ss; }; enum e1inp_line_role { E1INP_LINE_R_NONE, E1INP_LINE_R_BSC, E1INP_LINE_R_BTS, E1INP_LINE_R_MAX }; struct e1inp_driver { struct llist_head list; const char *name; int (*want_write)(struct e1inp_ts *ts); int (*line_update)(struct e1inp_line *line); void (*close)(struct e1inp_sign_link *link); void (*vty_show)(struct vty *vty, struct e1inp_line *line); int default_delay; int has_keepalive; }; struct e1inp_line_ops { union { struct { enum e1inp_line_role role; /* BSC or BTS mode. */ const char *addr; /* IP address .*/ void *dev; /* device parameters. */ } ipa; struct { const char *port; /* e.g. /dev/ttyUSB0 */ unsigned int delay; } rs232; } cfg; struct e1inp_sign_link * (*sign_link_up)(void *unit_info, struct e1inp_line *line, enum e1inp_sign_type type); void (*sign_link_down)(struct e1inp_line *line); int (*sign_link)(struct msgb *msg); }; struct e1inp_line { struct llist_head list; int refcnt; unsigned int num; const char *name; unsigned int port_nr; struct rate_ctr_group *rate_ctr; /* keepalive configuration */ int keepalive_num_probes; /* 0: disable, num, or E1INP_USE_DEFAULT */ int keepalive_idle_timeout; /* secs, or E1INP_USE_DEFAULT */ int keepalive_probe_interval; /* secs or E1INP_USE_DEFAULT */ /* array of timestlots */ struct e1inp_ts ts[NUM_E1_TS]; unsigned int num_ts; const struct e1inp_line_ops *ops; struct e1inp_driver *driver; void *driver_data; }; /* SS_L_INPUT signals */ enum e1inp_signal_input { S_L_INP_NONE, S_L_INP_TEI_UP, S_L_INP_TEI_DN, S_L_INP_TEI_UNKNOWN, S_L_INP_LINE_INIT, S_L_INP_LINE_ALARM, S_L_INP_LINE_NOALARM, }; /* register a driver with the E1 core */ int e1inp_driver_register(struct e1inp_driver *drv); /* fine a previously registered driver */ struct e1inp_driver *e1inp_driver_find(const char *name); /* register a line with the E1 core */ int e1inp_line_register(struct e1inp_line *line); /* get a line by its ID */ struct e1inp_line *e1inp_line_find(uint8_t e1_nr); /* create a line in the E1 input core */ struct e1inp_line *e1inp_line_create(uint8_t e1_nr, const char *driver_name); /* clone one existing E1 input line */ struct e1inp_line *e1inp_line_clone(void *ctx, struct e1inp_line *line); /* increment refcount use of E1 input line */ void e1inp_line_get(struct e1inp_line *line); /* decrement refcount use of E1 input line, release if unused */ void e1inp_line_put(struct e1inp_line *line); /* bind operations to one E1 input line */ void e1inp_line_bind_ops(struct e1inp_line *line, const struct e1inp_line_ops *ops); /* find a sign_link for given TEI and SAPI in a TS */ struct e1inp_sign_link * e1inp_lookup_sign_link(struct e1inp_ts *ts, uint8_t tei, uint8_t sapi); /* create a new signalling link in a E1 timeslot */ struct e1inp_sign_link * e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, struct gsm_bts_trx *trx, uint8_t tei, uint8_t sapi); /* configure and initialize one signalling e1inp_ts */ int e1inp_ts_config_sign(struct e1inp_ts *ts, struct e1inp_line *line); /* configure and initialize one timeslot dedicated to TRAU frames. */ int e1inp_ts_config_trau(struct e1inp_ts *ts, struct e1inp_line *line, int (*trau_rcv_cb)(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv)); /* Call from the Stack: configuration of this TS has changed */ int e1inp_update_ts(struct e1inp_ts *ts); /* Receive a packet from the E1 driver */ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, uint8_t tei, uint8_t sapi); int e1inp_rx_ts_lapd(struct e1inp_ts *e1i_ts, struct msgb *msg); /* called by driver if it wants to transmit on a given TS */ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, struct e1inp_sign_link **sign_link); /* called by driver in case some kind of link state event */ int e1inp_event(struct e1inp_ts *ts, int evt, uint8_t tei, uint8_t sapi); /* L2->L3 */ void e1inp_dlsap_up(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi, void *rx_cbdata); /* Write LAPD frames to the fd. */ int e1_set_pcap_fd(int fd); /* called by TRAU muxer to obtain the destination mux entity */ struct subch_mux *e1inp_get_mux(uint8_t e1_nr, uint8_t ts_nr); /* on an IPA BTS, the BTS needs to establish the RSL connection much * later than the OML connection. */ int e1inp_ipa_bts_rsl_connect(struct e1inp_line *line, const char *rem_addr, uint16_t rem_port); int e1inp_ipa_bts_rsl_connect_n(struct e1inp_line *line, const char *rem_addr, uint16_t rem_port, uint8_t trx_id); void e1inp_sign_link_destroy(struct e1inp_sign_link *link); int e1inp_line_update(struct e1inp_line *line); int e1inp_vty_init(void); struct gsm_network; int ipaccess_setup(struct gsm_network *gsmnet); extern struct llist_head e1inp_driver_list; extern struct llist_head e1inp_line_list; /* XXX */ struct input_signal_data { int link_type; uint8_t tei; uint8_t sapi; struct gsm_bts_trx *trx; struct e1inp_line *line; }; int abis_sendmsg(struct msgb *msg); int abis_rsl_sendmsg(struct msgb *msg); #endif /* _E1_INPUT_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/ipa.h000066400000000000000000000055301261736062400226310ustar00rootroot00000000000000#ifndef _OSMO_IPA_H_ #define _OSMO_IPA_H_ #include #include #include #include #include struct e1inp_line; struct e1inp_ts; struct msgb; struct ipa_server_link { struct e1inp_line *line; struct osmo_fd ofd; const char *addr; uint16_t port; int (*accept_cb)(struct ipa_server_link *link, int fd); void *data; }; struct ipa_server_link * ipa_server_link_create(void *ctx, struct e1inp_line *line, const char *addr, uint16_t port, int (*accept_cb)(struct ipa_server_link *link, int fd), void *data); void ipa_server_link_destroy(struct ipa_server_link *link); int ipa_server_link_open(struct ipa_server_link *link); void ipa_server_link_close(struct ipa_server_link *link); struct ipa_server_conn { struct ipa_server_link *server; struct osmo_fd ofd; struct llist_head tx_queue; int (*closed_cb)(struct ipa_server_conn *peer); int (*cb)(struct ipa_server_conn *peer, struct msgb *msg); void *data; struct msgb *pending_msg; }; struct ipa_server_conn * ipa_server_conn_create(void *ctx, struct ipa_server_link *link, int fd, int (*cb)(struct ipa_server_conn *peer, struct msgb *msg), int (*closed_cb)(struct ipa_server_conn *peer), void *data); void ipa_server_conn_destroy(struct ipa_server_conn *peer); void ipa_server_conn_send(struct ipa_server_conn *peer, struct msgb *msg); enum ipa_client_conn_state { IPA_CLIENT_LINK_STATE_NONE = 0, IPA_CLIENT_LINK_STATE_CONNECTING = 1, IPA_CLIENT_LINK_STATE_CONNECTED = 2, IPA_CLIENT_LINK_STATE_MAX }; struct ipa_client_conn { struct e1inp_line *line; struct osmo_fd *ofd; struct llist_head tx_queue; struct osmo_timer_list timer; enum ipa_client_conn_state state; const char *addr; uint16_t port; void (*updown_cb)(struct ipa_client_conn *link, int up); int (*read_cb)(struct ipa_client_conn *link, struct msgb *msg); int (*write_cb)(struct ipa_client_conn *link); void *data; struct msgb *pending_msg; }; struct ipa_client_conn * ipa_client_conn_create(void *ctx, struct e1inp_ts *ts, int priv_nr, const char *addr, uint16_t port, void (*updown)(struct ipa_client_conn *link, int), int (*read_cb)(struct ipa_client_conn *link, struct msgb *msgb), int (*write_cb)(struct ipa_client_conn *link), void *data); void ipa_client_conn_destroy(struct ipa_client_conn *link); int ipa_client_conn_open(struct ipa_client_conn *link); void ipa_client_conn_close(struct ipa_client_conn *link); void ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg); size_t ipa_client_conn_clear_queue(struct ipa_client_conn *link); int ipaccess_bts_handle_ccm(struct ipa_client_conn *link, struct ipaccess_unit *dev, struct msgb *msg); void ipa_msg_push_header(struct msgb *msg, uint8_t proto); #endif libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/ipa_proxy.h000066400000000000000000000001241261736062400240640ustar00rootroot00000000000000#ifndef _IPA_PROXY_H_ #define _IPA_PROXY_H_ void ipa_proxy_vty_init(void); #endif libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/ipaccess.h000066400000000000000000000004421261736062400236470ustar00rootroot00000000000000#ifndef _OSMO_ABIS_IPACCESS_H #define _OSMO_ABIS_IPACCESS_H #include #include /* quick solution to get openBSC's ipaccess tools working. */ extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); #endif /* _OSMO_ABIS_IPACCESS_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/lapd.h000066400000000000000000000040311261736062400227730ustar00rootroot00000000000000#ifndef OPENBSC_LAPD_H #define OPENBSC_LAPD_H #include #include #include struct lapd_profile { uint8_t k[64]; int n200; int n201; int n202; int t200_sec, t200_usec; int t201_sec, t201_usec; int t202_sec, t202_usec; int t203_sec, t203_usec; int short_address; }; extern const struct lapd_profile lapd_profile_isdn; extern const struct lapd_profile lapd_profile_abis; extern const struct lapd_profile lapd_profile_sat; struct lapd_instance { struct llist_head list; /* list of LAPD instances */ int network_side; void (*transmit_cb)(struct msgb *msg, void *cbdata); void *transmit_cbdata; void (*receive_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi, void *rx_cbdata); void *receive_cbdata; struct lapd_profile profile; /* must be a copy */ struct llist_head tei_list; /* list of TEI in this LAPD instance */ int pcap_fd; /* PCAP file descriptor */ }; enum lapd_recv_errors { LAPD_ERR_NONE = 0, LAPD_ERR_BAD_LEN, LAPD_ERR_BAD_ADDR, LAPD_ERR_UNKNOWN_S_CMD, LAPD_ERR_UNKNOWN_U_CMD, LAPD_ERR_UNKNOWN_TEI, LAPD_ERR_BAD_CMD, LAPD_ERR_NO_MEM, __LAPD_ERR_MAX }; struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei); int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error); void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, struct msgb *msg); struct lapd_instance *lapd_instance_alloc(int network_side, void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata, void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi, void *rx_cbdata), void *rx_cbdata, const struct lapd_profile *profile); void lapd_instance_free(struct lapd_instance *li); /* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi); /* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi); #endif /* OPENBSC_LAPD_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/lapd_pcap.h000066400000000000000000000004201261736062400237740ustar00rootroot00000000000000#ifndef _LAPD_PCAP_H_ #define _LAPD_PCAP_H_ #define OSMO_LAPD_PCAP_INPUT 0 #define OSMO_LAPD_PCAP_OUTPUT 1 int osmo_pcap_lapd_open(char *filename, mode_t mode); int osmo_pcap_lapd_write(int fd, int direction, struct msgb *msg); int osmo_pcap_lapd_close(int fd); #endif libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/subchan_demux.h000066400000000000000000000072041261736062400247050ustar00rootroot00000000000000#ifndef _SUBCH_DEMUX_H #define _SUBCH_DEMUX_H /* A E1 sub-channel (de)multiplexer with TRAU frame sync */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include /*! \defgroup subchan_demux * \brief E1 sub-channel multiplexer/demultiplexer * @{ * * \file subchan_demux.h */ /*! \brief number of 16k sub-channels inside one 64k E1 timeslot */ #define NR_SUBCH 4 /*! \brief size of TRAU frames in bytes */ #define TRAU_FRAME_SIZE 40 /*! \brief size of TRAU farmes in bits */ #define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8) /***********************************************************************/ /* DEMULTIPLEXER */ /***********************************************************************/ /*! \brief one subchannel inside the demultplexer */ struct demux_subch { /*! \brief bit-buffer for output bits */ uint8_t out_bitbuf[TRAU_FRAME_BITS]; /*! \brief next bit to be written in out_bitbuf */ uint16_t out_idx; /*! \brief number of consecutive zeros that we have received (for sync) */ unsigned int consecutive_zeros; /*! \brief are we in TRAU frame sync or not? */ unsigned int in_sync; }; /*! \brief one instance of a subchannel demultiplexer */ struct subch_demux { /*! \brief bitmask of currently active subchannels */ uint8_t chan_activ; /*! \brief one demux_subch struct for every subchannel */ struct demux_subch subch[NR_SUBCH]; /*! \brief callback to be called once we have received a * complete frame on a given subchannel */ int (*out_cb)(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *); /*! \brief user-provided data, transparently passed to out_cb() */ void *data; }; int subch_demux_init(struct subch_demux *dmx); int subch_demux_in(struct subch_demux *dmx, uint8_t *data, int len); int subch_demux_activate(struct subch_demux *dmx, int subch); int subch_demux_deactivate(struct subch_demux *dmx, int subch); /***********************************************************************/ /* MULTIPLEXER */ /***********************************************************************/ /*! \brief one element in the tx_queue of a muxer sub-channel */ struct subch_txq_entry { /*! \brief internal linked list header */ struct llist_head list; unsigned int bit_len; /*!< \brief total number of bits in 'bits' */ unsigned int next_bit; /*!< \brief next bit to be transmitted */ uint8_t bits[0]; /*!< \brief one bit per byte */ }; /*! \brief one sub-channel inside a multiplexer */ struct mux_subch { /*! \brief linked list of \ref subch_txq_entry */ struct llist_head tx_queue; }; /*! \brief one instance of the subchannel multiplexer */ struct subch_mux { /*! \brief array of sub-channels inside the multiplexer */ struct mux_subch subch[NR_SUBCH]; }; int subchan_mux_init(struct subch_mux *mx); int subchan_mux_out(struct subch_mux *mx, uint8_t *data, int len); int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const uint8_t *data, int len); /* }@ */ #endif /* _SUBCH_DEMUX_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/abis/trau_frame.h000066400000000000000000000052051261736062400242040ustar00rootroot00000000000000#ifndef _TRAU_FRAME_H #define _TRAU_FRAME_H /* TRAU frame handling according to GSM TS 08.60 */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include /*! \defgroup trau_frame TRAU frame handling * @{ * * \file trau_frame.h */ /*! \brief Maximum number of C-bits in a TRAU frame: * 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */ #define MAX_C_BITS 25 /*! \brief Maximum number of D-bits in a TRAU frame: * 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */ #define MAX_D_BITS 288 /*! \brief Maximum number of T-bits in a TRAU frame for all speech frames */ #define MAX_T_BITS 4 /*! \brief Maximum number of S-bits in a TRAU frame for OM */ #define MAX_S_BITS 6 /*! \brief Maximum number of M-bits in a TRAU frame for E-data */ #define MAX_M_BITS 2 /*! \brief a decoded TRAU frame, extracted C/D/T/S/M bits */ struct decoded_trau_frame { uint8_t c_bits[MAX_C_BITS]; uint8_t d_bits[MAX_D_BITS]; uint8_t t_bits[MAX_T_BITS]; uint8_t s_bits[MAX_S_BITS]; uint8_t m_bits[MAX_M_BITS]; }; #define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */ #define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */ #define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */ #define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */ #define TRAU_FT_OM_UP 0x07 /* 0 0 1 0 1 - 3.5.2 */ #define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */ #define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */ #define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */ #define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */ #define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */ #define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */ #define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */ int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits); int encode_trau_frame(uint8_t *trau_bits, const struct decoded_trau_frame *fr); int trau_frame_up2down(struct decoded_trau_frame *fr); uint8_t *trau_idle_frame(void); /* }@ */ #endif /* _TRAU_FRAME_H */ libosmo-abis-0.3.2+git86fc3c8/include/osmocom/trau/000077500000000000000000000000001261736062400217415ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/include/osmocom/trau/osmo_ortp.h000066400000000000000000000056041261736062400241400ustar00rootroot00000000000000#ifndef _OSMO_ORTP_H #define _OSMO_ORTP_H #include #include #include /* we cannot include ortp/ortp.h here, as they also use 'struct msgb' */ struct _RtpSession; /*! \brief default duration of a 20ms GSM codec frame */ #define GSM_RTP_DURATION 160 /*! \brief standard payload type for GSM Full Rate (FR) */ #define RTP_PT_GSM_FULL 3 /*! \brief Osmocom pseudo-static paylaod type for Half Rate (HR) */ #define RTP_PT_GSM_HALF 96 /*! \brief Osmocom pseudo-static paylaod type for Enhanced Full Rate (EFR) */ #define RTP_PT_GSM_EFR 97 /*! \brief Osmocom pseudo-static paylaod type for Adaptive Multi Rate (AMR) */ #define RTP_PT_AMR 98 /*! \brief Parameter to osmo_rtp_socket_param_set() */ enum osmo_rtp_param { OSMO_RTP_P_JITBUF = 1, OSMO_RTP_P_JIT_ADAP, }; /*! \brief Flag to indicate the socket is in polling-only mode */ #define OSMO_RTP_F_POLL 0x0001 /*! \brief A structure representing one RTP socket */ struct osmo_rtp_socket { /*! \biref list header for global list of sockets */ struct llist_head list; /*! \brief libortp RTP session pointer */ struct _RtpSession *sess; /*! \brief Osmo file descriptor for RTP socket FD */ struct osmo_fd rtp_bfd; /*! \brief Osmo file descriptor for RTCP socket FD */ struct osmo_fd rtcp_bfd; /*! \brief callback for incoming data */ void (*rx_cb)(struct osmo_rtp_socket *rs, const uint8_t *payload, unsigned int payload_len); /*! \brief Receive user timestamp, to be incremented by user */ uint32_t rx_user_ts; /*! \brief Transmit timestamp, incremented by library */ uint32_t tx_timestamp; /*! \brief Flags like OSMO_RTP_F_POLL */ unsigned int flags; void *priv; }; void osmo_rtp_init(void *ctx); struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags); int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port); int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port); int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type); int osmo_rtp_socket_free(struct osmo_rtp_socket *rs); int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload, unsigned int payload_len, unsigned int duration); int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs); int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs, uint32_t *ip, int *port); int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs, const char **addr, int *port); int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs, enum osmo_rtp_param param, int val); void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs, int subsys, int level, const char *pfx); void osmo_rtp_socket_stats(struct osmo_rtp_socket *rs, uint32_t *sent_packets, uint32_t *sent_octets, uint32_t *recv_packets, uint32_t *recv_octets, uint32_t *recv_lost, uint32_t *last_jitter); #endif /* _OSMO_ORTP_H */ libosmo-abis-0.3.2+git86fc3c8/libosmoabis.pc.in000066400000000000000000000003251261736062400211230ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: A-bis Core Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmoabis Cflags: -I${includedir}/ libosmo-abis-0.3.2+git86fc3c8/libosmotrau.pc.in000066400000000000000000000003371261736062400211630ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: Osmocom TRAU (E1/RTP) Library Description: C Utility Library Version: @VERSION@ Libs: -L${libdir} -losmotrau Cflags: -I${includedir}/ libosmo-abis-0.3.2+git86fc3c8/m4/000077500000000000000000000000001261736062400162075ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/m4/.gitignore000066400000000000000000000000241261736062400201730ustar00rootroot00000000000000/libtool.m4 /lt*.m4 libosmo-abis-0.3.2+git86fc3c8/src/000077500000000000000000000000001261736062400164565ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/src/Makefile.am000066400000000000000000000021771261736062400205210ustar00rootroot00000000000000# 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 ABIS_LIBVERSION=5:0:0 TRAU_LIBVERSION=1:0:0 AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS= -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) COMMONLIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) lib_LTLIBRARIES = libosmoabis.la libosmotrau.la libosmoabis_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(ABIS_LIBVERSION) libosmoabis_la_LIBADD = $(COMMONLIBS) libosmoabis_la_SOURCES = init.c \ e1_input.c \ e1_input_vty.c \ ipa_proxy.c \ subchan_demux.c \ trau_frame.c \ input/dahdi.c \ input/ipa.c \ input/ipaccess.c \ input/lapd.c \ input/lapd_pcap.c \ input/misdn.c \ input/rs232.c libosmotrau_la_CFLAGS = $(AM_CFLAGS) $(ORTP_CFLAGS) libosmotrau_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(TRAU_LIBVERSION) libosmotrau_la_LIBADD = $(COMMONLIBS) $(ORTP_LIBS) libosmotrau_la_SOURCES = trau/osmo_ortp.c libosmo-abis-0.3.2+git86fc3c8/src/e1_input.c000066400000000000000000000423141261736062400203520ustar00rootroot00000000000000/* OpenBSC Abis interface to E1 */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include "../config.h" #include #include #include #include #include #include #include #include #include #include #include #include //#define AF_COMPATIBILITY_FUNC //#include #ifndef AF_ISDN #define AF_ISDN 34 #define PF_ISDN AF_ISDN #endif #include #include #include #include #include #include #define NUM_E1_TS 32 static void *tall_e1inp_ctx; /* list of all E1 drivers */ LLIST_HEAD(e1inp_driver_list); /* list of all E1 lines */ LLIST_HEAD(e1inp_line_list); static void *tall_sigl_ctx; static const struct rate_ctr_desc e1inp_ctr_d[] = { [E1I_CTR_HDLC_ABORT] = { "hdlc.abort", "HDLC abort" }, [E1I_CTR_HDLC_BADFCS] = { "hdlc.bad_fcs", "HLDC Bad FCS" }, [E1I_CTR_HDLC_OVERR] = { "hdlc.overrun", "HDLC Overrun" }, [E1I_CTR_ALARM] = { "alarm", "Alarm" }, [E1I_CTR_REMOVED] = { "removed", "Line removed" }, }; static const struct rate_ctr_group_desc e1inp_ctr_g_d = { .group_name_prefix = "e1inp", .group_description = "E1 Input subsystem", .num_ctr = ARRAY_SIZE(e1inp_ctr_d), .ctr_desc = e1inp_ctr_d, }; /* * pcap writing of the misdn load * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat */ #define DLT_LINUX_LAPD 177 #define PCAP_INPUT 0 #define PCAP_OUTPUT 1 struct pcap_hdr { uint32_t magic_number; uint16_t version_major; uint16_t version_minor; int32_t thiszone; uint32_t sigfigs; uint32_t snaplen; uint32_t network; } __attribute__((packed)); struct pcaprec_hdr { uint32_t ts_sec; uint32_t ts_usec; uint32_t incl_len; uint32_t orig_len; } __attribute__((packed)); struct fake_linux_lapd_header { uint16_t pkttype; uint16_t hatype; uint16_t halen; uint64_t addr; int16_t protocol; } __attribute__((packed)); struct lapd_header { uint8_t ea1 : 1; uint8_t cr : 1; uint8_t sapi : 6; uint8_t ea2 : 1; uint8_t tei : 7; uint8_t control_foo; /* fake UM's ... */ } __attribute__((packed)); osmo_static_assert(offsetof(struct fake_linux_lapd_header, hatype) == 2, hatype_offset); osmo_static_assert(offsetof(struct fake_linux_lapd_header, halen) == 4, halen_offset); osmo_static_assert(offsetof(struct fake_linux_lapd_header, addr) == 6, addr_offset); osmo_static_assert(offsetof(struct fake_linux_lapd_header, protocol) == 14, proto_offset); osmo_static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size); static int pcap_fd = -1; int e1_set_pcap_fd(int fd) { struct pcap_hdr header = { .magic_number = 0xa1b2c3d4, .version_major = 2, .version_minor = 4, .thiszone = 0, .sigfigs = 0, .snaplen = 65535, .network = DLT_LINUX_LAPD, }; pcap_fd = fd; return write(pcap_fd, &header, sizeof(header)); } /* This currently only works for the D-Channel */ static void write_pcap_packet(int direction, int sapi, int tei, struct msgb *msg) { if (pcap_fd < 0) return; time_t cur_time; struct tm *tm; struct fake_linux_lapd_header header = { .pkttype = 4, .hatype = 0, .halen = 0, .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1, .protocol = ntohs(48), }; struct lapd_header lapd_header = { .ea1 = 0, .cr = direction == PCAP_OUTPUT ? 1 : 0, .sapi = sapi & 0x3F, .ea2 = 1, .tei = tei & 0x7F, .control_foo = 0x03 /* UI */, }; struct pcaprec_hdr payload_header = { .ts_sec = 0, .ts_usec = 0, .incl_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) + sizeof(struct lapd_header), .orig_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) + sizeof(struct lapd_header), }; cur_time = time(NULL); tm = localtime(&cur_time); payload_header.ts_sec = mktime(tm); write(pcap_fd, &payload_header, sizeof(payload_header)); write(pcap_fd, &header, sizeof(header)); write(pcap_fd, &lapd_header, sizeof(lapd_header)); write(pcap_fd, msg->l2h, msgb_l2len(msg)); } static const char *sign_types[] = { [E1INP_SIGN_NONE] = "None", [E1INP_SIGN_OML] = "OML", [E1INP_SIGN_RSL] = "RSL", [E1INP_SIGN_OSMO] = "OSMO", }; const char *e1inp_signtype_name(enum e1inp_sign_type tp) { if (tp >= ARRAY_SIZE(sign_types)) return "undefined"; return sign_types[tp]; } static const char *ts_types[] = { [E1INP_TS_TYPE_NONE] = "None", [E1INP_TS_TYPE_SIGN] = "Signalling", [E1INP_TS_TYPE_TRAU] = "TRAU", }; const char *e1inp_tstype_name(enum e1inp_ts_type tp) { if (tp >= ARRAY_SIZE(ts_types)) return "undefined"; return ts_types[tp]; } int abis_sendmsg(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct e1inp_driver *e1inp_driver; struct e1inp_ts *e1i_ts; ; msg->l2h = msg->data; /* don't know how to route this message. */ if (sign_link == NULL) { LOGP(DLINP, LOGL_ERROR, "abis_sendmsg: msg->dst == NULL: %s\n", osmo_hexdump(msg->data, msg->len)); talloc_free(msg); return -EINVAL; } e1i_ts = sign_link->ts; if (!osmo_timer_pending(&e1i_ts->sign.tx_timer)) { /* notify the driver we have something to write */ e1inp_driver = sign_link->ts->line->driver; e1inp_driver->want_write(e1i_ts); } msgb_enqueue(&sign_link->tx_list, msg); /* dump it */ write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); return 0; } int abis_rsl_sendmsg(struct msgb *msg) { return abis_sendmsg(msg); } /* Timeslot */ int e1inp_ts_config_trau(struct e1inp_ts *ts, struct e1inp_line *line, int (*trau_rcv_cb)(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv)) { if (ts->type == E1INP_TS_TYPE_TRAU && ts->line && line) return 0; ts->type = E1INP_TS_TYPE_TRAU; ts->line = line; subchan_mux_init(&ts->trau.mux); ts->trau.demux.out_cb = trau_rcv_cb; ts->trau.demux.data = ts; subch_demux_init(&ts->trau.demux); return 0; } int e1inp_ts_config_sign(struct e1inp_ts *ts, struct e1inp_line *line) { if (ts->type == E1INP_TS_TYPE_SIGN && ts->line && line) return 0; ts->type = E1INP_TS_TYPE_SIGN; ts->line = line; if (line && line->driver) ts->sign.delay = line->driver->default_delay; else ts->sign.delay = 100000; INIT_LLIST_HEAD(&ts->sign.sign_links); return 0; } struct e1inp_line *e1inp_line_find(uint8_t e1_nr) { struct e1inp_line *e1i_line; /* iterate over global list of e1 lines */ llist_for_each_entry(e1i_line, &e1inp_line_list, list) { if (e1i_line->num == e1_nr) return e1i_line; } return NULL; } struct e1inp_line * e1inp_line_create(uint8_t e1_nr, const char *driver_name) { struct e1inp_driver *driver; struct e1inp_line *line; int i; line = e1inp_line_find(e1_nr); if (line) { LOGP(DLINP, LOGL_ERROR, "E1 Line %u already exists\n", e1_nr); return NULL; } driver = e1inp_driver_find(driver_name); if (!driver) { LOGP(DLINP, LOGL_ERROR, "No such E1 driver '%s'\n", driver_name); return NULL; } line = talloc_zero(tall_e1inp_ctx, struct e1inp_line); if (!line) return NULL; line->driver = driver; line->num = e1_nr; line->rate_ctr = rate_ctr_group_alloc(line, &e1inp_ctr_g_d, line->num); line->num_ts = NUM_E1_TS; for (i = 0; i < line->num_ts; i++) { line->ts[i].num = i+1; line->ts[i].line = line; } line->refcnt++; llist_add_tail(&line->list, &e1inp_line_list); return line; } struct e1inp_line * e1inp_line_clone(void *ctx, struct e1inp_line *line) { struct e1inp_line *clone; /* clone virtual E1 line for this new OML link. */ clone = talloc_zero(ctx, struct e1inp_line); if (clone == NULL) return NULL; memcpy(clone, line, sizeof(struct e1inp_line)); clone->refcnt = 1; return clone; } void e1inp_line_get(struct e1inp_line *line) { line->refcnt++; } void e1inp_line_put(struct e1inp_line *line) { line->refcnt--; if (line->refcnt == 0) talloc_free(line); } void e1inp_line_bind_ops(struct e1inp_line *line, const struct e1inp_line_ops *ops) { line->ops = ops; } #if 0 struct e1inp_line *e1inp_line_find_create(uint8_t e1_nr) { struct e1inp_line *line; int i; line = e1inp_line_find(e1_nr); if (line) return line; line = talloc_zero(tall_e1inp_ctx, struct e1inp_line); if (!line) return NULL; line->num = e1_nr; for (i = 0; i < NUM_E1_TS; i++) { line->ts[i].num = i+1; line->ts[i].line = line; } llist_add_tail(&line->list, &e1inp_line_list); return line; } #endif static struct e1inp_ts *e1inp_ts_get(uint8_t e1_nr, uint8_t ts_nr) { struct e1inp_line *e1i_line; e1i_line = e1inp_line_find(e1_nr); if (!e1i_line) return NULL; return &e1i_line->ts[ts_nr-1]; } struct subch_mux *e1inp_get_mux(uint8_t e1_nr, uint8_t ts_nr) { struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr); if (!e1i_ts) return NULL; return &e1i_ts->trau.mux; } /* Signalling Link */ struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i, uint8_t tei, uint8_t sapi) { struct e1inp_sign_link *link; llist_for_each_entry(link, &e1i->sign.sign_links, list) { if (link->sapi == sapi && link->tei == tei) return link; } return NULL; } /* create a new signalling link in a E1 timeslot */ struct e1inp_sign_link * e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, struct gsm_bts_trx *trx, uint8_t tei, uint8_t sapi) { struct e1inp_sign_link *link; if (ts->type != E1INP_TS_TYPE_SIGN) return NULL; link = talloc_zero(tall_sigl_ctx, struct e1inp_sign_link); if (!link) return NULL; link->ts = ts; link->type = type; INIT_LLIST_HEAD(&link->tx_list); link->trx = trx; link->tei = tei; link->sapi = sapi; llist_add_tail(&link->list, &ts->sign.sign_links); return link; } void e1inp_sign_link_destroy(struct e1inp_sign_link *link) { struct msgb *msg; llist_del(&link->list); while (!llist_empty(&link->tx_list)) { msg = msgb_dequeue(&link->tx_list); msgb_free(msg); } if (link->ts->type == E1INP_TS_TYPE_SIGN) osmo_timer_del(&link->ts->sign.tx_timer); if (link->ts->line->driver->close) link->ts->line->driver->close(link); e1inp_line_put(link->ts->line); talloc_free(link); } /* XXX */ /* the E1 driver tells us he has received something on a TS */ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, uint8_t tei, uint8_t sapi) { struct e1inp_sign_link *link; int ret = 0; switch (ts->type) { case E1INP_TS_TYPE_SIGN: /* consult the list of signalling links */ write_pcap_packet(PCAP_INPUT, sapi, tei, msg); link = e1inp_lookup_sign_link(ts, tei, sapi); if (!link) { LOGP(DLMI, LOGL_ERROR, "didn't find signalling link for " "tei %d, sapi %d\n", tei, sapi); msgb_free(msg); return -EINVAL; } if (!ts->line->ops->sign_link) { LOGP(DLINP, LOGL_ERROR, "Fix your application, " "no action set for signalling messages.\n"); msgb_free(msg); return -ENOENT; } msg->dst = link; ts->line->ops->sign_link(msg); break; case E1INP_TS_TYPE_TRAU: ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg)); msgb_free(msg); break; default: ret = -EINVAL; LOGP(DLMI, LOGL_ERROR, "unknown TS type %u\n", ts->type); msgb_free(msg); break; } return ret; } /*! \brief Receive some data from the L1/HDLC into LAPD of a timeslot * \param[in] e1i_ts E1 Timeslot data structure * \param[in] msg Message buffer containing full LAPD message * * This is a wrapper around e1inp_rx_ts(), but feeding the incoming * message first into our LAPD code. This allows a driver to read raw * (HDLC decoded) data from the timeslot, instead of a LAPD stack * present in any underlying driver. */ int e1inp_rx_ts_lapd(struct e1inp_ts *e1i_ts, struct msgb *msg) { unsigned int sapi, tei; int ret = 0, error = 0; sapi = msg->data[0] >> 2; if ((msg->data[0] & 0x1)) tei = 0; else tei = msg->data[1] >> 1; DEBUGP(DLMI, "<= len = %d, sapi(%d) tei(%d)\n", msg->len, sapi, tei); ret = lapd_receive(e1i_ts->lapd, msg, &error); if (ret < 0) { switch(error) { case LAPD_ERR_UNKNOWN_TEI: /* We don't know about this TEI, probably the BSC * lost local states (it crashed or it was stopped), * notify the driver to see if it can do anything to * recover the existing signalling links with the BTS. */ e1inp_event(e1i_ts, S_L_INP_TEI_UNKNOWN, tei, sapi); return -EIO; } } return 0; } void e1inp_dlsap_up(struct osmo_dlsap_prim *dp, uint8_t tei, uint8_t sapi, void *rx_cbdata) { struct e1inp_ts *e1i_ts = rx_cbdata; struct msgb *msg = dp->oph.msg; switch (dp->oph.primitive) { case PRIM_DL_EST: DEBUGP(DLMI, "DL_EST: sapi(%d) tei(%d)\n", sapi, tei); e1inp_event(e1i_ts, S_L_INP_TEI_UP, tei, sapi); break; case PRIM_DL_REL: DEBUGP(DLMI, "DL_REL: sapi(%d) tei(%d)\n", sapi, tei); e1inp_event(e1i_ts, S_L_INP_TEI_DN, tei, sapi); break; case PRIM_DL_DATA: case PRIM_DL_UNIT_DATA: if (dp->oph.operation == PRIM_OP_INDICATION) { msg->l2h = msg->l3h; DEBUGP(DLMI, "RX: %s sapi=%d tei=%d\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)), sapi, tei); e1inp_rx_ts(e1i_ts, msg, tei, sapi); return; } break; case PRIM_MDL_ERROR: DEBUGP(DLMI, "MDL_EERROR: cause(%d)\n", dp->u.error_ind.cause); break; default: printf("ERROR: unknown prim\n"); break; } msgb_free(msg); return; } #define TSX_ALLOC_SIZE 4096 /* called by driver if it wants to transmit on a given TS */ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, struct e1inp_sign_link **sign_link) { struct e1inp_sign_link *link; struct msgb *msg = NULL; int len; switch (e1i_ts->type) { case E1INP_TS_TYPE_SIGN: /* FIXME: implement this round robin */ llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) { msg = msgb_dequeue(&link->tx_list); if (msg) { if (sign_link) *sign_link = link; break; } } break; case E1INP_TS_TYPE_TRAU: msg = msgb_alloc(TSX_ALLOC_SIZE, "TRAU_TX"); if (!msg) return NULL; len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40); if (len != 40) { LOGP(DLMI, LOGL_ERROR, "cannot transmit, failed to mux\n"); msgb_free(msg); return NULL; } msgb_put(msg, 40); break; default: LOGP(DLMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type); return NULL; } return msg; } static int e1inp_int_snd_event(struct e1inp_ts *ts, struct e1inp_sign_link *link, int evt) { struct input_signal_data isd; isd.line = ts->line; isd.link_type = link->type; isd.trx = link->trx; isd.tei = link->tei; isd.sapi = link->sapi; /* report further upwards */ osmo_signal_dispatch(SS_L_INPUT, evt, &isd); return 0; } /* called by driver in case some kind of link state event */ int e1inp_event(struct e1inp_ts *ts, int evt, uint8_t tei, uint8_t sapi) { struct e1inp_sign_link *link; link = e1inp_lookup_sign_link(ts, tei, sapi); if (!link) return -EINVAL; return e1inp_int_snd_event(ts, link, evt); } void e1inp_close_socket(struct e1inp_ts *ts, struct e1inp_sign_link *sign_link, struct osmo_fd *bfd) { e1inp_int_snd_event(ts, sign_link, S_L_INP_TEI_DN); /* the first e1inp_sign_link_destroy call closes the socket. */ if (bfd->fd != -1) { osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; } } /* register a driver with the E1 core */ int e1inp_driver_register(struct e1inp_driver *drv) { llist_add_tail(&drv->list, &e1inp_driver_list); return 0; } struct e1inp_driver *e1inp_driver_find(const char *name) { struct e1inp_driver *drv; llist_for_each_entry(drv, &e1inp_driver_list, list) { if (!strcasecmp(name, drv->name)) return drv; } return NULL; } int e1inp_line_update(struct e1inp_line *line) { struct input_signal_data isd; int rc; e1inp_line_get(line); if (line->driver && line->ops && line->driver->line_update) { rc = line->driver->line_update(line); } else rc = 0; /* Send a signal to anyone who is interested in new lines being * configured */ memset(&isd, 0, sizeof(isd)); isd.line = line; osmo_signal_dispatch(SS_L_INPUT, S_L_INP_LINE_INIT, &isd); return rc; } static int e1i_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_L_GLOBAL || signal != S_L_GLOBAL_SHUTDOWN) return 0; if (pcap_fd) { close(pcap_fd); pcap_fd = -1; } return 0; } void e1inp_misdn_init(void); void e1inp_dahdi_init(void); void e1inp_ipaccess_init(void); void e1inp_rs232_init(void); void e1inp_init(void) { tall_e1inp_ctx = talloc_named_const(libosmo_abis_ctx, 1, "e1inp"); tall_sigl_ctx = talloc_named_const(tall_e1inp_ctx, 1, "e1inp_sign_link"); osmo_signal_register_handler(SS_L_GLOBAL, e1i_sig_cb, NULL); e1inp_misdn_init(); #ifdef HAVE_DAHDI_USER_H e1inp_dahdi_init(); #endif e1inp_ipaccess_init(); e1inp_rs232_init(); } libosmo-abis-0.3.2+git86fc3c8/src/e1_input_vty.c000066400000000000000000000221161261736062400212520ustar00rootroot00000000000000/* E1 vty interface */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* CONFIG */ #define E1_DRIVER_NAMES "(misdn|misdn_lapd|dahdi|ipa)" #define E1_DRIVER_HELP "mISDN supported E1 Card (kernel LAPD)\n" \ "mISDN supported E1 Card (userspace LAPD)\n" \ "DAHDI supported E1/T1/J1 Card\n" \ "IPA TCP/IP input\n" \ "HSL TCP/IP input" #define E1_LINE_HELP "Configure E1/T1/J1 Line\n" "Line Number\n" DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd, "e1_line <0-255> driver " E1_DRIVER_NAMES, E1_LINE_HELP "Set driver for this line\n" E1_DRIVER_HELP) { struct e1inp_line *line; int e1_nr = atoi(argv[0]); line = e1inp_line_find(e1_nr); if (line) { vty_out(vty, "%% Line %d already exists%s", e1_nr, VTY_NEWLINE); return CMD_WARNING; } line = e1inp_line_create(e1_nr, argv[1]); if (!line) { vty_out(vty, "%% Error creating line %d%s", e1_nr, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_e1line_port, cfg_e1_line_port_cmd, "e1_line <0-255> port <0-255>", E1_LINE_HELP "Set physical port/span/card number\n" "E1/T1 Port/Span/Card number\n") { struct e1inp_line *line; int e1_nr = atoi(argv[0]); line = e1inp_line_find(e1_nr); if (!line) { vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE); return CMD_WARNING; } line->port_nr = atoi(argv[1]); return CMD_SUCCESS; } #define KEEPALIVE_HELP "Enable keep-alive probing\n" static int set_keepalive_params(struct vty *vty, int e1_nr, int idle, int num_probes, int probe_interval) { struct e1inp_line *line = e1inp_line_find(e1_nr); if (!line) { vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE); return CMD_WARNING; } if (!line->driver->has_keepalive && num_probes != 0) { vty_out(vty, "%% Driver '%s' does not support keep alive%s", line->driver->name, VTY_NEWLINE); return CMD_WARNING; } line->keepalive_idle_timeout = idle; line->keepalive_num_probes = num_probes; line->keepalive_probe_interval = probe_interval; return CMD_SUCCESS; } DEFUN(cfg_e1line_keepalive, cfg_e1_line_keepalive_cmd, "e1_line <0-255> keepalive", E1_LINE_HELP KEEPALIVE_HELP) { return set_keepalive_params(vty, atoi(argv[0]), E1INP_USE_DEFAULT, E1INP_USE_DEFAULT, E1INP_USE_DEFAULT); } DEFUN(cfg_e1line_keepalive_params, cfg_e1_line_keepalive_params_cmd, "e1_line <0-255> keepalive <1-300> <1-20> <1-300>", E1_LINE_HELP KEEPALIVE_HELP "Idle interval in seconds before probes are sent\n" "Number of probes to sent\n" "Delay between probe packets in seconds\n") { return set_keepalive_params(vty, atoi(argv[0]), atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); } DEFUN(cfg_e1line_no_keepalive, cfg_e1_line_no_keepalive_cmd, "no e1_line <0-255> keepalive", NO_STR E1_LINE_HELP KEEPALIVE_HELP) { return set_keepalive_params(vty, atoi(argv[0]), 0, 0, 0); } DEFUN(cfg_e1line_name, cfg_e1_line_name_cmd, "e1_line <0-255> name .LINE", E1_LINE_HELP "Set name for this line\n" "Human readable name\n") { struct e1inp_line *line; int e1_nr = atoi(argv[0]); line = e1inp_line_find(e1_nr); if (!line) { vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE); return CMD_WARNING; } if (line->name) { talloc_free((void *)line->name); line->name = NULL; } line->name = talloc_strdup(line, argv[1]); return CMD_SUCCESS; } DEFUN(cfg_e1inp, cfg_e1inp_cmd, "e1_input", "Configure E1/T1/J1 TDM input\n") { vty->node = L_E1INP_NODE; return CMD_SUCCESS; } static int e1inp_config_write(struct vty *vty) { struct e1inp_line *line; if (llist_empty(&e1inp_line_list)) return CMD_SUCCESS; vty_out(vty, "e1_input%s", VTY_NEWLINE); llist_for_each_entry(line, &e1inp_line_list, list) { vty_out(vty, " e1_line %u driver %s%s", line->num, line->driver->name, VTY_NEWLINE); vty_out(vty, " e1_line %u port %u%s", line->num, line->port_nr, VTY_NEWLINE); if (line->name) vty_out(vty, " e1_line %u name %s%s", line->num, line->name, VTY_NEWLINE); if (!line->keepalive_num_probes) vty_out(vty, " no e1_line %u keepalive%s", line->num, VTY_NEWLINE); else if (line->keepalive_idle_timeout == E1INP_USE_DEFAULT && line->keepalive_num_probes == E1INP_USE_DEFAULT && line->keepalive_probe_interval == E1INP_USE_DEFAULT) vty_out(vty, " e1_line %u keepalive%s", line->num, VTY_NEWLINE); else vty_out(vty, " e1_line %u keepalive %d %d %d%s", line->num, line->keepalive_idle_timeout, line->keepalive_num_probes, line->keepalive_probe_interval, VTY_NEWLINE); } return CMD_SUCCESS; } /* SHOW */ static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) { vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); } DEFUN(show_e1drv, show_e1drv_cmd, "show e1_driver", SHOW_STR "Display information about available E1 drivers\n") { struct e1inp_driver *drv; llist_for_each_entry(drv, &e1inp_driver_list, list) e1drv_dump_vty(vty, drv); return CMD_SUCCESS; } static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line, int stats) { vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s", line->num, line->name ? line->name : "", line->driver->name, VTY_NEWLINE); if (line->driver->vty_show) line->driver->vty_show(vty, line); if (stats) vty_out_rate_ctr_group(vty, " ", line->rate_ctr); } DEFUN(show_e1line, show_e1line_cmd, "show e1_line [line_nr] [stats]", SHOW_STR "Display information about a E1 line\n" "E1 Line Number\n" "Include statistics\n") { struct e1inp_line *line; int stats = 0; if (argc >= 1 && strcmp(argv[0], "stats")) { int num = atoi(argv[0]); if (argc >= 2) stats = 1; llist_for_each_entry(line, &e1inp_line_list, list) { if (line->num == num) { e1line_dump_vty(vty, line, stats); return CMD_SUCCESS; } } return CMD_WARNING; } if (argc >= 1 && !strcmp(argv[0], "stats")) stats = 1; llist_for_each_entry(line, &e1inp_line_list, list) e1line_dump_vty(vty, line, stats); return CMD_SUCCESS; } static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) { if (ts->type == E1INP_TS_TYPE_NONE) return; vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s", ts->num, ts->line->num, e1inp_tstype_name(ts->type), VTY_NEWLINE); } DEFUN(show_e1ts, show_e1ts_cmd, "show e1_timeslot [line_nr] [ts_nr]", SHOW_STR "Display information about a E1 timeslot\n" "E1 Line Number\n" "E1 Timeslot Number\n") { struct e1inp_line *line = NULL; struct e1inp_ts *ts; int ts_nr; if (argc <= 0) { llist_for_each_entry(line, &e1inp_line_list, list) { for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) { ts = &line->ts[ts_nr]; e1ts_dump_vty(vty, ts); } } return CMD_SUCCESS; } if (argc >= 1) { int num = atoi(argv[0]); struct e1inp_line *l; llist_for_each_entry(l, &e1inp_line_list, list) { if (l->num == num) { line = l; break; } } if (!line) { vty_out(vty, "E1 line %s is invalid%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } } if (argc >= 2) { ts_nr = atoi(argv[1]); if (ts_nr >= line->num_ts) { vty_out(vty, "E1 timeslot %s is invalid%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } ts = &line->ts[ts_nr]; e1ts_dump_vty(vty, ts); return CMD_SUCCESS; } else { for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) { ts = &line->ts[ts_nr]; e1ts_dump_vty(vty, ts); } return CMD_SUCCESS; } return CMD_SUCCESS; } struct cmd_node e1inp_node = { L_E1INP_NODE, "%s(config-e1_input)# ", 1, }; int e1inp_vty_init(void) { install_element(CONFIG_NODE, &cfg_e1inp_cmd); install_node(&e1inp_node, e1inp_config_write); vty_install_default(L_E1INP_NODE); install_element(L_E1INP_NODE, &cfg_e1_line_driver_cmd); install_element(L_E1INP_NODE, &cfg_e1_line_port_cmd); install_element(L_E1INP_NODE, &cfg_e1_line_name_cmd); install_element(L_E1INP_NODE, &cfg_e1_line_keepalive_cmd); install_element(L_E1INP_NODE, &cfg_e1_line_keepalive_params_cmd); install_element(L_E1INP_NODE, &cfg_e1_line_no_keepalive_cmd); install_element_ve(&show_e1drv_cmd); install_element_ve(&show_e1line_cmd); install_element_ve(&show_e1ts_cmd); return 0; } libosmo-abis-0.3.2+git86fc3c8/src/init.c000066400000000000000000000017571261736062400175770ustar00rootroot00000000000000/* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include #include #include void *libosmo_abis_ctx; void libosmo_abis_init(void *ctx) { libosmo_abis_ctx = talloc_named_const(ctx, 0, "abis"); e1inp_init(); } libosmo-abis-0.3.2+git86fc3c8/src/input/000077500000000000000000000000001261736062400176155ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/src/input/dahdi.c000066400000000000000000000341541261736062400210410ustar00rootroot00000000000000/* OpenBSC Abis input driver for DAHDI */ /* (C) 2008-2011 by Harald Welte * (C) 2009 by Holger Hans Peter Freyther * (C) 2010 by Digium and Matthew Fredrickson * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "config.h" #ifdef HAVE_DAHDI_USER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TS1_ALLOC_SIZE 300 struct span_cfg { struct llist_head list; unsigned int span_nr; unsigned int chan_base; unsigned int chan_num; }; static struct span_cfg *span_cfgs[DAHDI_MAX_SPANS]; static int reread_span_cfgs(void) { struct dahdi_spaninfo si; unsigned int basechan = 1; int span_nr; int fd; if ((fd = open("/dev/dahdi/ctl", O_RDWR)) < 0) { LOGP(DLMI, LOGL_ERROR, "Unable to open DAHDI ctl: %s\n", strerror(errno)); return -EIO; } for (span_nr = 1; span_nr < DAHDI_MAX_SPANS; span_nr++) { struct span_cfg *scfg; /* our array index starts at 0, but DAHDI span at 1 */ int i = span_nr - 1; /* clear any old cached information */ if (span_cfgs[i]) { talloc_free(span_cfgs[i]); span_cfgs[i] = NULL; } memset(&si, 0, sizeof(si)); si.spanno = span_nr; if (ioctl(fd, DAHDI_SPANSTAT, &si)) continue; /* create and link new span_cfg */ scfg = talloc_zero(NULL, struct span_cfg); if (!scfg) { close(fd); return -ENOMEM; } scfg->span_nr = span_nr; scfg->chan_num = si.totalchans; scfg->chan_base = basechan; span_cfgs[i] = scfg; basechan += si.totalchans; } close(fd); return 0; } /* Corresponds to dahdi/user.h, only PRI related events */ static const struct value_string dahdi_evt_names[] = { { DAHDI_EVENT_NONE, "NONE" }, { DAHDI_EVENT_ALARM, "ALARM" }, { DAHDI_EVENT_NOALARM, "NOALARM" }, { DAHDI_EVENT_ABORT, "HDLC ABORT" }, { DAHDI_EVENT_OVERRUN, "HDLC OVERRUN" }, { DAHDI_EVENT_BADFCS, "HDLC BAD FCS" }, { DAHDI_EVENT_REMOVED, "REMOVED" }, { 0, NULL } }; static void handle_dahdi_exception(struct e1inp_ts *ts) { int rc, evt; struct e1inp_line *line = ts->line; struct input_signal_data isd; rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt); if (rc < 0) return; LOGP(DLMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n", ts->line->num, ts->line->name, ts->num, get_value_string(dahdi_evt_names, evt)); isd.line = ts->line; switch (evt) { case DAHDI_EVENT_ALARM: /* we should notify the code that the line is gone */ osmo_signal_dispatch(SS_L_INPUT, S_L_INP_LINE_ALARM, &isd); rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_ALARM]); break; case DAHDI_EVENT_NOALARM: /* alarm has gone, we should re-start the SABM requests */ osmo_signal_dispatch(SS_L_INPUT, S_L_INP_LINE_NOALARM, &isd); break; case DAHDI_EVENT_ABORT: rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_ABORT]); break; case DAHDI_EVENT_OVERRUN: rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_OVERR]); break; case DAHDI_EVENT_BADFCS: rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_HDLC_BADFCS]); break; case DAHDI_EVENT_REMOVED: rate_ctr_inc(&line->rate_ctr->ctr[E1I_CTR_REMOVED]); break; } } static int handle_ts1_read(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1"); int ret; if (!msg) return -ENOMEM; ret = read(bfd->fd, msg->data, TS1_ALLOC_SIZE - 16); if (ret == -1) handle_dahdi_exception(e1i_ts); else if (ret < 0) { perror("read "); } msgb_put(msg, ret - 2); if (ret <= 3) { perror("read "); } return e1inp_rx_ts_lapd(e1i_ts, msg); } static int ts_want_write(struct e1inp_ts *e1i_ts) { /* We never include the DAHDI B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ if (e1i_ts->type == E1INP_TS_TYPE_TRAU) { LOGP(DLINP, LOGL_DEBUG, "Trying to write TRAU ts\n"); return 0; } e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE; return 0; } static void timeout_ts1_write(void *data) { struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; /* trigger write of ts1, due to tx delay timer */ ts_want_write(e1i_ts); } static void dahdi_write_msg(struct msgb *msg, void *cbdata) { struct osmo_fd *bfd = cbdata; struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; int ret; ret = write(bfd->fd, msg->data, msg->len + 2); msgb_free(msg); if (ret == -1) handle_dahdi_exception(e1i_ts); else if (ret < 0) LOGP(DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret); } static int handle_ts1_write(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct e1inp_sign_link *sign_link; struct msgb *msg; bfd->when &= ~BSC_FD_WRITE; /* get the next msg for this timeslot */ msg = e1inp_tx_ts(e1i_ts, &sign_link); if (!msg) { /* no message after tx delay timer */ return 0; } DEBUGP(DLMI, "TX: %s\n", osmo_hexdump(msg->data, msg->len)); lapd_transmit(e1i_ts->lapd, sign_link->tei, sign_link->sapi, msg); /* set tx delay timer for next event */ e1i_ts->sign.tx_timer.cb = timeout_ts1_write; e1i_ts->sign.tx_timer.data = e1i_ts; osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, 50000); return 0; } static int invertbits = 1; static uint8_t flip_table[256]; static void init_flip_bits(void) { int i,k; for (i = 0 ; i < 256 ; i++) { uint8_t sample = 0 ; for (k = 0; k<8; k++) { if ( i & 1 << k ) sample |= 0x80 >> k; } flip_table[i] = sample; } } static uint8_t * flip_buf_bits ( uint8_t * buf , int len) { int i; uint8_t * start = buf; for (i = 0 ; i < len; i++) { buf[i] = flip_table[(uint8_t)buf[i]]; } return start; } #define D_BCHAN_TX_GRAN 160 /* write to a B channel TS */ static int handle_tsX_write(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; uint8_t tx_buf[D_BCHAN_TX_GRAN]; struct subch_mux *mx = &e1i_ts->trau.mux; int ret; ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN); if (ret != D_BCHAN_TX_GRAN) { LOGP(DLINP, LOGL_DEBUG, "Huh, got ret of %d\n", ret); if (ret < 0) return ret; } DEBUGP(DLMIB, "BCHAN TX: %s\n", osmo_hexdump(tx_buf, D_BCHAN_TX_GRAN)); if (invertbits) { flip_buf_bits(tx_buf, ret); } ret = write(bfd->fd, tx_buf, ret); if (ret < D_BCHAN_TX_GRAN) LOGP(DLINP, LOGL_DEBUG, "send returns %d instead of %d\n", ret, D_BCHAN_TX_GRAN); return ret; } #define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN) /* FIXME: read from a B channel TS */ static int handle_tsX_read(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "DAHDI TSx"); int ret; if (!msg) return -ENOMEM; ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE); if (ret < 0 || ret != D_TSX_ALLOC_SIZE) { LOGP(DLINP, LOGL_DEBUG, "read error %d %s\n", ret, strerror(errno)); return ret; } if (invertbits) { flip_buf_bits(msg->data, ret); } msgb_put(msg, ret); msg->l2h = msg->data; DEBUGP(DLMIB, "BCHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret)); ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); /* physical layer indicates that data has been sent, * we thus can send some more data */ ret = handle_tsX_write(bfd); return ret; } /* callback from select.c in case one of the fd's can be read/written */ static int dahdi_fd_cb(struct osmo_fd *bfd, unsigned int what) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; unsigned int idx = ts_nr-1; struct e1inp_ts *e1i_ts = &line->ts[idx]; int rc = 0; switch (e1i_ts->type) { case E1INP_TS_TYPE_SIGN: if (what & BSC_FD_EXCEPT) handle_dahdi_exception(e1i_ts); if (what & BSC_FD_READ) rc = handle_ts1_read(bfd); if (what & BSC_FD_WRITE) rc = handle_ts1_write(bfd); break; case E1INP_TS_TYPE_TRAU: if (what & BSC_FD_EXCEPT) handle_dahdi_exception(e1i_ts); if (what & BSC_FD_READ) rc = handle_tsX_read(bfd); if (what & BSC_FD_WRITE) rc = handle_tsX_write(bfd); /* We never include the DAHDI B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ break; default: LOGP(DLINP, LOGL_NOTICE, "unknown E1 TS type %u\n", e1i_ts->type); break; } return rc; } static void dahdi_vty_show(struct vty *vty, struct e1inp_line *line) { struct span_cfg *scfg; if (line->port_nr >= ARRAY_SIZE(span_cfgs)) return; scfg = span_cfgs[line->port_nr]; if (!scfg) { vty_out(vty, "DAHDI Span %u non-existant%s", line->port_nr+1, VTY_NEWLINE); return; } vty_out(vty, "DAHDI Span #%u, Base Nr %u, Timeslots: %u%s", line->port_nr+1, scfg->chan_base, scfg->chan_num, VTY_NEWLINE); } static int dahdi_e1_line_update(struct e1inp_line *line); struct e1inp_driver dahdi_driver = { .name = "dahdi", .want_write = ts_want_write, .line_update = &dahdi_e1_line_update, .vty_show = &dahdi_vty_show, }; int dahdi_set_bufinfo(int fd, int as_sigchan) { struct dahdi_bufferinfo bi; int x = 0; if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) { LOGP(DLINP, LOGL_ERROR, "Error getting bufinfo\n"); return -EIO; } if (as_sigchan) { bi.numbufs = 4; bi.bufsize = 512; } else { bi.numbufs = 8; bi.bufsize = D_BCHAN_TX_GRAN; bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL; } if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { LOGP(DLINP, LOGL_ERROR, "Error setting bufinfo\n"); return -EIO; } if (!as_sigchan) { if (ioctl(fd, DAHDI_AUDIOMODE, &x)) { LOGP(DLINP, LOGL_ERROR, "Error setting bufinfo\n"); return -EIO; } } else { int one = 1; ioctl(fd, DAHDI_HDLCFCSMODE, &one); /* we cannot reliably check for the ioctl return value here * as this command will fail if the slot _already_ was a * signalling slot before :( */ } return 0; } static int dahdi_e1_setup(struct e1inp_line *line) { struct span_cfg *scfg; int ts, ret; reread_span_cfgs(); scfg = span_cfgs[line->port_nr]; if (!scfg) { LOGP(DLMI, LOGL_ERROR, "Line %u(%s): DAHDI Port %u (Span %u) " "doesn't exist\n", line->num, line->name, line->port_nr, line->port_nr+1); return -EIO; } line->num_ts = scfg->chan_num; /* TS0 is CRC4, don't need any fd for it */ for (ts = 1; ts <= scfg->chan_num; ts++) { unsigned int idx = ts-1; char openstr[128]; struct e1inp_ts *e1i_ts = &line->ts[idx]; struct osmo_fd *bfd = &e1i_ts->driver.dahdi.fd; int dev_nr; /* unregister FD if it was already registered */ if (bfd->list.next && bfd->list.next != LLIST_POISON1) osmo_fd_unregister(bfd); /* DAHDI device names/numbers just keep incrementing * even over multiple boards. So TS1 of the second * board will be 32 */ dev_nr = scfg->chan_base + idx; bfd->data = line; bfd->priv_nr = ts; bfd->cb = dahdi_fd_cb; snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", dev_nr); switch (e1i_ts->type) { case E1INP_TS_TYPE_NONE: /* close/release LAPD instance, if any */ if (e1i_ts->lapd) { lapd_instance_free(e1i_ts->lapd); e1i_ts->lapd = NULL; } if (bfd->fd) { close(bfd->fd); bfd->fd = 0; } continue; break; case E1INP_TS_TYPE_SIGN: if (!bfd->fd) bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); if (bfd->fd == -1) { LOGP(DLINP, LOGL_ERROR, "%s could not open %s %s\n", __func__, openstr, strerror(errno)); return -EIO; } bfd->when = BSC_FD_READ | BSC_FD_EXCEPT; ret = dahdi_set_bufinfo(bfd->fd, 1); if (ret < 0) return ret; if (!e1i_ts->lapd) e1i_ts->lapd = lapd_instance_alloc(1, dahdi_write_msg, bfd, e1inp_dlsap_up, e1i_ts, &lapd_profile_abis); break; case E1INP_TS_TYPE_TRAU: /* close/release LAPD instance, if any */ if (e1i_ts->lapd) { lapd_instance_free(e1i_ts->lapd); e1i_ts->lapd = NULL; } if (!bfd->fd) bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); if (bfd->fd == -1) { LOGP(DLINP, LOGL_ERROR, "%s could not open %s %s\n", __func__, openstr, strerror(errno)); return -EIO; } ret = dahdi_set_bufinfo(bfd->fd, 0); if (ret < 0) return -EIO; /* We never include the DAHDI B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;// | BSC_FD_WRITE; break; } if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "%s could not open %s %s\n", __func__, openstr, strerror(errno)); return bfd->fd; } ret = osmo_fd_register(bfd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not register FD: %s\n", strerror(ret)); return ret; } } return 0; } static int dahdi_e1_line_update(struct e1inp_line *line) { if (line->driver != &dahdi_driver) return -EINVAL; return dahdi_e1_setup(line); } int e1inp_dahdi_init(void) { init_flip_bits(); /* register the driver with the core */ return e1inp_driver_register(&dahdi_driver); } #endif /* HAVE_DAHDI_USER_H */ libosmo-abis-0.3.2+git86fc3c8/src/input/ipa.c000066400000000000000000000242151261736062400205360ustar00rootroot00000000000000#include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void ipa_msg_push_header(struct msgb *msg, uint8_t proto) { struct ipaccess_head *hh; msg->l2h = msg->data; hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); hh->proto = proto; hh->len = htons(msgb_l2len(msg)); } void ipa_client_conn_close(struct ipa_client_conn *link) { /* be safe against multiple calls */ if (link->ofd->fd != -1) { osmo_fd_unregister(link->ofd); close(link->ofd->fd); link->ofd->fd = -1; } msgb_free(link->pending_msg); link->pending_msg = NULL; } static void ipa_client_read(struct ipa_client_conn *link) { struct osmo_fd *ofd = link->ofd; struct msgb *msg; int ret; LOGP(DLINP, LOGL_DEBUG, "message received\n"); ret = ipa_msg_recv_buffered(ofd->fd, &msg, &link->pending_msg); if (ret < 0) { if (ret == -EAGAIN) return; if (ret == -EPIPE || ret == -ECONNRESET) LOGP(DLINP, LOGL_ERROR, "lost connection with server\n"); ipa_client_conn_close(link); if (link->updown_cb) link->updown_cb(link, 0); return; } else if (ret == 0) { LOGP(DLINP, LOGL_ERROR, "connection closed with server\n"); ipa_client_conn_close(link); if (link->updown_cb) link->updown_cb(link, 0); return; } if (link->read_cb) link->read_cb(link, msg); } static void ipa_client_write(struct ipa_client_conn *link) { if (link->write_cb) link->write_cb(link); } static int ipa_client_write_default_cb(struct ipa_client_conn *link) { struct osmo_fd *ofd = link->ofd; struct msgb *msg; struct llist_head *lh; int ret; LOGP(DLINP, LOGL_DEBUG, "sending data\n"); if (llist_empty(&link->tx_queue)) { ofd->when &= ~BSC_FD_WRITE; return 0; } lh = link->tx_queue.next; llist_del(lh); msg = llist_entry(lh, struct msgb, list); ret = send(link->ofd->fd, msg->data, msg->len, 0); if (ret < 0) { if (errno == EPIPE || errno == ENOTCONN) { ipa_client_conn_close(link); if (link->updown_cb) link->updown_cb(link, 0); } LOGP(DLINP, LOGL_ERROR, "error to send\n"); } msgb_free(msg); return 0; } static int ipa_client_fd_cb(struct osmo_fd *ofd, unsigned int what) { struct ipa_client_conn *link = ofd->data; int error, ret; socklen_t len = sizeof(error); switch(link->state) { case IPA_CLIENT_LINK_STATE_CONNECTING: ret = getsockopt(ofd->fd, SOL_SOCKET, SO_ERROR, &error, &len); if (ret >= 0 && error > 0) { ipa_client_conn_close(link); if (link->updown_cb) link->updown_cb(link, 0); return 0; } ofd->when &= ~BSC_FD_WRITE; LOGP(DLINP, LOGL_NOTICE, "connection done.\n"); link->state = IPA_CLIENT_LINK_STATE_CONNECTED; if (link->updown_cb) link->updown_cb(link, 1); break; case IPA_CLIENT_LINK_STATE_CONNECTED: if (what & BSC_FD_READ) { LOGP(DLINP, LOGL_DEBUG, "connected read\n"); ipa_client_read(link); } if (what & BSC_FD_WRITE) { LOGP(DLINP, LOGL_DEBUG, "connected write\n"); ipa_client_write(link); } break; default: break; } return 0; } struct ipa_client_conn * ipa_client_conn_create(void *ctx, struct e1inp_ts *ts, int priv_nr, const char *addr, uint16_t port, void (*updown_cb)(struct ipa_client_conn *link, int up), int (*read_cb)(struct ipa_client_conn *link, struct msgb *msgb), int (*write_cb)(struct ipa_client_conn *link), void *data) { struct ipa_client_conn *ipa_link; ipa_link = talloc_zero(ctx, struct ipa_client_conn); if (!ipa_link) return NULL; if (ts) { if (ts->line->driver == NULL) { talloc_free(ipa_link); return NULL; } ipa_link->ofd = &ts->driver.ipaccess.fd; } else { ipa_link->ofd = talloc_zero(ctx, struct osmo_fd); if (ipa_link->ofd == NULL) { talloc_free(ipa_link); return NULL; } } ipa_link->ofd->when |= BSC_FD_READ | BSC_FD_WRITE; ipa_link->ofd->priv_nr = priv_nr; ipa_link->ofd->cb = ipa_client_fd_cb; ipa_link->ofd->data = ipa_link; ipa_link->ofd->fd = -1; ipa_link->state = IPA_CLIENT_LINK_STATE_CONNECTING; ipa_link->addr = talloc_strdup(ipa_link, addr); ipa_link->port = port; ipa_link->updown_cb = updown_cb; ipa_link->read_cb = read_cb; /* default to generic write callback if not set. */ if (write_cb == NULL) ipa_link->write_cb = ipa_client_write_default_cb; else ipa_link->write_cb = write_cb; if (ts) ipa_link->line = ts->line; ipa_link->data = data; INIT_LLIST_HEAD(&ipa_link->tx_queue); return ipa_link; } void ipa_client_conn_destroy(struct ipa_client_conn *link) { talloc_free(link); } int ipa_client_conn_open(struct ipa_client_conn *link) { int ret; link->state = IPA_CLIENT_LINK_STATE_CONNECTING; ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP, link->addr, link->port, OSMO_SOCK_F_CONNECT|OSMO_SOCK_F_NONBLOCK); if (ret < 0) { if (errno != EINPROGRESS) return ret; } link->ofd->fd = ret; link->ofd->when |= BSC_FD_WRITE; if (osmo_fd_register(link->ofd) < 0) { close(ret); link->ofd->fd = -1; return -EIO; } return 0; } void ipa_client_conn_send(struct ipa_client_conn *link, struct msgb *msg) { msgb_enqueue(&link->tx_queue, msg); link->ofd->when |= BSC_FD_WRITE; } size_t ipa_client_conn_clear_queue(struct ipa_client_conn *link) { size_t deleted = 0; while (!llist_empty(&link->tx_queue)) { struct msgb *msg = msgb_dequeue(&link->tx_queue); msgb_free(msg); deleted += 1; } link->ofd->when &= ~BSC_FD_WRITE; return deleted; } static int ipa_server_fd_cb(struct osmo_fd *ofd, unsigned int what) { int fd, ret; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); struct ipa_server_link *link = ofd->data; fd = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); if (fd < 0) { LOGP(DLINP, LOGL_ERROR, "failed to accept from origin " "peer, reason=`%s'\n", strerror(errno)); return fd; } LOGP(DLINP, LOGL_NOTICE, "accept()ed new link from %s to port %u\n", inet_ntoa(sa.sin_addr), link->port); ret = link->accept_cb(link, fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "failed to processs accept()ed new link, " "reason=`%s'\n", strerror(-ret)); close(fd); return ret; } return 0; } struct ipa_server_link * ipa_server_link_create(void *ctx, struct e1inp_line *line, const char *addr, uint16_t port, int (*accept_cb)(struct ipa_server_link *link, int fd), void *data) { struct ipa_server_link *ipa_link; OSMO_ASSERT(accept_cb != NULL); ipa_link = talloc_zero(ctx, struct ipa_server_link); if (!ipa_link) return NULL; ipa_link->ofd.when |= BSC_FD_READ | BSC_FD_WRITE; ipa_link->ofd.cb = ipa_server_fd_cb; ipa_link->ofd.data = ipa_link; ipa_link->addr = talloc_strdup(ipa_link, addr); ipa_link->port = port; ipa_link->accept_cb = accept_cb; ipa_link->line = line; ipa_link->data = data; return ipa_link; } void ipa_server_link_destroy(struct ipa_server_link *link) { talloc_free(link); } int ipa_server_link_open(struct ipa_server_link *link) { int ret; ret = osmo_sock_init(AF_INET, SOCK_STREAM, IPPROTO_TCP, link->addr, link->port, OSMO_SOCK_F_BIND); if (ret < 0) return ret; link->ofd.fd = ret; if (osmo_fd_register(&link->ofd) < 0) { close(ret); return -EIO; } return 0; } void ipa_server_link_close(struct ipa_server_link *link) { osmo_fd_unregister(&link->ofd); close(link->ofd.fd); } static void ipa_server_conn_read(struct ipa_server_conn *conn) { struct osmo_fd *ofd = &conn->ofd; struct msgb *msg; int ret; LOGP(DLINP, LOGL_DEBUG, "message received\n"); ret = ipa_msg_recv_buffered(ofd->fd, &msg, &conn->pending_msg); if (ret < 0) { if (ret == -EAGAIN) return; if (ret == -EPIPE || ret == -ECONNRESET) LOGP(DLINP, LOGL_ERROR, "lost connection with server\n"); ipa_server_conn_destroy(conn); return; } else if (ret == 0) { LOGP(DLINP, LOGL_ERROR, "connection closed with server\n"); ipa_server_conn_destroy(conn); return; } if (conn->cb) conn->cb(conn, msg); return; } static void ipa_server_conn_write(struct ipa_server_conn *conn) { struct osmo_fd *ofd = &conn->ofd; struct msgb *msg; struct llist_head *lh; int ret; LOGP(DLINP, LOGL_DEBUG, "sending data\n"); if (llist_empty(&conn->tx_queue)) { ofd->when &= ~BSC_FD_WRITE; return; } lh = conn->tx_queue.next; llist_del(lh); msg = llist_entry(lh, struct msgb, list); ret = send(conn->ofd.fd, msg->data, msg->len, 0); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "error to send\n"); } msgb_free(msg); } static int ipa_server_conn_cb(struct osmo_fd *ofd, unsigned int what) { struct ipa_server_conn *conn = ofd->data; LOGP(DLINP, LOGL_DEBUG, "connected read/write\n"); if (what & BSC_FD_READ) ipa_server_conn_read(conn); if (what & BSC_FD_WRITE) ipa_server_conn_write(conn); return 0; } struct ipa_server_conn * ipa_server_conn_create(void *ctx, struct ipa_server_link *link, int fd, int (*cb)(struct ipa_server_conn *conn, struct msgb *msg), int (*closed_cb)(struct ipa_server_conn *conn), void *data) { struct ipa_server_conn *conn; conn = talloc_zero(ctx, struct ipa_server_conn); if (conn == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot allocate new peer in server, " "reason=`%s'\n", strerror(errno)); return NULL; } conn->server = link; conn->ofd.fd = fd; conn->ofd.data = conn; conn->ofd.cb = ipa_server_conn_cb; conn->ofd.when = BSC_FD_READ; conn->cb = cb; conn->closed_cb = closed_cb; conn->data = data; INIT_LLIST_HEAD(&conn->tx_queue); if (osmo_fd_register(&conn->ofd) < 0) { LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); talloc_free(conn); return NULL; } return conn; } void ipa_server_conn_destroy(struct ipa_server_conn *conn) { close(conn->ofd.fd); msgb_free(conn->pending_msg); osmo_fd_unregister(&conn->ofd); if (conn->closed_cb) conn->closed_cb(conn); talloc_free(conn); } void ipa_server_conn_send(struct ipa_server_conn *conn, struct msgb *msg) { msgb_enqueue(&conn->tx_queue, msg); conn->ofd.when |= BSC_FD_WRITE; } libosmo-abis-0.3.2+git86fc3c8/src/input/ipaccess.c000066400000000000000000000575671261736062400215770ustar00rootroot00000000000000/* 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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *tall_ipa_ctx; #define TS1_ALLOC_SIZE 900 #define DEFAULT_TCP_KEEPALIVE_IDLE_TIMEOUT 30 #define DEFAULT_TCP_KEEPALIVE_INTERVAL 3 #define DEFAULT_TCP_KEEPALIVE_RETRY_COUNT 10 static int ipaccess_drop(struct osmo_fd *bfd, struct e1inp_line *line) { int ret = 1; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; /* Error case: we did not see any ID_RESP yet for this socket. */ if (bfd->fd != -1) { LOGP(DLINP, LOGL_ERROR, "Forcing socket shutdown with " "no signal link set\n"); osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; ret = -ENOENT; } msgb_free(e1i_ts->pending_msg); e1i_ts->pending_msg = NULL; /* e1inp_sign_link_destroy releases the socket descriptors for us. */ line->ops->sign_link_down(line); return ret; } static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, struct osmo_fd *bfd) { struct tlv_parsed tlvp; uint8_t msg_type = *(msg->l2h); struct ipaccess_unit unit_data = {}; struct e1inp_sign_link *sign_link; char *unitid; int len, ret; /* Handle IPA PING, PONG and ID_ACK messages. */ ret = ipa_ccm_rcvmsg_base(msg, bfd); switch(ret) { case -1: /* error in IPA control message handling */ goto err; case 1: /* this is an IPA control message, skip further processing */ return 0; case 0: /* this is not an IPA control message, continue */ break; default: LOGP(DLINP, LOGL_ERROR, "Unexpected return from " "ipa_ccm_rcvmsg_base " "(ret=%d)\n", ret); goto err; } switch (msg_type) { case IPAC_MSGT_ID_RESP: DEBUGP(DLMI, "ID_RESP\n"); /* parse tags, search for Unit ID */ ret = ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2, msgb_l2len(msg)-2); DEBUGP(DLMI, "\n"); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "IPA response message " "with malformed TLVs\n"); ret = -EINVAL; goto err; } if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { LOGP(DLINP, LOGL_ERROR, "IPA response message " "without unit ID\n"); ret = -EINVAL; goto err; } len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT); if (len < 1) { LOGP(DLINP, LOGL_ERROR, "IPA response message " "with too small unit ID\n"); ret = -EINVAL; goto err; } unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT); unitid[len - 1] = '\0'; ipa_parse_unitid(unitid, &unit_data); if (!line->ops->sign_link_up) { LOGP(DLINP, LOGL_ERROR, "Unable to set signal link, closing socket.\n"); ret = -EINVAL; goto err; } /* the BSC creates the new sign links at this stage. */ if (bfd->priv_nr == E1INP_SIGN_OML) { sign_link = line->ops->sign_link_up(&unit_data, line, E1INP_SIGN_OML); if (sign_link == NULL) { LOGP(DLINP, LOGL_ERROR, "Unable to set signal link, " "closing socket.\n"); ret = -EINVAL; goto err; } } else if (bfd->priv_nr == E1INP_SIGN_RSL) { struct e1inp_ts *ts; struct osmo_fd *newbfd; struct e1inp_line *new_line; sign_link = line->ops->sign_link_up(&unit_data, line, E1INP_SIGN_RSL); if (sign_link == NULL) { LOGP(DLINP, LOGL_ERROR, "Unable to set signal link, " "closing socket.\n"); ret = -EINVAL; goto err; } /* this is a bugtrap, the BSC should be using the * virtual E1 line used by OML for this RSL link. */ if (sign_link->ts->line == line) { LOGP(DLINP, LOGL_ERROR, "Fix your BSC, you should use the " "E1 line used by the OML link for " "your RSL link.\n"); return 0; } /* Finally, we know which OML link is associated with * this RSL link, attach it to this socket. */ bfd->data = new_line = sign_link->ts->line; e1inp_line_get(new_line); ts = &new_line->ts[E1INP_SIGN_RSL+unit_data.trx_id-1]; newbfd = &ts->driver.ipaccess.fd; /* get rid of our old temporary bfd */ memcpy(newbfd, bfd, sizeof(*newbfd)); newbfd->priv_nr = E1INP_SIGN_RSL + unit_data.trx_id; osmo_fd_unregister(bfd); bfd->fd = -1; osmo_fd_register(newbfd); /* now we can release the dummy RSL line. */ e1inp_line_put(line); } break; default: LOGP(DLINP, LOGL_ERROR, "Unknown IPA message type\n"); ret = -EINVAL; goto err; } return 0; err: osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; e1inp_line_put(line); return ret; } static int handle_ts1_read(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct e1inp_sign_link *link; struct ipaccess_head *hh; struct msgb *msg = NULL; int ret; ret = ipa_msg_recv_buffered(bfd->fd, &msg, &e1i_ts->pending_msg); if (ret < 0) { if (ret == -EAGAIN) return 0; LOGP(DLINP, LOGL_NOTICE, "Sign link problems, " "closing socket. Reason: %s\n", strerror(-ret)); goto err; } else if (ret == 0) { LOGP(DLINP, LOGL_NOTICE, "Sign link vanished, dead socket\n"); goto err; } DEBUGP(DLMI, "RX %u: %s\n", ts_nr, osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); hh = (struct ipaccess_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { ipaccess_rcvmsg(line, msg, bfd); msgb_free(msg); return 0; } else if (e1i_ts->type == E1INP_TS_TYPE_NONE) { /* this sign link is not know yet.. complain. */ LOGP(DLINP, LOGL_ERROR, "Timeslot is not configured.\n"); ret = -EINVAL; goto err_msg; } /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg() * might have free'd it !!! */ link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); if (!link) { LOGP(DLINP, LOGL_ERROR, "no matching signalling link for " "hh->proto=0x%02x\n", hh->proto); ret = -EINVAL; goto err_msg; } msg->dst = link; /* XXX better use e1inp_ts_rx? */ if (!e1i_ts->line->ops->sign_link) { LOGP(DLINP, LOGL_ERROR, "Fix your application, " "no action set for signalling messages.\n"); ret = -EINVAL; goto err_msg; } if (e1i_ts->line->ops->sign_link(msg) < 0) { /* Don't close the signalling link if the upper layers report * an error, that's too strict. BTW, the signalling layer is * resposible for releasing the message. */ LOGP(DLINP, LOGL_ERROR, "Bad signalling message," "sign_link returned error\n"); } return 0; err_msg: msgb_free(msg); err: ipaccess_drop(bfd, line); return ret; } static int ts_want_write(struct e1inp_ts *e1i_ts) { e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; return 0; } static void ipaccess_close(struct e1inp_sign_link *sign_link) { struct e1inp_ts *e1i_ts = sign_link->ts; struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; return e1inp_close_socket(e1i_ts, sign_link, bfd); } static void timeout_ts1_write(void *data) { struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; /* trigger write of ts1, due to tx delay timer */ ts_want_write(e1i_ts); } static int __handle_ts1_write(struct osmo_fd *bfd, struct e1inp_line *line) { unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct e1inp_sign_link *sign_link; struct msgb *msg; int ret; bfd->when &= ~BSC_FD_WRITE; /* get the next msg for this timeslot */ msg = e1inp_tx_ts(e1i_ts, &sign_link); if (!msg) { /* no message after tx delay timer */ return 0; } switch (sign_link->type) { case E1INP_SIGN_OML: case E1INP_SIGN_RSL: case E1INP_SIGN_OSMO: break; default: bfd->when |= BSC_FD_WRITE; /* come back for more msg */ ret = -EINVAL; goto out; } msg->l2h = msg->data; ipa_prepend_header(msg, sign_link->tei); DEBUGP(DLMI, "TX %u: %s\n", ts_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg))); ret = send(bfd->fd, msg->data, msg->len, 0); if (ret != msg->len) { LOGP(DLINP, LOGL_ERROR, "failed to send A-bis IPA signalling " "message. Reason: %s\n", strerror(errno)); goto err; } /* set tx delay timer for next event */ e1i_ts->sign.tx_timer.cb = timeout_ts1_write; e1i_ts->sign.tx_timer.data = e1i_ts; /* Reducing this might break the nanoBTS 900 init. */ osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); out: msgb_free(msg); return ret; err: ipaccess_drop(bfd, line); msgb_free(msg); return ret; } static int handle_ts1_write(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; return __handle_ts1_write(bfd, line); } static int ipaccess_bts_write_cb(struct ipa_client_conn *link) { struct e1inp_line *line = link->line; return __handle_ts1_write(link->ofd, line); } /* callback from select.c in case one of the fd's can be read/written */ int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_ts1_read(bfd); if (what & BSC_FD_WRITE) rc = handle_ts1_write(bfd); return rc; } static int ipaccess_line_update(struct e1inp_line *line); struct e1inp_driver ipaccess_driver = { .name = "ipa", .want_write = ts_want_write, .line_update = ipaccess_line_update, .close = ipaccess_close, .default_delay = 0, .has_keepalive = 1, }; static void update_fd_settings(struct e1inp_line *line, int fd) { int ret; int val; if (line->keepalive_num_probes) { /* Enable TCP keepalive to find out if the connection is gone */ val = 1; ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); if (ret < 0) LOGP(DLINP, LOGL_NOTICE, "Failed to set keepalive: %s\n", strerror(errno)); else LOGP(DLINP, LOGL_NOTICE, "Keepalive is set: %i\n", ret); #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) /* The following options are not portable! */ val = line->keepalive_idle_timeout > 0 ? line->keepalive_idle_timeout : DEFAULT_TCP_KEEPALIVE_IDLE_TIMEOUT; ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)); if (ret < 0) LOGP(DLINP, LOGL_NOTICE, "Failed to set keepalive idle time: %s\n", strerror(errno)); val = line->keepalive_probe_interval > -1 ? line->keepalive_probe_interval : DEFAULT_TCP_KEEPALIVE_INTERVAL; ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)); if (ret < 0) LOGP(DLINP, LOGL_NOTICE, "Failed to set keepalive interval: %s\n", strerror(errno)); val = line->keepalive_num_probes > 0 ? line->keepalive_num_probes : DEFAULT_TCP_KEEPALIVE_RETRY_COUNT; ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)); if (ret < 0) LOGP(DLINP, LOGL_NOTICE, "Failed to set keepalive count: %s\n", strerror(errno)); #endif } } /* callback of the OML listening filedescriptor */ static int ipaccess_bsc_oml_cb(struct ipa_server_link *link, int fd) { int ret; int idx = 0; int i; struct e1inp_line *line; struct e1inp_ts *e1i_ts; struct osmo_fd *bfd; /* clone virtual E1 line for this new OML link. */ line = e1inp_line_clone(tall_ipa_ctx, link->line); if (line == NULL) { LOGP(DLINP, LOGL_ERROR, "could not clone E1 line\n"); return -ENOMEM; } /* create virrtual E1 timeslots for signalling */ e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line); /* initialize the fds */ for (i = 0; i < ARRAY_SIZE(line->ts); ++i) line->ts[i].driver.ipaccess.fd.fd = -1; e1i_ts = &line->ts[idx]; bfd = &e1i_ts->driver.ipaccess.fd; bfd->fd = fd; bfd->data = line; bfd->priv_nr = E1INP_SIGN_OML; bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ; ret = osmo_fd_register(bfd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); goto err_line; } update_fd_settings(line, bfd->fd); /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ ret = ipa_ccm_send_id_req(bfd->fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not send ID REQ. Reason: %s\n", strerror(errno)); goto err_socket; } return ret; err_socket: osmo_fd_unregister(bfd); err_line: close(bfd->fd); bfd->fd = -1; e1inp_line_put(line); return ret; } static int ipaccess_bsc_rsl_cb(struct ipa_server_link *link, int fd) { struct e1inp_line *line; struct e1inp_ts *e1i_ts; struct osmo_fd *bfd; int i, ret; /* We don't know yet which OML link to associate it with. Thus, we * allocate a temporary E1 line until we have received ID. */ line = e1inp_line_clone(tall_ipa_ctx, link->line); if (line == NULL) { LOGP(DLINP, LOGL_ERROR, "could not clone E1 line\n"); return -ENOMEM; } /* initialize the fds */ for (i = 0; i < ARRAY_SIZE(line->ts); ++i) line->ts[i].driver.ipaccess.fd.fd = -1; /* we need this to initialize this in case to avoid crashes in case * that the socket is closed before we've seen an ID_RESP. */ e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML-1], line); e1i_ts = &line->ts[E1INP_SIGN_RSL-1]; bfd = &e1i_ts->driver.ipaccess.fd; bfd->fd = fd; bfd->data = line; bfd->priv_nr = E1INP_SIGN_RSL; bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ; ret = osmo_fd_register(bfd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); goto err_line; } /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ ret = ipa_ccm_send_id_req(bfd->fd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not send ID REQ. Reason: %s\n", strerror(errno)); goto err_socket; } update_fd_settings(line, bfd->fd); return ret; err_socket: osmo_fd_unregister(bfd); err_line: close(bfd->fd); bfd->fd = -1; e1inp_line_put(line); return ret; } #define IPA_STRING_MAX 64 static struct msgb * ipa_bts_id_resp(struct ipaccess_unit *dev, uint8_t *data, int len, int trx_nr) { struct msgb *nmsg; char str[IPA_STRING_MAX]; uint8_t *tag; nmsg = ipa_msg_alloc(0); if (!nmsg) return NULL; *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP; while (len) { if (len < 2) { LOGP(DLINP, LOGL_NOTICE, "Short read of ipaccess tag\n"); msgb_free(nmsg); return NULL; } switch (data[1]) { case IPAC_IDTAG_UNIT: snprintf(str, sizeof(str), "%u/%u/%u", dev->site_id, dev->bts_id, trx_nr); break; case IPAC_IDTAG_MACADDR: snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", dev->mac_addr[0], dev->mac_addr[1], dev->mac_addr[2], dev->mac_addr[3], dev->mac_addr[4], dev->mac_addr[5]); break; case IPAC_IDTAG_LOCATION1: strncpy(str, dev->location1, IPA_STRING_MAX); break; case IPAC_IDTAG_LOCATION2: strncpy(str, dev->location2, IPA_STRING_MAX); break; case IPAC_IDTAG_EQUIPVERS: strncpy(str, dev->equipvers, IPA_STRING_MAX); break; case IPAC_IDTAG_SWVERSION: strncpy(str, dev->swversion, IPA_STRING_MAX); break; case IPAC_IDTAG_UNITNAME: snprintf(str, sizeof(str), "%s-%02x-%02x-%02x-%02x-%02x-%02x", dev->unit_name, dev->mac_addr[0], dev->mac_addr[1], dev->mac_addr[2], dev->mac_addr[3], dev->mac_addr[4], dev->mac_addr[5]); break; case IPAC_IDTAG_SERNR: strncpy(str, dev->serno, IPA_STRING_MAX); break; default: LOGP(DLINP, LOGL_NOTICE, "Unknown ipaccess tag 0x%02x\n", *data); msgb_free(nmsg); return NULL; } str[IPA_STRING_MAX-1] = '\0'; LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", data[1], str); tag = msgb_put(nmsg, 3 + strlen(str) + 1); tag[0] = 0x00; tag[1] = 1 + strlen(str) + 1; tag[2] = data[1]; memcpy(tag + 3, str, strlen(str) + 1); data += 2; len -= 2; } ipa_msg_push_header(nmsg, IPAC_PROTO_IPACCESS); return nmsg; } static struct msgb *ipa_bts_id_ack(void) { struct msgb *nmsg2; nmsg2 = ipa_msg_alloc(0); if (!nmsg2) return NULL; *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK; ipa_msg_push_header(nmsg2, IPAC_PROTO_IPACCESS); return nmsg2; } static void ipaccess_bts_updown_cb(struct ipa_client_conn *link, int up) { struct e1inp_line *line = link->line; if (up) return; if (line->ops->sign_link_down) line->ops->sign_link_down(line); } /* handle incoming message to BTS, check if it is an IPA CCM, and if yes, * handle it accordingly (PING/PONG/ID_REQ/ID_RESP/ID_ACK) */ int ipaccess_bts_handle_ccm(struct ipa_client_conn *link, struct ipaccess_unit *dev, struct msgb *msg) { struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; struct msgb *rmsg; int ret = 0; /* special handling for IPA CCM. */ if (hh->proto == IPAC_PROTO_IPACCESS) { uint8_t msg_type = *(msg->l2h); /* ping, pong and acknowledgment cases. */ ret = ipa_ccm_rcvmsg_bts_base(msg, link->ofd); if (ret < 0) goto err; /* this is a request for identification from the BSC. */ if (msg_type == IPAC_MSGT_ID_GET) { uint8_t *data = msgb_l2(msg); int len = msgb_l2len(msg); int trx_nr = 0; if (link->ofd->priv_nr >= E1INP_SIGN_RSL) trx_nr = link->ofd->priv_nr - E1INP_SIGN_RSL; LOGP(DLINP, LOGL_NOTICE, "received ID get\n"); rmsg = ipa_bts_id_resp(dev, data + 1, len - 1, trx_nr); ret = ipa_send(link->ofd->fd, rmsg->data, rmsg->len); if (ret != rmsg->len) { LOGP(DLINP, LOGL_ERROR, "cannot send ID_RESP " "message. Reason: %s\n", strerror(errno)); goto err_rmsg; } msgb_free(rmsg); /* send ID_ACK. */ rmsg = ipa_bts_id_ack(); ret = ipa_send(link->ofd->fd, rmsg->data, rmsg->len); if (ret != rmsg->len) { LOGP(DLINP, LOGL_ERROR, "cannot send ID_ACK " "message. Reason: %s\n", strerror(errno)); goto err_rmsg; } msgb_free(rmsg); } return 1; } return 0; err_rmsg: msgb_free(rmsg); err: ipa_client_conn_close(link); return -1; } static int ipaccess_bts_read_cb(struct ipa_client_conn *link, struct msgb *msg) { struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; struct e1inp_ts *e1i_ts = NULL; struct e1inp_sign_link *sign_link; int ret = 0; /* special handling for IPA CCM. */ if (hh->proto == IPAC_PROTO_IPACCESS) { uint8_t msg_type = *(msg->l2h); /* this is a request for identification from the BSC. */ if (msg_type == IPAC_MSGT_ID_GET) { if (!link->line->ops->sign_link_up) { LOGP(DLINP, LOGL_ERROR, "Unable to set signal link, " "closing socket.\n"); ret = -EINVAL; goto err; } } } /* core CCM handling */ ret = ipaccess_bts_handle_ccm(link, link->line->ops->cfg.ipa.dev, msg); if (ret < 0) goto err; if (ret == 1 && hh->proto == IPAC_PROTO_IPACCESS) { uint8_t msg_type = *(msg->l2h); if (msg_type == IPAC_MSGT_ID_GET) { sign_link = link->line->ops->sign_link_up(msg, link->line, link->ofd->priv_nr); if (sign_link == NULL) { LOGP(DLINP, LOGL_ERROR, "Unable to set signal link, " "closing socket.\n"); ret = -EINVAL; goto err; } } msgb_free(msg); return ret; } else if (link->port == IPA_TCP_PORT_OML) e1i_ts = &link->line->ts[0]; else if (link->port == IPA_TCP_PORT_RSL) e1i_ts = &link->line->ts[link->ofd->priv_nr-1]; OSMO_ASSERT(e1i_ts != NULL); /* look up for some existing signaling link. */ sign_link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); if (sign_link == NULL) { LOGP(DLINP, LOGL_ERROR, "no matching signalling link for " "hh->proto=0x%02x\n", hh->proto); ret = -EIO; goto err; } msg->dst = sign_link; /* XXX better use e1inp_ts_rx? */ if (!link->line->ops->sign_link) { LOGP(DLINP, LOGL_ERROR, "Fix your application, " "no action set for signalling messages.\n"); ret = -ENOENT; goto err; } link->line->ops->sign_link(msg); return 0; err: ipa_client_conn_close(link); msgb_free(msg); return ret; } struct ipaccess_line { int line_already_initialized; }; static int ipaccess_line_update(struct e1inp_line *line) { int ret = -ENOENT; struct ipaccess_line *il; if (!line->driver_data) line->driver_data = talloc_zero(line, struct ipaccess_line); if (!line->driver_data) { LOGP(DLINP, LOGL_ERROR, "ipaccess: OOM in line update\n"); return -ENOMEM; } il = line->driver_data; /* We only initialize this line once. */ if (il->line_already_initialized) return 0; il->line_already_initialized = 1; switch(line->ops->cfg.ipa.role) { case E1INP_LINE_R_BSC: { struct ipa_server_link *oml_link, *rsl_link; LOGP(DLINP, LOGL_NOTICE, "enabling ipaccess BSC mode\n"); oml_link = ipa_server_link_create(tall_ipa_ctx, line, "0.0.0.0", IPA_TCP_PORT_OML, ipaccess_bsc_oml_cb, NULL); if (oml_link == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot create OML " "BSC link: %s\n", strerror(errno)); return -ENOMEM; } if (ipa_server_link_open(oml_link) < 0) { LOGP(DLINP, LOGL_ERROR, "cannot open OML BSC link: %s\n", strerror(errno)); ipa_server_link_destroy(oml_link); return -EIO; } rsl_link = ipa_server_link_create(tall_ipa_ctx, line, "0.0.0.0", IPA_TCP_PORT_RSL, ipaccess_bsc_rsl_cb, NULL); if (rsl_link == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot create RSL " "BSC link: %s\n", strerror(errno)); return -ENOMEM; } if (ipa_server_link_open(rsl_link) < 0) { LOGP(DLINP, LOGL_ERROR, "cannot open RSL BSC link: %s\n", strerror(errno)); ipa_server_link_destroy(rsl_link); return -EIO; } ret = 0; break; } case E1INP_LINE_R_BTS: { struct ipa_client_conn *link; LOGP(DLINP, LOGL_NOTICE, "enabling ipaccess BTS mode\n"); link = ipa_client_conn_create(tall_ipa_ctx, &line->ts[E1INP_SIGN_OML-1], E1INP_SIGN_OML, line->ops->cfg.ipa.addr, IPA_TCP_PORT_OML, ipaccess_bts_updown_cb, ipaccess_bts_read_cb, ipaccess_bts_write_cb, line); if (link == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot create OML " "BTS link: %s\n", strerror(errno)); return -ENOMEM; } if (ipa_client_conn_open(link) < 0) { LOGP(DLINP, LOGL_ERROR, "cannot open OML BTS link: %s\n", strerror(errno)); ipa_client_conn_close(link); ipa_client_conn_destroy(link); return -EIO; } ret = 0; break; } default: break; } return ret; } /* backwards compatibility */ int e1inp_ipa_bts_rsl_connect(struct e1inp_line *line, const char *rem_addr, uint16_t rem_port) { return e1inp_ipa_bts_rsl_connect_n(line, rem_addr, rem_port, 0); } int e1inp_ipa_bts_rsl_connect_n(struct e1inp_line *line, const char *rem_addr, uint16_t rem_port, uint8_t trx_nr) { struct ipa_client_conn *rsl_link; if (E1INP_SIGN_RSL+trx_nr-1 >= NUM_E1_TS) { LOGP(DLINP, LOGL_ERROR, "cannot create RSL BTS link: " "trx_nr (%d) out of range\n", trx_nr); return -EINVAL; } rsl_link = ipa_client_conn_create(tall_ipa_ctx, &line->ts[E1INP_SIGN_RSL+trx_nr-1], E1INP_SIGN_RSL+trx_nr, rem_addr, rem_port, ipaccess_bts_updown_cb, ipaccess_bts_read_cb, ipaccess_bts_write_cb, line); if (rsl_link == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot create RSL " "BTS link: %s\n", strerror(errno)); return -ENOMEM; } if (ipa_client_conn_open(rsl_link) < 0) { LOGP(DLINP, LOGL_ERROR, "cannot open RSL BTS link: %s\n", strerror(errno)); ipa_client_conn_close(rsl_link); ipa_client_conn_destroy(rsl_link); return -EIO; } return 0; } void e1inp_ipaccess_init(void) { tall_ipa_ctx = talloc_named_const(libosmo_abis_ctx, 1, "ipa"); e1inp_driver_register(&ipaccess_driver); } libosmo-abis-0.3.2+git86fc3c8/src/input/lapd.c000066400000000000000000000425031261736062400207050ustar00rootroot00000000000000/* OpenBSC minimal LAPD implementation */ /* (C) 2009 by oystein@homelien.no * (C) 2009 by Holger Hans Peter Freyther * (C) 2010 by Digium and Matthew Fredrickson * (C) 2011 by Harald Welte * (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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include #define LAPD_ADDR2(sapi, cr) ((((sapi) & 0x3f) << 2) | (((cr) & 0x1) << 1)) #define LAPD_ADDR3(tei) ((((tei) & 0x7f) << 1) | 0x1) #define LAPD_ADDR_SAPI(addr) ((addr) >> 2) #define LAPD_ADDR_CR(addr) (((addr) >> 1) & 0x1) #define LAPD_ADDR_EA(addr) ((addr) & 0x1) #define LAPD_ADDR_TEI(addr) ((addr) >> 1) #define LAPD_CTRL_I4(ns) (((ns) & 0x7f) << 1) #define LAPD_CTRL_I5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1)) #define LAPD_CTRL_S4(s) ((((s) & 0x3) << 2) | 0x1) #define LAPD_CTRL_S5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1)) #define LAPD_CTRL_U4(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3) #define LAPD_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0) #define LAPD_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1) #define LAPD_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3) #define LAPD_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3) #define LAPD_CTRL_U_PF(ctrl) (((ctrl) >> 4) & 0x1) #define LAPD_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2) #define LAPD_CTRL_S_PF(ctrl) (ctrl & 0x1) #define LAPD_CTRL_I_Ns(ctrl) (((ctrl) & 0xFE) >> 1) #define LAPD_CTRL_I_P(ctrl) (ctrl & 0x1) #define LAPD_CTRL_Nr(ctrl) (((ctrl) & 0xFE) >> 1) #define LAPD_LEN(len) ((len << 2) | 0x1) #define LAPD_EL 0x1 #define LAPD_SET_K(n, o) {n,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o} const struct lapd_profile lapd_profile_isdn = { .k = LAPD_SET_K(7,7), .n200 = 3, .n201 = 260, .n202 = 3, .t200_sec = 1, .t200_usec = 0, .t201_sec = 1, .t201_usec = 0, .t202_sec = 2, .t202_usec = 0, .t203_sec = 10, .t203_usec = 0, .short_address = 0 }; const struct lapd_profile lapd_profile_abis = { .k = LAPD_SET_K(2,1), .n200 = 3, .n201 = 260, .n202 = 0, /* infinite */ .t200_sec = 0, .t200_usec = 240000, .t201_sec = 1, .t201_usec = 0, .t202_sec = 2, .t202_usec = 0, .t203_sec = 10, .t203_usec = 0, .short_address = 0 }; const struct lapd_profile lapd_profile_sat = { .k = LAPD_SET_K(15,15), .n200 = 5, .n201 = 260, .n202 = 5, .t200_sec = 2, .t200_usec = 400000, .t201_sec = 2, .t201_usec = 400000, .t202_sec = 2, .t202_usec = 400000, .t203_sec = 20, .t203_usec = 0, .short_address = 1 }; typedef enum { LAPD_TEI_NONE = 0, LAPD_TEI_ASSIGNED, LAPD_TEI_ACTIVE, } lapd_tei_state; const char *lapd_tei_states[] = { "NONE", "ASSIGNED", "ACTIVE", }; /* Structure representing an allocated TEI within a LAPD instance. */ struct lapd_tei { struct llist_head list; struct lapd_instance *li; uint8_t tei; lapd_tei_state state; struct llist_head sap_list; }; /* Structure representing a SAP within a TEI. It includes exactly one datalink * instance. */ struct lapd_sap { struct llist_head list; struct lapd_tei *tei; uint8_t sapi; struct lapd_datalink dl; }; /* Resolve TEI structure from given numeric TEI */ static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) { struct lapd_tei *lt; llist_for_each_entry(lt, &li->tei_list, list) { if (lt->tei == tei) return lt; } return NULL; }; /* Change state of TEI */ static void lapd_tei_set_state(struct lapd_tei *teip, int newstate) { LOGP(DLLAPD, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n", teip->tei, lapd_tei_states[teip->state], lapd_tei_states[newstate]); teip->state = newstate; }; /* Allocate a new TEI */ struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei) { struct lapd_tei *teip; teip = talloc_zero(li, struct lapd_tei); if (!teip) return NULL; teip->li = li; teip->tei = tei; llist_add(&teip->list, &li->tei_list); INIT_LLIST_HEAD(&teip->sap_list); lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); return teip; } /* Find a SAP within a given TEI */ static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi) { struct lapd_sap *sap; llist_for_each_entry(sap, &teip->sap_list, list) { if (sap->sapi == sapi) return sap; } return NULL; } static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg); static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx); /* Allocate a new SAP within a given TEI */ static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi) { struct lapd_sap *sap; struct lapd_datalink *dl; struct lapd_instance *li; struct lapd_profile *profile; int k; sap = talloc_zero(teip, struct lapd_sap); if (!sap) return NULL; LOGP(DLLAPD, LOGL_NOTICE, "LAPD Allocating SAP for SAPI=%u / TEI=%u\n", sapi, teip->tei); sap->sapi = sapi; sap->tei = teip; dl = &sap->dl; li = teip->li; profile = &li->profile; k = profile->k[sapi & 0x3f]; LOGP(DLLAPD, LOGL_NOTICE, "k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d" "\n", k, profile->n200, profile->n201, profile->t200_sec, profile->t200_usec, profile->t203_sec, profile->t203_usec); lapd_dl_init(dl, k, 128, profile->n201); dl->use_sabme = 1; /* use SABME instead of SABM (GSM) */ dl->send_ph_data_req = send_ph_data_req; dl->send_dlsap = send_dlsap; dl->n200 = profile->n200; dl->n200_est_rel = profile->n200; dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec; dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec; dl->lctx.dl = &sap->dl; dl->lctx.sapi = sapi; dl->lctx.tei = teip->tei; dl->lctx.n201 = profile->n201; lapd_set_mode(&sap->dl, (teip->li->network_side) ? LAPD_MODE_NETWORK : LAPD_MODE_USER); llist_add(&sap->list, &teip->sap_list); return sap; } /* Free SAP instance, including the datalink */ static void lapd_sap_free(struct lapd_sap *sap) { /* free datalink structures and timers */ lapd_dl_exit(&sap->dl); llist_del(&sap->list); talloc_free(sap); } /* Free TEI instance */ static void lapd_tei_free(struct lapd_tei *teip) { struct lapd_sap *sap, *sap2; llist_for_each_entry_safe(sap, sap2, &teip->sap_list, list) { lapd_sap_free(sap); } llist_del(&teip->list); talloc_free(teip); } /* Input function into TEI manager */ static int lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) { uint8_t entity; uint8_t ref; uint8_t mt; uint8_t action; uint8_t e; uint8_t resp[8]; struct lapd_tei *teip; struct msgb *msg; if (len < 5) { LOGP(DLLAPD, LOGL_ERROR, "LAPD TEIMGR frame receive len %d < 5" ", ignoring\n", len); return -EINVAL; }; entity = data[0]; ref = data[1]; mt = data[3]; action = data[4] >> 1; e = data[4] & 1; DEBUGP(DLLAPD, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, " "e %x\n", entity, ref, mt, action, e); switch (mt) { case 0x01: /* IDENTITY REQUEST */ DEBUGP(DLLAPD, "LAPD TEIMGR: identity request for TEI %u\n", action); teip = teip_from_tei(li, action); if (!teip) { LOGP(DLLAPD, LOGL_INFO, "TEI MGR: New TEI %u\n", action); teip = lapd_tei_alloc(li, action); if (!teip) return -ENOMEM; } /* Send ACCEPT */ memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8); resp[7] = (action << 1) | 1; msg = msgb_alloc_headroom(56, 56, "DL EST"); msg->l2h = msgb_push(msg, 8); memcpy(msg->l2h, resp, 8); /* write to PCAP file, if enabled. */ osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg); LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len)); li->transmit_cb(msg, li->transmit_cbdata); if (teip->state == LAPD_TEI_NONE) lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); break; default: LOGP(DLLAPD, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x " "action %x\n", mt, action); break; }; return 0; } /* General input function for any data received for this LAPD instance */ int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error) { int i; struct lapd_msg_ctx lctx; int rc; struct lapd_sap *sap; struct lapd_tei *teip; /* write to PCAP file, if enabled. */ osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_INPUT, msg); LOGP(DLLAPD, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len)); if (msg->len < 2) { LOGP(DLLAPD, LOGL_ERROR, "LAPD frame receive len %d < 2, " "ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; msg->l2h = msg->data; memset(&lctx, 0, sizeof(lctx)); i = 0; /* adress field */ lctx.sapi = LAPD_ADDR_SAPI(msg->l2h[i]); lctx.cr = LAPD_ADDR_CR(msg->l2h[i]); lctx.lpd = 0; if (!LAPD_ADDR_EA(msg->l2h[i])) { if (msg->len < 3) { LOGP(DLLAPD, LOGL_ERROR, "LAPD frame with TEI receive " "len %d < 3, ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; i++; lctx.tei = LAPD_ADDR_TEI(msg->l2h[i]); } i++; /* control field */ if (LAPD_CTRL_is_I(msg->l2h[i])) { lctx.format = LAPD_FORM_I; lctx.n_send = LAPD_CTRL_I_Ns(msg->l2h[i]); i++; if (msg->len < 3 && i == 2) { LOGP(DLLAPD, LOGL_ERROR, "LAPD I frame without TEI " "receive len %d < 3, ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; if (msg->len < 4 && i == 3) { LOGP(DLLAPD, LOGL_ERROR, "LAPD I frame with TEI " "receive len %d < 4, ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]); lctx.p_f = LAPD_CTRL_I_P(msg->l2h[i]); } else if (LAPD_CTRL_is_S(msg->l2h[i])) { lctx.format = LAPD_FORM_S; lctx.s_u = LAPD_CTRL_S_BITS(msg->l2h[i]); i++; if (msg->len < 3 && i == 2) { LOGP(DLLAPD, LOGL_ERROR, "LAPD S frame without TEI " "receive len %d < 3, ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; if (msg->len < 4 && i == 3) { LOGP(DLLAPD, LOGL_ERROR, "LAPD S frame with TEI " "receive len %d < 4, ignoring\n", msg->len); *error = LAPD_ERR_BAD_LEN; return -EINVAL; }; lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]); lctx.p_f = LAPD_CTRL_S_PF(msg->l2h[i]); } else if (LAPD_CTRL_is_U(msg->l2h[i])) { lctx.format = LAPD_FORM_U; lctx.s_u = LAPD_CTRL_U_BITS(msg->l2h[i]); lctx.p_f = LAPD_CTRL_U_PF(msg->l2h[i]); } else lctx.format = LAPD_FORM_UKN; i++; /* length */ msg->l3h = msg->l2h + i; msgb_pull(msg, i); lctx.length = msg->len; /* perform TEI assignment, if received */ if (lctx.tei == 127) { rc = lapd_tei_receive(li, msg->data, msg->len); msgb_free(msg); return rc; } /* resolve TEI and SAPI */ teip = teip_from_tei(li, lctx.tei); if (!teip) { LOGP(DLLAPD, LOGL_NOTICE, "LAPD Unknown TEI %u\n", lctx.tei); *error = LAPD_ERR_UNKNOWN_TEI; msgb_free(msg); return -EINVAL; } sap = lapd_sap_find(teip, lctx.sapi); if (!sap) { LOGP(DLLAPD, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, " "allocating\n", lctx.tei, lctx.sapi); sap = lapd_sap_alloc(teip, lctx.sapi); if (!sap) { *error = LAPD_ERR_NO_MEM; msgb_free(msg); return -ENOMEM; } } lctx.dl = &sap->dl; lctx.n201 = lctx.dl->maxf; if (msg->len > lctx.n201) { LOGP(DLLAPD, LOGL_ERROR, "message len %d > N201(%d) " "(discarding)\n", msg->len, lctx.n201); msgb_free(msg); *error = LAPD_ERR_BAD_LEN; return -EINVAL; } /* send to LAPD */ return lapd_ph_data_ind(msg, &lctx); } /* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi) { struct lapd_sap *sap; struct lapd_tei *teip; struct osmo_dlsap_prim dp; struct msgb *msg; teip = teip_from_tei(li, tei); if (!teip) teip = lapd_tei_alloc(li, tei); sap = lapd_sap_find(teip, sapi); if (sap) return -EEXIST; sap = lapd_sap_alloc(teip, sapi); if (!sap) return -ENOMEM; LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH request TEI=%d SAPI=%d\n", tei, sapi); /* prepare prim */ msg = msgb_alloc_headroom(56, 56, "DL EST"); msg->l3h = msg->data; osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &sap->dl.lctx); } /* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi) { struct lapd_tei *teip; struct lapd_sap *sap; struct osmo_dlsap_prim dp; struct msgb *msg; teip = teip_from_tei(li, tei); if (!teip) return -ENODEV; sap = lapd_sap_find(teip, sapi); if (!sap) return -ENODEV; LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE request TEI=%d SAPI=%d\n", tei, sapi); /* prepare prim */ msg = msgb_alloc_headroom(56, 56, "DL REL"); msg->l3h = msg->data; osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg); /* send to L2 */ return lapd_recv_dlsap(&dp, &sap->dl.lctx); } /* Transmit Data (DL-DATA request) on the given LAPD Instance / TEI / SAPI */ void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, struct msgb *msg) { struct lapd_tei *teip = teip_from_tei(li, tei); struct lapd_sap *sap; struct osmo_dlsap_prim dp; if (!teip) { LOGP(DLLAPD, LOGL_ERROR, "LAPD Cannot transmit on " "non-existing TEI %u\n", tei); msgb_free(msg); return; } sap = lapd_sap_find(teip, sapi); if (!sap) { LOGP(DLLAPD, LOGL_INFO, "LAPD Tx on unknown SAPI=%u " "in TEI=%u\n", sapi, tei); msgb_free(msg); return; } /* prepare prim */ msg->l3h = msg->data; osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg); /* send to L2 */ lapd_recv_dlsap(&dp, &sap->dl.lctx); }; static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg) { struct lapd_datalink *dl = lctx->dl; struct lapd_sap *sap = container_of(dl, struct lapd_sap, dl); struct lapd_instance *li = sap->tei->li; int format = lctx->format; int addr_len; /* control field */ switch (format) { case LAPD_FORM_I: msg->l2h = msgb_push(msg, 2); msg->l2h[0] = LAPD_CTRL_I4(lctx->n_send); msg->l2h[1] = LAPD_CTRL_I5(lctx->n_recv, lctx->p_f); break; case LAPD_FORM_S: msg->l2h = msgb_push(msg, 2); msg->l2h[0] = LAPD_CTRL_S4(lctx->s_u); msg->l2h[1] = LAPD_CTRL_S5(lctx->n_recv, lctx->p_f); break; case LAPD_FORM_U: msg->l2h = msgb_push(msg, 1); msg->l2h[0] = LAPD_CTRL_U4(lctx->s_u, lctx->p_f); break; default: msgb_free(msg); return -EINVAL; } /* address field */ if (li->profile.short_address && lctx->tei == 0) addr_len = 1; else addr_len = 2; msg->l2h = msgb_push(msg, addr_len); msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr); if (addr_len == 1) msg->l2h[0] |= 0x1; else msg->l2h[1] = LAPD_ADDR3(lctx->tei); /* write to PCAP file, if enabled. */ osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg); /* forward frame to L1 */ LOGP(DLLAPD, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len)); li->transmit_cb(msg, li->transmit_cbdata); return 0; } /* A DL-SAP message is received from datalink instance and forwarded to L3 */ static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx) { struct lapd_datalink *dl = lctx->dl; struct lapd_sap *sap = container_of(dl, struct lapd_sap, dl); struct lapd_instance *li; uint8_t tei, sapi; char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication" : "confirm"; li = sap->tei->li; tei = lctx->tei; sapi = lctx->sapi; switch (dp->oph.primitive) { case PRIM_DL_EST: LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-ESTABLISH %s TEI=%d " "SAPI=%d\n", op, lctx->tei, lctx->sapi); break; case PRIM_DL_REL: LOGP(DLLAPD, LOGL_NOTICE, "LAPD DL-RELEASE %s TEI=%d " "SAPI=%d\n", op, lctx->tei, lctx->sapi); lapd_sap_free(sap); /* note: sap and dl is now gone, don't use it anymore */ break; default: ; } li->receive_cb(dp, tei, sapi, li->receive_cbdata); return 0; } /* Allocate a new LAPD instance */ struct lapd_instance *lapd_instance_alloc(int network_side, void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata, void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi, void *rx_cbdata), void *rx_cbdata, const struct lapd_profile *profile) { struct lapd_instance *li; li = talloc_zero(NULL, struct lapd_instance); if (!li) return NULL; li->network_side = network_side; li->transmit_cb = tx_cb; li->transmit_cbdata = tx_cbdata; li->receive_cb = rx_cb; li->receive_cbdata = rx_cbdata; li->pcap_fd = -1; memcpy(&li->profile, profile, sizeof(li->profile)); INIT_LLIST_HEAD(&li->tei_list); return li; } void lapd_instance_free(struct lapd_instance *li) { struct lapd_tei *teip, *teip2; /* Free all TEI instances */ llist_for_each_entry_safe(teip, teip2, &li->tei_list, list) { lapd_tei_free(teip); } talloc_free(li); } libosmo-abis-0.3.2+git86fc3c8/src/input/lapd_pcap.c000066400000000000000000000102431261736062400217040ustar00rootroot00000000000000/* (C) 2008-2012 by Harald Welte * * All Rights Reserved * * Author: Harald Welte * Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * pcap writing of the mlapd load * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat */ #define DLT_LINUX_LAPD 177 struct pcap_hdr { uint32_t magic_number; uint16_t version_major; uint16_t version_minor; int32_t thiszone; uint32_t sigfigs; uint32_t snaplen; uint32_t network; } __attribute__((packed)); struct pcap_rechdr { uint32_t ts_sec; uint32_t ts_usec; uint32_t incl_len; uint32_t orig_len; } __attribute__((packed)); struct pcap_lapdhdr { uint16_t pkttype; uint16_t hatype; uint16_t halen; uint64_t addr; int16_t protocol; } __attribute__((packed)); osmo_static_assert(offsetof(struct pcap_lapdhdr, hatype) == 2, hatype_offset); osmo_static_assert(offsetof(struct pcap_lapdhdr, halen) == 4, halen_offset); osmo_static_assert(offsetof(struct pcap_lapdhdr, addr) == 6, addr_offset); osmo_static_assert(offsetof(struct pcap_lapdhdr, protocol) == 14, proto_offset); osmo_static_assert(sizeof(struct pcap_lapdhdr) == 16, lapd_header_size); int osmo_pcap_lapd_open(char *filename, mode_t mode) { int fd; struct pcap_hdr pcap_header = { .magic_number = 0xa1b2c3d4, .version_major = 2, .version_minor = 4, .thiszone = 0, .sigfigs = 0, .snaplen = 65535, .network = DLT_LINUX_LAPD, }; LOGP(DLLAPD, LOGL_NOTICE, "opening LAPD pcap file `%s'\n", filename); fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, mode); if (fd < 0) { LOGP(DLLAPD, LOGL_ERROR, "failed to open PCAP file: %s\n", strerror(errno)); return -1; } if (write(fd, &pcap_header, sizeof(pcap_header)) != sizeof(pcap_header)) { LOGP(DLLAPD, LOGL_ERROR, "cannot write PCAP header: %s\n", strerror(errno)); close(fd); return -1; } return fd; } /* This currently only works for the D-Channel */ int osmo_pcap_lapd_write(int fd, int direction, struct msgb *msg) { int numbytes = 0; struct timeval tv; struct pcap_rechdr pcap_rechdr; struct pcap_lapdhdr header; char buf[sizeof(struct pcap_rechdr) + sizeof(struct pcap_lapdhdr) + msg->len]; /* PCAP file has not been opened, skip. */ if (fd < 0) return 0; pcap_rechdr.ts_sec = 0; pcap_rechdr.ts_usec = 0; pcap_rechdr.incl_len = msg->len + sizeof(struct pcap_lapdhdr); pcap_rechdr.orig_len = msg->len + sizeof(struct pcap_lapdhdr); header.pkttype = 4; header.hatype = 0; header.halen = 0; header.addr = direction == OSMO_LAPD_PCAP_OUTPUT ? 0x0 : 0x1; header.protocol = ntohs(48); gettimeofday(&tv, NULL); pcap_rechdr.ts_sec = tv.tv_sec; pcap_rechdr.ts_usec = tv.tv_usec; memcpy(buf + numbytes, &pcap_rechdr, sizeof(pcap_rechdr)); numbytes += sizeof(pcap_rechdr); memcpy(buf + numbytes, &header, sizeof(header)); numbytes += sizeof(header); memcpy(buf + numbytes, msg->data, msg->len); numbytes += msg->len; if (write(fd, buf, numbytes) != numbytes) { LOGP(DLLAPD, LOGL_ERROR, "cannot write packet to PCAP: %s\n", strerror(errno)); return -1; } return numbytes; } int osmo_pcap_lapd_close(int fd) { LOGP(DLLAPD, LOGL_NOTICE, "closing LAPD pcap file\n"); return close(fd); } libosmo-abis-0.3.2+git86fc3c8/src/input/misdn.c000066400000000000000000000442361261736062400211040ustar00rootroot00000000000000/* OpenBSC Abis input driver for mISDNuser */ /* (C) 2008-2011 by Harald Welte * (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /*! \file misdn.c * \brief Osmocom A-bis input driver for mISDN * * This driver has two modes of operations, exported via two different * \ref e1_input_driver structures: * "misdn" is the classic version and it uses the in-kernel LAPD * implementation. This is somewhat limited in e.g. the fact that * you can only have one E1 timeslot in signaling mode. * "misdn_lapd" is a newer version which uses userspace LAPD code * contained in libosmo-abis. It offers the same flexibilty as the * DAHDI driver, i.e. any number of signaling slots. */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include //#define AF_COMPATIBILITY_FUNC //#include #ifndef AF_ISDN #define AF_ISDN 34 #define PF_ISDN AF_ISDN #endif #include #include #include #include #include #include #include #define TS1_ALLOC_SIZE 300 /*! \brief driver-specific data for \ref e1inp_line::driver_data */ struct misdn_line { int use_userspace_lapd; int dummy_dchannel; }; const struct value_string prim_names[] = { { PH_CONTROL_IND, "PH_CONTROL_IND" }, { PH_DATA_IND, "PH_DATA_IND" }, { PH_DATA_CNF, "PH_DATA_CNF" }, { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" }, { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" }, { DL_RELEASE_IND, "DL_RELEASE_IND" }, { DL_RELEASE_CNF, "DL_RELEASE_CNF" }, { DL_DATA_IND, "DL_DATA_IND" }, { DL_UNITDATA_IND, "DL_UNITDATA_IND" }, { DL_INFORMATION_IND, "DL_INFORMATION_IND" }, { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" }, { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" }, { 0, NULL } }; static int handle_ts1_read(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; struct misdn_line *mline = line->driver_data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct e1inp_sign_link *link; struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1"); struct sockaddr_mISDN l2addr; struct mISDNhead *hh; socklen_t alen; int ret; if (!msg) return -ENOMEM; hh = (struct mISDNhead *) msg->data; alen = sizeof(l2addr); ret = recvfrom(bfd->fd, msg->data, 300, 0, (struct sockaddr *) &l2addr, &alen); if (ret < 0) { fprintf(stderr, "recvfrom error %s\n", strerror(errno)); msgb_free(msg); return ret; } if (alen != sizeof(l2addr)) { fprintf(stderr, "%s error len\n", __func__); msgb_free(msg); return -EINVAL; } msgb_put(msg, ret); DEBUGP(DLMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); DEBUGP(DLMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n", ret, hh->prim, hh->id, get_value_string(prim_names, hh->prim)); switch (hh->prim) { case DL_INFORMATION_IND: /* mISDN tells us which channel number is allocated for this * tuple of (SAPI, TEI). */ DEBUGP(DLMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n", l2addr.channel, l2addr.sapi, l2addr.tei); link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); if (!link) { DEBUGPC(DLMI, "mISDN message for unknown sign_link\n"); msgb_free(msg); return -EINVAL; } /* save the channel number in the driver private struct */ link->driver.misdn.channel = l2addr.channel; msgb_free(msg); break; case DL_ESTABLISH_IND: DEBUGP(DLMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n", l2addr.channel, l2addr.sapi, l2addr.tei); /* For some strange reason, sometimes the DL_INFORMATION_IND tells * us the wrong channel, and we only get the real channel number * during the DL_ESTABLISH_IND */ link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); if (!link) { DEBUGPC(DLMI, "mISDN message for unknown sign_link\n"); msgb_free(msg); return -EINVAL; } /* save the channel number in the driver private struct */ link->driver.misdn.channel = l2addr.channel; ret = e1inp_event(e1i_ts, S_L_INP_TEI_UP, l2addr.tei, l2addr.sapi); msgb_free(msg); break; case DL_RELEASE_IND: DEBUGP(DLMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n", l2addr.channel, l2addr.sapi, l2addr.tei); ret = e1inp_event(e1i_ts, S_L_INP_TEI_DN, l2addr.tei, l2addr.sapi); msgb_free(msg); break; case DL_DATA_IND: case DL_UNITDATA_IND: msg->l2h = msg->data + MISDN_HEADER_LEN; DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); if (mline->use_userspace_lapd) { LOGP(DLMI, LOGL_ERROR, "DL_DATA_IND but userspace LAPD ?!?\n"); msgb_free(msg); return -EIO; } ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi); break; case PH_ACTIVATE_IND: DEBUGP(DLMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", l2addr.channel, l2addr.sapi, l2addr.tei); msgb_free(msg); break; case PH_DEACTIVATE_IND: DEBUGP(DLMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", l2addr.channel, l2addr.sapi, l2addr.tei); msgb_free(msg); break; case PH_DATA_IND: if (!mline->use_userspace_lapd) { LOGP(DLMI, LOGL_ERROR, "PH_DATA_IND but kernel LAPD ?!?\n"); return -EIO; } /* remove the Misdn Header */ msgb_pull(msg, MISDN_HEADER_LEN); /* hand into the LAPD code */ DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msg->data, msg->len)); ret = e1inp_rx_ts_lapd(e1i_ts, msg); break; default: msgb_free(msg); break; } return ret; } static int ts_want_write(struct e1inp_ts *e1i_ts) { /* We never include the mISDN B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ if (e1i_ts->type == E1INP_TS_TYPE_TRAU) return 0; e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; return 0; } static void timeout_ts1_write(void *data) { struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; /* trigger write of ts1, due to tx delay timer */ ts_want_write(e1i_ts); } static int handle_ts1_write(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; struct misdn_line *mline = line->driver_data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct e1inp_sign_link *sign_link; struct sockaddr_mISDN sa; struct msgb *msg; struct mISDNhead *hh; uint8_t *l2_data; int ret; bfd->when &= ~BSC_FD_WRITE; /* get the next msg for this timeslot */ msg = e1inp_tx_ts(e1i_ts, &sign_link); if (!msg) { /* no message after tx delay timer */ return 0; } if (mline->use_userspace_lapd) { DEBUGP(DLMI, "TX %u/%u/%u: %s\n", line->num, sign_link->tei, sign_link->sapi, osmo_hexdump(msg->data, msg->len)); lapd_transmit(e1i_ts->lapd, sign_link->tei, sign_link->sapi, msg); ret = 0; } else { l2_data = msg->data; /* prepend the mISDNhead */ hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh)); hh->prim = DL_DATA_REQ; DEBUGP(DLMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n", sign_link->driver.misdn.channel, sign_link->tei, sign_link->sapi, osmo_hexdump(l2_data, msg->len - MISDN_HEADER_LEN)); /* construct the sockaddr */ sa.family = AF_ISDN; sa.sapi = sign_link->sapi; sa.dev = sign_link->tei; sa.channel = sign_link->driver.misdn.channel; ret = sendto(bfd->fd, msg->data, msg->len, 0, (struct sockaddr *)&sa, sizeof(sa)); if (ret < 0) fprintf(stderr, "%s sendto failed %d\n", __func__, ret); msgb_free(msg); } /* set tx delay timer for next event */ e1i_ts->sign.tx_timer.cb = timeout_ts1_write; e1i_ts->sign.tx_timer.data = e1i_ts; osmo_timer_schedule(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); return ret; } /*! \brief call-back from LAPD code, called when it wants to Tx data */ static void misdn_write_msg(struct msgb *msg, void *cbdata) { struct osmo_fd *bfd = cbdata; // struct e1inp_line *line = bfd->data; // unsigned int ts_nr = bfd->priv_nr; // struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct mISDNhead *hh; int ret; DEBUGP(DLMI, "PH_DATA_REQ: len=%d %s\n", msg->len, osmo_hexdump(msg->data, msg->len)); hh = (struct mISDNhead *) msgb_push(msg, MISDN_HEADER_LEN); hh->prim = PH_DATA_REQ; hh->id = 0; ret = write(bfd->fd, msg->data, msg->len); if (ret < 0) LOGP(DLMI, LOGL_NOTICE, "write failed %d\n", ret); msgb_free(msg); } /* write to a B channel TS */ static int handle_tsX_write(struct osmo_fd *bfd, int len) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct mISDNhead *hh; uint8_t tx_buf[len + sizeof(*hh)]; struct subch_mux *mx = &e1i_ts->trau.mux; int ret; hh = (struct mISDNhead *) tx_buf; hh->prim = PH_DATA_REQ; hh->id = 0; subchan_mux_out(mx, tx_buf+sizeof(*hh), len); DEBUGP(DLMIB, "BCHAN TX: %s\n", osmo_hexdump(tx_buf+sizeof(*hh), len)); ret = send(bfd->fd, tx_buf, sizeof(*hh) + len, 0); if (ret < sizeof(*hh) + len) DEBUGP(DLMIB, "send returns %d instead of %zu\n", ret, sizeof(*hh) + len); return ret; } #define TSX_ALLOC_SIZE 4096 /* FIXME: read from a B channel TS */ static int handle_tsX_read(struct osmo_fd *bfd) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "mISDN TSx"); struct mISDNhead *hh; int ret; if (!msg) return -ENOMEM; hh = (struct mISDNhead *) msg->data; ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0); if (ret < 0) { fprintf(stderr, "recvfrom error %s\n", strerror(errno)); return ret; } msgb_put(msg, ret); if (hh->prim != PH_CONTROL_IND) DEBUGP(DLMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n", ret, hh->prim, hh->id, get_value_string(prim_names, hh->prim)); switch (hh->prim) { case PH_DATA_IND: msg->l2h = msg->data + MISDN_HEADER_LEN; DEBUGP(DLMIB, "BCHAN RX: %s\n", osmo_hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); /* the number of bytes received indicates that data to send */ handle_tsX_write(bfd, msgb_l2len(msg)); return e1inp_rx_ts(e1i_ts, msg, 0, 0); case PH_ACTIVATE_IND: case PH_DATA_CNF: break; default: break; } /* FIXME: why do we free signalling msgs in the caller, and trau not? */ msgb_free(msg); return ret; } /* callback from select.c in case one of the fd's can be read/written */ static int misdn_fd_cb(struct osmo_fd *bfd, unsigned int what) { struct e1inp_line *line = bfd->data; unsigned int ts_nr = bfd->priv_nr; unsigned int idx = ts_nr-1; struct e1inp_ts *e1i_ts = &line->ts[idx]; int rc = 0; switch (e1i_ts->type) { case E1INP_TS_TYPE_SIGN: if (what & BSC_FD_READ) rc = handle_ts1_read(bfd); if (what & BSC_FD_WRITE) rc = handle_ts1_write(bfd); break; case E1INP_TS_TYPE_TRAU: if (what & BSC_FD_READ) rc = handle_tsX_read(bfd); /* We never include the mISDN B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ break; default: fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); break; } return rc; } static int activate_bchan(struct e1inp_line *line, int ts, int act) { struct mISDNhead hh; int ret; unsigned int idx = ts-1; struct e1inp_ts *e1i_ts = &line->ts[idx]; struct osmo_fd *bfd = &e1i_ts->driver.misdn.fd; fprintf(stdout, "activate bchan\n"); if (act) hh.prim = PH_ACTIVATE_REQ; else hh.prim = PH_DEACTIVATE_REQ; hh.id = MISDN_ID_ANY; ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0); if (ret < 0) { fprintf(stdout, "could not send ACTIVATE_RQ %s\n", strerror(errno)); } return ret; } static int mi_e1_line_update(struct e1inp_line *line); static int mi_e1_line_update_lapd(struct e1inp_line *line); struct e1inp_driver misdn_driver = { .name = "misdn", .want_write = ts_want_write, .default_delay = 50000, .line_update = &mi_e1_line_update, }; struct e1inp_driver misdn_lapd_driver = { .name = "misdn_lapd", .want_write = ts_want_write, .default_delay = 50000, .line_update = &mi_e1_line_update_lapd, }; static int mi_e1_setup(struct e1inp_line *line, int release_l2) { struct misdn_line *mline = line->driver_data; int ts, ret; mline->dummy_dchannel = -1; if (mline->use_userspace_lapd) { /* Open dummy d-channel in order to use b-channels. * Also it is required to define the mode. */ if (mline->dummy_dchannel < 0) { struct sockaddr_mISDN addr; mline->dummy_dchannel = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_NT_E1); if (mline->dummy_dchannel < 0) { fprintf(stderr, "%s could not open socket %s\n", __func__, strerror(errno)); return mline->dummy_dchannel; } memset(&addr, 0, sizeof(addr)); addr.family = AF_ISDN; addr.dev = line->port_nr; addr.channel = 0; addr.sapi = 0; addr.tei = GROUP_TEI; ret = bind(mline->dummy_dchannel, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { fprintf(stderr, "could not bind l2 socket %s\n", strerror(errno)); return -EIO; } } } /* TS0 is CRC4, don't need any fd for it */ for (ts = 1; ts < NUM_E1_TS; ts++) { unsigned int idx = ts-1; struct e1inp_ts *e1i_ts = &line->ts[idx]; struct osmo_fd *bfd = &e1i_ts->driver.misdn.fd; struct sockaddr_mISDN addr; bfd->data = line; bfd->priv_nr = ts; bfd->cb = misdn_fd_cb; switch (e1i_ts->type) { case E1INP_TS_TYPE_NONE: continue; break; case E1INP_TS_TYPE_SIGN: if (mline->use_userspace_lapd) bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_HDLC); else bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT); bfd->when = BSC_FD_READ; break; case E1INP_TS_TYPE_TRAU: bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); /* We never include the mISDN B-Channel FD into the * writeset, since it doesn't support poll() based * write flow control */ bfd->when = BSC_FD_READ; break; } if (bfd->fd < 0) { fprintf(stderr, "%s could not open socket %s\n", __func__, strerror(errno)); return bfd->fd; } memset(&addr, 0, sizeof(addr)); addr.family = AF_ISDN; addr.dev = line->port_nr; switch (e1i_ts->type) { case E1INP_TS_TYPE_SIGN: if (mline->use_userspace_lapd) { addr.channel = ts; e1i_ts->lapd = lapd_instance_alloc(1, misdn_write_msg, bfd, e1inp_dlsap_up, e1i_ts, &lapd_profile_abis); } else { addr.channel = 0; /* SAPI not supported yet in kernel */ //addr.sapi = e1inp_ts->sign.sapi; addr.sapi = 0; addr.tei = GROUP_TEI; } break; case E1INP_TS_TYPE_TRAU: addr.channel = ts; break; default: DEBUGP(DLMI, "unsupported E1 TS type: %u\n", e1i_ts->type); break; } ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); if (ret < 0) { fprintf(stderr, "could not bind l2 socket %s\n", strerror(errno)); return -EIO; } if (e1i_ts->type == E1INP_TS_TYPE_SIGN) { if (!mline->use_userspace_lapd) { ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2); if (ret < 0) { fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno)); return -EIO; } } else activate_bchan(line, ts, 1); } /* FIXME: only activate B-Channels once we start to * use them to conserve CPU power */ if (e1i_ts->type == E1INP_TS_TYPE_TRAU) activate_bchan(line, ts, 1); ret = osmo_fd_register(bfd); if (ret < 0) { fprintf(stderr, "could not register FD: %s\n", strerror(ret)); return ret; } } return 0; } static int _mi_e1_line_update(struct e1inp_line *line) { struct mISDN_devinfo devinfo; int sk, ret, cnt; if (line->driver != &misdn_driver && line->driver != &misdn_lapd_driver) return -EINVAL; /* open the ISDN card device */ sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); if (sk < 0) { fprintf(stderr, "%s could not open socket %s\n", __func__, strerror(errno)); return sk; } ret = ioctl(sk, IMGETCOUNT, &cnt); if (ret) { fprintf(stderr, "%s error getting interf count: %s\n", __func__, strerror(errno)); close(sk); return -ENODEV; } //DEBUGP(DLMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); printf("%d device%s found\n", cnt, (cnt==1)?"":"s"); #if 1 devinfo.id = line->port_nr; ret = ioctl(sk, IMGETDEVINFO, &devinfo); if (ret < 0) { fprintf(stdout, "error getting info for device %d: %s\n", line->port_nr, strerror(errno)); close(sk); return -ENODEV; } fprintf(stdout, " id: %d\n", devinfo.id); fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); fprintf(stdout, " protocol: %d\n", devinfo.protocol); fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); fprintf(stdout, " name: %s\n", devinfo.name); #endif close(sk); if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) { fprintf(stderr, "error: card is not of type E1 (NT-mode)\n"); return -EINVAL; } ret = mi_e1_setup(line, 1); if (ret) return ret; return 0; } static int mi_e1_line_update(struct e1inp_line *line) { struct misdn_line *ml; if (!line->driver_data) line->driver_data = talloc_zero(line, struct misdn_line); ml = line->driver_data; ml->use_userspace_lapd = 0; return _mi_e1_line_update(line); } static int mi_e1_line_update_lapd(struct e1inp_line *line) { struct misdn_line *ml; if (!line->driver_data) line->driver_data = talloc_zero(line, struct misdn_line); ml = line->driver_data; ml->use_userspace_lapd = 1; return _mi_e1_line_update(line); } void e1inp_misdn_init(void) { /* register the driver with the core */ e1inp_driver_register(&misdn_driver); e1inp_driver_register(&misdn_lapd_driver); } libosmo-abis-0.3.2+git86fc3c8/src/input/rs232.c000066400000000000000000000157541261736062400206500ustar00rootroot00000000000000/* T-Link interface using POSIX serial port */ /* (C) 2008-2011 by Harald Welte * * All Rights Reserved * * Authors: Harald Welte * Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include static void *tall_rs232_ctx; struct serial_handle { struct e1inp_line *line; struct msgb *rx_msg; unsigned int rxmsg_bytes_missing; unsigned int delay_ms; }; #define CRAPD_HDR_LEN 10 static int handle_ser_write(struct osmo_fd *bfd); static void rs232_build_msg(struct msgb *msg) { uint8_t *crapd; unsigned int len; msg->l2h = msg->data; /* prepend CRAPD header */ crapd = msgb_push(msg, CRAPD_HDR_LEN); len = msg->len - 2; crapd[0] = (len >> 8) & 0xff; crapd[1] = len & 0xff; /* length of bytes startign at crapd[2] */ crapd[2] = 0x00; crapd[3] = 0x07; crapd[4] = 0x01; crapd[5] = 0x3e; crapd[6] = 0x00; crapd[7] = 0x00; crapd[8] = msg->len - 10; /* length of bytes starting at crapd[10] */ crapd[9] = crapd[8] ^ 0x38; } /* select.c callback in case we can write to the rs232 */ static int handle_ser_write(struct osmo_fd *bfd) { struct serial_handle *sh = bfd->data; struct e1inp_ts *e1i_ts = &sh->line->ts[0]; struct e1inp_sign_link *sign_link; struct msgb *msg; int written; bfd->when &= ~BSC_FD_WRITE; /* get the next msg for this timeslot */ msg = e1inp_tx_ts(e1i_ts, &sign_link); if (!msg) { /* no message after tx delay timer */ return 0; } DEBUGP(DLMI, "rs232 TX: %s\n", osmo_hexdump(msg->data, msg->len)); rs232_build_msg(msg); /* send over serial line */ written = write(bfd->fd, msg->data, msg->len); if (written < msg->len) { LOGP(DLMI, LOGL_ERROR, "rs232: short write\n"); msgb_free(msg); return -1; } msgb_free(msg); usleep(sh->delay_ms*1000); return 0; } #define SERIAL_ALLOC_SIZE 300 /* select.c callback in case we can read from the rs232 */ static int handle_ser_read(struct osmo_fd *bfd) { struct serial_handle *sh = bfd->data; struct msgb *msg; int rc = 0; if (!sh->rx_msg) { sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE, "rs232 Rx"); sh->rx_msg->l2h = NULL; } msg = sh->rx_msg; /* first read two byes to obtain length */ if (msg->len < 2) { rc = read(bfd->fd, msg->tail, 2 - msg->len); if (rc < 0) { LOGP(DLMI, LOGL_ERROR, "rs232: error reading from " "serial port: %s\n", strerror(errno)); msgb_free(msg); return rc; } msgb_put(msg, rc); if (msg->len >= 2) { /* parse CRAPD payload length */ if (msg->data[0] != 0) { LOGP(DLMI, LOGL_ERROR, "Suspicious header byte 0: 0x%02x\n", msg->data[0]); } sh->rxmsg_bytes_missing = msg->data[0] << 8; sh->rxmsg_bytes_missing += msg->data[1]; if (sh->rxmsg_bytes_missing < CRAPD_HDR_LEN -2) { LOGP(DLMI, LOGL_ERROR, "Invalid length in hdr: %u\n", sh->rxmsg_bytes_missing); } } } else { /* try to read as many of the missing bytes as are available */ rc = read(bfd->fd, msg->tail, sh->rxmsg_bytes_missing); if (rc < 0) { LOGP(DLMI, LOGL_ERROR, "rs232: error reading from " "serial port: %s", strerror(errno)); msgb_free(msg); return rc; } msgb_put(msg, rc); sh->rxmsg_bytes_missing -= rc; if (sh->rxmsg_bytes_missing == 0) { struct e1inp_ts *e1i_ts = &sh->line->ts[0]; /* we have one complete message now */ sh->rx_msg = NULL; if (msg->len > CRAPD_HDR_LEN) msg->l2h = msg->data + CRAPD_HDR_LEN; DEBUGP(DLMI, "rs232 RX: %s", osmo_hexdump(msg->data, msg->len)); /* don't use e1inp_tx_ts() here, this header does not * contain any SAPI and TEI values. */ if (!e1i_ts->line->ops->sign_link) { LOGP(DLMI, LOGL_ERROR, "rs232: no callback set, " "skipping message.\n"); return -EINVAL; } e1i_ts->line->ops->sign_link(msg); } } return rc; } /* select.c callback */ static int serial_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_ser_read(bfd); if (rc < 0) return rc; if (what & BSC_FD_WRITE) rc = handle_ser_write(bfd); return rc; } static int rs232_want_write(struct e1inp_ts *e1i_ts) { e1i_ts->driver.rs232.fd.when |= BSC_FD_WRITE; return 0; } static int rs232_setup(struct e1inp_line *line, const char *serial_port, unsigned int delay_ms) { int rc; struct osmo_fd *bfd = &line->ts[0].driver.rs232.fd; struct serial_handle *ser_handle; struct termios tio; rc = open(serial_port, O_RDWR); if (rc < 0) { LOGP(DLMI, LOGL_ERROR, "rs232: cannot open serial port: %s", strerror(errno)); return rc; } bfd->fd = rc; /* set baudrate */ rc = tcgetattr(bfd->fd, &tio); if (rc < 0) { LOGP(DLMI, LOGL_ERROR, "rs232: tcgetattr says: %s", strerror(errno)); return rc; } cfsetispeed(&tio, B19200); cfsetospeed(&tio, B19200); tio.c_cflag |= (CREAD | CLOCAL | CS8); tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); 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); rc = tcsetattr(bfd->fd, TCSADRAIN, &tio); if (rc < 0) { LOGP(DLMI, LOGL_ERROR, "rs232: tcsetattr says: %s", strerror(errno)); return rc; } ser_handle = talloc_zero(tall_rs232_ctx, struct serial_handle); if (ser_handle == NULL) { close(bfd->fd); LOGP(DLMI, LOGL_ERROR, "rs232: cannot allocate memory for " "serial handler\n"); return -ENOMEM; } ser_handle->line = line; ser_handle->delay_ms = delay_ms; bfd->when = BSC_FD_READ; bfd->cb = serial_fd_cb; bfd->data = ser_handle; rc = osmo_fd_register(bfd); if (rc < 0) { close(bfd->fd); LOGP(DLMI, LOGL_ERROR, "rs232: could not register FD: %s\n", strerror(rc)); return rc; } return 0; } static int rs232_line_update(struct e1inp_line *line); static struct e1inp_driver rs232_driver = { .name = "rs232", .want_write = rs232_want_write, .line_update = rs232_line_update, }; static int rs232_line_update(struct e1inp_line *line) { if (line->driver != &rs232_driver) return -EINVAL; return rs232_setup(line, line->ops->cfg.rs232.port, line->ops->cfg.rs232.delay); } int e1inp_rs232_init(void) { return e1inp_driver_register(&rs232_driver); } libosmo-abis-0.3.2+git86fc3c8/src/ipa_proxy.c000066400000000000000000000427721261736062400206500ustar00rootroot00000000000000#include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void *tall_ipa_proxy_ctx; /* * data structures used by the IPA VTY commands */ static LLIST_HEAD(ipa_instance_list); enum ipa_proxy_instance_net_type { IPA_INSTANCE_T_NONE, IPA_INSTANCE_T_BIND, IPA_INSTANCE_T_CONNECT, IPA_INSTANCE_T_MAX }; struct ipa_proxy_instance_net { char *addr; uint16_t port; enum ipa_proxy_instance_net_type type; }; struct ipa_proxy_instance { struct llist_head head; #define IPA_INSTANCE_NAME 16 char name[IPA_INSTANCE_NAME]; struct ipa_proxy_instance_net net; int refcnt; }; static LLIST_HEAD(ipa_proxy_route_list); /* Several routes pointing to the same instances share this. */ struct ipa_proxy_route_shared { int refcnt; /* this file descriptor is used to accept() new connections. */ struct osmo_fd bfd; struct { struct ipa_proxy_instance *inst; struct bitvec streamid_map; uint8_t streamid_map_data[(0xff+1)/8]; uint8_t streamid[0xff + 1]; } src; struct { struct ipa_proxy_instance *inst; struct bitvec streamid_map; uint8_t streamid_map_data[(0xff+1)/8]; uint8_t streamid[0xff + 1]; } dst; struct llist_head conn_list; }; /* One route is composed of two instances. */ struct ipa_proxy_route { struct llist_head head; struct { uint8_t streamid; } src; struct { uint8_t streamid; } dst; struct ipa_proxy_route_shared *shared; }; enum ipa_conn_state { IPA_CONN_S_NONE, IPA_CONN_S_CONNECTING, IPA_CONN_S_CONNECTED, IPA_CONN_S_MAX }; /* One route may forward more than one connection. */ struct ipa_proxy_conn { struct llist_head head; struct ipa_server_conn *src; struct ipa_client_conn *dst; struct ipa_proxy_route *route; }; /* * socket callbacks used by IPA VTY commands */ static int ipa_sock_dst_cb(struct ipa_client_conn *link, struct msgb *msg) { struct ipaccess_head *hh; struct ipa_proxy_conn *conn = link->data; LOGP(DLINP, LOGL_NOTICE, "received message from client side\n"); hh = (struct ipaccess_head *)msg->data; /* check if we have a route for this message. */ if (bitvec_get_bit_pos( &conn->route->shared->dst.streamid_map, hh->proto) != ONE) { LOGP(DLINP, LOGL_NOTICE, "we don't have a " "route for streamid 0x%x\n", hh->proto); msgb_free(msg); return 0; } /* mangle message, if required. */ hh->proto = conn->route->shared->src.streamid[hh->proto]; ipa_server_conn_send(conn->src, msg); return 0; } static int ipa_sock_src_cb(struct ipa_server_conn *peer, struct msgb *msg) { struct ipaccess_head *hh; struct ipa_proxy_conn *conn = peer->data; LOGP(DLINP, LOGL_NOTICE, "received message from server side\n"); hh = (struct ipaccess_head *)msg->data; /* check if we have a route for this message. */ if (bitvec_get_bit_pos(&conn->route->shared->src.streamid_map, hh->proto) != ONE) { LOGP(DLINP, LOGL_NOTICE, "we don't have a " "route for streamid 0x%x\n", hh->proto); msgb_free(msg); return 0; } /* mangle message, if required. */ hh->proto = conn->route->shared->dst.streamid[hh->proto]; ipa_client_conn_send(conn->dst, msg); return 0; } static int ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd) { struct ipa_proxy_route *route = link->data; struct ipa_proxy_conn *conn; conn = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_conn); if (conn == NULL) { LOGP(DLINP, LOGL_ERROR, "cannot allocate memory for " "origin IPA\n"); close(fd); return -ENOMEM; } conn->route = route; conn->src = ipa_server_conn_create(tall_ipa_proxy_ctx, link, fd, ipa_sock_src_cb, NULL, conn); if (conn->src == NULL) { LOGP(DLINP, LOGL_ERROR, "could not create server peer: %s\n", strerror(errno)); return -ENOMEM; } LOGP(DLINP, LOGL_NOTICE, "now trying to connect to destination\n"); conn->dst = ipa_client_conn_create(NULL, NULL, 0, route->shared->dst.inst->net.addr, route->shared->dst.inst->net.port, NULL, ipa_sock_dst_cb, NULL, conn); if (conn->dst == NULL) { LOGP(DLINP, LOGL_ERROR, "could not create client: %s\n", strerror(errno)); return -ENOMEM; } if (ipa_client_conn_open(conn->dst) < 0) { LOGP(DLINP, LOGL_ERROR, "could not start client: %s\n", strerror(errno)); return -ENOMEM; } llist_add(&conn->head, &route->shared->conn_list); return 0; } /* * VTY commands for IPA */ static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[]) { struct ipa_proxy_instance *ipi; enum ipa_proxy_instance_net_type type; struct in_addr addr; uint16_t port; if (argc < 4) return CMD_ERR_INCOMPLETE; llist_for_each_entry(ipi, &ipa_instance_list, head) { if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0) continue; vty_out(vty, "%% instance `%s' already exists%s", ipi->name, VTY_NEWLINE); return CMD_WARNING; } if (strncmp(argv[1], "bind", IPA_INSTANCE_NAME) == 0) type = IPA_INSTANCE_T_BIND; else if (strncmp(argv[1], "connect", IPA_INSTANCE_NAME) == 0) type = IPA_INSTANCE_T_CONNECT; else return CMD_ERR_INCOMPLETE; if (inet_aton(argv[2], &addr) < 0) { vty_out(vty, "%% invalid address %s%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } port = atoi(argv[3]); ipi = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_instance); if (ipi == NULL) { vty_out(vty, "%% can't allocate memory for new instance%s", VTY_NEWLINE); return CMD_WARNING; } strncpy(ipi->name, argv[0], IPA_INSTANCE_NAME); ipi->name[IPA_INSTANCE_NAME - 1] = '\0'; ipi->net.type = type; ipi->net.addr = talloc_strdup(tall_ipa_proxy_ctx, argv[2]); ipi->net.port = port; llist_add_tail(&ipi->head, &ipa_instance_list); return CMD_SUCCESS; } DEFUN(ipa_instance_add, ipa_instance_add_cmd, "ipa instance NAME (bind|connect) IP tcp port PORT", "Bind or connect instance to address and port") { return __ipa_instance_add(vty, argc, argv); } DEFUN(ipa_instance_del, ipa_instance_del_cmd, "no ipa instance NAME", "Delete instance to address and port") { struct ipa_proxy_instance *ipi; if (argc < 1) return CMD_ERR_INCOMPLETE; llist_for_each_entry(ipi, &ipa_instance_list, head) { if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0) continue; if (ipi->refcnt > 0) { vty_out(vty, "%% instance `%s' is in use%s", ipi->name, VTY_NEWLINE); return CMD_WARNING; } llist_del(&ipi->head); talloc_free(ipi); return CMD_SUCCESS; } vty_out(vty, "%% instance `%s' does not exist%s", ipi->name, VTY_NEWLINE); return CMD_WARNING; } DEFUN(ipa_instance_show, ipa_instance_show_cmd, "ipa instance show", "Show existing ipaccess proxy instances") { struct ipa_proxy_instance *this; llist_for_each_entry(this, &ipa_instance_list, head) { vty_out(vty, "instance %s %s %s tcp port %u%s", this->name, this->net.addr, this->net.type == IPA_INSTANCE_T_BIND ? "bind" : "connect", this->net.port, VTY_NEWLINE); } return CMD_SUCCESS; } static int __ipa_route_add(struct vty *vty, int argc, const char *argv[]) { struct ipa_proxy_instance *ipi = vty->index; struct ipa_proxy_instance *src = NULL, *dst = NULL; uint32_t src_streamid, dst_streamid; struct ipa_proxy_route *route, *matching_route = NULL; struct ipa_proxy_route_shared *shared = NULL; int ret; if (argc < 4) return CMD_ERR_INCOMPLETE; llist_for_each_entry(ipi, &ipa_instance_list, head) { if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) { src = ipi; continue; } if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) { dst = ipi; continue; } } if (src == NULL) { vty_out(vty, "%% instance `%s' does not exists%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (dst == NULL) { vty_out(vty, "%% instance `%s' does not exists%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } if (src->net.type != IPA_INSTANCE_T_BIND) { vty_out(vty, "%% instance `%s' is not of bind type%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (dst->net.type != IPA_INSTANCE_T_CONNECT) { vty_out(vty, "%% instance `%s' is not of connect type%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } src_streamid = strtoul(argv[1], NULL, 16); if (src_streamid > 0xff) { vty_out(vty, "%% source streamid must be " ">= 0x00 and <= 0xff%s", VTY_NEWLINE); return CMD_WARNING; } dst_streamid = strtoul(argv[3], NULL, 16); if (dst_streamid > 0xff) { vty_out(vty, "%% destination streamid must be " ">= 0x00 and <= 0xff%s", VTY_NEWLINE); return CMD_WARNING; } llist_for_each_entry(route, &ipa_proxy_route_list, head) { if (route->shared->src.inst == src && route->shared->dst.inst == dst) { if (route->src.streamid == src_streamid && route->dst.streamid == dst_streamid) { vty_out(vty, "%% this route already exists%s", VTY_NEWLINE); return CMD_WARNING; } matching_route = route; break; } } /* new route for this configuration. */ route = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route); if (route == NULL) { vty_out(vty, "%% can't allocate memory for new route%s", VTY_NEWLINE); return CMD_WARNING; } route->src.streamid = src_streamid; route->dst.streamid = dst_streamid; if (matching_route != NULL) { /* there's already a master route for these configuration. */ if (matching_route->shared->src.inst != src) { vty_out(vty, "%% route does not contain " "source instance `%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (matching_route->shared->dst.inst != dst) { vty_out(vty, "%% route does not contain " "destination instance `%s'%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } /* use already existing shared routing information. */ shared = matching_route->shared; } else { struct ipa_server_link *link; /* this is a brand new route, allocate shared routing info. */ shared = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route_shared); if (shared == NULL) { vty_out(vty, "%% can't allocate memory for " "new route shared%s", VTY_NEWLINE); return CMD_WARNING; } shared->src.streamid_map.data_len = sizeof(shared->src.streamid_map_data); shared->src.streamid_map.data = shared->src.streamid_map_data; shared->dst.streamid_map.data_len = sizeof(shared->dst.streamid_map_data); shared->dst.streamid_map.data = shared->dst.streamid_map_data; link = ipa_server_link_create(tall_ipa_proxy_ctx, NULL, "0.0.0.0", src->net.port, ipa_sock_src_accept_cb, route); if (link == NULL) { vty_out(vty, "%% can't bind instance `%s' to port%s", src->name, VTY_NEWLINE); return CMD_WARNING; } if (ipa_server_link_open(link) < 0) { vty_out(vty, "%% can't bind instance `%s' to port%s", src->name, VTY_NEWLINE); return CMD_WARNING; } INIT_LLIST_HEAD(&shared->conn_list); } route->shared = shared; src->refcnt++; route->shared->src.inst = src; dst->refcnt++; route->shared->dst.inst = dst; shared->src.streamid[src_streamid] = dst_streamid; shared->dst.streamid[dst_streamid] = src_streamid; ret = bitvec_set_bit_pos(&shared->src.streamid_map, src_streamid, ONE); if (ret < 0) { vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE); return CMD_WARNING; } ret = bitvec_set_bit_pos(&shared->dst.streamid_map, dst_streamid, ONE); if (ret < 0) { vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE); return CMD_WARNING; } shared->refcnt++; llist_add_tail(&route->head, &ipa_proxy_route_list); return CMD_SUCCESS; } DEFUN(ipa_route_add, ipa_route_add_cmd, "ipa route instance NAME streamid HEXNUM " "instance NAME streamid HEXNUM", "Add IPA route") { return __ipa_route_add(vty, argc, argv); } DEFUN(ipa_route_del, ipa_route_del_cmd, "no ipa route instance NAME streamid HEXNUM " "instance NAME streamid HEXNUM", "Delete IPA route") { struct ipa_proxy_instance *ipi = vty->index; struct ipa_proxy_instance *src = NULL, *dst = NULL; uint32_t src_streamid, dst_streamid; struct ipa_proxy_route *route, *matching_route = NULL; struct ipa_proxy_conn *conn, *tmp; if (argc < 4) return CMD_ERR_INCOMPLETE; llist_for_each_entry(ipi, &ipa_instance_list, head) { if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) { src = ipi; continue; } if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) { dst = ipi; continue; } } if (src == NULL) { vty_out(vty, "%% instance `%s' does not exists%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (dst == NULL) { vty_out(vty, "%% instance `%s' does not exists%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } if (src->net.type != IPA_INSTANCE_T_BIND) { vty_out(vty, "%% instance `%s' is not of bind type%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } if (dst->net.type != IPA_INSTANCE_T_CONNECT) { vty_out(vty, "%% instance `%s' is not of connect type%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } src_streamid = strtoul(argv[1], NULL, 16); if (src_streamid > 0xff) { vty_out(vty, "%% source streamid must be " ">= 0x00 and <= 0xff%s", VTY_NEWLINE); return CMD_WARNING; } dst_streamid = strtoul(argv[3], NULL, 16); if (dst_streamid > 0xff) { vty_out(vty, "%% destination streamid must be " ">= 0x00 and <= 0xff%s", VTY_NEWLINE); return CMD_WARNING; } llist_for_each_entry(route, &ipa_proxy_route_list, head) { if (route->shared->src.inst == src && route->shared->dst.inst == dst && route->src.streamid == src_streamid && route->dst.streamid == dst_streamid) { matching_route = route; break; } } if (matching_route == NULL) { vty_out(vty, "%% no route with that configuration%s", VTY_NEWLINE); return CMD_WARNING; } /* delete this route from list. */ llist_del(&matching_route->head); if (--matching_route->shared->refcnt == 0) { /* nobody else using this route, release all resources. */ llist_for_each_entry_safe(conn, tmp, &matching_route->shared->conn_list, head) { ipa_server_conn_destroy(conn->src); llist_del(&conn->head); talloc_free(conn); } osmo_fd_unregister(&route->shared->bfd); close(route->shared->bfd.fd); route->shared->bfd.fd = -1; talloc_free(route->shared); } else { /* otherwise, revert the mapping that this route applies. */ bitvec_set_bit_pos(&matching_route->shared->src.streamid_map, src_streamid, ZERO); bitvec_set_bit_pos(&matching_route->shared->dst.streamid_map, dst_streamid, ZERO); matching_route->shared->src.streamid[src_streamid] = 0x00; matching_route->shared->dst.streamid[dst_streamid] = 0x00; } matching_route->shared->src.inst->refcnt--; matching_route->shared->dst.inst->refcnt--; talloc_free(matching_route); return CMD_SUCCESS; } DEFUN(ipa_route_show, ipa_route_show_cmd, "ipa route show", "Show existing ipaccess proxy routes") { struct ipa_proxy_route *this; llist_for_each_entry(this, &ipa_proxy_route_list, head) { vty_out(vty, "route instance %s streamid 0x%.2x " "instance %s streamid 0x%.2x%s", this->shared->src.inst->name, this->src.streamid, this->shared->dst.inst->name, this->dst.streamid, VTY_NEWLINE); } return CMD_SUCCESS; } /* * Config for ipaccess-proxy */ DEFUN(ipa_cfg, ipa_cfg_cmd, "ipa", "Configure the ipaccess proxy") { vty->index = NULL; vty->node = L_IPA_NODE; return CMD_SUCCESS; } /* all these below look like enable commands, but without the ipa prefix. */ DEFUN(ipa_route_cfg_add, ipa_route_cfg_add_cmd, "route instance NAME streamid HEXNUM " "instance NAME streamid HEXNUM", "Add IPA route") { return __ipa_route_add(vty, argc, argv); } DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd, "instance NAME (bind|connect) IP tcp port PORT", "Bind or connect instance to address and port") { return __ipa_instance_add(vty, argc, argv); } struct cmd_node ipa_node = { L_IPA_NODE, "%s(config-ipa)# ", 1, }; static int ipa_cfg_write(struct vty *vty) { bool heading = false; struct ipa_proxy_instance *inst; struct ipa_proxy_route *route; llist_for_each_entry(inst, &ipa_instance_list, head) { if (!heading) { vty_out(vty, "ipa%s", VTY_NEWLINE); heading = true; } vty_out(vty, " instance %s %s %s tcp port %u%s", inst->name, inst->net.type == IPA_INSTANCE_T_BIND ? "bind" : "connect", inst->net.addr, inst->net.port, VTY_NEWLINE); } llist_for_each_entry(route, &ipa_proxy_route_list, head) { vty_out(vty, " route instance %s streamid 0x%.2x " "instance %s streamid 0x%.2x%s", route->shared->src.inst->name, route->src.streamid, route->shared->dst.inst->name, route->dst.streamid, VTY_NEWLINE); } return CMD_SUCCESS; } void ipa_proxy_vty_init(void) { tall_ipa_proxy_ctx = talloc_named_const(libosmo_abis_ctx, 1, "ipa_proxy"); install_element(ENABLE_NODE, &ipa_instance_add_cmd); install_element(ENABLE_NODE, &ipa_instance_del_cmd); install_element(ENABLE_NODE, &ipa_instance_show_cmd); install_element(ENABLE_NODE, &ipa_route_add_cmd); install_element(ENABLE_NODE, &ipa_route_del_cmd); install_element(ENABLE_NODE, &ipa_route_show_cmd); install_element(CONFIG_NODE, &ipa_cfg_cmd); install_node(&ipa_node, ipa_cfg_write); vty_install_default(L_IPA_NODE); install_element(L_IPA_NODE, &ipa_instance_cfg_add_cmd); install_element(L_IPA_NODE, &ipa_route_cfg_add_cmd); } libosmo-abis-0.3.2+git86fc3c8/src/subchan_demux.c000066400000000000000000000214001261736062400214440ustar00rootroot00000000000000/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include #include #include #include #include #include #include #include /*! \addtogroup subchan_demux * @{ * * \file subchan_demux.c */ void *tall_tqe_ctx; static inline void append_bit(struct demux_subch *sch, uint8_t bit) { sch->out_bitbuf[sch->out_idx++] = bit; } #define SYNC_HDR_BITS 16 /* check if we have just completed the 16 bit zero + 1 bit one sync * header, in accordance with GSM TS 08.60 Chapter 4.8.1 */ static int sync_hdr_complete(struct demux_subch *sch, uint8_t bit) { if (bit == 0) sch->consecutive_zeros++; else { if (sch->consecutive_zeros >= SYNC_HDR_BITS) { sch->consecutive_zeros = 0; return 1; } sch->consecutive_zeros = 0; } return 0; } /* resynchronize to current location */ static void resync_to_here(struct demux_subch *sch) { memset(sch->out_bitbuf, 0, SYNC_HDR_BITS); sch->out_bitbuf[SYNC_HDR_BITS] = 1; /* set index in a way that we can continue receiving bits after * the end of the SYNC header */ sch->out_idx = SYNC_HDR_BITS + 1; sch->in_sync = 1; } /*! \brief initialize subchannel demuxer structure * \param[in,out] dmx subchannel demuxer */ int subch_demux_init(struct subch_demux *dmx) { int i; dmx->chan_activ = 0; for (i = 0; i < NR_SUBCH; i++) { struct demux_subch *sch = &dmx->subch[i]; sch->out_idx = 0; memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf)); } return 0; } /*! \brief Input some data from the 64k full-slot into subchannel demux * \param[in] dmx subchannel demuxer * \param[in] data pointer to buffer containing input data * \param[in] len length of \a data in bytes * \returns 0 in case of success, <0 otherwise. * * Input some arbitrary (modulo 4) number of bytes of a 64k E1 channel, * split it into the 16k subchannels */ int subch_demux_in(struct subch_demux *dmx, uint8_t *data, int len) { int i, c; for (i = 0; i < len; i++) { uint8_t inbyte = data[i]; for (c = 0; c < NR_SUBCH; c++) { struct demux_subch *sch = &dmx->subch[c]; uint8_t inbits; uint8_t bit; /* ignore inactive subchannels */ if (!(dmx->chan_activ & (1 << c))) continue; inbits = inbyte >> (c << 1); /* two bits for each subchannel */ if (inbits & 0x01) bit = 1; else bit = 0; append_bit(sch, bit); if (sync_hdr_complete(sch, bit)) resync_to_here(sch); if (inbits & 0x02) bit = 1; else bit = 0; append_bit(sch, bit); if (sync_hdr_complete(sch, bit)) resync_to_here(sch); /* FIXME: verify the first bit in octet 2, 4, 6, ... * according to TS 08.60 4.8.1 */ /* once we have reached TRAU_FRAME_BITS, call * the TRAU frame handler callback function */ if (sch->out_idx >= TRAU_FRAME_BITS) { if (sch->in_sync) { dmx->out_cb(dmx, c, sch->out_bitbuf, sch->out_idx, dmx->data); sch->in_sync = 0; } sch->out_idx = 0; } } } return i; } /*! \brief activate a given sub-channel * \param[in] dmx subchannel demuxer * \param[in] subch sub-channel number * \returns 0 in case of success, <0 otherwise * * Activating a sub-channel will casuse the \ref subch_demux::out_cb * callback to be called for any incoming data from the full slot */ int subch_demux_activate(struct subch_demux *dmx, int subch) { if (subch >= NR_SUBCH) return -EINVAL; dmx->chan_activ |= (1 << subch); return 0; } /*! \brief deactivate a given sub-channel * \param[in] dmx subchannel demuxer * \param[in] subch sub-channel number * \returns 0 in case of success, <0 otherwise * * Deactivating a sub-channel will casuse the \ref subch_demux::out_cb * callback no longer to be called. */ int subch_demux_deactivate(struct subch_demux *dmx, int subch) { if (subch >= NR_SUBCH) return -EINVAL; dmx->chan_activ &= ~(1 << subch); return 0; } /* MULTIPLEXER */ static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr) { /* allocate and initialize with idle pattern */ return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(), TRAU_FRAME_BITS); } /* return the requested number of bits from the specified subchannel */ static int get_subch_bits(struct subch_mux *mx, int subch, uint8_t *bits, int num_requested) { struct mux_subch *sch = &mx->subch[subch]; int num_bits = 0; while (num_bits < num_requested) { struct subch_txq_entry *txe; int num_bits_left; int num_bits_thistime; /* make sure we have a valid entry at top of tx queue. * if not, add an idle frame */ if (llist_empty(&sch->tx_queue)) alloc_add_idle_frame(mx, subch); if (llist_empty(&sch->tx_queue)) return -EIO; txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); num_bits_left = txe->bit_len - txe->next_bit; if (num_bits_left < num_requested) num_bits_thistime = num_bits_left; else num_bits_thistime = num_requested; /* pull the bits from the txe */ memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime); txe->next_bit += num_bits_thistime; /* free the tx_queue entry if it is fully consumed */ if (txe->next_bit >= txe->bit_len) { llist_del(&txe->list); talloc_free(txe); } /* increment global number of bits dequeued */ num_bits += num_bits_thistime; } return num_requested; } /* compact an array of 8 single-bit bytes into one byte of 8 bits */ static uint8_t compact_bits(const uint8_t *bits) { uint8_t ret = 0; int i; for (i = 0; i < 8; i++) ret |= (bits[i] ? 1 : 0) << i; return ret; } /* obtain a single output byte from the subchannel muxer */ static int mux_output_byte(struct subch_mux *mx, uint8_t *byte) { uint8_t bits[8]; int rc; /* combine two bits of every subchan */ rc = get_subch_bits(mx, 0, &bits[0], 2); rc = get_subch_bits(mx, 1, &bits[2], 2); rc = get_subch_bits(mx, 2, &bits[4], 2); rc = get_subch_bits(mx, 3, &bits[6], 2); *byte = compact_bits(bits); return rc; } /*! \brief Request the output of some muxed bytes from the subchan muxer * \param[in] mx subchannel muxer * \param[out] data caller-allocated buffer for data * \param[in] len number of bytes to be filled into \a data * \returns actual number of bytes filled into \a data */ int subchan_mux_out(struct subch_mux *mx, uint8_t *data, int len) { int i; for (i = 0; i < len; i++) { int rc; rc = mux_output_byte(mx, &data[i]); if (rc < 0) break; } return i; } static int llist_len(struct llist_head *head) { struct llist_head *entry; int i = 0; llist_for_each(entry, head) i++; return i; } /* evict the 'num_evict' number of oldest entries in the queue */ static void tx_queue_evict(struct mux_subch *sch, int num_evict) { struct subch_txq_entry *tqe; int i; for (i = 0; i < num_evict; i++) { if (llist_empty(&sch->tx_queue)) return; tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); llist_del(&tqe->list); talloc_free(tqe); } } /*! \brief enqueue some data into the tx_queue of a given subchannel * \param[in] mx subchannel muxer instance * \param[in] s_nr subchannel number * \param[in] data pointer to buffer with data * \param[in] len length of \a data * \returns 0 in case of success, <0 in case of error */ int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const uint8_t *data, int len) { struct mux_subch *sch = &mx->subch[s_nr]; int list_len = llist_len(&sch->tx_queue); struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx, sizeof(*tqe) + len); if (!tqe) return -ENOMEM; tqe->bit_len = len; memcpy(tqe->bits, data, len); if (list_len > 2) tx_queue_evict(sch, list_len-2); llist_add_tail(&tqe->list, &sch->tx_queue); return 0; } /*! \brief initialize one subchannel muxer instance * \param[in,out] mx subchannel muxer instance, caller-allocated * \returns 0 in success, < 0 in case of error */ int subchan_mux_init(struct subch_mux *mx) { int i; memset(mx, 0, sizeof(*mx)); for (i = 0; i < NR_SUBCH; i++) { struct mux_subch *sch = &mx->subch[i]; INIT_LLIST_HEAD(&sch->tx_queue); } return 0; } /* }@ */ libosmo-abis-0.3.2+git86fc3c8/src/trau/000077500000000000000000000000001261736062400174315ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/src/trau/osmo_ortp.c000066400000000000000000000355111261736062400216230ustar00rootroot00000000000000/* (C) 2011 by Harald Welte * (C) 2011 by On-Waves e.h.f * 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 osmo_ortp.c * \brief Integration of libortp into osmocom framework (select, logging) */ #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" static PayloadType *payload_type_efr; static PayloadType *payload_type_hr; static RtpProfile *osmo_pt_profile; static void *tall_rtp_ctx; /* malloc integration */ static void *osmo_ortp_malloc(size_t sz) { return talloc_size(tall_rtp_ctx, sz); } static void *osmo_ortp_realloc(void *ptr, size_t sz) { return talloc_realloc_size(tall_rtp_ctx, ptr, sz); } static void osmo_ortp_free(void *ptr) { talloc_free(ptr); } static OrtpMemoryFunctions osmo_ortp_memfn = { .malloc_fun = osmo_ortp_malloc, .realloc_fun = osmo_ortp_realloc, .free_fun = osmo_ortp_free }; /* logging */ struct level_map { OrtpLogLevel ortp; int osmo_level; }; static const struct level_map level_map[] = { { ORTP_DEBUG, LOGL_DEBUG }, { ORTP_MESSAGE, LOGL_INFO }, { ORTP_WARNING, LOGL_NOTICE }, { ORTP_ERROR, LOGL_ERROR }, { ORTP_FATAL, LOGL_FATAL }, }; static int ortp_to_osmo_lvl(OrtpLogLevel lev) { int i; for (i = 0; i < ARRAY_SIZE(level_map); i++) { if (level_map[i].ortp == lev) return level_map[i].osmo_level; } /* default */ return LOGL_ERROR; } static void my_ortp_logfn(OrtpLogLevel lev, const char *fmt, va_list args) { osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0, 0, fmt, args); } /* ORTP signal callbacks */ static void ortp_sig_cb_ssrc(RtpSession *rs, void *data) { int port = rtp_session_get_local_port(rs); #if 0 /* post 0.20.0 ORTP has this function */ uint32_t ssrc = rtp_session_get_recv_ssrc(rs); #else uint32_t ssrc = rs->rcv.ssrc; #endif LOGP(DLMIB, LOGL_INFO, "osmo-ortp(%d): ssrc_changed to 0x%08x\n", port, ssrc); } static void ortp_sig_cb_pt(RtpSession *rs, void *data) { int port = rtp_session_get_local_port(rs); int pt = rtp_session_get_recv_payload_type(rs); LOGP(DLMIB, LOGL_NOTICE, "osmo-ortp(%d): payload_type_changed to 0x%02x\n", port, pt); } static void ortp_sig_cb_net(RtpSession *rs, void *data) { int port = rtp_session_get_local_port(rs); LOGP(DLMIB, LOGL_ERROR, "osmo-ortp(%d): network_error\n", port); } static void ortp_sig_cb_ts(RtpSession *rs, void *data) { int port = rtp_session_get_local_port(rs); uint32_t ts = rtp_session_get_current_recv_ts(rs); LOGP(DLMIB, LOGL_NOTICE, "osmo-ortp(%d): timestamp_jump, new TS %d\n", port, ts); } /*! \brief poll the socket for incoming data * \param[in] rs the socket to be polled * \returns number of packets received + handed to the rx_cb */ int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs) { mblk_t *mblk; mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts); if (mblk) { rtp_get_payload(mblk, &mblk->b_rptr); /* hand into receiver */ if (rs->rx_cb) rs->rx_cb(rs, mblk->b_rptr, mblk->b_wptr - mblk->b_rptr); //rs->rx_user_ts += 160; freemsg(mblk); return 1; } else { LOGP(DLMIB, LOGL_INFO, "osmo_rtp_poll(%u): ERROR!\n", rs->rx_user_ts); return 0; } } /* Osmo FD callbacks */ static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what) { struct osmo_rtp_socket *rs = fd->data; mblk_t *mblk; if (what & BSC_FD_READ) { /* in polling mode, we don't want to be called here */ if (rs->flags & OSMO_RTP_F_POLL) { fd->when &= ~BSC_FD_READ; return 0; } mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts); if (mblk) { rtp_get_payload(mblk, &mblk->b_rptr); /* hand into receiver */ if (rs->rx_cb) rs->rx_cb(rs, mblk->b_rptr, mblk->b_wptr - mblk->b_rptr); freemsg(mblk); } else LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n", rs->rx_user_ts); rs->rx_user_ts += 160; } /* writing is not queued at the moment, so BSC_FD_WRITE * shouldn't occur */ return 0; } static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what) { struct osmo_rtp_socket *rs = fd->data; /* We probably don't need this at all, as * rtp_session_recvm_with_ts() will alway also poll the RTCP * file descriptor for new data */ return rtp_session_rtcp_recv(rs->sess); } static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs) { rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess); rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess); rs->rtp_bfd.when = rs->rtcp_bfd.when = BSC_FD_READ; rs->rtp_bfd.when = rs->rtcp_bfd.when = 0; rs->rtp_bfd.data = rs->rtcp_bfd.data = rs; rs->rtp_bfd.cb = osmo_rtp_fd_cb; rs->rtcp_bfd.cb = osmo_rtcp_fd_cb; osmo_fd_register(&rs->rtp_bfd); osmo_fd_register(&rs->rtcp_bfd); return 0; } static void create_payload_types() { PayloadType *pt; /* EFR */ pt = payload_type_new(); pt->type = PAYLOAD_AUDIO_PACKETIZED; pt->clock_rate = 8000; pt->mime_type = "EFR"; pt->normal_bitrate = 12200; pt->channels = 1; payload_type_efr = pt; /* HR */ pt = payload_type_new(); pt->type = PAYLOAD_AUDIO_PACKETIZED; pt->clock_rate = 8000; pt->mime_type = "HR"; pt->normal_bitrate = 6750; /* FIXME */ pt->channels = 1; payload_type_hr = pt; /* create a new RTP profile as clone of AV profile */ osmo_pt_profile = rtp_profile_clone(&av_profile); /* add the GSM specific payload types. They are all dynamically * assigned, but in the Osmocom GSM system we have allocated * them as follows: */ rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr); rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr); rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr); } /* public functions */ /*! \brief initialize Osmocom RTP code * \param[in] ctx default talloc context for library-internal allocations */ void osmo_rtp_init(void *ctx) { tall_rtp_ctx = ctx; ortp_set_memory_functions(&osmo_ortp_memfn); ortp_init(); ortp_set_log_level_mask(0xffff); ortp_set_log_handler(my_ortp_logfn); create_payload_types(); } int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs, enum osmo_rtp_param param, int val) { int rc = 0; switch (param) { case OSMO_RTP_P_JITBUF: rtp_session_enable_jitter_buffer(rs->sess, (val) ? TRUE : FALSE); if (val) rtp_session_set_jitter_compensation(rs->sess, val); break; #if 0 case OSMO_RTP_P_JIT_ADAP: rc = jitter_control_enable_adaptive(rs->sess, val); break; #endif default: return -EINVAL; } return rc; } /*! \brief Create a new RTP socket * \param[in] talloc_cxt talloc context for this allocation. NULL for * dafault context * \param[in] flags Flags like OSMO_RTP_F_POLL * \returns pointer to library-allocated \a struct osmo_rtp_socket */ struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags) { struct osmo_rtp_socket *rs; if (!talloc_ctx) talloc_ctx = tall_rtp_ctx; rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket); if (!rs) return NULL; rs->flags = flags; rs->sess = rtp_session_new(RTP_SESSION_SENDRECV); if (!rs->sess) { talloc_free(rs); return NULL; } rtp_session_set_data(rs->sess, rs); rtp_session_set_profile(rs->sess, osmo_pt_profile); rtp_session_set_jitter_compensation(rs->sess, 100); //jitter_control_enable_adaptive(rs->sess, 0); rtp_session_signal_connect(rs->sess, "ssrc_changed", (RtpCallback) ortp_sig_cb_ssrc, (unsigned long) rs); rtp_session_signal_connect(rs->sess, "payload_type_changed", (RtpCallback) ortp_sig_cb_pt, (unsigned long) rs); rtp_session_signal_connect(rs->sess, "network_error", (RtpCallback) ortp_sig_cb_net, (unsigned long) rs); rtp_session_signal_connect(rs->sess, "timestamp_jump", (RtpCallback) ortp_sig_cb_ts, (unsigned long) rs); /* initialize according to the RFC */ rtp_session_set_seq_number(rs->sess, random()); rs->tx_timestamp = random(); return rs; } /*! \brief bind a RTP socket to a local port * \param[in] rs OsmoRTP socket * \param[in] ip hostname/ip as string * \param[in] port UDP port number, -1 for random selection * \returns 0 on success, <0 on error */ int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port) { int rc; #if HAVE_ORTP_021 rc = rtp_session_set_local_addr(rs->sess, ip, port, port+1); #else rc = rtp_session_set_local_addr(rs->sess, ip, port); #endif if (rc < 0) return rc; rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess); rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess); return 0; } /*! \brief connect a OsmoRTP socket to a remote port * \param[in] rs OsmoRTP socket * \param[in] ip String representation of remote hostname or IP address * \param[in] port UDP port number to connect to * * If the OsmoRTP socket is not in POLL mode, this function will also * cause the RTP and RTCP file descriptors to be registred with the * libosmocore select() loop integration. * * \returns 0 on success, <0 in case of error */ int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port) { int rc; /* enable the use of connect() so later getsockname() will * actually return the IP address that was chosen for the local * sid of the connection */ rtp_session_set_connected_mode(rs->sess, 1); rc = rtp_session_set_remote_addr(rs->sess, ip, port); if (rc < 0) return rc; if (rs->flags & OSMO_RTP_F_POLL) return rc; else return osmo_rtp_socket_fdreg(rs); } /*! \brief Send one RTP frame via a RTP socket * \param[in] rs OsmoRTP socket * \param[in] payload pointer to buffer with RTP payload data * \param[in] payload_len length of \a payload in bytes * \param[in] duration duration in number of RTP clock ticks * \returns 0 on success, <0 in case of error. */ int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload, unsigned int payload_len, unsigned int duration) { mblk_t *mblk; int rc; mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE, payload, payload_len); if (!mblk) return -ENOMEM; rc = rtp_session_sendm_with_ts(rs->sess, mblk, rs->tx_timestamp); rs->tx_timestamp += duration; if (rc < 0) { /* no need to free() the mblk, as rtp_session_rtp_send() * unconditionally free()s the mblk even in case of * error */ return rc; } return rc; } /*! \brief Set the payload type of a RTP socket * \param[in] rs OsmoRTP socket * \param[in] payload_type RTP payload type * \returns 0 on success, < 0 otherwise */ int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type) { int rc; rc = rtp_session_set_payload_type(rs->sess, payload_type); //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000); return rc; } /*! \brief completely close the RTP socket and release all resources * \param[in] rs OsmoRTP socket to be released * \returns 0 on success */ int osmo_rtp_socket_free(struct osmo_rtp_socket *rs) { if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1) osmo_fd_unregister(&rs->rtp_bfd); if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1) osmo_fd_unregister(&rs->rtcp_bfd); if (rs->sess) { rtp_session_release_sockets(rs->sess); rtp_session_destroy(rs->sess); rs->sess = NULL; } talloc_free(rs); return 0; } /*! \brief obtain the locally bound IPv4 address and UDP port * \param[in] rs OsmoRTP socket * \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address * \oaram[out] port Pointer to caller-allocated int for UDP port number * \returns 0 on success, <0 on error, -EIO in case of IPv6 socket */ int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs, uint32_t *ip, int *port) { int rc; struct sockaddr_storage ss; struct sockaddr_in *sin = (struct sockaddr_in *) &ss; socklen_t alen = sizeof(ss); rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen); if (rc < 0) return rc; if (ss.ss_family != AF_INET) return -EIO; *ip = ntohl(sin->sin_addr.s_addr); *port = rtp_session_get_local_port(rs->sess); return 0; } /*! \brief obtain the locally bound address and port * \param[in] rs OsmoRTP socket * \param[out] addr caller-allocated char ** to which the string pointer for * the address is stored * \param[out] port caller-allocated int * to which the port number is * stored * \returns 0 on success, <0 in case of error */ int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs, const char **addr, int *port) { int rc; struct sockaddr_storage ss; socklen_t alen = sizeof(ss); static char hostbuf[256]; memset(hostbuf, 0, sizeof(hostbuf)); rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen); if (rc < 0) return rc; rc = getnameinfo((struct sockaddr *)&ss, alen, hostbuf, sizeof(hostbuf), NULL, 0, NI_NUMERICHOST); if (rc < 0) return rc; *port = rtp_session_get_local_port(rs->sess); *addr = hostbuf; return 0; } void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs, int subsys, int level, const char *pfx) { const rtp_stats_t *stats; stats = rtp_session_get_stats(rs->sess); if (!stats) return; LOGP(subsys, level, "%sRTP Tx(%"PRIu64" pkts, %"PRIu64" bytes) " "Rx(%"PRIu64" pkts, %"PRIu64" bytes, %"PRIu64" late, " "%"PRIu64" loss, %"PRIu64" qmax)\n", pfx, stats->packet_sent, stats->sent, stats->packet_recv, stats->hw_recv, stats->outoftime, stats->cum_packet_loss, stats->discarded); } void osmo_rtp_socket_stats(struct osmo_rtp_socket *rs, uint32_t *sent_packets, uint32_t *sent_octets, uint32_t *recv_packets, uint32_t *recv_octets, uint32_t *recv_lost, uint32_t *last_jitter) { const rtp_stats_t *stats; *sent_packets = *sent_octets = *recv_packets = *recv_octets = 0; *recv_lost = *last_jitter = 0; stats = rtp_session_get_stats(rs->sess); if (stats) { /* truncate from 64bit to 32bit here */ *sent_packets = stats->packet_sent; *sent_octets = stats->sent; *recv_packets = stats->packet_recv; *recv_octets = stats->recv; *recv_lost = stats->cum_packet_loss; } #if HAVE_ORTP_021 const jitter_stats_t *jitter; jitter = rtp_session_get_jitter_stats(rs->sess); if (jitter) *last_jitter = jitter->jitter; #endif } libosmo-abis-0.3.2+git86fc3c8/src/trau_frame.c000066400000000000000000000205651261736062400207570ustar00rootroot00000000000000/* TRAU frame handling according to GSM TS 08.60 */ /* (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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "internal.h" #include #include #include #include #include #include #include #include /*! \addtogroup trau_frame * @{ * * \file trau_frame.c */ static uint32_t get_bits(const uint8_t *bitbuf, int offset, int num) { int i; uint32_t ret = 0; for (i = offset; i < offset + num; i++) { ret = ret << 1; if (bitbuf[i]) ret |= 1; } return ret; } /* Decode according to 3.1.1 */ static void decode_fr(struct decoded_trau_frame *fr, const uint8_t *trau_bits) { int i; int d_idx = 0; /* C1 .. C15 */ memcpy(fr->c_bits+0, trau_bits+17, 15); /* C16 .. C21 */ memcpy(fr->c_bits+15, trau_bits+310, 6); /* T1 .. T4 */ memcpy(fr->t_bits+0, trau_bits+316, 4); /* D1 .. D255 */ for (i = 32; i < 304; i+= 16) { memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); d_idx += 15; } /* D256 .. D260 */ memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); } /* Decode according to 3.1.2 */ static void decode_amr(struct decoded_trau_frame *fr, const uint8_t *trau_bits) { int i; int d_idx = 0; /* C1 .. C15 */ memcpy(fr->c_bits+0, trau_bits+17, 15); /* C16 .. C25 */ memcpy(fr->c_bits+15, trau_bits+33, 10); /* T1 .. T4 */ memcpy(fr->t_bits+0, trau_bits+316, 4); /* D1 .. D5 */ memcpy(fr->d_bits, trau_bits+43, 5); /* D6 .. D245 */ for (i = 48; i < 304; i += 16) { memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); d_idx += 15; } /* D246 .. D256 */ memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); } static void decode_data(struct decoded_trau_frame *fr, const uint8_t *trau_bits) { /* C1 .. C15 */ memcpy(fr->c_bits+0, trau_bits+17, 15); /* octets 4 .. 39 */ memcpy(fr->d_bits, trau_bits+32, 288); } int decode_trau_frame(struct decoded_trau_frame *fr, const uint8_t *trau_bits) { uint8_t cbits5 = get_bits(trau_bits, 17, 5); switch (cbits5) { case TRAU_FT_FR_UP: case TRAU_FT_FR_DOWN: case TRAU_FT_IDLE_UP: case TRAU_FT_IDLE_DOWN: case TRAU_FT_EFR: decode_fr(fr, trau_bits); break; case TRAU_FT_AMR: decode_amr(fr, trau_bits); break; case TRAU_FT_DATA_UP: case TRAU_FT_DATA_DOWN: decode_data(fr, trau_bits); break; case TRAU_FT_OM_UP: case TRAU_FT_OM_DOWN: case TRAU_FT_D145_SYNC: case TRAU_FT_EDATA: LOGP(DLMUX, LOGL_NOTICE, "can't decode unimplemented TRAU " "Frame Type 0x%02x\n", cbits5); return -1; break; default: LOGP(DLMUX, LOGL_NOTICE, "can't decode unknown TRAU " "Frame Type 0x%02x\n", cbits5); return -1; break; } return 0; } const uint8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 }; const uint8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 }; const uint8_t ft_data_down_bits[] = { 1, 0, 1, 1, 0 }; /*! \brief modify an uplink TRAU frame so we can send it downlink * \param[in,out] fr the uplink TRAU frame that is to be converted * \returns 0 in case of success, < 0 in caes of error */ int trau_frame_up2down(struct decoded_trau_frame *fr) { uint8_t cbits5 = get_bits(fr->c_bits, 0, 5); switch (cbits5) { case TRAU_FT_FR_UP: memcpy(fr->c_bits, ft_fr_down_bits, 5); /* clear time alignment */ memset(fr->c_bits+5, 0, 6); /* FIXME: SP / BFI in case of DTx */ /* C12 .. C21 are spare and coded as '1' */ memset(fr->c_bits+11, 0x01, 10); break; case TRAU_FT_EFR: /* clear time alignment */ memset(fr->c_bits+5, 0, 6); /* set UFE appropriately */ fr->c_bits[11] = 1; /* C12 (UFE), good frame (TODO) */ /* C13 .. C15 are spare and coded as '1' */ memset(fr->c_bits+12, 0x01, 3); /* SP / BFI in case of DTx */ fr->c_bits[15] = 1; /* C16 (SP), no DTX (TODO) */ /* C17 .. C21 are spare and coded as '1' */ memset(fr->c_bits+16, 0x01, 5); break; case TRAU_FT_IDLE_UP: memcpy(fr->c_bits, ft_idle_down_bits, 5); /* clear time alignment */ memset(fr->c_bits+5, 0, 6); /* FIXME: SP / BFI in case of DTx */ /* C12 .. C21 are spare and coded as '1' */ memset(fr->c_bits+11, 0x01, 10); break; case TRAU_FT_DATA_UP: memcpy(fr->c_bits, ft_data_down_bits, 5); break; case TRAU_FT_FR_DOWN: case TRAU_FT_IDLE_DOWN: case TRAU_FT_OM_DOWN: case TRAU_FT_DATA_DOWN: /* we cannot convert a downlink to a downlink frame */ return -EINVAL; break; case TRAU_FT_AMR: case TRAU_FT_OM_UP: case TRAU_FT_D145_SYNC: case TRAU_FT_EDATA: LOGP(DLMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " "0x%02x\n", cbits5); return -1; break; default: LOGP(DLMUX, LOGL_NOTICE, "unknown TRAU Frame Type " "0x%02x\n", cbits5); return -1; break; } return 0; } static void encode_fr(uint8_t *trau_bits, const struct decoded_trau_frame *fr) { int i; int d_idx = 0; trau_bits[16] = 1; /* C1 .. C15 */ memcpy(trau_bits+17, fr->c_bits+0, 15); /* D1 .. D255 */ for (i = 32; i < 304; i+= 16) { trau_bits[i] = 1; memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15); d_idx += 15; } /* D256 .. D260 */ trau_bits[304] = 1; memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); /* C16 .. C21 */ memcpy(trau_bits+310, fr->c_bits+15, 6); /* FIXME: handle timing adjustment */ /* T1 .. T4 */ memcpy(trau_bits+316, fr->t_bits+0, 4); } static void encode_data(uint8_t *trau_bits, const struct decoded_trau_frame *fr) { trau_bits[16] = 1; /* C1 .. C15 */ memcpy(trau_bits+17, fr->c_bits+0, 15); /* octets 4 .. 39 */ memcpy(trau_bits+32, fr->d_bits, 288); } /*! \brief encode a TRAU frame from the decoded bits * \param[out] trau_bits output buffer, will contain encoded bits * \param[in] fr decoded trau frame structure * \returns 0 in case of success, < 0 in case of error */ int encode_trau_frame(uint8_t *trau_bits, const struct decoded_trau_frame *fr) { uint8_t cbits5 = get_bits(fr->c_bits, 0, 5); /* 16 bits of sync header */ memset(trau_bits, 0, 16); switch (cbits5) { case TRAU_FT_FR_UP: case TRAU_FT_FR_DOWN: case TRAU_FT_IDLE_UP: case TRAU_FT_IDLE_DOWN: case TRAU_FT_EFR: encode_fr(trau_bits, fr); break; case TRAU_FT_DATA_UP: case TRAU_FT_DATA_DOWN: encode_data(trau_bits, fr); break; case TRAU_FT_AMR: case TRAU_FT_OM_UP: case TRAU_FT_OM_DOWN: case TRAU_FT_D145_SYNC: case TRAU_FT_EDATA: LOGP(DLMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " "0x%02x\n", cbits5); return -1; break; default: LOGP(DLMUX, LOGL_NOTICE, "unknown TRAU Frame Type " "0x%02x\n", cbits5); return -1; break; } return 0; } static struct decoded_trau_frame fr_idle_frame = { .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */ .t_bits = { 1, 1, 1, 1 }, }; static uint8_t encoded_idle_frame[TRAU_FRAME_BITS]; static int dbits_initted = 0; /*! \brief return pointer to global buffer containing a TRAU idle frame */ uint8_t *trau_idle_frame(void) { /* only initialize during the first call */ if (!dbits_initted) { /* set all D-bits to 1 */ memset(&fr_idle_frame.d_bits, 0x01, 260); memset(&fr_idle_frame.c_bits, 0x01, 25); /* spare are set to 1 */ /* set Downlink Idle Speech Frame pattern */ fr_idle_frame.c_bits[0] = 0; /* C1 */ fr_idle_frame.c_bits[1] = 1; /* C2 */ fr_idle_frame.c_bits[2] = 1; /* C3 */ fr_idle_frame.c_bits[3] = 1; /* C4 */ fr_idle_frame.c_bits[4] = 0; /* C5 */ /* set no Time Alignment pattern */ fr_idle_frame.c_bits[5] = 0; /* C6 */ fr_idle_frame.c_bits[6] = 0; /* C7 */ fr_idle_frame.c_bits[7] = 0; /* C8 */ fr_idle_frame.c_bits[8] = 0; /* C9 */ fr_idle_frame.c_bits[9] = 0; /* C10 */ fr_idle_frame.c_bits[10] = 0; /* C11 */ /* already set to 1, but maybe we need to modify it in the future */ fr_idle_frame.c_bits[11] = 1; /* C12 (UFE), good frame */ fr_idle_frame.c_bits[15] = 1; /* C16 (SP), no DTX */ encode_fr(encoded_idle_frame, &fr_idle_frame); dbits_initted = 1; /* set it to 1 to not call it again */ } return encoded_idle_frame; } /* }@ */ libosmo-abis-0.3.2+git86fc3c8/tests/000077500000000000000000000000001261736062400170315ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/tests/Makefile.am000066400000000000000000000053621261736062400210730ustar00rootroot00000000000000AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS) check_PROGRAMS = e1inp_ipa_bsc_test \ e1inp_ipa_bts_test \ ipa_proxy_test \ subchan_demux/subchan_demux_test \ ipa_recv/ipa_recv_test e1inp_ipa_bsc_test_SOURCES = e1inp_ipa_bsc_test.c e1inp_ipa_bsc_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) e1inp_ipa_bts_test_SOURCES = e1inp_ipa_bts_test.c e1inp_ipa_bts_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) ipa_proxy_test_SOURCES = ipa_proxy_test.c ipa_proxy_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) subchan_demux_subchan_demux_test_SOURCES = subchan_demux/subchan_demux_test.c subchan_demux_subchan_demux_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) ipa_recv_ipa_recv_test_SOURCES = ipa_recv/ipa_recv_test.c ipa_recv_ipa_recv_test_LDADD = $(top_builddir)/src/libosmoabis.la \ $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) # boilerplate for the tests # 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) \ subchan_demux/subchan_demux_test.ok \ ipa_recv/ipa_recv_test.ok TESTSUITE = $(srcdir)/testsuite DISTCLEANFILES = atconfig 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 $@ libosmo-abis-0.3.2+git86fc3c8/tests/e1inp_ipa_bsc_test.c000066400000000000000000000144171261736062400227370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include static void *tall_test; static struct e1inp_sign_link *oml_sign_link, *rsl_sign_link; #define DBSCTEST 0 struct log_info_cat bsc_test_cat[] = { [DBSCTEST] = { .name = "DBSCTEST", .description = "BSC-mode test", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; static struct e1inp_sign_link * sign_link_up(void *dev, struct e1inp_line *line, enum e1inp_sign_type type) { struct e1inp_sign_link *sign_link = NULL; struct e1inp_line *oml_line; switch(type) { case E1INP_SIGN_OML: LOGP(DBSCTEST, LOGL_NOTICE, "OML link up request received.\n"); e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML - 1], line); sign_link = oml_sign_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], E1INP_SIGN_OML, NULL, 255, 0); break; case E1INP_SIGN_RSL: if (!oml_sign_link) { LOGP(DBSCTEST, LOGL_ERROR, "OML link not yet set, " "giving up\n"); return NULL; } LOGP(DBSCTEST, LOGL_NOTICE, "RSL link up request received.\n"); /* We have to use the same line that the OML link. */ oml_line = oml_sign_link->ts->line; e1inp_ts_config_sign(&oml_line->ts[E1INP_SIGN_RSL - 1], oml_line); sign_link = rsl_sign_link = e1inp_sign_link_create(&oml_line->ts[E1INP_SIGN_RSL - 1], E1INP_SIGN_RSL, NULL, 0, 0); break; default: break; } if (sign_link) LOGP(DBSCTEST, LOGL_NOTICE, "signal link has been set up.\n"); return sign_link; } static void sign_link_down(struct e1inp_line *line) { LOGP(DBSCTEST, LOGL_NOTICE, "signal link has been closed\n"); if (oml_sign_link) { e1inp_sign_link_destroy(oml_sign_link); oml_sign_link = NULL; } if (rsl_sign_link) { e1inp_sign_link_destroy(rsl_sign_link); rsl_sign_link = NULL; } } static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) { oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_ONLY; oh->sequence = 0; oh->length = len; } static void fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) { struct abis_om_fom_hdr *foh = (struct abis_om_fom_hdr *) oh->data; fill_om_hdr(oh, len+sizeof(*foh)); foh->msg_type = msg_type; foh->obj_class = obj_class; foh->obj_inst.bts_nr = bts_nr; foh->obj_inst.trx_nr = trx_nr; foh->obj_inst.ts_nr = ts_nr; } #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); } static int abis_nm_sw_act_req_ack(struct e1inp_sign_link *sign_link, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t *attr, int att_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); if (attr) { uint8_t *ptr = msgb_put(msg, att_len); memcpy(ptr, attr, att_len); } msg->dst = sign_link; return abis_sendmsg(msg); } static int abis_nm_rx_sw_act_req(struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); struct abis_om_fom_hdr *foh = msgb_l3(msg); int ret; ret = abis_nm_sw_act_req_ack(msg->dst, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, foh->data, oh->length-sizeof(*foh)); return ret; } static int abis_nm_rcvmsg_fom(struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); uint8_t mt = foh->msg_type; int ret = 0; switch (mt) { case NM_MT_SW_ACT_REQ: /* Software activate request from BTS. */ ret = abis_nm_rx_sw_act_req(msg); break; default: break; } return ret; } static int abis_nm_rcvmsg(struct msgb *msg) { int ret = 0; struct abis_om_hdr *oh = msgb_l2(msg); msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: ret = abis_nm_rcvmsg_fom(msg); break; default: LOGP(DBSCTEST, LOGL_ERROR, "unknown OML message\n"); break; } return ret; } static int sign_link(struct msgb *msg) { int ret = 0; struct e1inp_sign_link *link = msg->dst; switch(link->type) { case E1INP_SIGN_RSL: LOGP(DBSCTEST, LOGL_NOTICE, "RSL message received.\n"); break; case E1INP_SIGN_OML: LOGP(DBSCTEST, LOGL_NOTICE, "OML message received.\n"); ret = abis_nm_rcvmsg(msg); break; default: LOGP(DBSCTEST, LOGL_ERROR, "Unknown signallin message.\n"); break; } msgb_free(msg); return ret; } const struct log_info bsc_test_log_info = { .filter_fn = NULL, .cat = bsc_test_cat, .num_cat = ARRAY_SIZE(bsc_test_cat), }; static struct e1inp_line *line; static void sighandler(int foo) { e1inp_line_put(line); exit(EXIT_SUCCESS); } int main(void) { tall_test = talloc_named_const(NULL, 1, "e1inp_test"); libosmo_abis_init(tall_test); osmo_init_logging(&bsc_test_log_info); struct e1inp_line_ops ops = { .cfg = { .ipa = { .addr = "0.0.0.0", .role = E1INP_LINE_R_BSC, }, }, .sign_link_up = sign_link_up, .sign_link_down = sign_link_down, .sign_link = sign_link, }; if (signal(SIGINT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR) { perror("Cannot set sighandler"); exit(EXIT_FAILURE); } #define LINENR 0 line = e1inp_line_create(LINENR, "ipa"); if (line == NULL) { LOGP(DBSCTEST, LOGL_ERROR, "problem creating E1 line\n"); exit(EXIT_FAILURE); } e1inp_line_bind_ops(line, &ops); /* * Depending if this is a real or virtual E1 lines: * - real (ISDN): create signal link for OML and RSL before line up. * - vitual (INET): we create it in signal_link_up(...) callback. * * The signal link is created via e1inp_sign_link_create(...) * * See e1_reconfig_trx and e1_reconfig_bts in libbsc/e1_config.c, * it explains how this is done with ISDN. */ if (e1inp_line_update(line) < 0) { LOGP(DBSCTEST, LOGL_ERROR, "problem creating E1 line\n"); exit(EXIT_FAILURE); } LOGP(DBSCTEST, LOGL_NOTICE, "entering main loop\n"); while (1) { osmo_select_main(0); } return 0; } libosmo-abis-0.3.2+git86fc3c8/tests/e1inp_ipa_bts_test.c000066400000000000000000000171641261736062400227620ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include static void *tall_test; static struct e1inp_sign_link *oml_sign_link, *rsl_sign_link; #define DBTSTEST 0 struct log_info_cat bts_test_cat[] = { [DBTSTEST] = { .name = "DBTSTEST", .description = "BTS-mode test", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info bts_test_log_info = { .filter_fn = NULL, .cat = bts_test_cat, .num_cat = ARRAY_SIZE(bts_test_cat), }; enum bts_state_machine { BTS_TEST_OML_SIGN_LINK_DOWN = 0, BTS_TEST_OML_SIGN_LINK_UP, BTS_TEST_OML_WAIT_SW_ACT_ACK, }; static struct osmo_fd bts_eventfd; static int eventfds[2]; static enum bts_state_machine bts_state = BTS_TEST_OML_SIGN_LINK_DOWN; static struct e1inp_sign_link * sign_link_up(void *unit, struct e1inp_line *line, enum e1inp_sign_type type) { struct e1inp_sign_link *sign_link = NULL; switch(type) { case E1INP_SIGN_OML: LOGP(DBTSTEST, LOGL_NOTICE, "OML link up request received.\n"); e1inp_ts_config_sign(&line->ts[E1INP_SIGN_OML - 1], line); sign_link = oml_sign_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], E1INP_SIGN_OML, NULL, 255, 0); if (!oml_sign_link) { LOGP(DBTSTEST, LOGL_ERROR, "cannot create OML sign link\n"); } if (oml_sign_link) { unsigned int event_type = 0; /* tell GSM 12.21 that we're ready via our eventfd. */ if (write(eventfds[1], &event_type, sizeof(unsigned int)) < 0) { LOGP(DBTSTEST, LOGL_ERROR, "cannot write " "event fd.\n"); } /* Now we can send OML messages to the BSC. */ bts_state = BTS_TEST_OML_SIGN_LINK_UP; } e1inp_ipa_bts_rsl_connect_n(line, "127.0.0.1", IPA_TCP_PORT_RSL, 0); break; case E1INP_SIGN_RSL: LOGP(DBTSTEST, LOGL_NOTICE, "RSL link up request received.\n"); e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL - 1], line); sign_link = rsl_sign_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL - 1], E1INP_SIGN_RSL, NULL, 0, 0); if (!rsl_sign_link) { LOGP(DBTSTEST, LOGL_ERROR, "cannot create RSL sign link\n"); } break; default: return NULL; } if (sign_link) LOGP(DBTSTEST, LOGL_NOTICE, "signal link has been set up.\n"); return sign_link; } static void sign_link_down(struct e1inp_line *line) { LOGP(DBTSTEST, LOGL_NOTICE, "signal link has been closed\n"); e1inp_sign_link_destroy(oml_sign_link); e1inp_sign_link_destroy(rsl_sign_link); } static int abis_nm_rcvmsg_fom(struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); uint8_t mt = foh->msg_type; int ret = 0; switch (mt) { case NM_MT_SW_ACT_REQ_ACK: /* SW activate request ACK from BSC. */ LOGP(DBTSTEST, LOGL_NOTICE, "Receive SW Act Req ACK\n"); break; default: LOGP(DBTSTEST, LOGL_ERROR, "unknown OML message\n"); break; } return ret; } static int abis_nm_rcvmsg(struct msgb *msg) { int ret = 0; struct abis_om_hdr *oh = msgb_l2(msg); msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: ret = abis_nm_rcvmsg_fom(msg); break; default: LOGP(DBTSTEST, LOGL_ERROR, "unknown OML message\n"); break; } return ret; } static int sign_link(struct msgb *msg) { int ret = 0; struct e1inp_sign_link *link = msg->dst; switch(link->type) { case E1INP_SIGN_OML: LOGP(DBTSTEST, LOGL_NOTICE, "OML message received.\n"); ret = abis_nm_rcvmsg(msg); break; case E1INP_SIGN_RSL: LOGP(DBTSTEST, LOGL_NOTICE, "RSL message received.\n"); break; default: LOGP(DBTSTEST, LOGL_ERROR, "Unknown signalling message.\n"); break; } msgb_free(msg); return ret; } static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) { oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_ONLY; oh->sequence = 0; oh->length = len; } static void fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) { struct abis_om_fom_hdr *foh = (struct abis_om_fom_hdr *) oh->data; fill_om_hdr(oh, len+sizeof(*foh)); foh->msg_type = msg_type; foh->obj_class = obj_class; foh->obj_inst.bts_nr = bts_nr; foh->obj_inst.trx_nr = trx_nr; foh->obj_inst.ts_nr = ts_nr; } #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "BTS/test"); } static int abis_nm_sw_act_req(struct e1inp_sign_link *sign_link, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, uint8_t *attr, int att_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t msgtype = NM_MT_SW_ACT_REQ; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); if (attr) { uint8_t *ptr = msgb_put(msg, att_len); memcpy(ptr, attr, att_len); } msg->dst = sign_link; return abis_sendmsg(msg); } static int test_bts_gsm_12_21_cb(struct osmo_fd *ofd, unsigned int what) { int ret, event_type; struct ipaccess_unit *unit = ofd->data; if (read(eventfds[0], &event_type, sizeof(unsigned int)) < 0) { LOGP(DBTSTEST, LOGL_ERROR, "error receiving event\n"); return 0; } switch(bts_state) { case BTS_TEST_OML_SIGN_LINK_DOWN: /* Do nothing until OML link becomes ready. */ break; case BTS_TEST_OML_SIGN_LINK_UP: /* OML link is up, send SW ACT REQ. */ ret = abis_nm_sw_act_req(oml_sign_link, 0, unit->bts_id, unit->trx_id, 0, NULL, 0); if (ret < 0) { LOGP(DBTSTEST, LOGL_ERROR, "cannot send SW ACT REQ\n"); break; } bts_state = BTS_TEST_OML_WAIT_SW_ACT_ACK; break; case BTS_TEST_OML_WAIT_SW_ACT_ACK: /* ... things should continue after this. */ break; } return 0; } static struct e1inp_line *line; static void sighandler(int foo) { e1inp_line_put(line); exit(EXIT_SUCCESS); } int main(void) { struct ipaccess_unit bts_dev_info = { .site_id = 1801, .bts_id = 0, .trx_id = 0, .unit_name = "testBTS", .equipvers = "0.1", .swversion = "0.1", .mac_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, .location1 = "testBTS", .location2 = "testBTS", .serno = "", }; tall_test = talloc_named_const(NULL, 1, "e1inp_test"); libosmo_abis_init(tall_test); osmo_init_logging(&bts_test_log_info); struct e1inp_line_ops ops = { .cfg = { .ipa = { .role = E1INP_LINE_R_BTS, .addr = "127.0.0.1", .dev = &bts_dev_info, }, }, .sign_link_up = sign_link_up, .sign_link_down = sign_link_down, .sign_link = sign_link, }; #define LINENR 0 if (signal(SIGINT, sighandler) == SIG_ERR || signal(SIGTERM, sighandler) == SIG_ERR) { perror("Cannot set sighandler"); exit(EXIT_FAILURE); } line = e1inp_line_create(LINENR, "ipa"); if (line == NULL) { LOGP(DBTSTEST, LOGL_ERROR, "problem enabling E1 line\n"); exit(EXIT_FAILURE); } e1inp_line_bind_ops(line, &ops); if (e1inp_line_update(line) < 0) { LOGP(DBTSTEST, LOGL_ERROR, "problem enabling E1 line\n"); exit(EXIT_FAILURE); } LOGP(DBTSTEST, LOGL_NOTICE, "entering main loop\n"); if (pipe(eventfds) < 0) { LOGP(DBTSTEST, LOGL_ERROR, "cannot create pipe fds\n"); exit(EXIT_FAILURE); } bts_eventfd.fd = eventfds[0]; bts_eventfd.cb = test_bts_gsm_12_21_cb; bts_eventfd.when = BSC_FD_READ; bts_eventfd.data = &bts_dev_info; if (osmo_fd_register(&bts_eventfd) < 0) { LOGP(DBTSTEST, LOGL_ERROR, "could not register event fd\n"); exit(EXIT_FAILURE); } while (1) { osmo_select_main(0); } return 0; } libosmo-abis-0.3.2+git86fc3c8/tests/ipa_proxy_test.c000066400000000000000000000023251261736062400222500ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include static void *tall_test; #define DIPA_PROXY_TEST 0 struct log_info_cat ipa_proxy_test_cat[] = { [DIPA_PROXY_TEST] = { .name = "DLINP_IPA_PROXY_TEST", .description = "IPA proxy test", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info ipa_proxy_test_log_info = { .filter_fn = NULL, .cat = ipa_proxy_test_cat, .num_cat = ARRAY_SIZE(ipa_proxy_test_cat), }; static struct vty_app_info vty_info = { .name = "ipa-proxy-test", .version = "1.0", }; #define IPA_PROXY_TEST_TELNET_PORT 4260 int main(void) { tall_test = talloc_named_const(NULL, 1, "ipa proxy test"); libosmo_abis_init(tall_test); osmo_init_logging(&ipa_proxy_test_log_info); vty_init(&vty_info); ipa_proxy_vty_init(); telnet_init(tall_test, NULL, IPA_PROXY_TEST_TELNET_PORT); LOGP(DIPA_PROXY_TEST, LOGL_NOTICE, "entering main loop\n"); while (1) { osmo_select_main(0); } } libosmo-abis-0.3.2+git86fc3c8/tests/ipa_recv/000077500000000000000000000000001261736062400206215ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/tests/ipa_recv/ipa_recv_test.c000066400000000000000000000127071261736062400236230ustar00rootroot00000000000000/* IPA receive test */ /* * (C) 2014 by On-Waves * (C) 2014 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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *ipa_test_messages[] = { "Hello IPA", "A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz", "Hello again IPA", "", "Next is empty", NULL, "Bye", "Bye", }; static void append_ipa_message(struct msgb *msg, int proto, const char *text) { int len = 0; unsigned char *l2; if (text) len = strlen(text) + 1; msgb_put_u16(msg, len); msgb_put_u8(msg, proto); l2 = msgb_put(msg, len); if (text) strcpy((char *)l2, text); } static int receive_messages(int fd, struct msgb **pending_msg) { struct msgb *msg; char dummy; int rc; while (1) { if (recv(fd, &dummy, 1, MSG_PEEK) < 1) { rc = -EAGAIN; break; } msg = NULL; rc = ipa_msg_recv_buffered(fd, &msg, pending_msg); fprintf(stderr, "ipa_msg_recv_buffered: %d, msg %s NULL, " "pending_msg %s NULL\n", rc, msg ? "!=" : "==", !pending_msg ? "??" : *pending_msg ? "!=" : "=="); if (pending_msg && !!msg == !!*pending_msg) printf( "got msg %s NULL, pending_msg %s NULL, " "returned: %s\n", msg ? "!=" : "==", *pending_msg ? "!=" : "==", rc == 0 ? "EOF" : rc > 0 ? "OK" : strerror(-rc)); else if (!pending_msg && rc == -EAGAIN) printf( "got msg %s NULL, " "returned: %s\n", msg ? "!=" : "==", rc == 0 ? "EOF" : rc > 0 ? "OK" : strerror(-rc)); if (rc == 0) return 0; if (rc == -EAGAIN) break; if (rc < 0) { printf("ipa_msg_recv_buffered failed with: %s\n", strerror(-rc)); return rc; } printf("got IPA message, size=%d, proto=%d, text=\"%s\"\n", rc, msg->data[2], msg->l2h); msgb_free(msg); }; return rc; } static int slurp_data(int fd) { int rc; char buf[256]; int count = 0; do { rc = recv(fd, buf, sizeof(buf), 0); if (rc <= 0) break; count += rc; } while (1); return count; }; static void test_complete_recv(int do_not_assemble) { int sv[2]; struct msgb *msg_out = msgb_alloc(4096, "msg_out"); struct msgb *pending_msg = NULL; int rc, i; printf("Testing IPA recv with complete messages%s.\n", do_not_assemble ? "" : " with assembling enabled"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) err(1, "socketpair"); fcntl(sv[0], F_SETFL, O_NONBLOCK); for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) append_ipa_message(msg_out, 200, ipa_test_messages[i]); while (msg_out->len > 0) { rc = write(sv[1], msg_out->data, msg_out->len); if (rc == -1) err(1, "write"); msgb_pull(msg_out, rc); } for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) { rc = receive_messages(sv[0], do_not_assemble ? NULL : &pending_msg); if (pending_msg) printf("Unexpected partial message: size=%d\n", pending_msg->len); if (rc == 0) break; if (rc < 0 && rc != -EAGAIN) break; } rc = slurp_data(sv[0]); printf("done: unread %d, unsent %d\n", rc, msg_out->len); close(sv[1]); close(sv[0]); msgb_free(msg_out); msgb_free(pending_msg); } static void test_partial_recv(int do_not_assemble) { int sv[2]; struct msgb *msg_out = msgb_alloc(4096, "msg_out"); struct msgb *pending_msg = NULL; int rc, i; printf("Testing IPA recv with partitioned messages%s.\n", do_not_assemble ? "" : " with assembling enabled"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) err(1, "socketpair"); fcntl(sv[0], F_SETFL, O_NONBLOCK); for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) append_ipa_message(msg_out, 200, ipa_test_messages[i]); while (msg_out->len > 0) { int len = 5; if (len > msg_out->len) len = msg_out->len; if (write(sv[1], msg_out->data, len) == -1) err(1, "write"); msgb_pull(msg_out, len); if (msg_out->len == 0) shutdown(sv[1], SHUT_WR); rc = receive_messages(sv[0], do_not_assemble ? NULL : &pending_msg); if (rc == 0) break; if (rc < 0 && rc != -EAGAIN) break; } rc = slurp_data(sv[0]); printf("done: unread %d, unsent %d\n", rc, msg_out->len); close(sv[1]); close(sv[0]); msgb_free(msg_out); msgb_free(pending_msg); } static struct log_info info = {}; int main(int argc, char **argv) { osmo_init_logging(&info); log_set_all_filter(osmo_stderr_target, 1); log_set_log_level(osmo_stderr_target, LOGL_INFO); printf("Testing the IPA layer.\n"); /* run the tests */ test_complete_recv(1); test_partial_recv(1); test_complete_recv(0); test_partial_recv(0); printf("No crashes.\n"); return 0; } libosmo-abis-0.3.2+git86fc3c8/tests/ipa_recv/ipa_recv_test.ok000066400000000000000000000036371261736062400240140ustar00rootroot00000000000000Testing the IPA layer. Testing IPA recv with complete messages. got IPA message, size=10, proto=200, text="Hello IPA" got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz" got IPA message, size=16, proto=200, text="Hello again IPA" got IPA message, size=1, proto=200, text="" got IPA message, size=14, proto=200, text="Next is empty" got msg == NULL, returned: Resource temporarily unavailable got IPA message, size=4, proto=200, text="Bye" got IPA message, size=4, proto=200, text="Bye" done: unread 0, unsent 0 Testing IPA recv with partitioned messages. ipa_msg_recv_buffered failed with: Input/output error done: unread 0, unsent 154 Testing IPA recv with complete messages with assembling enabled. got IPA message, size=10, proto=200, text="Hello IPA" got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz" got IPA message, size=16, proto=200, text="Hello again IPA" got IPA message, size=1, proto=200, text="" got IPA message, size=14, proto=200, text="Next is empty" got msg == NULL, pending_msg == NULL, returned: Resource temporarily unavailable got IPA message, size=4, proto=200, text="Bye" got IPA message, size=4, proto=200, text="Bye" done: unread 0, unsent 0 Testing IPA recv with partitioned messages with assembling enabled. got IPA message, size=10, proto=200, text="Hello IPA" got IPA message, size=86, proto=200, text="A longer test message. ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz" got IPA message, size=16, proto=200, text="Hello again IPA" got IPA message, size=1, proto=200, text="" got IPA message, size=14, proto=200, text="Next is empty" got msg == NULL, pending_msg == NULL, returned: Resource temporarily unavailable got IPA message, size=4, proto=200, text="Bye" got IPA message, size=4, proto=200, text="Bye" done: unread 0, unsent 0 No crashes. libosmo-abis-0.3.2+git86fc3c8/tests/subchan_demux/000077500000000000000000000000001261736062400216565ustar00rootroot00000000000000libosmo-abis-0.3.2+git86fc3c8/tests/subchan_demux/subchan_demux_test.c000066400000000000000000000141371261736062400257140ustar00rootroot00000000000000/* Subchan Demux syncing test */ /* (C) 2012 by Holger Hans Peter Freyther * (C) 2012 by Tobias Engel * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include static uint8_t test_frames[] = { /* (transmission order is from left to right) */ /* Frame 1 */ 0, 0, 0, 0, 0, 0, 0, 0, /* sync header: 16 bits zero... */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, /* ... and 1 bit one (on the left) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Frame 2 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, /* <== This zero-bit could be mistaken for being the start of the sync header if there is no check for the following one bit */ /* Frame 3 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, /* Frame 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, }; static int data_cb(struct subch_demux *demux, int ch, uint8_t *data, int len, void *p) { printf("DATA_CB Channel(%d): %s\n", ch, osmo_hexdump(data, len)); return 0; } static void test_csd(void) { int i; uint8_t muxbyte; struct subch_demux demux; memset(&demux, 0, sizeof(demux)); subch_demux_init(&demux); demux.chan_activ = 1; demux.out_cb = data_cb; /* Push data into the demuxer and see what happens. */ printf("Testing the csd sync.\n"); for(i = 0; i < sizeof(test_frames); i += 2) { muxbyte = 0; muxbyte |= test_frames[i]; muxbyte |= (test_frames[i+1] << 1); subch_demux_in(&demux, &muxbyte, 1); } } int main(int argc, char **argv) { printf("Testing the subchannel demux.\n"); /* run the tests */ test_csd(); printf("No crashes.\n"); return 0; } libosmo-abis-0.3.2+git86fc3c8/tests/subchan_demux/subchan_demux_test.ok000066400000000000000000000076241261736062400261060ustar00rootroot00000000000000Testing the subchannel demux. Testing the csd sync. DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 DATA_CB Channel(0): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 No crashes. libosmo-abis-0.3.2+git86fc3c8/tests/testsuite.at000066400000000000000000000015241261736062400214120ustar00rootroot00000000000000AT_INIT AT_BANNER([Regression tests.]) # Example for tests.. copy and uncomment. This creates a new category # and test. It will copy the expected output to expout and then run # the given test. The stdout will be compared with the expout to determine # if the test was successfull. # AT_SETUP([NAME]) # AT_KEYWORDS([NAME]) # cat $abs_srcdir/NAME/NAME_test.ok > expout # AT_CHECK([$abs_top_builddir/tests/NAME/NAME_test], [], [expout]) # AT_CLEANUP AT_SETUP([ipa_recv]) AT_KEYWORDS([ipa_recv]) cat $abs_srcdir/ipa_recv/ipa_recv_test.ok > expout AT_CHECK([$abs_top_builddir/tests/ipa_recv/ipa_recv_test], [], [expout],[ignore]) AT_CLEANUP AT_SETUP([subchan_demux]) AT_KEYWORDS([subchan_demux]) cat $abs_srcdir/subchan_demux/subchan_demux_test.ok > expout AT_CHECK([$abs_top_builddir/tests/subchan_demux/subchan_demux_test], [], [expout]) AT_CLEANUP