pax_global_header00006660000000000000000000000064133266525610014523gustar00rootroot0000000000000052 comment=08d02dd5100cbff47922427e3beb31a3373bf75c osmo-bsc-1.3.0/000077500000000000000000000000001332665256100132465ustar00rootroot00000000000000osmo-bsc-1.3.0/.gitignore000066400000000000000000000016271332665256100152440ustar00rootroot00000000000000debian/*.log *.o *.lo *.a .deps Makefile Makefile.in bscconfig.h bscconfig.h.in *.pc *.*~ *.sw? .libs *.pyc *.gcda *.gcno **/TAGS #configure aclocal.m4 autom4te.cache/ config.log config.status config.guess config.sub configure compile depcomp install-sh missing stamp-h1 libtool ltmain.sh m4/*.m4 # git-version-gen magic .tarball-version .version osmo-bsc-*.tar.bz2 osmo-bsc-*.tar.gz # apps and app data hlr.sqlite3 src/utils/bs11_config src/ipaccess/ipaccess-config src/ipaccess/abisip-find src/ipaccess/ipaccess-firmware src/ipaccess/ipaccess-proxy src/utils/isdnsync src/osmo-bsc_nat/osmo-bsc_nat src/osmo-bsc_nat/*.cfg* src/osmo-bsc/osmo-bsc src/osmo-bsc/*.cfg* src/utils/meas_vis src/utils/meas_json src/utils/osmo-meas-pcap2db src/utils/osmo-meas-udp2db tags /deps #tests tests/testsuite.dir tests/*/*_test tests/atconfig tests/atlocal tests/package.m4 tests/testsuite tests/testsuite.log writtenconfig/ osmo-bsc-1.3.0/.gitreview000066400000000000000000000000621332665256100152520ustar00rootroot00000000000000[gerrit] host=gerrit.osmocom.org project=osmo-bsc osmo-bsc-1.3.0/.mailmap000066400000000000000000000012741332665256100146730ustar00rootroot00000000000000Harald Welte Harald Welte Harald Welte Holger Hans Peter Freyther Holger Hans Peter Freyther Holger Hans Peter Freyther Andreas Eversberg Andreas Eversberg Andreas Eversberg Pablo Neira Ayuso Max Suraev Tom Tsou osmo-bsc-1.3.0/AUTHORS000066400000000000000000000005371332665256100143230ustar00rootroot00000000000000Harald Welte Holger Freyther Jan Luebbe Stefan Schmidt Daniel Willmann Andreas Eversberg Sylvain Munaut <246tnt@gmail.com> Jacob Erlbeck Neels Hofmeyr osmo-bsc-1.3.0/COPYING000066400000000000000000001033301332665256100143010ustar00rootroot00000000000000 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 . osmo-bsc-1.3.0/Makefile.am000066400000000000000000000011371332665256100153040ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 ## FIXME: automake >= 1.13 or autoconf >= 2.70 provide better suited AC_CONFIG_MACRO_DIRS for configure.ac ## remove line below when OE toolchain is updated to version which include those ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) SUBDIRS = \ doc \ include \ src \ tests \ $(NULL) BUILT_SOURCES = $(top_srcdir)/.version EXTRA_DIST = git-version-gen osmoappdesc.py .version @RELMAKE@ $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version osmo-bsc-1.3.0/README000066400000000000000000000021721332665256100141300ustar00rootroot00000000000000About OsmoBSC ============= OsmoBSC originated from the OpenBSC project, which started as a minimalistic all-in-one implementation of the GSM Network. In 2017, OpenBSC had reached maturity and diversity (including M3UA SIGTRAN and 3G support in the form of IuCS and IuPS interfaces) that naturally lead to a separation of the all-in-one approach to fully independent separate programs as in typical GSM networks. OsmoBSC was one of the parts split off from the old openbsc.git. Before, it worked as a standalone osmo-bsc binary as well as a combination of libbsc and libmsc, i.e. the old OsmoNITB. Since the standalone OsmoMSC with a true A interface (and IuCS for 3G support) is available, OsmoBSC exists only as a separate standalone entity. OsmoBSC exposes - A over IP towards an MSC (e.g. OsmoMSC); - Abis interfaces towards various kinds of BTS; - The Osmocom typical telnet VTY and CTRL interfaces. Find OsmoBSC issue tracker and wiki online at https://osmocom.org/projects/osmobsc https://osmocom.org/projects/osmobsc/wiki OsmoBSC-NAT is a specialized solution to navigating RTP streams through a NAT. (Todo: describe in more detail) osmo-bsc-1.3.0/README.vty-tests000066400000000000000000000004421332665256100161070ustar00rootroot00000000000000To run the configuration parsing and output (VTY) test suite, first install git://git.osmocom.org/python/osmo-python-tests and pass the following configure options here: ./configure --enable-external-tests The VTY tests are then included in the standard check target: make check osmo-bsc-1.3.0/configure.ac000066400000000000000000000140341332665256100155360ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script AC_INIT([osmo-bsc], m4_esyscmd([./git-version-gen .tarball-version]), [openbsc@lists.osmocom.org]) dnl *This* is the root dir, even if an install-sh exists in ../ or ../../ AC_CONFIG_AUX_DIR([.]) AM_INIT_AUTOMAKE([dist-bzip2]) AC_CONFIG_TESTDIR(tests) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl include release helper RELMAKE='-include osmo-release.mk' AC_SUBST([RELMAKE]) dnl checks for programs AC_PROG_MAKE_SET AC_PROG_CC AC_PROG_INSTALL LT_INIT dnl check for pkg-config (explained in detail in libosmocore/configure.ac) AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) if test "x$PKG_CONFIG_INSTALLED" = "xno"; then AC_MSG_WARN([You need to install pkg-config]) fi PKG_PROG_PKG_CONFIG([0.20]) dnl check for AX_CHECK_COMPILE_FLAG m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [ AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.]) ]) dnl checks for libraries AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) AC_SUBST(LIBRARY_DL) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.12.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.5.1) PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0) PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 0.10.0) PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.10.0) PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.4.0) dnl checks for header files AC_HEADER_STDC found_pcap=yes AC_CHECK_HEADERS(pcap/pcap.h,,found_pcap=no) AM_CONDITIONAL(HAVE_PCAP, test "$found_pcap" = yes) found_cdk=yes AC_CHECK_HEADERS(cdk/cdk.h,,found_cdk=no) AM_CONDITIONAL(HAVE_LIBCDK, test "$found_cdk" = yes) found_sqlite3=yes PKG_CHECK_MODULES(SQLITE3, sqlite3, ,found_sqlite3=no) AM_CONDITIONAL(HAVE_SQLITE3, test "$found_sqlite3" = yes) AC_SUBST(found_sqlite3) dnl Checks for typedefs, structures and compiler characteristics AC_ARG_ENABLE(sanitize, [AS_HELP_STRING( [--enable-sanitize], [Compile with address sanitizer enabled], )], [sanitize=$enableval], [sanitize="no"]) if test x"$sanitize" = x"yes" then CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" fi AC_ARG_ENABLE(werror, [AS_HELP_STRING( [--enable-werror], [Turn all compiler warnings into errors, with exceptions: a) deprecation (allow upstream to mark deprecation without breaking builds); b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) ] )], [werror=$enableval], [werror="no"]) if test x"$werror" = x"yes" then WERROR_FLAGS="-Werror" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" CFLAGS="$CFLAGS $WERROR_FLAGS" CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" fi # 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) AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"]) AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"]) AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"]) AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"]) AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"]) AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"]) # Coverage build taken from WebKit's configure.in AC_MSG_CHECKING([whether to enable code coverage support]) AC_ARG_ENABLE(coverage, AC_HELP_STRING([--enable-coverage], [enable code coverage support [default=no]]), [],[enable_coverage="no"]) AC_MSG_RESULT([$enable_coverage]) if test "$enable_coverage" = "yes"; then COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" AC_SUBST([COVERAGE_CFLAGS]) AC_SUBST([COVERAGE_LDFLAGS]) fi AC_ARG_ENABLE(profile, [AS_HELP_STRING([--enable-profile], [Compile with profiling support enabled], )], [profile=$enableval], [profile="no"]) if test x"$profile" = x"yes" then CFLAGS="$CFLAGS -pg" CPPFLAGS="$CPPFLAGS -pg" fi AC_ARG_ENABLE([external_tests], AC_HELP_STRING([--enable-external-tests], [Include the VTY/CTRL tests in make check [default=no]]), [enable_ext_tests="$enableval"],[enable_ext_tests="no"]) if test "x$enable_ext_tests" = "xyes" ; then AC_CHECK_PROG(PYTHON2_AVAIL,python2,yes) if test "x$PYTHON2_AVAIL" != "xyes" ; then AC_MSG_ERROR([Please install python2 to run the VTY/CTRL tests.]) fi AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes) if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.]) fi fi AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) AC_MSG_RESULT([$enable_ext_tests]) AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") AC_MSG_RESULT([CFLAGS="$CFLAGS"]) AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) dnl Generate the output AM_CONFIG_HEADER(bscconfig.h) AC_OUTPUT( include/Makefile include/osmocom/Makefile include/osmocom/bsc/Makefile src/Makefile src/libfilter/Makefile src/osmo-bsc/Makefile src/ipaccess/Makefile src/utils/Makefile tests/Makefile tests/atlocal tests/gsm0408/Makefile tests/bsc/Makefile tests/codec_pref/Makefile tests/abis/Makefile tests/subscr/Makefile tests/nanobts_omlattr/Makefile tests/handover/Makefile doc/Makefile doc/examples/Makefile Makefile) osmo-bsc-1.3.0/contrib/000077500000000000000000000000001332665256100147065ustar00rootroot00000000000000osmo-bsc-1.3.0/contrib/a-link/000077500000000000000000000000001332665256100160615ustar00rootroot00000000000000osmo-bsc-1.3.0/contrib/a-link/sccp-split-by-con.lua000066400000000000000000000106711332665256100220370ustar00rootroot00000000000000-- Split trace based on SCCP Source -- There are still bugs to find... bugs bugs bugs... hmm do local function init_listener() print("CREATED LISTENER") local tap = Listener.new("ip", "sccp && (ip.src == 172.16.1.81 || ip.dst == 172.16.1.81)") local sccp_type_field = Field.new("sccp.message_type") local sccp_src_field = Field.new("sccp.slr") local sccp_dst_field = Field.new("sccp.dlr") local msg_type_field = Field.new("gsm_a.dtap_msg_mm_type") local lu_rej_field = Field.new("gsm_a.dtap.rej_cause") local ip_src_field = Field.new("ip.src") local ip_dst_field = Field.new("ip.dst") -- local bssmap_msgtype_field = Field.new("gsm_a.bssmap_msgtype") -- assignment failure 0x03 -- -- local dtap_cause_field = Field.new("gsm_a_dtap.cause") local dtap_cc_field = Field.new("gsm_a.dtap_msg_cc_type") local connections = {} function check_failure(con) check_lu_reject(con) check_disconnect(con) check_failures(con) end -- cipher mode reject function check_failures(con) local msgtype = bssmap_msgtype_field() if not msgtype then return end msgtype = tonumber(msgtype) if msgtype == 89 then print("Cipher mode reject") con[4] = true elseif msgtype == 0x03 then print("Assignment failure") con[4] = true elseif msgtype == 0x22 then print("Clear Request... RF failure?") con[4] = true end end -- check if a DISCONNECT is normal function check_disconnect(con) local msg_type = dtap_cc_field() if not msg_type then return end if tonumber(msg_type) ~= 0x25 then return end local cause = dtap_cause_field() if not cause then return end cause = tonumber(cause) if cause ~= 0x10 then print("DISCONNECT != Normal") con[4] = true end end -- check if we have a LU Reject function check_lu_reject(con) local msg_type = msg_type_field() if not msg_type then return end msg_type = tonumber(tostring(msg_type)) if msg_type == 0x04 then print("LU REJECT with " .. tostring(lu_rej_field())) con[4] = true end end function tap.packet(pinfo,tvb,ip) local ip_src = tostring(ip_src_field()) local ip_dst = tostring(ip_dst_field()) local sccp_type = tonumber(tostring(sccp_type_field())) local sccp_src = sccp_src_field() local sccp_dst = sccp_dst_field() local con if sccp_type == 0x01 then elseif sccp_type == 0x2 then local src = string.format("%s-%s", ip_src, tostring(sccp_src)) local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local datestring = os.date("%Y%m%d%H%M%S") local pcap_name = string.format("alink_trace_%s-%s_%s.pcap", src, dst, datestring) local dumper = Dumper.new_for_current(pcap_name) local con = { ip_src, tostring(sccp_src), tostring(sccp_dst), false, dumper, pcap_name } dumper:dump_current() connections[src] = con connections[dst] = con elseif sccp_type == 0x4 then -- close a connection... remove it from the list local src = string.format("%s-%s", ip_src, tostring(sccp_src)) local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[src] if not con then return end con[5]:dump_current() con[5]:flush() -- this causes a crash on unpacted wireshark con[5]:close() -- the connection had a failure if con[4] == true then local datestring = os.date("%Y%m%d%H%M%S") local new_name = string.format("alink_failure_%s_%s-%s.pcap", datestring, con[2], con[3]) os.rename(con[6], new_name) else os.remove(con[6]) end -- clear the old connection connections[src] = nil connections[dst] = nil elseif sccp_type == 0x5 then -- not handled yet... we should verify stuff here... local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[dst] if not con then return end con[5]:dump_current() elseif sccp_type == 0x6 then local dst = string.format("%s-%s", ip_dst, tostring(sccp_dst)) local con = connections[dst] if not con then print("DON'T KNOW THIS CONNECTION for " .. ip_dst) return end con[5]:dump_current() check_failure(con) end end function tap.draw() print("DRAW") end function tap.reset() print("RESET") end end init_listener() end osmo-bsc-1.3.0/contrib/jenkins.sh000077500000000000000000000023511332665256100167070ustar00rootroot00000000000000#!/usr/bin/env bash # jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" exit 2 fi set -ex base="$PWD" deps="$base/deps" inst="$deps/install" export deps inst osmo-clean-workspace.sh mkdir "$deps" || true osmo-build-dep.sh libosmocore "" '--disable-doxygen --enable-gnutls' verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib" osmo-build-dep.sh libosmo-abis osmo-build-dep.sh libosmo-netif osmo-build-dep.sh libosmo-sccp osmo-build-dep.sh osmo-mgw set +x echo echo echo echo " =============================== osmo-bsc ===============================" echo set -x cd "$base" autoreconf --install --force ./configure --enable-sanitize --enable-external-tests --enable-werror $MAKE $PARALLEL_MAKE LD_LIBRARY_PATH="$inst/lib" $MAKE check \ || cat-testlogs.sh LD_LIBRARY_PATH="$inst/lib" \ DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests --enable-werror" \ $MAKE distcheck \ || cat-testlogs.sh osmo-clean-workspace.sh osmo-bsc-1.3.0/contrib/systemd/000077500000000000000000000000001332665256100163765ustar00rootroot00000000000000osmo-bsc-1.3.0/contrib/systemd/osmo-bsc.service000066400000000000000000000003451332665256100215040ustar00rootroot00000000000000[Unit] Description=Osmocom Base Station Controller (BSC) Wants=osmo-mgw.service [Service] Type=simple Restart=always ExecStart=/usr/bin/osmo-bsc -c /etc/osmocom/osmo-bsc.cfg -s RestartSec=2 [Install] WantedBy=multi-user.target osmo-bsc-1.3.0/debian/000077500000000000000000000000001332665256100144705ustar00rootroot00000000000000osmo-bsc-1.3.0/debian/abisip-find.install000066400000000000000000000000241332665256100202410ustar00rootroot00000000000000usr/bin/abisip-find osmo-bsc-1.3.0/debian/changelog000066400000000000000000000704431332665256100163520ustar00rootroot00000000000000osmo-bsc (1.3.0) unstable; urgency=medium [ Philipp Maier ] * a_reset: cleanup + remove dead code * gscon: remove dead code * gscon: pick suitable payload type / encoding name for MGCP * lcls: set codec info when performing MGW operation * codec_pref: move match_codec_pref() to separate c-file and add unit-test * codec_pref: check bts codec support * chan_alloc: reset rtp voice related bits in lchan_free() * rsl: use 3GPP assigned payload type constants from libosmo-netif [ Stefan Sperling ] * show all global counters of osmo-bsc in vty * add counter for connection attempts from BTS with unknown unit id * fix misaligned memory write access in abis_nm_ipaccess_rsl_connect() * increment 'paging responded' counter for active paging only * fix handling of invalid pchan names in vty [ Pau Espin Pedrol ] * nat: Add jitter buffer on the uplink receiver * acc_ramp: Increase log level of some messages * chan_alloc: Print bts nr on chan alloc failure * abis_rsl.c: Fix whitespace * abis_rsl: rsl_rx_chan_rqd: Format bts log string as in everywhere else * pcu_sock: Log event pcu_sock created * osmo-bsc: Clean help description of cmd line parameters * osmo-bsc: Add -V param to print version * debian: Move meas related binaries into new package osmo-bsc-meas-utils * bsc-filter: Remove unused func barr_adapt and set barr_find static * bsc_vty: Write access list entries when storing bsc config * Init access_lists before passing it as a parameter * Rename bsc_msg_acc_lst_vty_init to have more uniform prefix * filter: vty: Print policy list in cmd show access-list * filter: Replace '.' in counter names with ':' * filter: Allocate each ctr group with a different idx * ctrl: Avoid sending back received ERROR msgs [ Neels Hofmeyr ] * bsc_api.c: actually log with context * abis_rsl.h: drop unused rsl_chan_activate() declaration * cosmetic: bsc_dyn_ts.c: make local functions static * cosmetic: define TCH_F_PDCH_PENDING_MASK as actual bitwise or * cosmetic: logging and ordering in handle_ass_compl() * doc: add msc charts on Assignment/Handover internals * tests: remove bssap_test * tests: remove channel_test * bsc_test: drop "scan to MSC" code path * dissolve libbsc: move all to src/osmo-bsc, link .o files * remove struct bsc_api * cosmetic: magic number: use RSL_ACT_ constant for chan act * cosmetic: gscon: undup code: add common assignment_failed() * assignment: signal assignment failure on chan act nack * log: fix logging in rsl_rx_chan_act_nack() * log: assignment: add two logs on unexpected lchan release * use libosmocore's gsm0808_permitted_speech(), gsm0808_chosen_channel() * cosmetic: penalty timers: constify, tweak doc * cosmetic: bsc_subscr_alloc: log initial get * gscon: put subscriber a little later * try to pick up subsrc IMSI on l3-compl * store subscriber identity on paging * cosmetic: handover_test: add IMSI to subscr for logging * doc: tweak msc charts on Assignment/Handover: act_timer * doc: add lchan-release.msc * doc: add ms-channel-request.msc * doc: charts: illustrate new plan for ts and lchans * cosmetic: gscon: drop odd use of OSMO_STRINGIFY * HO: introduce T7, T8, T101 timers * drop dead code: conn->T10, handled by gscon instead * make T10 configurable like the rest of them * fix dyn TS init: properly identify BTS on OML OPSTART ACK * cosmetic / linking: move str_to_imsi() out of abis_rsl.c * cosmetic: name osmo-bsc's root ctx 'osmo-bsc', not 'openbsc' * call osmo_xua_msg_tall_ctx_init() * fix handover start: dealloc ho if event not permitted * ho cfg: fix unit strings * hodec2 log: less verbose, more concise logging * various logging: fix missing/extra newlines * BTS codec pref legacy compat: allow all codecs per default [ Harald Welte ] * bsc: Fix check for MSC-side FSM allocation failure * vty: Permit selection of other ASP protocol than M3UA * bsc: Add mgcp_port_to_cic() to determine CIC from RTP Port * bsc: Use correct MGCP endpoint name for IPA/SCCPlite * bsc: Don't reject ASSIGNMENT for Audio in IPA/SCCPlite case * bsc: Don't include AoIP IEs in ASSIGNMENT COMPLETE over SCCPlite * bsc: Don't create MSC-side MGCP connection in IPA/SCCPlite case * remove remaining bits of osmo-bsc_nat * Remove 'struct bsc_msc_connection' + fix IPA-encapsulated CTRL * move 'extern struct gsm_network *bsc_gsmnet" to header file * VTY: Print some more information in "show conns" * Add initial 3GPP LCLS support to OsmoBSC * LCLS: add VTY config to enable/disable LCLS on per-MSC basis * Reject ASSIGNMENT REQ with CIC but no AoIP transp addr in AoIP case * Ignore "dest" command in MSC node * Explicitly register CTRL-over-IPA callback with libosmo-sigtran * Remove unused logging subsystems DCC and DMGCP * remove traces of osmo-bsc_nat in python test (osmoappdesc/test_runner) * Add missing event string name for GSCON_EV_LCLS_FAIL * bsc_subscr_conn_fsm: BSC must not release SCCP connection * absi_rsl: Fix segfault in rsl_rx_conn_fail() [ Daniel Willmann ] * git-version-gen: Don't check for .git directory -- Pau Espin Pedrol Fri, 27 Jul 2018 19:25:05 +0200 osmo-bsc (1.2.1) unstable; urgency=medium [ Philipp Maier ] * bsc_api/GSCON: prevent unnecessary channel mode modifications [ Neels Hofmeyr ] * resurrect meas_feed.c: vty, vty-test * dyn ts, bts_ipaccess_nanobts.c: init PDCH on Chan OPSTART ACK * dyn TS, assignment: set lchan state to LCHAN_S_ACT_REQ in the proper place * dyn TS, assignment: allow switch from PDCH with associated conn * dyn TS: init only when both RSL and the Channel OM are established * dyn TS: allow any pchan type changes, fix for gprs mode none * debug log: verbosely log all lchan alloc choices * deprecate dyn_ts_allow_tch_f and by default allow all TCH * fix default fallbacks in audio_support_to_gsm88() * log: indicate hr/fr in audio_support_to_gsm88() error * cosmetic: dyn ts init: undup logging for gprs = none [ Vadim Yanitskiy ] * osmo_bsc_vty.c: fix: write MGW configuration -- Pau Espin Pedrol Tue, 15 May 2018 14:10:38 +0200 osmo-bsc (1.2.0) unstable; urgency=medium [ Neels Hofmeyr ] * vty: skip installing cmds now always installed by default * bssap: paging: page entire BSS for unimplemented cell id list * fix build: bssap test broke by undefined references * osmo-bsc RESET FSM: use distinct struct names * osmo-bsc: SCCP addrs: default only if unset, reject invalid * osmo-bsc vty: be fatal for addressbook entry errors * use osmo_sccp_inst_addr_name() instead of looking up ss7 * add --enable-sanitize config option * bsc_init: fix Werror: define rc for 2quater with si2q_count == 0 * bsc filter: don't ignore imsi-allow on "global" filter level * compiler warnings: drop some unused variables * compiler warnings: constify in abis_nm.c * cleanup: drop unused gsm_bts.role * compiler warnings: add includes in abis_rsl.h, gsm_data_shared.h * cosmetic: handover.h: use "#pragma once", declare structs, comments * examples: add osmo-bsc-minimal.cfg * HO prep: pass gsm_network to gsm_bts_alloc() already * fix segfault upon release paging on BSSMAP Reset: init llist * log typo fix in gsm0808_cipher_mode() * debug log: log Cipher Mode info upon sending down RSL/A-bis * fix bssmap_handle_cipher_mode()'s encryption decision * abisip-find: add getopt option parsing in preparation for a new option * abisip-find: add -l to list base stations instead of streaming replies * abisip-find: update copyright * abisip-find: add timeout option * abisip-find: add --interval option * vty: fix 'show lchan ...' arg [lchan_nr] to [<0-7>] * vty: change handover command's arg LCHAN_NR to <0-7> * vty: cosmetic: use common BTS, TRX, TS, LCHAN strings * vty: add various manual handover and assignment trigger commands * osmo_bsc_mgcp: cosmetic: introduce mgcp_init(), soak up fsm init * HO: fix recovery from failed handover * HO prep: introduce per-BTS handover config, with defaults on net node * HO: add indicators for inter-cell and async ho, use for chan act type * cosmetic: explicitly init ho_ref start value * fixup: neigh_meas_avg: detect invalid window size as <=0, log if invalid * fixup: neigh_meas_avg: fix condition to reduce window size * HO: enable handover by initializing at startup; rename init function * HO: add handover algo 2 parameters; skip HO 1 if HO 2 is configured * HO: rename gsm_bts_neighbor() to bts_by_arfcn_bsic() * HO: make bts_by_arfcn_bsic() public * libcommon: eliminate bsc_version.c * libcommon: eliminate common_vty.c * libcommon: eliminate debug.c * libcommon: eliminate socket.c * libcommon: eliminate talloc_ctx.c * ipaccess-proxy: don't redefine tall_bsc_ctx * libcommon: join gsm_data_shared.* into gsm_data.* * drop libcommon completely, move remaining files to libbsc * libcommon-cs: move a_reset.c into libbsc * libcommon-cs: move gsm_network_init() into bsc_network_init() * gsm_network: drop unused trans_list * libcommon_cs: move gsm48 bits to libbsc * libcommon-cs: move vty bits to libbsc/bsc_vty.c * common_cs.h: mv gsm_encr to gsm_data.h * drop libcommon-cs completely * drop unused common.h * gsm_network: drop unused subscr_epxire_timer * vty: 'show bts': write '(none)' if none are found. * vty: 'show bts': fix indenting * bts chan_load: ignore unusable BTS * handover_logic.c: always do inter-cell channel activation * handover_logic.c: on HO command, send new lchan's MS power * HO: process_meas_rep: guard against modulo zero * HO: cosmetic: bsc_handover_start(): "fix" memcpy for AMR config * HO: add new_lchan_type arg to bsc_handover_start() * HO: cosmetic: bsc_handover_start_lchan_change(): tweak local vars * HO: always do async handover * HO: bsc_handover_start_lchan_change(): set MS to max power on handover * HO: logging: more logs, and more concise logging * HO: move penalty timers to own file as proper API * HO: store speech codec list from BSSMAP Assignment in conn * HO: cfg: tweak vty write * vty: 'show bts': list the TRXs' ARFCNs * vty: 'show bts': print neighbor cells * HO: cfg: separate hodec1 from hodec2 parameters * HO: lchan: store last seen measurement report nr, tweak log * HO: clearly mark conn penalty timer member for hodec2 * HO: cosmetic: handover_decision.c: make process_meas_rep() return void * HO: introduce ho decision callbacks * HO: cosmetic: getting a chan activ nack on a non-ho lchan is not an error * HO: Implement load based handover, as handover_decision_2.c * HO: vty: rename ho decision 1 vty to 'handover1' with 'handover' alias * cosmetic: adjust copyrights on handover_cfg.c,_vty.c * HO: vty: clearly mark 'handover foo' as legacy alias for 'handover1 foo' * drop unused libbsc/meas_proc.c * HO: fix minor issues found by coverity * bsc_api.c: fix log string format * fix build: gprs_ra_id_by_bts(): ensure to init all values * compiler warning: chan_compat_with_mode(): clearly handle all enum vals * add test for gsm48_ra_id_by_bts() * add test for abis_nm_ipaccess_cgi() * ctrl_test_runner: add tests for 3-digit MNC * gsm48_ra_id_by_bts(): struct gsm48_ra_id* instead of buf * cosmetic: bsc_network_init(): imply default 001-01 PLMN * implement support for 3-digit MNC with leading zeros * gsm48_parse_meas_rep(): set num_cell=0 if no neighbor cells are reported * cosmetic: hodec2: log nr of neighbors in meas report * cosmetic: typo in log: handover_decision2.c: 'measuements' * handover_test: explicitly wrap abis_rsl_sendmsg() * pcu_if: implement support for 3-digit MNC * configure: add --enable-werror * cosmetic: abis_nm: use osmo_cell_global_id, parse 3-digit MNC * fixup: apply mnc3 change also in ipaccess/network_listen.c * vty: drop unused vty definitions (*_NODE, msc_*) * gsm0408_test: drop LAI encoding test * range_enc_arfcns: avoid runtime error on zero size * fix gsm0408_test: properly free bts struct after each test * move init from gsm_bts_alloc_register() to gsm_bts_alloc(); fix gsm0408_test * cosmetic: gsm0408_test: drop unused arg from bts_init() * ctx cleanup: use non-NULL talloc ctx for osmo_init_logging2() * vty: re-add 'timeout-ping' and 'timeout-pong' as dummy commands * resurrect meas_feed.c from openbsc.git history * resurrect meas_feed.c: make it compile, add logging [ Max ] * Check OML state per-BTS * OML: consider administrative state when reporting * cosmetic: remove obsolete ROLE_BSC * cosmetic: tighten function type signatures * cosmetic: drop unused include * cosmetic: mark gsm_objclass2mo as static * OML: expand status reporting checks * Generate SI2ter Rest Octets * Generate SI2bis Rest Octets * Fix tests after rate_ctr change * Remove unneeded .py scripts * Enable sanitize for CI tests * Migrate from OpenSSL to osmo_get_rand_id() * Add optional profiling support * Fix .deb builds * Check and handle SMS encoding failure * Remove obsolete ./configure option * cosmetic: remove duplicated code * RSL: print link state per-TRX * vty: fix OML link state printing * cosmetic: log prim operation as text [ Philipp Maier ] * mgcp: use osmo-mgw to switch RTP streams * cosmetic: remove distracting newline * cosmetic: reorder case list * cosmetic: replace term MGCP-GW with MGW * mgcp: add missing out state * mgcp: remove unused variable * reset: remove name variable from reset context * doc: add example configuration for osmo-mgw * auth: remove obsolete VTY commands * bssap: remove libosmo-legacy-mgcp dependancy * sccp-lite: remove obsolete VTY commands * auth: remove unused structs * auth: remove obsolete VTY commands * mgcp: use hexadecimal digits in endpoint names * mgcp: use mgw assigned connection identifiers * mgcp: add missing switch case * mgcp: do not fail silently on snprintf() * cosmetic: remove duplicate logging * cosmetic: do not cast void pointer * cosmetic: add missing log prefix * cosmetic: correct sourcecode formatting * cosmetic: use fsm pointer from parameter list * mgcp: cosmetic fixups * paging: paging_flush_bts: be sure pending_requests is initalized * cosmetic: osmo_bsc_mgcp: improve comments * mgcp: cancel transactions on timeout * mgcp: validate rtp connection data in MGW response (ip/port) * mgcp: log file and line of calls to handle_error() * cosmetic: mgcp: remove duplicate logging * abis_rsl: permit first EstablishInd only on SAPI=0 * abis_rsl: do not allow SACCH in MF mode on SAPI=0 * SIGTRAN: correct wrong log category * bsc_api: drop unknown RR messages. * paging: page all bts when no cell is associated * paging: fix paging attemt rate counter * a_reset: Add FSM event names * gsm_data: use feature list from libosmocore * bsc_vty: display bts features in show bts * cosmetic: remove unused enum members * cosmetic: fix typo * cosmetic: fix argument order of forward_dtap() * cosmetic: remove needless fixme note. * cosmetic: fix incomplete sentence in comment. * Cosmetic: fix missing semicolon after osmo-assert * cosmetic: remove dead code and obsolete fixmes * cosmetic: remove old, already commented-out code * ipaccess: make ipaccess-config build again * bs11: make bs11_config build again * cosmetic: remove dead code: osmo_bsc_reset.c * gscon: fix illegal state transitions * cosmetic: remove dead code * cosmetic: Add fixme note for OS#3112 * inform A-RESET FSM about MSC CR timeouts * gscon: fix assignment of signalling channels * cosmetic: Add note about libosmo-legacy-mgcp to configure.ac [ Harald Welte ] * debian: Add dependency to libosmo-mgcp-client-dev * debian: Increase required libosmo-legacy-mgcp-dev version * configure.ac/debian: Require libosmo-mgcp-client-dev >= 1.2.0 * osmo-bsc: Print NOTICE message on unimplemented BSSMAP UDT * Move many counters from BSC-global to per-BTS granularity * rate_ctr: Use ':' as separator, not '.' * Remove 'msc' counter group from BSC * Change T3101 default from 10s to 3s. * paging.c: add more documentation on what the functions actually do * paging: Remove obsolete paging call-back support * paging: Stop all paging if MSC sends us BSSMAP RESET * Fix per-BTS counter group index * libbsc: paging: more reasonable (and detailed) paging statistics * Reduce T3113 default from 60s to 10s * Add per-BTS rate_ctr for total + failed number of RSL CHAN_ACT * Add new per-BTS "rsl:unknown" counter to count unknown RSL messages * Add a new counter "rsl:ipa_nack" to count number of IPA related NACKs * Add new "chan:mode_modify_nack" counter to count RSL MODE MODIFY NACK * Remove dead code left over from NITB split * Remove unused RRLP options/codec * Remove bogus vty config for LU reject cause * Remove bogus MM INFO configuration * Remove some more dead code * remove libosmo-sccp dependency for osmo-bsc * osmo_bsc_bssap.c: Spelling fixes in comment * Remove unused struct osmo_bsc_sccp_con member sccp_queue_size * osmo_bsc.h: document every field in 'struct osmo_bsc_sccp_con' * osmo-bsc: Move user plane/voice related bits into sub-structure * gsm_data.h: Document all fields of gsm_subscriber_connection * remove unused 'lac' member of 'struct gsm_subscriber_connection' * BSC: Add "show subscriber all" command * BSC: Fix bsc_subsc leak on paging * bsc_test.c: Use proper network/bts/lchan structures * cosmetic: Hide all accesses to conn->bts behind conn_get_bts() * Reduce T3109 default from 19s to 5s * Make libcommon, libcommon-cs, libfilter, utils depend on mgcp/sigtran * cosmetic: Remove data/len variables in bssmap_handle_assignm_req() * bssmap_handle_assignm_req(): Decode channel type as first step * remove obsolete gsm_subscriber_connection.bts member * update.gitignore with 'tags' files and 'deps' directory * gsm_data_shared.h: Remove unused sacch_deact member field * vty: print RTP IP of lchan if actually bound; print remote (mgw) IP * osmo-bsc: Add talloc context introspection via VTY * Structural reform: Get rid of osmo_bsc_sccp_con * vty: Permit codec-list containing both full-rate and half-rate codecs * logging: Remove obsolete log categories * Permit set of multiple different A5 ciphers * bssmap_handle_assignm_req(): Use proper cause values * bssmap_handle_assignm_req(): Use more conscise error/log message texts * bssmap_handle_assignm_req(): Don't print log statemens in malloc failure case * chan_compat_with_mode: signalling works over all channel types * osmo-bts/nanobts: Set RACH_Busy Threshold to -90 dBm * Align syntax of "handover" + "assignment" command with that of lchan act/deact * Revert "Generate the S_L_INP_TEI_UP signal earlier." * bsc_vty: Merge more VTY documentation string #defines * sysinfo: Fix regression causing missing L2 Pseudo-Length in SI5/SI6 (Closes: #3059) * introduce an osmo_fsm for gsm_subscriber_connection * cosmetic: Fix infinite number of formatting errors in gscon_fsm_states * abis_nm: Improve and fix OML logging * paging: Unify formatting of log messages with (bts=%d) prefix * RR: Send RR STATUS in case of unsupported/unknown message * BSSAP: Fix test_codec_pref() implementation for AMR * BSSAP: document match_codec_pref() more thoroughly * GSCON FSM: Fix argument order when calling gsm0808_assign_req() * bssmap: State correct speech codec in ASSIGNMENT COMPLETE * Start Dynamic PDCH Initialization after RSL is up * "show timeslot": Show dynamic PDCH state also for Osmocom-style dyn PDCH [ Alexander Couzens ] * debian: remove doublicated project name in example files * use _NUM_CHREQ_T to define the size of ctype_by_chreq * pcuif_proto.h: fix whitespaces and indention * pcuif_proto.h: add features of version 7 (txt indication) [ Pau Espin Pedrol ] * tests: Fix selection of python version * Use type bool for boolean fields in gsm48_si_ro_info * vty: Add cmd to configure 3g Early Classmark Sending * cosmetic: bsc_vty: Fix trailing whitespace * cosmetic: bsc_vty: Document bvci reserved values * osmo_bsc_bssap.c: Fix discard of const qualifier in assignment * debian: Move abisip-find from osmo-bsc to its own package * abisip-find: Add option to bind to a specific source address * abisip-find: Force stdout buffer flush * abisip-find: Add --format-json option * ipaccess-config: Enable logging all categories to print errors * ipaccess-config: Add missing path with log error * ipaccess-config: Improve handling of last parameter * abisip-find: Improve use information output * ipaccess-config: Check cmdlie arg unit-id format * bsc_api.c: bsc_handle_lchan_signal: Remove unused variable * bsc_subscr_conn_fsm.c: Fix wrong param list passed to LOGPFSML * tests: handover_test.c: Add missing header * pcu_sock.c: Avoid breaking strict-aliasing on ptr derreference * contrib: jenkins.sh: Add --enable-werror flag * contrib: osmo-bsc.service: Update description * contrib: osmo-bsc.service: Fix osmo-mgw.service dependency * libbsc: nokia_site: Fix uninitialized return val * bsc_vty: Fix uninitialized var false positive on gcc 7.3.1 * paging: paging_request_bts: Fix wrong return value * bssap: Log non handled paging requests * libbsc: set_net_mcc_mnc_apply: Fix memleak on parsing incorrect mcc mnc * bsc_nat: ctrl: fix memleak on reply receival * bsc_nat: forward_to_bsc: remove one level of indentation * bsc_nat: forward_to_bsc: Fix memleak on send failure * bsc_nat: Drop redundant ccon ptr in bsc_cmd_list * bsc_nat: ctrl: Fix crash on receveing bsc reply * use osmo_init_logging2 * chan_alloc.c: Fix log var formatting issues * abis_rsl.c: abis_rsl_rx_cchan: Print msg type name for unimplemented messages received * abis_rsl.c: Clean ericsson specific imm assign code * gsm_data_shared.h: Remove unused enum gsm_paging_event [ Stefan Sperling ] * Fix "CTRL GET msc_connection_status" response. * Support control connection status query for a particular MSC. * Implement support for paging by LAI. * Add TAGS files (produced by 'make tags') to .gitignore file. * Implement support for CELL_IDENT_NO_CELL. * Implement support for paging based on CI (cell identifier). * Move BTS selection for paging from osmo_bsc_grace.c into osmo_bsc_bssap.c. * Implement support for paging based on a Cell Global Identifier. * Implement support for paging based on LAC and CI. * Show the BTS number for outgoing paging commands in debug log. * Split paging cases in bssmap_handle_paging() off into helper functions. * Remove an unused variable. * Improve an error message in page_lai_and_lac() * Make "waiting indicator" of IMMEDIATE ASSIGN REJECT dynamic. * Add stat items for the BTS's channel load average and T3122. * Make RSL connection attempts time out. * fix handover_test link error * Add support for Access Control Class ramping. * Generate the S_L_INP_TEI_UP signal earlier. * use libosmocore to parse cell identifiers in osmo-bsc * fix an error message in bssmap_handle_paging() * change return type of page_subscriber() to void * Generate the S_L_INP_TEI_UP signal earlier. * fix a format string error in bts_update_t3122_chan_load() * fix initialization of acc ramping * only log actual access control class ramping changes * ensure that acc_ramp_init() is only called once * trigger acc ramping based on trx rf-locked state * rename helper functions in the acc ramp code to avoid confusion * trigger acc ramping on state-changed-event reports * only trigger acc ramping if trx 0 is usable and unlocked * fix handling of state changes in acc ramping * properly skip paging is OML link is down * extend documentation of paging_flush_bts() * flush paging when RSL link is dropped [ Vadim Yanitskiy ] * bsc/gsm_04_80.h: use '#pragma once' instead of includes * bsc/gsm_04_80.h: clean up useless declarations * libbsc/bsc_vty.c: prevent uninitialized access * doc/examples: use NECI = 1 by default [ Ivan Kluchnikov ] * handover_decision: Fix condition for power budget handover attempt * handover_decision: log HO causes more accurately [ Andreas Eversberg ] * HO: Send Channel Mode and Multirate IE along with handover command * HO: Add function to count currently ongoing handovers to a given BTS * Fix: If paging for half rate was requested, use hr, if supported by MS * HO: Assign SDCCH on channel request * Fix of checking TCH rate at chan_compat_with_mode * HO: Count the actual meas.rep. get_meas_rep_avg fails if not reached * HO: Count neighbor measurements and reduce window of neigh_meas_avg * HO: Changed availablilty of ts_is_usable() from static to extern * HO: Always update rqd_ta after receiving measurement report * HO: If handover logic is used to do assignment, signal assignment result * HO: Add handover decision debugging category * Do not perform assignment, if the new channel equals the current one * Allow assignment to TCH channel with signalling only mode * Correctly set T3105 for ipaccess BTS type * HO: fix: increase the number of measurement report history to 10 * HO: Change debug category at handover decision: DHO -> DHODEC * HO: Count number of free timeslot on a given BTS * HO: add queue to cache DTAP messages during handover/assignment * Fix: meas_rep.c will only use valid DL measurement reports * HO: Add a penalty timer list to the subscriber connection entity [ Keith ] * Cosmetic: Fix typo: Siganlling->Signalling -- Pau Espin Pedrol Thu, 03 May 2018 18:40:11 +0200 osmo-bsc (1.1.2) unstable; urgency=medium * Debian: depend on libosmo-sigtran (bsc) and libosmo-sccp (bsc-nat) * debian/control: Specify versions of packages we depend upon -- Harald Welte Sun, 29 Oct 2017 09:03:33 +0100 osmo-bsc (1.1.1) unstable; urgency=medium [ Neels Hofmeyr ] * jenkins: use osmo-clean-workspace.sh before and after build [ Harald Welte ] * Debian: re-introduce missing build dependency to libssl-dev -- Harald Welte Sat, 28 Oct 2017 21:49:00 +0200 osmo-bsc (1.1.0) unstable; urgency=medium [ Alexander Couzens ] * Initial release. * debian/rules: show testsuite.log when tests are failing [ Neels Hofmeyr ] * jenkins: fix build: osmo-mgw from master, not pre_release * drop files unrelated to osmo-bsc * rename openbsc.pc to osmo-bsc.pc * rewrite README * move include/openbsc to include/osmocom/bsc * drop MGCP client from osmo-bsc * fix vty tests: vty no longer goes to parent node implicitly * doc/examples: tweak osmo-bsc.cfg, add osmo-bsc_custom-sccp.cfg * add ';' after OSMO_ASSERT() [ Harald Welte ] * configure.ac: No more libosmogb dependency * configure.ac: remove --enable-osmo-bsc, --enable-nat * configure.ac: remove smpp_mirror, which has no relation to a BSC * contrib/jenkins.sh: MGCP is unconditional now * configure.ac: Remove --enable-mgcp-transcoding * configure.ac: Remove --enable-iu * configure.ac: Remove checks for libgtp + c-ares * configure.ac: Remove check for GMTOFF * configure.ac: Package is now called osmo-bsc, not openbsc * libbsc: document arguments of generate_bcch_chan_list() * Make sure BA-IND in all SI2xxx is '0' and in all SI5xxx is '1' * gsm0408_test: Verify that BA-IND is 0 in SI2xxx and 1 in SI5xxx * .gitignore: Update to post-NITB-split realities * Remove any references to RANAP and Iu * Fix nanobts_omlattr unit test * nanobts_omlattra_test: Initialize logging before executing tests * osmo-bsc: Initialize logging before initializing rate_ctr * Rename osmo_fsm to avoid illegal space in name + more meaningful name [ Max ] * Make TRX rf locking more visible * SI13: drop PBCCH-related bits * Wrap channel state assignment in macro * Further cleanup leftovers from BSC/MSC split * CTRL: cleanup write-only command functions * Show OML link uptime in vty * Fix repo split aftermath * SI2q: cleanup UARFCN addition * OML: consider RSL link state * SI2q: fix generation for multiple UARFCNs * Remove pkg-config file * ctrl: add oml-uptime command * SI1q: fix EARFCN appender [ Pau Espin Pedrol ] * Remove unneeded dbi dependency * bsc_api: Fix NULL secondary_lchan access in handle_ass_fail * libbsc: Use correct printf formatting for uint64_t * bsc_vty: Improve description of mid-call-text cmd -- Harald Welte Sat, 28 Oct 2017 11:19:03 +0200 osmo-bsc (0.1.0) UNRELEASED; urgency=low [ Alexander Couzens ] * Initial release. -- Alexander Couzens Tue, 08 Aug 2017 01:12:56 +0000 osmo-bsc-1.3.0/debian/compat000066400000000000000000000000021332665256100156660ustar00rootroot000000000000009 osmo-bsc-1.3.0/debian/control000066400000000000000000000043661332665256100161040ustar00rootroot00000000000000Source: osmo-bsc Section: net Priority: extra Maintainer: Alexander Couzens Build-Depends: debhelper (>=9), dh-autoreconf, autotools-dev, autoconf, automake, libtool, pkg-config, python-minimal, libtalloc-dev, libosmocore-dev (>= 0.12.0), libosmo-sccp-dev (>= 0.10.0), libosmo-sigtran-dev (>= 0.10.0), libosmo-abis-dev (>= 0.5.1), libosmo-netif-dev (>= 0.3.0), libosmo-mgcp-client-dev (>= 1.4.0) Standards-Version: 3.9.8 Vcs-Git: git://git.osmocom.org/osmo-bsc.git Vcs-Browser: https://git.osmocom.org/osmo-bsc/ Homepage: https://projects.osmocom.org/projects/osmo-bsc Package: osmo-bsc Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: OsmoBSC: Osmocom's Base Station Controller for 2G circuit-switched mobile networks Package: osmo-bsc-dbg Section: debug Architecture: any Multi-Arch: same Depends: osmo-bsc (= ${binary:Version}), ${misc:Depends} Description: OsmoBSC: Osmocom's Base Station Controller for 2G circuit-switched mobile networks Package: abisip-find Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: Command line utility to find ip.access compatible BTS Package: osmo-bsc-ipaccess-utils Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: Command line utilities for ip.access nanoBTS This package contains utilities that are specific for nanoBTS when being used together with OpenBSC. It contains mainly two tools: ipaccess-config and ipaccess-proxy. Package: osmo-bsc-bs11-utils Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: Command line utilities for Siemens BS-11 BTS There is a tool in this package for configuring the Siemens BS-11 BTS. Additionally, it contains one tool for making use of an ISDN-card and the public telephone network as frequency standard for the E1 line. Package: osmo-bsc-meas-utils Architecture: any Multi-Arch: foreign Depends: ${misc:Depends}, ${shlibs:Depends} Description: Command line utilities to manage measurement reports. osmo-bsc-1.3.0/debian/copyright000066400000000000000000000153611332665256100164310ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: osmo-bsc Source: git://git.osmocom.org/osmo-bsc Files: * Copyright: 2008-2015 Holger Hans Peter Freyther 2008-2016 Harald Welte 2009-2015 On-Waves 2010-2011 Daniel Willmann 2011-2017 sysmocom s.f.m.c. GmbH 2014-2015 Alexander Chemeris 2008 Jan Luebbe 2013 Andreas Eversberg License: AGPL-3.0+ 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 . Files: src/libbsc/bsc_ctrl_lookup.c src/libbsc/pcu_sock.c Copyright: 2008-2010 Harald Welte 2009-2012 Andreas Eversberg 2010-2011 Daniel Willmann 2010-2011 On-Waves 2012 Holger Hans Peter Freyther License: GPL-2.0+ 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. . On Debian systems, the complete text of the GNU General Public License Version 2 can be found in `/usr/share/common-licenses/GPL-2'. Files: osmoappdesc.py Copyright: 2013 Katerina Barone-Adesi License: GPL-3.0+ 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 . Most systems won't be able to use these, so they're separated out . On Debian systems, the complete text of the GNU General Public License Version 3 can be found in `/usr/share/common-licenses/GPL-3'. Files: tests/vty_test_runner.py Copyright: 2013 Holger Hans Peter Freyther 2013 Katerina Barone-Adesi License: GPL-3.0+ 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 . . On Debian systems, the complete text of the GNU General Public License Version 3 can be found in `/usr/share/common-licenses/GPL-3'. Files: include/mISDNif.h Copyright: 2008 Karsten Keil License: LGPL-2.1 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. . On Debian systems, the complete text of the GNU Lesser General Public License Version 2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'. Files: include/openbsc/bsc_msc_data.h Copyright: 2010-2015 Holger Hans Peter Freyther 2010-2015 On-Waves License: AGPL-3.0+ 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 . . NOTE: This is about a *remote* MSC for OsmoBSC and is not part of libmsc. Files: src/libbsc/bts_nokia_site.c Copyright: 2011 Dieter Spaar License: AGPL-3.0+ 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 . . TODO: Attention: There are some static variables used for states during configuration. Those variables have to be moved to a BTS specific context, otherwise there will most certainly be problems if more than one Nokia BTS is used. osmo-bsc-1.3.0/debian/osmo-bsc-bs11-utils.install000066400000000000000000000000451332665256100215030ustar00rootroot00000000000000usr/bin/bs11_config usr/bin/isdnsync osmo-bsc-1.3.0/debian/osmo-bsc-ipaccess-utils.install000066400000000000000000000000571332665256100225320ustar00rootroot00000000000000usr/bin/ipaccess-config usr/bin/ipaccess-proxy osmo-bsc-1.3.0/debian/osmo-bsc-meas-utils.install000066400000000000000000000000221332665256100216550ustar00rootroot00000000000000usr/bin/meas_json osmo-bsc-1.3.0/debian/osmo-bsc.install000066400000000000000000000003111332665256100175750ustar00rootroot00000000000000usr/bin/osmo-bsc usr/share/doc/osmo-bsc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg usr/share/doc/osmo-bsc/examples usr/share/doc/osmo-bsc/examples/osmo-bsc/osmo-bsc.cfg usr/share/doc/osmo-bsc/examples osmo-bsc-1.3.0/debian/osmo-bsc.service000077700000000000000000000000001332665256100262362../contrib/systemd/osmo-bsc.serviceustar00rootroot00000000000000osmo-bsc-1.3.0/debian/rules000077500000000000000000000040351332665256100155520ustar00rootroot00000000000000#!/usr/bin/make -f # You must remove unused comment lines for the released package. # See debhelper(7) (uncomment to enable) # This is an autogenerated template for debian/rules. # # Output every command that modifies files on the build system. #export DH_VERBOSE = 1 # # Copy some variable definitions from pkg-info.mk and vendor.mk # under /usr/share/dpkg/ to here if they are useful. # # See FEATURE AREAS/ENVIRONMENT in dpkg-buildflags(1) # Apply all hardening options #export DEB_BUILD_MAINT_OPTIONS = hardening=+all # Package maintainers to append CFLAGS #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic # Package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed # # With debhelper version 9 or newer, the dh command exports # all buildflags. So there is no need to include the # /usr/share/dpkg/buildflags.mk file here if compat is 9 or newer. # # These are rarely used code. (START) # # The following include for *.mk magically sets miscellaneous # variables while honoring existing values of pertinent # environment variables: # # Architecture-related variables such as DEB_TARGET_MULTIARCH: #include /usr/share/dpkg/architecture.mk # Vendor-related variables such as DEB_VENDOR: #include /usr/share/dpkg/vendor.mk # Package-related variables such as DEB_DISTRIBUTION #include /usr/share/dpkg/pkg-info.mk # # You may alternatively set them susing a simple script such as: # DEB_VENDOR ?= $(shell dpkg-vendor --query Vendor) # # These are rarely used code. (END) # # main packaging script based on dh7 syntax %: dh $@ --with autoreconf override_dh_auto_configure: dh_auto_configure -- $(CONFIGURE_FLAGS) # # Do not install libtool archive, python .pyc .pyo #override_dh_install: # dh_install --list-missing -X.la -X.pyc -X.pyo # See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg override_dh_strip: dh_strip -posmo-bsc --dbg-package=osmo-bsc-dbg # Print test results in case of a failure override_dh_auto_test: dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) osmo-bsc-1.3.0/debian/source/000077500000000000000000000000001332665256100157705ustar00rootroot00000000000000osmo-bsc-1.3.0/debian/source/format000066400000000000000000000000151332665256100171770ustar00rootroot000000000000003.0 (native) osmo-bsc-1.3.0/doc/000077500000000000000000000000001332665256100140135ustar00rootroot00000000000000osmo-bsc-1.3.0/doc/BS11-OML.txt000066400000000000000000000014121332665256100156450ustar00rootroot00000000000000The Siemens BS-11 supports the following additional GSM 12.21 OML operations: CREATE OBJECT abis_om_fom_hdr.obj_class can be A3: A5: ALCO, BBSIG, CCLK, GPSU, LI, PA A8: EnvaBTSE A9: BPORT the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the abis_om_fom_hdr.bts_nr indicates the type of the object. enum abis_bs11_objtype { BS11_OBJ_ALCO = 0x01, BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */ BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */ BS11_OBJ_CCLK = 0x04, BS11_OBJ_GPSU = 0x06, BS11_OBJ_LI = 0x07, BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/ }; In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx number. In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT shall be used. osmo-bsc-1.3.0/doc/Makefile.am000066400000000000000000000011541332665256100160500ustar00rootroot00000000000000SUBDIRS = \ examples \ $(NULL) msc: \ $(builddir)/handover.png \ $(builddir)/assignment.png \ $(builddir)/lchan-release.png \ $(builddir)/ms-channel-request.png \ $(builddir)/timeslot.png \ $(builddir)/lchan.png \ $(builddir)/ts-and-lchan-fsm-lifecycle.png \ $(builddir)/handover-inter-bsc-mo.png \ $(builddir)/handover-inter-bsc-mt.png \ $(NULL) dot: \ $(builddir)/timeslot-fsm.png \ $(builddir)/lchan-fsm.png \ $(NULL) $(builddir)/%.png: $(srcdir)/%.msc mscgen -T png -o $@ $< $(builddir)/%.png: $(srcdir)/%.dot dot -Tpng $< > $@ .PHONY: poll poll: while true; do $(MAKE) msc dot; sleep 1; done osmo-bsc-1.3.0/doc/assignment.msc000066400000000000000000000133151332665256100166720ustar00rootroot00000000000000msc { hscale=3; ms [label="MS/BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"]; ms note mgw_msc [label="lchan allocation sequence for BSSMAP Assignment Request"]; bsc_gscon <= mgw_msc [label="BSSMAP Assignment Request"]; bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_LCHAN"]; bsc_lchan <- bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"]; |||; --- [label="IF returned lchan is NULL"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; ---; |||; bsc_gscon box bsc_gscon [label="store lchan pointer in conn->lchan_for_assignment"]; bsc_lchan <- bsc_gscon [label="lchan_activate(FOR_ASSIGNMENT)"]; ...; |||; --- [label="on lchan FSM error or timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; --- [label="END: 'on error'"]; ...; ...; --- [label="IF lchan FSM decides that it is an lchan for speech"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"]; --- [label="IF there is an MGW endpoint for the BTS already (conn->user_plane.fi_bts)"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; --- [label="ELSE: no MGW endpoint for the BTS side yet"]; bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_CRCX_BTS"]; bsc_gscon box bsc_gscon [label="assignment_created_mgw_endpoint = true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"]; bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"]; bsc_mgcp => mgw_msc [label="CRCX (for BTS)"]; bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"]; ...; --- [label="On Timeout"]; bsc_mgcp note bsc_mgcp [label="On timeouit, the MGCP FSM will terminate, emitting the parent_term event set upon mgcp_conn_create():"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"]; bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate handler. It sets conn->user_plane.fi_bts = NULL."]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"]; bsc_lchan note bsc_gscon [label="conn FSM timeout handler exits and relies on the lchan FSM signalling error, which should actually happen immediately:"]; bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"]; bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"]; bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_LCHAN"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; --- [label="END: lchan FSM decides that it is an lchan for speech"]; ...; ...; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"]; bsc_gscon abox bsc_gscon [label="ST_ASSIGNMENT_\nWAIT_COMPLETE\nT10, 6s"]; ms <= bsc_gscon [label="RR Assignment"]; ...; --- [label="On Timeout"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_LCHAN_RELEASE"]; bsc_gscon box bsc_gscon [label="'forget' all about conn->lchan_for_assignment"]; --- [label="IF assignment_created_mgw_endpoint == true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; bsc_gscon note bsc_mgcp [label="If the MGW endpoint didn't exist before the Assignment, release it now. If there was one before this, it is probably still in use by a previous lchan, so keep it in place."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; --- [label="END: 'On Timeout'"]; ...; ms => bsc_gscon [label="RR Assignment Complete"]; bsc_gscon -> bsc_lchan [label="OLD lchan: LCHAN_EV_LCHAN_RELEASE"]; bsc_gscon box bsc_gscon [label="conn->lchan = conn->lchan_for_assignment"]; --- [label="IF: chan_mode a speech mode?"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_MDCX_BTS"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"]; bsc_mgcp note bsc_mgcp [label="same mgcp FSM as above, for BTS side"]; bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"]; bsc_mgcp => mgw_msc [label="MDCX (for BTS)"]; ...; --- [label="On Timeout"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request"]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="MDCX OK"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CRCX_MSC"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"]; bsc_mgcp note bsc_mgcp [label="second mgcp FSM for MSC side"]; bsc_mgcp => mgw_msc [label="CRCX (for MSC)"]; ...; --- [label="On Timeout"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Failure"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request"]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="CRCX OK (for MSC)"]; bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_CRCX_RESP_MSC"]; --- [label="END: chan_mode a speech mode?"]; bsc_gscon => mgw_msc [label="BSSMAP Assignment Complete"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; } osmo-bsc-1.3.0/doc/e1-data-model.txt000066400000000000000000000103361332665256100170710ustar00rootroot00000000000000E1 related data model This data model describes the physical relationship of the individual parts in the network, it is not the logical/protocol side of the GSM network. A BTS is connected to the BSC by some physical link. It could be an actual E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP. To further complicate the fact, multiple BTS can share one such pysical link. On a single E1 line, we can easily accomodate up to three BTS with two TRX each. Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's view of a BTS connected to it. We call this 'bts_link'. A bts_link can be * all the TCP and UDP streams of a Abis-over-IP BTS * a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link * a serial line exclusively used for OML messages (T-Link) A bts_link can be registered with the OpenBSC core at runtime. struct trx_link { struct gsm_bts_trx *trx; }; struct bts_link { struct gsm_bts *bts; struct trx_link trx_links[NUM_TRX]; }; Interface from stack to input core: ====================================================================== int abis_rsl_sendmsg(struct msgb *msg); send a message through a RSL link to the TRX specified by the caller in msg->trx. int abis_rsl_rcvmsg(struct msgb *msg); receive a message from a RSL link from the TRX specified by the caller in msg->trx. int abis_nm_sendmsg(struct msgb *msg); send a message through a OML link to the BTS specified by the caller in msg->trx->bts. The caller can just use bts->c0 to get the first TRX in a BTS. (OML messages are not really sent to a TRX but to the BTS) int abis_nm_rcvmsg(struct msgb *msg); receive a message from a OML link from the BTS specified by the caller in msg->trx->bts. The caller can just use bts->c0 to get the first TRX in a BTS. int abis_link_event(int event, void *data); signal some event (such as layer 1 connect/disconnect) from the input core to the stack. int subch_demux_in(mx, const uint8_t *data, int len); receive 'len' bytes from a given E1 timeslot (TRAU frames) int subchan_mux_out(mx, uint8_t *data, int len); obtain 'len' bytes of output data to be sent on E1 timeslot Intrface by Input Core for Input Plugins ====================================================================== int btslink_register_plugin(); Configuration for the E1 input module ====================================================================== BTS BTS number number of TRX OML link E1 line number timeslot number [subslot number] SAPI TEI for each TRX RSL link E1 line number timeslot number [subslot number] SAPI TEI for each TS E1 line number timeslot number subslot number E1 input module data model ====================================================================== enum e1inp_sign_type { E1INP_SIGN_NONE, E1INP_SIGN_OML, E1INP_SIGN_RSL, }; struct e1inp_sign_link { /* list of signalling links */ struct llist_head list; 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; } enum e1inp_ts_type { E1INP_TS_TYPE_NONE, E1INP_TS_TYPE_SIGN, E1INP_TS_TYPE_TRAU, }; /* A timeslot in the E1 interface */ struct e1inp_ts { enum e1inp_ts_type type; struct e1inp_line *line; union { struct { struct llist_head sign_links; } 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; } misdn; } driver; }; struct e1inp_line { unsigned int num; char *name; struct e1inp_ts ts[NR_E1_TS]; char *e1inp_driver; void *driver_data; }; /* 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); /* Send a packet, callback function in the driver */ int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg) struct e1inp_driver { const char *name; int (*want_write)(struct e1inp_ts *ts); }; osmo-bsc-1.3.0/doc/examples/000077500000000000000000000000001332665256100156315ustar00rootroot00000000000000osmo-bsc-1.3.0/doc/examples/Makefile.am000066400000000000000000000010201332665256100176560ustar00rootroot00000000000000CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' dist-hook: for f in $$($(CFG_FILES)); do \ j="$(distdir)/$$f" && \ mkdir -p "$$(dirname $$j)" && \ $(INSTALL_DATA) $(srcdir)/$$f $$j; \ done install-data-hook: for f in $$($(CFG_FILES)); do \ j="$(DESTDIR)$(docdir)/examples/$$f" && \ mkdir -p "$$(dirname $$j)" && \ $(INSTALL_DATA) $(srcdir)/$$f $$j; \ done uninstall-hook: @$(PRE_UNINSTALL) for f in $$($(CFG_FILES)); do \ j="$(DESTDIR)$(docdir)/examples/$$f" && \ $(RM) $$j; \ done osmo-bsc-1.3.0/doc/examples/osmo-bsc/000077500000000000000000000000001332665256100173535ustar00rootroot00000000000000osmo-bsc-1.3.0/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg000066400000000000000000000011461332665256100232040ustar00rootroot00000000000000network network country code 901 mobile network code 70 bts 0 type sysmobts band GSM-1800 location_area_code 23 ip.access unit_id 1800 0 trx 0 rf_locked 0 arfcn 868 nominal power 23 timeslot 0 phys_chan_config CCCH+SDCCH4 timeslot 1 phys_chan_config SDCCH8 timeslot 2 phys_chan_config TCH/F timeslot 3 phys_chan_config TCH/F timeslot 4 phys_chan_config TCH/F timeslot 5 phys_chan_config TCH/F timeslot 6 phys_chan_config TCH/F timeslot 7 phys_chan_config TCH/F e1_input e1_line 0 driver ipa msc 0 allow-emergency deny codec-list hr3 osmo-bsc-1.3.0/doc/examples/osmo-bsc/osmo-bsc.cfg000066400000000000000000000044311332665256100215600ustar00rootroot00000000000000! osmo-bsc default configuration ! (assumes STP to run on 127.0.0.1 and uses default point codes) ! e1_input e1_line 0 driver ipa network network country code 1 mobile network code 1 encryption a5 0 neci 1 paging any use tch 0 handover 0 handover algorithm 1 handover1 window rxlev averaging 10 handover1 window rxqual averaging 1 handover1 window rxlev neighbor averaging 10 handover1 power budget interval 6 handover1 power budget hysteresis 3 handover1 maximum distance 9999 dyn_ts_allow_tch_f 0 periodic location update 30 bts 0 type sysmobts band DCS1800 cell_identity 0 location_area_code 1 base_station_id_code 63 ms max power 15 cell reselection hysteresis 4 rxlev access min 0 radio-link-timeout 32 channel allocator ascending rach tx integer 9 rach max transmission 7 channel-descrption attach 1 channel-descrption bs-pa-mfrms 5 channel-descrption bs-ag-blks-res 1 early-classmark-sending forbidden ip.access unit_id 0 0 oml ip.access stream_id 255 line 0 neighbor-list mode manual-si5 neighbor-list add arfcn 100 neighbor-list add arfcn 200 si5 neighbor-list add arfcn 10 si5 neighbor-list add arfcn 20 codec-support fr gprs mode none no force-combined-si trx 0 rf_locked 0 arfcn 871 nominal power 23 ! to use full TRX power, set max_power_red 0 max_power_red 20 rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 hopping enabled 0 timeslot 1 phys_chan_config TCH/F hopping enabled 0 timeslot 2 phys_chan_config TCH/F hopping enabled 0 timeslot 3 phys_chan_config TCH/F hopping enabled 0 timeslot 4 phys_chan_config TCH/F hopping enabled 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 timeslot 6 phys_chan_config TCH/F hopping enabled 0 timeslot 7 phys_chan_config TCH/F hopping enabled 0 msc 0 no bsc-welcome-text no bsc-msc-lost-text no bsc-grace-text type normal allow-emergency allow amr-config 12_2k forbidden amr-config 10_2k forbidden amr-config 7_95k forbidden amr-config 7_40k forbidden amr-config 6_70k forbidden amr-config 5_90k allowed amr-config 5_15k forbidden amr-config 4_75k forbidden mgw remote-ip 127.0.0.1 mgw remote-port 2427 mgw endpoint-range 1 31 bsc mid-call-timeout 0 no missing-msc-text osmo-bsc-1.3.0/doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg000066400000000000000000000037661332665256100241120ustar00rootroot00000000000000! osmo-bsc configuration example for custom SCCP addresses ! e1_input e1_line 0 driver ipa network network country code 1 mobile network code 1 encryption a5 0 neci 1 paging any use tch 0 handover 0 handover algorithm 1 handover1 window rxlev averaging 10 handover1 window rxqual averaging 1 handover1 window rxlev neighbor averaging 10 handover1 power budget interval 6 handover1 power budget hysteresis 3 handover1 maximum distance 9999 dyn_ts_allow_tch_f 0 periodic location update 30 bts 0 type sysmobts band DCS1800 cell_identity 0 location_area_code 1 base_station_id_code 63 ms max power 15 cell reselection hysteresis 4 rxlev access min 0 radio-link-timeout 32 channel allocator ascending rach tx integer 9 rach max transmission 7 channel-descrption attach 1 channel-descrption bs-pa-mfrms 5 channel-descrption bs-ag-blks-res 1 early-classmark-sending forbidden ip.access unit_id 0 0 oml ip.access stream_id 255 line 0 neighbor-list mode manual-si5 neighbor-list add arfcn 100 neighbor-list add arfcn 200 si5 neighbor-list add arfcn 10 si5 neighbor-list add arfcn 20 codec-support fr gprs mode none no force-combined-si trx 0 rf_locked 0 arfcn 871 nominal power 23 ! to use full TRX power, set max_power_red 0 max_power_red 20 rsl e1 tei 0 timeslot 0 phys_chan_config CCCH+SDCCH4 hopping enabled 0 timeslot 1 phys_chan_config TCH/F hopping enabled 0 timeslot 2 phys_chan_config TCH/F hopping enabled 0 timeslot 3 phys_chan_config TCH/F hopping enabled 0 timeslot 4 phys_chan_config TCH/F hopping enabled 0 timeslot 5 phys_chan_config TCH/F hopping enabled 0 timeslot 6 phys_chan_config TCH/F hopping enabled 0 timeslot 7 phys_chan_config TCH/F hopping enabled 0 cs7 instance 0 point-code 0.42.1 !asp asp-clnt-OsmoBSC 2905 0 m3ua ! remote-ip 10.23.24.1 ! where to reach the STP sccp-address msc_remote point-code 0.23.1 msc 0 msc-addr msc_remote osmo-bsc-1.3.0/doc/handover-inter-bsc-mo.msc000066400000000000000000000030071332665256100206220ustar00rootroot00000000000000msc { hscale=2; ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], msc_[label="MSC"]; ms note msc_ [label="inter-BSC Handover to another BSS"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon box bsc_gscon [label="bsc_handover_start(): init conn->ho"]; bsc_gscon -> bsc_gscon [label="GSCON_EV_HO_START (inter-BSC MO)"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MO_\nWAIT_HO_CMD\nT7"]; bsc_gscon => msc_ [label="BSSMAP Handover Required"]; ...; --- [label="On Timeout"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; --- [label="END: 'On Timeout'"]; ...; bsc_gscon <= msc_ [label="BSSMAP Handover Command"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MO_\nWAIT_CLEAR_CMD\nT8"]; ms <= bsc_gscon [label="RR Handover Command"]; ...; --- [label="On Timeout"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; --- [label="END: 'On Timeout'"]; ...; msc_ note msc_ [label="Remote BSS reported Handover Complete to the MSC, this connection has been superseded."]; bsc_gscon <= msc_ [label="BSSMAP Clear Command"]; bsc_gscon abox bsc_gscon [label="ST_CLEARING"]; bsc_gscon => msc_ [label="BSSMAP Clear Complete"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; } osmo-bsc-1.3.0/doc/handover-inter-bsc-mt.msc000066400000000000000000000170711332665256100206350ustar00rootroot00000000000000msc { hscale=3; ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"]; ms note mgw_msc [label="inter-BSC Handover, from remote BSS"]; bsc_gscon <= mgw_msc [label="N-Connect: BSSMAP Handover Request"]; bsc_gscon box bsc_gscon [label="bsc_subscr_con_allocate()"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_LCHAN"]; bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode()"]; bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_TS_READY"]; ...; --- [label="on no lchan, lchan FSM error or timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; --- [label="END: 'on error'"]; ...; --- [label="IF lchan FSM decides that it is an lchan for speech"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_CRCX_BTS"]; bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"]; bsc_mgcp => mgw_msc [label="CRCX (for BTS)"]; bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"]; bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"]; ...; --- [label="On Timeout"]; bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term event set upon mgcp_conn_create():"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"]; bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate handler. It sets conn->user_plane.fi_bts = NULL."]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"]; bsc_lchan note bsc_gscon [label="conn FSM error handler exits and relies on the lchan FSM signalling error, which should actually happen immediately:"]; bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"]; bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_ACTIV_ACK"]; bsc_gscon note bsc_gscon [label="MSC-side CRCX needs from Handover Required the RTP IP address and port of the MSC's MGW; from MGW CRCX ACK (BTS) the conn->user_plane.mgw_endpoint; The Call-ID, which we actually derive from the SCCP connection ID"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_CRCX_MSC"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"]; bsc_mgcp note bsc_mgcp [label="second mgcp FSM for MSC side"]; bsc_mgcp => mgw_msc [label="CRCX (for MSC)"]; ...; --- [label="On Timeout"]; bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term event set upon mgcp_conn_create():"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_MSC"]; bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_MSC is handled by the conn FSM allstate handler. It sets conn->user_plane.fi_msc = NULL."]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete() (FSM for BTS)"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Failure"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="CRCX OK (for MSC)"]; bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_CRCX_RESP_MSC"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_LCHAN"]; --- [label="END: chan_mode a speech mode?"]; --- [label="END: lchan FSM decides that it is an lchan for speech"]; ...; ...; bsc_lchan note bsc_lchan [label="TODO: when does the MS send RLL Establish Ind? I guess like this:"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RLL_ESTABLISH"]; ms => bsc_lchan [label="RLL Establish Ind"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_\nWAIT_HO_ACCEPT\nsanity timer?"]; bsc_gscon box bsc_gscon [label="Compose RR Handover Command"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Request Acknowledge"]; mgw_msc note mgw_msc [label="MSC forwards the RR HO Cmd to the remote BSS"]; ...; --- [label="On timeout"]; bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; --- [label="END: On timeout"]; ...; ms => bsc_gscon [label="RR Handover Accept"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Detect"]; mgw_msc note mgw_msc [label="MSC switches call to new path"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_SABM\nT3105"]; ...; ms => bsc_gscon [label="SABM"]; bsc_lchan note bsc_lchan [label="SABM: Layer 2 message containing ?"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_\nWAIT_MDCX_BTS\nT?"]; bsc_lchan note bsc_lchan [label="TODO: what is UA?"]; ms <= bsc_gscon [label="RR UA"]; bsc_gscon note bsc_gscon [label="TODO: at what point do we know the information needed for BTS MDCX?"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"]; bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"]; bsc_mgcp => mgw_msc [label="MDCX (for BTS)"]; ...; --- [label="Should the Handover Complete arrive early"]; ms => bsc_gscon [label="RR Handover Complete"]; bsc_gscon box bsc_gscon [label="handover_complete_received = true"]; ---; ...; --- [label="On timeout of the mgcp FSM"]; bsc_gscon note mgw_msc [label="MGCP FSM terminates"]; bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_FAIL_BTS"]; bsc_lchan note bsc_gscon [label="The phone has already taken on the new lchan, but now we happen to not be able to use it. The only sensible thing is to end the conn."]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request\n(Equipment Failure)"]; bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; --- [label="END: On timeout of the mgcp FSM"]; ...; bsc_mgcp <= mgw_msc [label="MDCX OK"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"]; --- [label="IF !handover_complete_received"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_MT_WAIT_HO_COMPL\nT?"]; --- [label="ELSE"]; bsc_gscon -> bsc_gscon [label="gscon_handover_post_complete()"]; ---; ...; ms => bsc_gscon [label="RR Handover Complete"]; bsc_gscon box bsc_gscon [label="gscon_handover_post_complete()"]; bsc_gscon => mgw_msc [label="BSSMAP Handover Complete"]; bsc_gscon note bsc_gscon [label="handover_end(success), conn->ho = NULL"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; } osmo-bsc-1.3.0/doc/handover.msc000066400000000000000000000133571332665256100163360ustar00rootroot00000000000000# Handover between cells, intra-BSC msc { hscale=3; ms [label="MS via BTS"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], bsc_mgcp[label="BSC mgcp FSM"], mgw_msc[label="MGW/MSC"]; ms note mgw_msc [label="intra-BSC Handover sequence"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon box bsc_gscon [label="bsc_handover_start(): init conn->ho"]; bsc_gscon -> bsc_gscon [label="GSCON_EV_HO_START (intra-BSC)"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_LCHAN"]; bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"]; ...; --- [label="on lchan FSM error or timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; ms note bsc_gscon [label="MS happily continues on the old lchan."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; --- [label="END: 'on error'"]; ...; ...; --- [label="IF lchan FSM decides that it is an lchan for speech"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"]; --- [label="IF there is an MGW endpoint for the BTS already (conn->user_plane.fi_bts)"]; bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = false"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; --- [label="ELSE: no MGW endpoint for the BTS side yet"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_CRCX_BTS"]; bsc_gscon box bsc_gscon [label="handover_created_mgw_endpoint = true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_create()"]; bsc_mgcp => mgw_msc [label="CRCX (for BTS)"]; bsc_mgcp abox bsc_mgcp [label="ST_CRCX_RESP (MGCP_MGW_TIMEOUT = 4s)"]; bsc_gscon note bsc_mgcp [label="conn FSM relies on mgcp FSM timeout"]; ...; --- [label="On Timeout"]; bsc_mgcp note bsc_mgcp [label="On timeout, the MGCP FSM will terminate, emitting the parent_term event set upon mgcp_conn_create():"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS"]; bsc_gscon note bsc_gscon [label="GSCON_EV_MGW_FAIL_BTS is handled by the conn FSM allstate handler. It sets conn->user_plane.fi_bts = NULL."]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_ERROR"]; bsc_lchan note bsc_gscon [label="conn FSM error handler exits and relies on the lchan FSM signalling error, which should actually happen immediately:"]; bsc_gscon <- bsc_lchan [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; --- [label="IF handover_created_mgw_endpoint == true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; ---; ms note bsc_gscon [label="MS happily continues on the old lchan."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; --- [label="END: 'On Timeout'"]; ...; bsc_mgcp <= mgw_msc [label="CRCX OK (for BTS)"]; bsc_mgcp box bsc_mgcp [label="libosmo-mgcp-client fsm_crcx_resp_cb()"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_CRCX_RESP_BTS"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_LCHAN"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; --- [label="END: lchan FSM decides that it is an lchan for speech"]; ...; ...; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_DETECT\nT3103"]; bsc_gscon box bsc_gscon [label="gsm48_send_ho_cmd()"]; ms <= bsc_gscon [label="RR Handover Command"]; ...; --- [label="On timeout"]; bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; bsc_gscon box bsc_gscon [label="handover_end(fail)"]; --- [label="IF handover_created_mgw_endpoint == true"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; ---; ms note bsc_gscon [label="MS happily continues on the old lchan."]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; --- [label="END: On timeout"]; ...; ms => bsc_gscon [label="RR Handover Detect"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_MDCX_BTS\ncontinue T3103"]; bsc_gscon -> bsc_lchan [label="OLD lchan: LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; bsc_lchan note bsc_gscon [label="officially take over new lchan: conn->lchan = ho->new_lchan"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_modify()"]; bsc_mgcp note bsc_mgcp [label="mgcp FSM that was established for old lchan, for BTS side"]; bsc_mgcp abox bsc_mgcp [label="ST_MDCX_RESP"]; bsc_mgcp => mgw_msc [label="MDCX (for BTS)"]; ...; --- [label="Should the Handover Complete arrive early"]; ms => bsc_gscon [label="RR Handover Complete"]; bsc_gscon box bsc_gscon [label="handover_complete_received = true"]; ---; ...; --- [label="On timeout of the mgcp FSM"]; bsc_gscon note mgw_msc [label="MGCP FSM terminates"]; bsc_gscon <- bsc_mgcp [label="GSCON_EV_MGW_FAIL_BTS"]; bsc_lchan note bsc_gscon [label="The phone has already taken on the new lchan, but now we happen to not be able to use it. The only sensible thing is to end the conn."]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request\n(Equipment Failure)"]; bsc_lchan <- bsc_gscon [label="NEW lchan: LCHAN_EV_RELEASE"]; ms <=> bsc_lchan [label="release procedure (async)"]; bsc_gscon -> bsc_mgcp [label="mgcp_conn_delete()"]; ...; bsc_mgcp <= mgw_msc [label="MDCX OK"]; bsc_mgcp abox bsc_mgcp [label="ST_READY"]; bsc_mgcp -> bsc_gscon [label="GSCON_EV_MGW_MDCX_RESP_BTS"]; --- [label="IF !handover_complete_received"]; bsc_gscon abox bsc_gscon [label="ST_HANDOVER_\nWAIT_COMPLETE\ncontinue T3103"]; --- [label="ELSE"]; bsc_gscon -> bsc_gscon [label="gscon_handover_post_complete()"]; ---; ...; ms => bsc_gscon [label="RR Handover Complete"]; bsc_gscon box bsc_gscon [label="gscon_handover_post_complete()"]; bsc_gscon note bsc_gscon [label="handover_end(success), conn->ho = NULL"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; } osmo-bsc-1.3.0/doc/handover.txt000066400000000000000000000052421332665256100163650ustar00rootroot00000000000000Ideas about a handover algorithm ====================================================================== This is mostly based on the results presented in Chapter 8 of "Performance Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen and Joeroen Wigard. === Reasons for performing handover === Section 2.1.1: Handover used in their CAPACITY simulation: 1) Interference Handover Average RXLEV is satisfactory high, but average RXQUAL too low indicates interference to the channel. Handover should be made. 2) Bad Quality Averaged RXQUAL is lower than a threshold 3) Low Level / Signal Strength Average RXLEV is lower than a threshold 4) Distance Handover MS is too far away from a cell (measured by TA) 5) Power budget / Better Cell RX Level of neighbor cell is at least "HO Margin dB" dB better than the current serving cell. === Ideal parameters for HO algorithm === Chapter 8, Section 2.2, Table 24: Window RXLEV averaging: 10 SACCH frames (no weighting) Window RXQUAL averaging: 1 SACCH frame (no averaging) Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5 Interference Threshold: 1 of the last AV-RXLEV > -85 dBm & 3 of the last 4 AV-RXQUAL values >= 5 Power Budget: Level of neighbor cell > 3 dB better Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?) Distance Handover: Disabled Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm Evaluation rule 2: Level of candidate cell > 3dB better own cell Timer Successful HO: 5 SACCH frames Timer Unsuccessful HO: 1 SACCH frame In a non-frequency hopping case, RXQUAL threshold can be decreased to RXLEV >= 4 When frequency hopping is enabled, the following additional parameters should be introduced: * No intra-cell handover * Use a HO Margin of 2dB === Handover Channel Reservation === In loaded network, each cell should reserve some channels for handovers, rather than using all of them for new call establishment. This reduces the need to drop calls due to failing handovers, at the expense of failing new call attempts. === Dynamic HO Margin === The handover margin (hysteresis) should depend on the RXQUAL. Optimal results were achieved with the following settings: * RXQUAL <= 4: 9 dB * RXQUAL == 5: 6 dB * RXQUAL >= 6: 1 dB == Actual Handover on a protocol level == After the BSC has decided a handover shall be done, it has to # allocate a channel at the new BTS # allocate a handover reference # activate the channel on the BTS side using RSL CHANNEL ACTIVATION, indicating the HO reference # BTS responds with CHAN ACT ACK, including GSM frame number # BSC sends 04.08 HO CMD to MS using old BTS osmo-bsc-1.3.0/doc/lchan-fsm.dot000066400000000000000000000037501332665256100164000ustar00rootroot00000000000000digraph G { rankdir=TB; invisible [style="invisible"] UNUSED [penwidth=3.0] WAIT_TS_READY WAIT_MGW_ENDPOINT_AVAILABLE WAIT_ACTIV_ACK WAIT_IPACC_CRCX_ACK WAIT_IPACC_MDCX_ACK WAIT_RLL_ESTABLISH ACTIVE [penwidth=3.0] WAIT_SAPIS_RELEASED WAIT_BEFORE_RF_RELEASE WAIT_RF_RELEASE_ACK WAIT_AFTER_ERROR BORKEN ts [label="timeslot FSM",shape=box3d]; gscon [label="conn FSM",shape=box3d]; UNUSED -> WAIT_TS_READY [label="lchan_allocate()"] WAIT_TS_READY -> WAIT_ACTIV_ACK WAIT_ACTIV_ACK -> WAIT_RLL_ESTABLISH WAIT_RLL_ESTABLISH -> WAIT_MGW_ENDPOINT_AVAILABLE [label="TCH"] WAIT_MGW_ENDPOINT_AVAILABLE -> WAIT_IPACC_CRCX_ACK [label="IPACC BTS"] WAIT_MGW_ENDPOINT_AVAILABLE -> ACTIVE WAIT_IPACC_CRCX_ACK -> WAIT_IPACC_MDCX_ACK WAIT_IPACC_MDCX_ACK -> ACTIVE WAIT_RLL_ESTABLISH -> ACTIVE [label="non-TCH"]; WAIT_RLL_ESTABLISH -> WAIT_RF_RELEASE_ACK [label="timeout",style=dashed,constraint=false] ACTIVE -> WAIT_SAPIS_RELEASED [label="LCHAN_EV_\nRELEASE"] WAIT_SAPIS_RELEASED -> WAIT_BEFORE_RF_RELEASE WAIT_SAPIS_RELEASED -> WAIT_RF_RELEASE_ACK [label="timeout",style=dashed,constraint=false] WAIT_BEFORE_RF_RELEASE -> WAIT_RF_RELEASE_ACK [label="T3111"] WAIT_RF_RELEASE_ACK -> UNUSED WAIT_RF_RELEASE_ACK -> WAIT_AFTER_ERROR [label="release was\ndue to error"] WAIT_AFTER_ERROR -> UNUSED [label="T3111+2s"] WAIT_TS_READY -> ts [label="TS_EV_\nLCHAN_\nREQUESTED",style=dotted,penwidth=3] UNUSED -> ts [label="TS_EV_\nLCHAN_\nUNUSED",style=dotted,penwidth=3] ts -> WAIT_TS_READY [label="LCHAN_EV_\nTS_READY",style=dotted] WAIT_TS_READY -> UNUSED [label="error/timeout",style=dashed,constraint=false] {WAIT_ACTIV_ACK,WAIT_RF_RELEASE_ACK} -> BORKEN [label="error/timeout",style=dashed] {WAIT_MGW_ENDPOINT_AVAILABLE,WAIT_IPACC_CRCX_ACK,WAIT_IPACC_MDCX_ACK} -> WAIT_SAPIS_RELEASED [label=error,style=dashed] WAIT_TS_READY -> gscon [label="GSCON_EV_\nENSURE_\nMGW_ENDPOINT",style=dotted] gscon -> WAIT_MGW_ENDPOINT_AVAILABLE [label="LCHAN_EV_\nMGW_ENDPOINT_\n{AVAILABLE,ERROR}",style=dotted] } osmo-bsc-1.3.0/doc/lchan-release.msc000066400000000000000000000072611332665256100172300ustar00rootroot00000000000000msc { hscale=2; ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], msc_[label="MSC"]; ms note bsc_gscon [label="various lchan release scenarios"]; ms rbox msc_ [label="MSC releases"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; bsc_gscon abox bsc_gscon [label="ST_ACTIVE"]; bsc_gscon <= msc_ [label="BSSMAP Clear Command"]; bsc_gscon abox bsc_gscon [label="ST_CLEARING"]; bsc_gscon => msc_ [label="BSSMAP Clear Complete"]; bsc_gscon -> bsc_lchan [label="LCHAN_EV_RELEASE"]; --- [label="IF SAPIs besides SAPI[0] are active"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"]; bts <= bsc_lchan [label="RSL Release Request (Local End)..."]; bts <= bsc_lchan [label="...for each SAPI, except link_id=0"]; ms <= bsc_lchan [label="RR Channel Release"]; bts <= bsc_lchan [label="RSL Deactivate SACCH",ID="if appropriate pchan"]; ...; bts => bsc_lchan [label="RSL Release ACKs"]; --- [label="END: SAPIs besides SAPI[0] are active"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"]; bsc_gscon note bsc_gscon [label="has already forgotten the lchan above."]; ...; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; ...; bts => bsc_lchan [label="RSL RF Channel Release ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; ...; ...; ms rbox msc_ [label="BSC releases, outside of conn FSM's flow"]; bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"]; --- [label="IF SAPIs besides SAPI[0] are active"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"]; bts <= bsc_lchan [label="RSL Release Request (Local End)..."]; bts <= bsc_lchan [label="...for each SAPI, except link_id=0"]; ms <= bsc_lchan [label="RR Channel Release",ID="if conn is present"]; bts <= bsc_lchan [label="RSL Deactivate SACCH",ID="if appropriate pchan"]; ...; bts => bsc_lchan [label="RSL Release ACKs"]; --- [label="END: SAPIs besides SAPI[0] are active"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"]; bsc_gscon note bsc_gscon [label="conn FSM notices that its primary lchan is gone"]; bsc_gscon => msc_ [label="BSSMAP Clear Request"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; ...; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; ...; bts => bsc_lchan [label="RSL RF Channel Release ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; ...; bsc_gscon <= msc_ [label="BSSMAP Clear Command"]; bsc_gscon abox bsc_gscon [label="ST_CLEARING"]; bsc_gscon => msc_ [label="BSSMAP Clear Complete"]; ...; ...; ms rbox msc_ [label="MS releases"]; ms => bts [label="DISC"]; bts => bsc_lchan [label="RLL Release Ind..."]; bts => bsc_lchan [label="...for each SAPI"]; bsc_lchan note bsc_lchan [label="The lchan FSM notices when all SAPIs have been released"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"]; ...; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\n4s"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN"]; bsc_gscon note bsc_gscon [label="conn FSM notices that its primary lchan is gone"]; bsc_gscon => msc_ [label="BSSMAP Clear Request"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CLEAR_CMD"]; ...; bts => bsc_lchan [label="RSL RF Channel Release ACK"]; ...; bsc_gscon <= msc_ [label="BSSMAP Clear Command"]; bsc_gscon => msc_ [label="BSSMAP Clear Complete"]; } osmo-bsc-1.3.0/doc/lchan.msc000066400000000000000000000333671332665256100156200ustar00rootroot00000000000000msc { hscale=2; bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts [label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"], bsc_gscon[label="BSC conn FSM"], mgw_msc[label="MGW/MSC"]; bts box mgw_msc [label="lchan allocation sequence"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bts rbox mgw_msc [label="Channel Request from MS"]; bts => bsc [label="RSL Channel Request"]; bsc box bsc [label="lchan_select_by_type(chan_type)"]; bsc -> bsc_lchan [label="lchan_activate(lchan, FOR_MS_CHANNEL_REQUEST)"]; bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"]; |||; |||; bts rbox mgw_msc [label="Channel Request from BSSMAP Assignment"]; bsc_gscon <= mgw_msc [label="BSSMAP Assignment request"]; bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"]; bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_ASSIGNMENT)"]; bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"]; |||; |||; bts rbox mgw_msc [label="Channel Request from Handover Decision"]; bsc note bsc [label="target lchan typically already chosen by Handover Decision"]; bsc -> bsc_gscon [label="GSCON_EV_HO_START (intra-BSC)"]; bsc_lchan <- bsc_gscon [label="lchan_activate(lchan, FOR_HANDOVER)"]; bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"]; |||; |||; bts rbox mgw_msc [label="Channel Request from intra-BSC-MT-Handover"]; bsc_gscon <- mgw_msc [label="BSSMAP Handover Request"]; bsc_gscon box bsc_gscon [label="lchan_select_by_chan_mode(chan_mode)"]; bsc box bsc [label="lchan_activate(lchan, FOR_HANDOVER)"]; bsc_lchan rbox bsc_lchan [label="Continue at\nlchan_activate()\n"]; |||; |||; bts rbox mgw_msc [label="lchan_activate()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_\nWAIT_TS_READY\n(timeout: ? s, Tnnnn)"]; |||; |||; --- [label="TCH?"]; bsc_lchan note bsc_gscon [label="This is skipped when FOR_MS_CHANNEL_REQUEST. If the MS requests a TCH lchan, and we end up actually giving it a TCH because no SDCCH are available, we can not set up an RTP stream because there is not even an L3 conn yet."]; bsc_lchan note bsc_gscon [label="The lchan FSM asks the conn FSM to have an MGW endpoint ready as early as possible. Either the conn already has such MGW endpoint from a previous lchan, in which case it immediately replies, or it requests one from the MGW, in which case we wait for a response in 'TCH? (2)' below."]; bsc_lchan -> bsc_gscon [label="GSCON_EV_ENSURE_MGW_ENDPOINT"]; --- [label="IF conn has user_plane.fi_bts in state ST_READY"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"]; bsc_lchan note bsc_lchan [label="lchan_activate() continues"]; --- [label="ELSE (no MGW endpoint available yet)"]; bsc_gscon => mgw_msc [label="CRCX (for BTS) via mgcp_conn_create()"]; bsc_gscon abox bsc_gscon [label="ST_WAIT_CRCX_BTS\n(timeout: ? s, Tnnnn)"]; bsc_lchan <- bsc_gscon [label="(event dispatch returns)"]; bsc_lchan note bsc_lchan [label="lchan_activate() continues"]; ...; bsc_gscon note bsc_gscon [label="async:"]; bsc_gscon <= mgw_msc [label="CRCX OK (for BTS)"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"]; bsc_lchan note bsc_lchan [label="As soon as we reach LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE, this triggers immedate action (s.b.), but until then, only the flag gets set to true."]; ...; --- [label="CRCX timeout"]; bsc_gscon note bsc_gscon [label="conn FSM should fire on CRCX timeout"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_ERROR"]; bsc_gscon note bsc_gscon [label="conn FSM should not assume anything and wait for GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_lchan rbox bsc_lchan [label="Do 'On any error'"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"]; --- [label="END: 'TCH?'"]; |||; |||; bsc_lchan box bsc_lchan [label="lchan_activate() exits"]; bsc_lchan note bsc_lchan [label="still in\nlchan_request()\nLCHAN_ST_WAIT_\nTS_READY"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED"]; ...; --- [label="on error from TS or timeout:"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"]; bsc_lchan rbox bsc_lchan [label="Do 'On any error'"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"]; ---; ...; bsc_ts abox bsc_ts [label="TS_ST_IN_USE"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"]; bsc_lchan box bsc_lchan [label="lchan_fsm_\npre_lchan_activ()"]; |||; |||; bts rbox mgw_msc [label="mode FOR_MS_CHANNEL_REQUEST"]; bts note bsc_lchan [label="This is the simple case where the MS requested a channel, and there is no L3 conn to the MSC; no matter if this is SDDCH or a TCH channel type, we will not prepare an RTP stream."]; bsc_lchan note bsc_lchan [label="still in lchan_fsm_\npre_lchan_activ()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nACTIV_ACK\n(timeout: ? s, Tnnnn)"]; bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTRA_IMM_ASS)"]; bts note bsc_lchan [label="If any errors occur from now on, we don't want to send an RR Immediate Assignment Reject anymore."]; bsc_lchan box bsc_lchan [label="sent_chan_activ = true"]; ...; --- [label="on timeout"]; bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"]; ---; ...; bts => bsc_lchan [label="RSL Chan Activ ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\nT3101"]; bsc_lchan note bsc_lchan [label="Now the lchan is assigned, but has no L3 conn yet. On errors, this will either go into graceful release or into broken state, but will not trigger any events to a (non-existing) conn."]; ...; --- [label="on timeout"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RF_RELEASE_ACK\n(T?, 4s)"]; ---; ...; bts => bsc_lchan [label="RLL Establish Ind"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; |||; |||; bts rbox mgw_msc [label="modes FOR_ASSIGNMENT and FOR_HANDOVER"]; bsc_lchan note bsc_lchan [label="still in lchan_fsm_\npre_lchan_activ()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nACTIV_ACK\n(timeout: ? s, Tnnnn)"]; bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTRA_NORM_ASS)",ID=FOR_ASSIGNMENT]; bts <= bsc_lchan [label="RSL Chan Activ (RSL_ACT_INTER_ASYNC)",ID=FOR_HANDOVER]; ...; --- [label="on timeout"]; bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"]; ---; bts => bsc_lchan [label="RSL Chan Activ ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\nT3101"]; ...; --- [label="on timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_lchan -> bsc_lchan [label="lchan_fsm_pre_rf_release()"]; ---; ...; bts => bsc_lchan [label="RLL Establish Indication"]; |||; --- [label="TCH? (2)"]; --- [label="mgw_endpoint_available == false?"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nMGW_ENDPOINT_\nAVAILABLE"]; bsc_lchan note bsc_lchan [label="rely on conn FSM timeout; apply only a long sanity timeout."]; ...; bsc_gscon <= mgw_msc [label="CRCX OK (for BTS)"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_MGW_ENDPOINT_AVAILABLE"]; bsc_lchan box bsc_lchan [label="mgw_endpoint_available = true"]; bsc_lchan <- bsc_lchan [label="re-invoke lchan_fsm_pre_lchan_activ()"]; --- [label="END: 'TCH? (2)'"]; |||; --- [label="is BTS using IPA Abis? (osmo-bts, ip.access)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nIPACC_CRCX_ACK\n(timeout: ? s, Tnnnn)"]; bts <= bsc_lchan [label="IPACC CRCX"]; ...; --- [label="on timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_lchan -> bsc_lchan [label="lchan_graceful_release()"]; ---; ...; bts => bsc_lchan [label="IPACC CRCX ACK"]; bts note bsc_lchan [label="The IPACC CRCX ACK tells us what port the IPA Abis based BTS has assigned to this lchan. AoIP: we need to forward this to the MGW (BTS side) with an MDCX; SCCPlite: we forward this to the MSC during BSSMAP Assignment Complete (TODO: is this correct??)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nIPACC_MDCX_ACK\n(timeout: ? s, Tnnnn)"]; bts <= bsc_lchan [label="IPACC MDCX"]; bts note bsc_lchan [label="The IPACC MDCX tells IPA Abis based BTSes the IP address and RTP port assigned by the BTS side of the MGW. AoIP: the MGW CRCX (BTS) must thus happen before this; SCCPlite: the RTP port is already known from the timeslot+multiplex information."]; ...; --- [label="on timeout"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_lchan -> bsc_lchan [label="lchan_graceful_release()"]; ---; ...; bts => bsc_lchan [label="IPACC MDCX ACK"]; --- [label="END: is BTS using IPA Abis? (osmo-bts, ip.access)"]; |||; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; bsc_lchan box bsc_lchan [label="lchan_fsm_post_lchan_activ()"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ACTIVE"]; bts <= bsc_gscon [label="RR Assignment",ID="BSSMAP Assignment Request"]; bts <= bsc_gscon [label="RR Handover Command",ID="intra-BSC HO"]; bsc_gscon => mgw_msc [label="BSSMAP Handover\nRequest Acknowledge",ID="inter-BSC-MT HO"]; ...; ---[label="On error"]; bsc_lchan rbox bsc_lchan [label="Continue at 'When the lchan is no longer used'"]; ---; ...; bts => bsc_gscon [label="RR Assignment Complete",ID="BSSMAP Assignment Request"]; bts => bsc_gscon [label="RR Handover Detect",ID="intra-BSC HO"]; bts => bsc_gscon [label="RR Handover Accept",ID="inter-BSC-MT HO"]; bsc_gscon note bsc_gscon [label="conn FSM takes care of MGW endpoints for BTS side (possibly redirect) and MSC side (possibly create). More information in e.g. assignment.msc and handover.msc"]; ...; ...; ...; bts rbox mgw_msc [label="When the lchan is no longer used"]; --- [label="IF the MS or BTS release the lchan"]; bts -> bsc_lchan [label="RLL Release Ind for SAPI=0"]; --- [label="IF the BSC other than the conn FSM decides to release"]; bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"]; --- [label="IF the MSC or conn FSM release the lchan"]; bsc_lchan <- bsc_gscon [label="LCHAN_EV_RELEASE"]; ---; bsc note bsc_gscon [label="The LCHAN_EV_RELEASE's data pointer possibly indicates an error cause"]; bsc_lchan note bsc_gscon [label="If the conn FSM requested a release, it probably has already forgotten about this lchan. However, if the MS/BTS initiated the release, make sure the conn FSM is informed:"]; bsc_lchan box bsc_lchan [label="lchan_graceful_release()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nSAPIS_RELEASED\nT3109"]; --- [label="TCH and got as far as Chan Activ Ack?"]; bts <= bsc_lchan [label="RSL Deactivate SACCH"]; ---; bts <= bsc_lchan [label="RLL Release Request (Local End)..."]; bts <= bsc_lchan [label="...for all SAPIs except [0]"]; |||; --- [label="SAPI[0] in use?"]; bsc_lchan note bsc_lchan [label="for bts->nokia.no_loc_rel_cnf we do not expect Release Confirm messages and this state immediately advances to lchan_fsm_pre_rf_release()"]; ...; --- [label="on timeout"]; bsc_lchan box bsc_lchan [label="Anyway try RF Channel Release, continue with lchan_fsm_wait_before_rf_release()"]; ---; ...; bts => bsc_lchan [label="RLL Release Confirm..."]; bts => bsc_lchan [label="...for each SAPI except [0]"]; bsc_lchan box bsc_lchan [label="Stay in\nLCHAN_ST_WAIT_\nSAPIS_RELEASED\nuntil only SAPI[0] remains active"]; --- [label="END: 'SAPI[0] in use?'"]; |||; bsc_lchan box bsc_lchan [label="lchan_fsm_wait_before_rf_release()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nBEFORE_RF_RELEASE\nT3111"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_FORGET_LCHAN (data=lchan)"]; bsc_gscon note bsc_gscon [label="conn FSM immediately forgets about the lchan"]; bsc_gscon => mgw_msc [label="BSSMAP Clear Request?"]; ...; bsc_lchan box bsc_lchan [label="T3111 expires"]; bsc_lchan box bsc_lchan [label="lchan_fsm_pre_rf_release()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRF_RELEASE_ACK\nT3111"]; bsc_lchan box bsc_lchan [label="for each bsc_rll_req matching this lchan: disable timer, call cb(BSC_RLLR_IND_REL_IND)"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; ...; --- [label="on timeout"]; bsc_lchan rbox bsc_lchan [label="Continue at: 'On any error', 'unrecoverable'"]; ---; ...; bts => bsc_lchan [label="RSL RF Channel Release Ack"]; bsc_lchan box bsc_lchan [label="lchan_fsm_post_rf_release()"]; |||; --- [label="IF an error cause was indicated on LCHAN_EV_RELEASE"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nAFTER_ERROR\n(timeout: T3111+2 s, T?)"]; ...; bsc_lchan box bsc_lchan [label="timer expires"]; --- [label="END: 'an error cause was indicated on LCHAN_EV_RELEASE'"]; |||; bsc_lchan box bsc_lchan [label="lchan_fsm_release_complete()"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"]; bsc_ts abox bsc_ts [label="TS_ST_UNUSED"]; |||; |||; bts rbox mgw_msc [label="On any error"]; |||; --- [label="IF FOR_MS_CHANNEL_REQUEST && !sent_chan_activ"]; bts <= bsc_lchan [label="RR Immediate Assign Reject"]; |||; --- [label="IF FOR_ASSIGNMENT or FOR_HANDOVER"]; bsc_lchan -> bsc_gscon [label="GSCON_EV_LCHAN_ALLOC_ERROR"]; bsc_gscon note bsc_gscon [label="conn FSM shall immediately 'forget' the lchan"]; bsc_gscon => mgw_msc [label="BSSMAP\nAssignment Failure",ID=FOR_ASSIGNMENT]; bsc_gscon => mgw_msc [label="BSSMAP\nHandover Failure",ID="inter-BSC-MT HO"]; ---; |||; --- [label="IF unrecoverable error"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_BORKEN"]; bsc_lchan note bsc_lchan [label="The broken state usually stays around until the BTS disconnects."]; ...; bts note bsc_lchan [label="If an ACK comes in late, for specific BTS models, we may choose to 'repair' the lchan so that it is usable again."]; bts -> bsc_lchan [label="Chan Release ACK"]; bsc_lchan -> bsc_lchan [label="lchan_fsm_post_rf_release()"]; } osmo-bsc-1.3.0/doc/ms-channel-request.msc000066400000000000000000000053571332665256100202440ustar00rootroot00000000000000msc { hscale=2; ms [label="MS"], bts [label="BTS"], bsc[label="BSC"], bsc_lchan[label="BSC lchan FSM"]; ms note bsc_lchan [label="lchan allocation sequence for RSL Channel Request"]; ms => bts [label="RR Channel Request"]; bts => bsc [label="RSL Channel Request"]; bsc box bsc [label="rsl_rx_chan_rqd()"]; bsc note bsc [label="Obtain RACH data from Request: - Reference - Access Delay (TA) - Request Reason - Channel Type"]; bsc note bsc [label="If the reason is PDCH, the RACH Request is forwarded to PCU and BSC is no longer concerned (rsl_rx_pchan_rqd())."]; bsc note bsc [label="Always try to allocate an SDCCH regardless of the requested type, only if no SDCCH is available, look for the actually requested channel type."]; bsc box bsc [label="lchan_select_by_type(SDCCH)"]; --- [label="IF no lchan is available (neither SDCCH nor requested type)"]; bsc note bsc [label="Figure out T3122 value from bts->T3122, network->T3122 or GSM_T3122_DEFAULT"]; bsc box bsc [label="rsl_send_imm_ass_rej(wait_ind=T3122)"]; bsc note bsc [label="..."]; bts <= bsc [label="RR Immediate Assign Reject"]; ms <= bts [label="RR Immediate Assign Reject (possibly grouped with up to 4 others)"]; bsc note bsc [label="rsl_rx_pchan_rqd() exits, no channel is allocated."]; --- [label="END: no lchan is available"]; bsc box bsc [label="Store RACH data in lchan->rqd_ref, rqd_ta"]; bsc -> bsc_lchan [label="lchan_allocate(FOR_MS_CHANNEL_REQUEST)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_ACTIV_ACK\nT3103"]; bsc_lchan note bsc_lchan [label="The lchan FSM knows that FOR_MS_CHANNEL_REQUEST is about Immediate Assignment."]; bts <= bsc_lchan [label="RSL Chan Activ (Immediate Assignment)"]; ...; --- [label="on any error"]; bts <= bsc_lchan [label="RR Immediate Assign Reject"]; ms <= bts [label="RR Immediate Assign Reject (possibly grouped with up to 4 others)"]; ---; ...; bts => bsc_lchan [label="RSL Chan Activ ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_\nRLL_ESTABLISH\ncontinue T3103"]; ...; --- [label="on timeout"]; bsc_lchan box bsc_lchan [label="lchan_error_release(deact_sacch=true)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_RF_RELEASE_ACK"]; bts <= bsc_lchan [label="RLL Release Request (Local End)..."]; bts <= bsc_lchan [label="...for all SAPIs including [0]"]; bts <= bsc_lchan [label="RSL Deactivate SACCH"]; bts <= bsc_lchan [label="RSL RF Channel Release"]; ...; bts => bsc_lchan [label="RSL RF Channel Release ACK"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_WAIT_AFTER_ERROR"]; ...; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; ---; ms => bsc_lchan [label="RLL Establish Ind"]; bsc_lchan box bsc_lchan [label="associate lchan FSM with new conn FSM"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; } osmo-bsc-1.3.0/doc/timeslot-fsm.dot000066400000000000000000000022671332665256100171550ustar00rootroot00000000000000digraph G { rankdir=TB; invisible [style="invisible"] invisible2 [style="invisible"] NOT_INITIALIZED lchan [label="lchan FSM",shape=box3d]; UNUSED IN_USE BORKEN PDCH WAIT_PDCH_ACT WAIT_PDCH_DEACT invisible -> NOT_INITIALIZED [label="OML\nOpstart ACK",style=dotted] invisible2 -> NOT_INITIALIZED [label="RSL\nbootstrapped",style=dotted] NOT_INITIALIZED -> UNUSED [label="OML+RSL ready"] UNUSED -> IN_USE [label="first\nlchan\nrequested\nby lchan\nFSM"] IN_USE -> UNUSED [label="last lchan\nunused"] UNUSED -> PDCH [label="onenter:\ndedicated PDCH\nand GPRS\nis enabled"] UNUSED -> WAIT_PDCH_ACT [label="onenter:\ndyn TS\nand GPRS\nis enabled"] WAIT_PDCH_ACT -> PDCH [label="dyn TS:\nPDCH activated"] PDCH -> WAIT_PDCH_DEACT [label="dyn TS:\nlchan of specific\npchan requested"] WAIT_PDCH_DEACT -> UNUSED [label="lchan\nunused\n(e.g. error)",style=dashed] WAIT_PDCH_DEACT -> IN_USE [label="dyn TS:\nPDCH released"] lchan -> {UNUSED} [label="TS_EV_LCHAN_\nREQUESTED",style=dotted] {IN_USE} -> lchan [label="LCHAN_EV_\nTS_READY",style=dotted] lchan -> IN_USE [label="TS_EV_LCHAN_\nUNUSED",style=dotted] {WAIT_PDCH_ACT,WAIT_PDCH_DEACT} -> BORKEN [label=timeout,style=dashed] } osmo-bsc-1.3.0/doc/timeslot.msc000066400000000000000000000066571332665256100163750ustar00rootroot00000000000000msc { hscale=2; bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"]; bsc_ts abox bsc_ts [label="NOT_INITIALIZED (no timeout)"]; ...; bsc note bsc_ts [label="OML and RSL may be established in any order"]; bts => bsc_ts [label="OML: Channel OPSTART ACK"]; bsc -> bsc_ts [label="RSL bootstrapped"]; bsc_ts abox bsc_ts [label="UNUSED (no timeout)"]; |||; bts rbox bsc_lchan [label="UNUSED, onenter"]; bsc_ts abox bsc_ts [label="UNUSED"]; --- [label="GPRS enabled?"]; --- [label="IF: dedicated PDCH?"]; bsc_ts abox bsc_ts [label="PDCH (no timeout)"]; |||; --- [label="IF: dynamic timeslot"]; bsc_ts abox bsc_ts [label="WAIT_PDCH_ACT (?s, Tnnnn)"]; bts <= bsc_ts [label="RSL Chan Activ of PDCH",ID="Osmocom style"]; bts <= bsc_ts [label="RSL PDCH Act",ID="ip.access style"]; ...; --- [label="timeout:"]; bsc_ts abox bsc_ts [label="BORKEN"]; ---; ...; bts => bsc_ts [label="RSL RF Chan Activ ACK",ID="Osmocom style"]; bts => bsc_ts [label="RSL PDCH Act ACK",ID="ip.access style"]; bsc_ts abox bsc_ts [label="PDCH (no timeout)"]; --- [label="END: GPRS enabled?"]; ...; ...; bts rbox bsc_lchan [label="UNUSED, on event"]; bsc_ts abox bsc_ts [label="UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED (data=lchan)"]; bsc_ts abox bsc_ts [label="IN_USE"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"]; bts <= bsc_lchan [label="RSL Chan Activ (and so on)"]; ...; bts rbox bsc_lchan [label="IN_USE, second lchan"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED (data=lchan)"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"]; bts <= bsc_lchan [label="RSL Chan Activ (and so on)"]; ...; ...; bts rbox bsc_lchan [label="IN_USE, when lchan FSM releases (both regularly, or due to error)"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (data=lchan)"]; --- [label="IF all lchan->fi->state == LCHAN_ST_UNUSED"]; bsc_ts abox bsc_ts [label="UNUSED"]; ---; ...; ...; bts rbox bsc_lchan [label="PDCH on lchan request"]; bsc_ts note bsc_lchan [label="TS_EV_LCHAN_REQUESTED should only come in on lchans where it makes sense, both from TS kind as well as not conflicting with other users of the lchan."]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_REQUESTED"]; bsc_ts abox bsc_ts [label="WAIT_PDCH_DEACT (?s, Tnnnn)"]; bts <= bsc_ts [label="RSL RF Chan Release of PDCH",ID="Osmocom style"]; bts <= bsc_ts [label="RSL PDCH Deact",ID="ip.access style"]; ...; --- [label="timeout:"]; bsc_ts abox bsc_ts [label="BORKEN"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"]; ---; ...; bts => bsc_ts [label="RSL RF Chan Release ACK",ID="Osmocom style"]; bts => bsc_ts [label="RSL PDCH Deact ACK",ID="ip.access style"]; --- [label="IF all lchan->fi->state == LCHAN_ST_UNUSED"]; bsc_ts note bsc_lchan [label="If the lchan FSM decided to give up in the meantime, nr of active lchans might have dropped back to zero."]; bsc_ts abox bsc_ts [label="UNUSED"]; bsc_ts note bsc_ts [label="onenter at UNUSED state will trigger back to PDCH mode"]; |||; --- [label="IF at least one lchan->state != LCHAN_ST_UNUSED"]; bsc_ts abox bsc_ts [label="IN_USE"]; bsc_ts rbox bsc_ts [label="Continue at 'IN_USE' above"]; ...; ...; bts rbox bsc_lchan [label="on erratic event"]; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_ERROR"]; bsc_lchan box bsc_lchan [label="release lchan"]; ...; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"]; bsc_ts note bsc_ts [label="log error but ignore"]; ...; } osmo-bsc-1.3.0/doc/ts-and-lchan-fsm-lifecycle.msc000066400000000000000000000114411332665256100215110ustar00rootroot00000000000000msc { hscale=2; bts [label="MS/BTS"], bsc[label="BSC"], bsc_ts[label="BSC timeslot FSM"], bsc_lchan[label="BSC lchan FSM"]; bsc box bsc [label="gsm_bts_alloc()"]; bsc box bsc [label="bts->c0 = gsm_bts_trx_alloc()"]; bsc -> bsc_ts; bsc_ts box bsc_ts [label="trx->ts[*].fi = osmo_fsm_inst_alloc(timeslot_fsm)"]; bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"]; bsc -> bsc_lchan; bsc_lchan box bsc_lchan [label="ts->lchan[*].ts = ts;\nts->lchan[*].nr = i;"]; bsc_lchan box bsc_lchan [label="ts->lchan[*].fi = NULL"]; bsc_ts note bsc_lchan [label="lchan_select() will only pick lchans from initialized timeslots of the right pchan kind. lchan_select() shall OSMO_ASSERT(lchan->fi)."]; ...; ...; bts rbox bsc_lchan [label="reading config file"]; ...; bsc box bsc [label="timeslot N"]; bsc box bsc [label="phys_chan_config X"]; bsc_ts box bsc_ts [label="ts->pchan_from_config = X"]; bsc_ts note bsc_ts [label="still TS_ST_NOT_INITIALIZED"]; ...; bsc box bsc [label="trx 1..*"]; bsc box bsc [label="bts->trx_list add gsm_bts_trx_alloc()"]; bsc_ts rbox bsc_lchan [label="same as for c0 above"]; ...; ...; bts rbox bsc_lchan [label="Starting Operation"]; bts => bsc_ts [label="OML Channel OPSTART ACK"]; bsc_ts box bsc_ts [label="ts_on_oml_opstart()"]; bsc_ts box bsc_ts [label="ts->pchan_on_init = pchan_from_config"]; --- [label="IF dedicated TS"]; bsc_ts box bsc_ts [label="ts->pchan = ts->pchan_on_init"]; --- [label="ELSE: dyn TS"]; bsc_ts box bsc_ts [label="ts->pchan = NONE"]; --- [label="END: dyn TS"]; bsc_ts note bsc_lchan [label="Normally, the lchan FSM never terminates. Logic dictates that the lchan is a child of the timeslot FSM, but it's not actually of functional importance beyond basic sanity. Parent term event: TS_EV_LCHAN_UNUSED"]; bsc_ts box bsc_ts [label="Determine N = maximum number of lchans applicable to pchan_on_init"]; bsc_ts -> bsc_lchan; bsc_lchan box bsc_lchan [label="ts->lchan[all N].type = osmo_fsm_inst_alloc(lchan_fsm)"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED\n(initial state)"]; bsc_ts -> bsc_ts [label="ts_check_init()"]; ...; bsc -> bsc_ts [label="RSL bootstrapped"]; bsc_ts -> bsc_ts [label="ts_check_init()"]; ...; bsc_ts box bsc_ts [label="ts_check_init()"]; --- [label="as soon as both OML and RSL are ready:"]; bsc_ts box bsc_ts [label="ts_on_init()"]; bsc_ts abox bsc_ts [label="TS_ST_UNUSED"]; --- [label="dyn TS"]; bsc_ts box bsc_ts [label="onenter of TS_ST_UNUSED:"]; bsc_ts abox bsc_ts [label="TS_ST_WAIT_PDCH_ACT"]; ...; bsc_ts abox bsc_ts [label="PDCH"]; --- [label="END: dyn TS"]; --- [label="END: OML and RSL ready"]; ...; bsc box bsc [label="lchan_select() picks an unused lchan"]; bsc -> bsc_lchan [label="lchan_allocate()"]; bsc_lchan -> bsc_ts [label="TS_EV_LCHAN_REQUESTED"]; --- [label="dyn TS"]; bsc_ts note bsc_ts [label="possibly switch from PDCH...\n(see timeslot FSM)"]; bsc_ts box bsc_ts [label="ts->pchan =\n requested GSM_PCHAN_XXX type"]; ---; bsc_ts -> bsc_lchan [label="LCHAN_EV_TS_READY"]; bsc_lchan note bsc_lchan [label="RSL Chan Alloc and so fort..."]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_ACTIVE"]; ...; bsc -> bsc_lchan [label="LCHAN_EV_RELEASE"]; bsc_lchan note bsc_lchan [label="...RSL RF Chan Release..."]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED"]; bsc_ts abox bsc_ts [label="TS_ST_UNUSED"]; --- [label="dyn TS"]; bsc_ts note bsc_ts [label="possibly switch to PDCH"]; ---; ...; ...; bts rbox bsc_lchan [label="BTS RSL is dropped"]; bsc box bsc [label="ipaccess_drop_rsl()"]; bsc -> bsc_ts [label="ts[*]:"]; bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"]; bsc_ts note bsc_lchan [label="If it's just the RSL being dropped, transition lchan FSMs to LCHAN_ST_UNUSED, but keep them allocated. Unless OML is re-established, any vty pchan modifications must not take effect."]; bsc_ts -> bsc_lchan [label="lchan[*]:"]; bsc_lchan abox bsc_lchan [label="LCHAN_ST_UNUSED"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (ignored)"]; ...; --- [label="when RSL comes back later:"]; bsc -> bsc_ts [label="RSL bootstrapped"]; bsc_ts box bsc_ts [label="ts_check_init()"]; bsc_ts rbox bsc_ts [label="see ts_check_init() above"]; ...; ...; bts rbox bsc_lchan [label="BTS OML is dropped"]; bsc note bsc [label="As part of OML drop, RSL is also dropped:"]; bsc box bsc [label="ipaccess_drop_rsl()"]; bsc -> bsc_ts [label="ts[*]:"]; bsc_ts abox bsc_ts [label="TS_ST_NOT_INITIALIZED"]; bsc rbox bsc [label="see 'BTS RSL is dropped' above"]; bsc -> bsc_ts [label="ts[*]:"]; bsc_ts -> bsc_lchan [label="lchan[*]:"]; bsc_lchan box bsc_lchan [label="osmo_fsm_inst_term()"]; bsc_ts <- bsc_lchan [label="TS_EV_LCHAN_UNUSED (ignored)"]; bsc_lchan box bsc_lchan [label="lchan->fi = NULL"]; bsc rbox bsc [label="Continue at 'Starting Operation'"]; } osmo-bsc-1.3.0/git-version-gen000077500000000000000000000125001332665256100162070ustar00rootroot00000000000000#!/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: osmo-bsc-1.3.0/include/000077500000000000000000000000001332665256100146715ustar00rootroot00000000000000osmo-bsc-1.3.0/include/Makefile.am000066400000000000000000000001361332665256100167250ustar00rootroot00000000000000SUBDIRS = \ osmocom \ $(NULL) noinst_HEADERS = \ mISDNif.h \ compat_af_isdn.h \ $(NULL) osmo-bsc-1.3.0/include/compat_af_isdn.h000066400000000000000000000010331332665256100200050ustar00rootroot00000000000000#ifdef MISDN_OLD_AF_COMPATIBILITY #undef AF_ISDN #undef PF_ISDN extern int AF_ISDN; #define PF_ISDN AF_ISDN int AF_ISDN; #endif extern void init_af_isdn(void); #ifdef AF_COMPATIBILITY_FUNC #ifdef MISDN_OLD_AF_COMPATIBILITY void init_af_isdn(void) { int s; /* test for new value */ AF_ISDN = 34; s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); if (s >= 0) { close(s); return; } AF_ISDN = 27; s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); if (s >= 0) { close(s); return; } } #else void init_af_isdn(void) { } #endif #endif osmo-bsc-1.3.0/include/mISDNif.h000066400000000000000000000232701332665256100162770ustar00rootroot00000000000000/* * * 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 */ osmo-bsc-1.3.0/include/osmocom/000077500000000000000000000000001332665256100163455ustar00rootroot00000000000000osmo-bsc-1.3.0/include/osmocom/Makefile.am000066400000000000000000000000341332665256100203760ustar00rootroot00000000000000SUBDIRS = \ bsc \ $(NULL) osmo-bsc-1.3.0/include/osmocom/bsc/000077500000000000000000000000001332665256100171145ustar00rootroot00000000000000osmo-bsc-1.3.0/include/osmocom/bsc/Makefile.am000066400000000000000000000015001332665256100211440ustar00rootroot00000000000000noinst_HEADERS = \ a_reset.h \ abis_nm.h \ abis_om2000.h \ abis_rsl.h \ acc_ramp.h \ arfcn_range_encode.h \ bsc_msg_filter.h \ bsc_rll.h \ bsc_subscriber.h \ bsc_subscr_conn_fsm.h \ bss.h \ bts_ipaccess_nanobts_omlattr.h \ chan_alloc.h \ codec_pref.h \ ctrl.h \ debug.h \ e1_config.h \ gsm_04_08_utils.h \ gsm_04_80.h \ gsm_data.h \ handover.h \ handover_cfg.h \ handover_decision.h \ handover_decision_2.h \ handover_vty.h \ ipaccess.h \ meas_feed.h \ meas_rep.h \ misdn.h \ network_listen.h \ openbscdefines.h \ osmo_bsc.h \ osmo_bsc_grace.h \ osmo_bsc_rf.h \ osmo_bsc_sigtran.h \ bsc_msc_data.h \ osmux.h \ paging.h \ pcu_if.h \ pcuif_proto.h \ rest_octets.h \ rs232.h \ signal.h \ system_information.h \ ussd.h \ vty.h \ bsc_api.h \ penalty_timers.h \ osmo_bsc_lcls.h \ $(NULL) osmo-bsc-1.3.0/include/osmocom/bsc/a_reset.h000066400000000000000000000025471332665256100207170ustar00rootroot00000000000000/* (C) 2017 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 . * */ #pragma once /* Create and start state machine which handles the reset/reset-ack procedure */ struct osmo_fsm_inst *a_reset_alloc(void *ctx, const char *name, void *cb, void *priv); /* Confirm that we sucessfully received a reset acknowlege message */ void a_reset_ack_confirm(struct osmo_fsm_inst *reset_fsm); /* Report a failed connection */ void a_reset_conn_fail(struct osmo_fsm_inst *reset_fsm); /* Report a successful connection */ void a_reset_conn_success(struct osmo_fsm_inst *reset_fsm); /* Check if we have a connection to a specified msc */ bool a_reset_conn_ready(struct osmo_fsm_inst *reset_fsm); osmo-bsc-1.3.0/include/osmocom/bsc/abis_nm.h000066400000000000000000000163761332665256100207120ustar00rootroot00000000000000/* GSM Network Management messages on the A-bis interface * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ /* (C) 2008-2009 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 . * */ #ifndef _NM_H #define _NM_H #include #include #include #include /* max number of attributes represented as 3GPP TS 52.021 §9.4.62 SW Description array */ #define MAX_BTS_ATTR 5 /* The BCCH info from an ip.access test, in host byte order * and already parsed... */ struct ipac_bcch_info { struct llist_head list; uint16_t info_type; uint8_t freq_qual; uint16_t arfcn; uint8_t rx_lev; uint8_t rx_qual; int16_t freq_err; uint16_t frame_offset; uint32_t frame_nr_offset; uint8_t bsic; struct osmo_cell_global_id cgi; uint8_t ba_list_si2[16]; uint8_t ba_list_si2bis[16]; uint8_t ba_list_si2ter[16]; uint8_t ca_list_si1[16]; }; /* PUBLIC */ struct msgb; struct abis_nm_cfg { /* callback for unidirectional reports */ int (*report_cb)(struct msgb *, struct abis_om_fom_hdr *); /* callback for software activate requests from BTS */ int (*sw_act_req)(struct msgb *); }; extern int abis_nm_rcvmsg(struct msgb *msg); int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len); int abis_nm_rx(struct msgb *msg); int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2); int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state); int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, const uint8_t *attr, uint8_t attr_len); int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len); int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb); int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len); int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg); int abis_nm_event_reports(struct gsm_bts *bts, int on); int abis_nm_reset_resource(struct gsm_bts *bts); int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data); int abis_nm_software_load_status(struct gsm_bts *bts); int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, gsm_cbfn *cbfn, void *cb_data); int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, uint8_t e1_port1, uint8_t ts1); int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t test_nr, uint8_t auton_report, struct msgb *msg); /* Siemens / BS-11 specific */ int abis_nm_bs11_reset_resource(struct gsm_bts *bts); int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx, uint8_t attr_len, const uint8_t *attr); int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_delete_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx); int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx); int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); int abis_nm_bs11_get_serno(struct gsm_bts *bts); int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level); int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on); int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); int abis_nm_bs11_get_cclk(struct gsm_bts *bts); int abis_nm_bs11_get_state(struct gsm_bts *bts); int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn); int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport); int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg); int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); int abis_nm_bs11_restart(struct gsm_bts *bts); /* ip.access nanoBTS specific commands */ int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, int attr_len); int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len); int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, uint32_t ip, uint16_t port, uint8_t stream); void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts); int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf); const char *ipacc_testres_name(uint8_t res); /* Functions calling into other code parts */ bool all_trx_rsl_connected_unlocked(const struct gsm_bts *bts); bool nm_is_running(const struct gsm_nm_state *s); int abis_nm_vty_init(void); void abis_nm_clear_queue(struct gsm_bts *bts); int _abis_nm_sendmsg(struct msgb *msg); void abis_nm_queue_send_next(struct gsm_bts *bts); /* for bs11_config. */ int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw, const size_t len); /* Helper functions for updating attributes */ int abis_nm_update_max_power_red(struct gsm_bts_trx *trx); struct gsm_bts_trx_ts *abis_nm_get_ts(const struct msgb *oml_msg); #endif /* _NM_H */ osmo-bsc-1.3.0/include/osmocom/bsc/abis_om2000.h000066400000000000000000000076551332665256100212150ustar00rootroot00000000000000#ifndef OPENBSC_ABIS_OM2K_H #define OPENBSC_ABIS_OM2K_H /* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface * implemented based on protocol trace analysis, no formal documentation */ /* (C) 2010-2011 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 . * */ enum abis_om2k_mo_cls { OM2K_MO_CLS_TRXC = 0x01, OM2K_MO_CLS_TS = 0x03, OM2K_MO_CLS_TF = 0x04, OM2K_MO_CLS_IS = 0x05, OM2K_MO_CLS_CON = 0x06, OM2K_MO_CLS_DP = 0x07, OM2K_MO_CLS_CF = 0x0a, OM2K_MO_CLS_TX = 0x0b, OM2K_MO_CLS_RX = 0x0c, }; enum om2k_mo_state { OM2K_MO_S_RESET = 0, OM2K_MO_S_STARTED, OM2K_MO_S_ENABLED, OM2K_MO_S_DISABLED, }; /* on-wire format for IS conn group */ struct om2k_is_conn_grp { uint16_t icp1; uint16_t icp2; uint8_t cont_idx; } __attribute__ ((packed)); /* internal data formant for IS conn group */ struct is_conn_group { struct llist_head list; uint16_t icp1; uint16_t icp2; uint8_t ci; }; /* on-wire format for CON Path */ struct om2k_con_path { uint16_t ccp; uint8_t ci; uint8_t tag; uint8_t tei; } __attribute__ ((packed)); /* internal data format for CON group */ struct con_group { /* links list of CON groups in BTS */ struct llist_head list; struct gsm_bts *bts; /* CON Group ID */ uint8_t cg; /* list of CON paths in this group */ struct llist_head paths; }; /* internal data format for CON path */ struct con_path { /* links with con_group.paths */ struct llist_head list; /* CON Connection Point */ uint16_t ccp; /* Contiguity Index */ uint8_t ci; /* Tag */ uint8_t tag; /* TEI */ uint8_t tei; }; extern const struct abis_om2k_mo om2k_mo_cf; extern const struct abis_om2k_mo om2k_mo_is; extern const struct abis_om2k_mo om2k_mo_con; extern const struct abis_om2k_mo om2k_mo_tf; extern const struct value_string om2k_mo_class_short_vals[]; int abis_om2k_rcvmsg(struct msgb *msg); extern const struct abis_om2k_mo om2k_mo_cf; int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational); int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); int abis_om2k_tx_is_conf_req(struct gsm_bts *bts); int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts); int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); struct osmo_fsm_inst *om2k_bts_fsm_start(struct gsm_bts *bts); void abis_om2k_bts_init(struct gsm_bts *bts); void abis_om2k_trx_init(struct gsm_bts_trx *trx); int abis_om2k_vty_init(void); struct vty; void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts); #endif /* OPENBCS_ABIS_OM2K_H */ osmo-bsc-1.3.0/include/osmocom/bsc/abis_rsl.h000066400000000000000000000103361332665256100210660ustar00rootroot00000000000000/* GSM Radio Signalling Link messages on the A-bis interface * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (C) 2008 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 . * */ #ifndef _RSL_H #define _RSL_H #include #include #include #include #include #include struct gsm_bts; struct gsm_lchan; struct gsm_bts_trx_ts; #define GSM48_LEN2PLEN(a) (((a) << 2) | 1) #define rsl_lchan_set_state(lch_, st_) \ rsl_lchan_set_state_with_log(lch_, st_, __BASE_FILE__, __LINE__) int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len); int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len); int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref); int rsl_chan_mode_modify_req(struct gsm_lchan *ts); int rsl_encryption_cmd(struct msgb *msg); int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs); int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val); int rsl_data_request(struct msgb *msg, uint8_t link_id); int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id); int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id); /* Ericcson vendor specific RSL extensions */ int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val); /* Siemens vendor-specific RSL extensions */ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); /* ip.access specfic RSL extensions */ int rsl_ipacc_crcx(struct gsm_lchan *lchan); int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, uint8_t rtp_payload2); int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act); int abis_rsl_rcvmsg(struct msgb *msg); int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, enum rsl_rel_mode release_mode); int rsl_lchan_set_state_with_log(struct gsm_lchan *lchan, enum gsm_lchan_state state, const char *file, unsigned line); int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *broken); /* to be provided by external code */ int rsl_deact_sacch(struct gsm_lchan *lchan); /* BCCH related code */ int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf); int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, const uint8_t *data, int len); int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db); int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm); /* SMSCB functionality */ int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, struct rsl_ie_cb_cmd_type cb_command, const uint8_t *data, int len); /* some Nokia specific stuff */ int rsl_nokia_si_begin(struct gsm_bts_trx *trx); int rsl_nokia_si_end(struct gsm_bts_trx *trx); /* required for Nokia BTS power control */ int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction); int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, enum rsl_rel_mode release_mode); int rsl_start_t3109(struct gsm_lchan *lchan); int rsl_direct_rf_release(struct gsm_lchan *lchan); void dyn_ts_init(struct gsm_bts_trx_ts *ts); int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config to_pchan); #endif /* RSL_MT_H */ osmo-bsc-1.3.0/include/osmocom/bsc/acc_ramp.h000066400000000000000000000133461332665256100210410ustar00rootroot00000000000000/* (C) 2018 by sysmocom s.f.m.c. GmbH * * Author: Stefan Sperling * * 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 . * */ #pragma once #include #include #include #include /*! * Access control class (ACC) ramping is used to slowly make the cell available to * an increasing number of MS. This avoids overload at startup time in cases where * a lot of MS would discover the new cell and try to connect to it all at once. */ #define ACC_RAMP_STEP_SIZE_MIN 1 /* allow at most 1 new ACC per ramp step */ #define ACC_RAMP_STEP_SIZE_DEFAULT ACC_RAMP_STEP_SIZE_MIN #define ACC_RAMP_STEP_SIZE_MAX 10 /* allow all ACC in one step (effectively disables ramping) */ #define ACC_RAMP_STEP_INTERVAL_MIN 30 /* 30 seconds */ #define ACC_RAMP_STEP_INTERVAL_MAX 600 /* 10 minutes */ /*! * Data structure used to manage ACC ramping. Please avoid setting or reading fields * in this structure directly. Use the accessor functions below instead. */ struct acc_ramp { struct gsm_bts *bts; /*!< backpointer to BTS using this ACC ramp */ bool acc_ramping_enabled; /*!< whether ACC ramping is enabled */ /*! * Bitmask which keeps track of access control classes that are currently denied * access. The function acc_ramp_apply() uses this mask to modulate bits from * octets 2 and 3 in RACH Control Parameters (see 3GPP 44.018 10.5.2.29). * Ramping is only concerned with ACCs 0-9. While any of the bits 0-9 is set, * the corresponding ACC is barred. * ACCs 11-15 should always be allowed, and ACC 10 denies emergency calls for * all ACCs from 0-9 inclusive; these ACCs are ignored in this implementation. */ uint16_t barred_accs; /*! * This controls the maximum number of ACCs to allow per ramping step (1 - 10). * The compile-time default value is ACC_RAMP_STEP_SIZE_DEFAULT. * This value can be changed by VTY configuration. * A value of ACC_RAMP_STEP_SIZE_MAX effectively disables ramping. */ unsigned int step_size; /*! * Ramping step interval in seconds. * This value depends on the current BTS channel load average, unless * it has been overriden by VTY configuration. */ unsigned int step_interval_sec; bool step_interval_is_fixed; struct osmo_timer_list step_timer; }; /*! * Enable or disable ACC ramping. * When enabled, ramping begins once acc_ramp_start() is called. * When disabled, an ACC ramping process in progress will continue * unless acc_ramp_abort() is called as well. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline void acc_ramp_set_enabled(struct acc_ramp *acc_ramp, bool enable) { acc_ramp->acc_ramping_enabled = enable; } /*! * Return true if ACC ramping is currently enabled, else false. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline bool acc_ramp_is_enabled(struct acc_ramp *acc_ramp) { return acc_ramp->acc_ramping_enabled; } /*! * Return the current ACC ramp step size. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline unsigned int acc_ramp_get_step_size(struct acc_ramp *acc_ramp) { return acc_ramp->step_size; } /*! * Return the current ACC ramp step interval (in seconds) * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline unsigned int acc_ramp_get_step_interval(struct acc_ramp *acc_ramp) { return acc_ramp->step_interval_sec; } /*! * If the step interval is dynamic, return true, else return false. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline bool acc_ramp_step_interval_is_dynamic(struct acc_ramp *acc_ramp) { return !(acc_ramp->step_interval_is_fixed); } /*! * Return bitmasks which correspond to access control classes that are currently * denied access. Ramping is only concerned with those bits which control access * for ACCs 0-9, and any of the other bits will always be set to zero in these masks, i.e. * it is safe to OR these bitmasks with the corresponding fields in struct gsm48_rach_control. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline uint8_t acc_ramp_get_barred_t2(struct acc_ramp *acc_ramp) { return ((acc_ramp->barred_accs >> 8) & 0x03); }; static inline uint8_t acc_ramp_get_barred_t3(struct acc_ramp *acc_ramp) { return (acc_ramp->barred_accs & 0xff); } /*! * Potentially mark certain Access Control Classes (ACCs) as barred in accordance to ACC ramping. * \param[in] rach_control RACH control parameters in which barred ACCs will be configured. * \param[in] acc_ramp Pointer to acc_ramp structure. */ static inline void acc_ramp_apply(struct gsm48_rach_control *rach_control, struct acc_ramp *acc_ramp) { rach_control->t2 |= acc_ramp_get_barred_t2(acc_ramp); rach_control->t3 |= acc_ramp_get_barred_t3(acc_ramp); } void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts); int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size); int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval); void acc_ramp_set_step_interval_dynamic(struct acc_ramp *acc_ramp); void acc_ramp_trigger(struct acc_ramp *acc_ramp); void acc_ramp_abort(struct acc_ramp *acc_ramp); osmo-bsc-1.3.0/include/osmocom/bsc/arfcn_range_encode.h000066400000000000000000000015311332665256100230470ustar00rootroot00000000000000#ifndef ARFCN_RANGE_ENCODE_H #define ARFCN_RANGE_ENCODE_H #include enum gsm48_range { ARFCN_RANGE_INVALID = -1, ARFCN_RANGE_128 = 127, ARFCN_RANGE_256 = 255, ARFCN_RANGE_512 = 511, ARFCN_RANGE_1024 = 1023, }; #define RANGE_ENC_MAX_ARFCNS 29 int range_enc_determine_range(const int *arfcns, int size, int *f0_out); int range_enc_arfcns(enum gsm48_range rng, const int *arfcns, int sze, int *out, int idx); int range_enc_find_index(enum gsm48_range rng, const int *arfcns, int size); int range_enc_filter_arfcns(int *arfcns, const int sze, const int f0, int *f0_included); int range_enc_range128(uint8_t *chan_list, int f0, int *w); int range_enc_range256(uint8_t *chan_list, int f0, int *w); int range_enc_range512(uint8_t *chan_list, int f0, int *w); int range_enc_range1024(uint8_t *chan_list, int f0, int f0_incl, int *w); #endif osmo-bsc-1.3.0/include/osmocom/bsc/bsc_api.h000066400000000000000000000031361332665256100206700ustar00rootroot00000000000000/* GSM 08.08 like API for OpenBSC */ #ifndef OPENBSC_BSC_API_H #define OPENBSC_BSC_API_H #include "gsm_data.h" #define BSC_API_CONN_POL_ACCEPT 0 #define BSC_API_CONN_POL_REJECT 1 void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci); void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr); int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel); void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg); void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause); void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause); int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len); void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate); int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch); int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate); int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, const uint8_t *key, int len, int include_imeisv); int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, uint8_t *mi, int chan_type); int gsm0808_clear(struct gsm_subscriber_connection *conn); int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); #endif osmo-bsc-1.3.0/include/osmocom/bsc/bsc_msc_data.h000066400000000000000000000107521332665256100216740ustar00rootroot00000000000000/* * Data for the true BSC * * (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2015 by On-Waves * (C) 2018 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 . * */ /* * NOTE: This is about a *remote* MSC for OsmoBSC and is not part of libmsc. */ #ifndef _OSMO_MSC_DATA_H #define _OSMO_MSC_DATA_H #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include struct osmo_bsc_rf; struct gsm_network; struct gsm_audio_support { uint8_t hr : 1, ver : 7; }; enum { MSC_CON_TYPE_NORMAL, MSC_CON_TYPE_LOCAL, }; enum bsc_lcls_mode { BSC_LCLS_MODE_DISABLED, BSC_LCLS_MODE_MGW_LOOP, /* we may later introduce BTS_LOOP here: direct RTP between BTSs */ }; /*! /brief Information on a remote MSC for libbsc. */ struct bsc_msc_data { struct llist_head entry; /* Back pointer */ struct gsm_network *network; int allow_emerg; int type; /* local call routing */ char *local_pref; regex_t local_pref_reg; /* Connection data */ struct osmo_plmn_id core_plmn; int core_lac; int core_ci; int rtp_base; bool is_authenticated; /* audio codecs */ struct gsm48_multi_rate_conf amr_conf; struct gsm_audio_support **audio_support; int audio_length; enum bsc_lcls_mode lcls_mode; /* ussd welcome text */ char *ussd_welcome_txt; int nr; /* ussd msc connection lost text */ char *ussd_msc_lost_txt; /* ussd text when MSC has entered the grace period */ char *ussd_grace_txt; char *acc_lst_name; /* Sigtran connection data */ struct { uint32_t cs7_instance; bool cs7_instance_valid; struct osmo_sccp_instance *sccp; struct osmo_sccp_user *sccp_user; /* IPA or M3UA or SUA? */ enum osmo_ss7_asp_protocol asp_proto; /* Holds a copy of the our local MSC address, * this will be the sccp-address that is associated * with the A interface of this particular BSC, * this address is filled up by the VTY interface */ struct osmo_sccp_addr bsc_addr; char *bsc_addr_name; /* Holds a copy of the MSC address. This is the * address of the MSC that handles the calls of * this BSC. The address is configured via the * VTY interface */ struct osmo_sccp_addr msc_addr; char *msc_addr_name; /* Pointer to the osmo-fsm that controls the * BSSMAP RESET procedure */ struct osmo_fsm_inst *reset_fsm; } a; }; /* * Per BSC data. */ struct osmo_bsc_data { struct gsm_network *network; /* msc configuration */ struct llist_head mscs; /* rf ctl related bits */ char *mid_call_txt; int mid_call_timeout; char *rf_ctrl_name; struct osmo_bsc_rf *rf_ctrl; int auto_off_timeout; /* ussd text when there is no MSC available */ char *ussd_no_msc_txt; char *acc_lst_name; }; int osmo_bsc_msc_init(struct bsc_msc_data *msc); int osmo_bsc_sccp_init(struct gsm_network *gsmnet); int osmo_bsc_audio_init(struct gsm_network *network); struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *, int); struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *, int); /* Helper function to calculate the port number for a given * timeslot/multiplex. This functionality is needed to support * the sccp-lite scenario where the MGW is handled externally */ static inline int mgcp_timeslot_to_port(int multiplex, int timeslot, int base) { if (timeslot == 0) { LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n"); timeslot = 255; } return base + (timeslot + (32 * multiplex)) * 2; } static inline int mgcp_port_to_cic(uint16_t port, uint16_t base) { if (port < base) return -EINVAL; return (port - base) / 2; } #endif osmo-bsc-1.3.0/include/osmocom/bsc/bsc_msg_filter.h000066400000000000000000000043431332665256100222530ustar00rootroot00000000000000#pragma once #include #include #include #include #include struct vty; struct gsm48_hdr; struct bsc_filter_reject_cause { int lu_reject_cause; int cm_reject_cause; }; struct bsc_filter_barr_entry { struct rb_node node; char *imsi; int cm_reject_cause; int lu_reject_cause; }; enum bsc_filter_acc_ctr { ACC_LIST_LOCAL_FILTER, ACC_LIST_GLOBAL_FILTER, }; struct bsc_msg_acc_lst { struct llist_head list; /* counter */ struct rate_ctr_group *stats; /* the name of the list */ const char *name; struct llist_head fltr_list; }; struct bsc_msg_acc_lst_entry { struct llist_head list; /* the filter */ char *imsi_allow; regex_t imsi_allow_re; char *imsi_deny; regex_t imsi_deny_re; /* reject reasons for the access lists */ int cm_reject_cause; int lu_reject_cause; }; enum { FLT_CON_TYPE_NONE, FLT_CON_TYPE_LU, FLT_CON_TYPE_CM_SERV_REQ, FLT_CON_TYPE_PAG_RESP, FLT_CON_TYPE_SSA, FLT_CON_TYPE_LOCAL_REJECT, FLT_CON_TYPE_OTHER, }; struct bsc_filter_state { char *imsi; int imsi_checked; int con_type; }; struct bsc_filter_request { void *ctx; struct rb_root *black_list; struct llist_head *access_lists; const char *local_lst_name; const char *global_lst_name; int bsc_nr; }; /** * Content filtering. */ int bsc_msg_filter_initial(struct gsm48_hdr *hdr, size_t size, struct bsc_filter_request *req, int *con_type, char **imsi, struct bsc_filter_reject_cause *cause); int bsc_msg_filter_data(struct gsm48_hdr *hdr, size_t size, struct bsc_filter_request *req, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause); /* IMSI allow/deny handling */ struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *lst, const char *name); struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *lst, const char *name); void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst); struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *); int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *imsi); void bsc_msg_acc_lst_vty_init(void *ctx, struct llist_head *lst, int node); void bsc_msg_acc_lst_write(struct vty *vty); osmo-bsc-1.3.0/include/osmocom/bsc/bsc_rll.h000066400000000000000000000007021332665256100207040ustar00rootroot00000000000000#ifndef _BSC_RLL_H #define _BSC_RLL_H #include enum bsc_rllr_ind { BSC_RLLR_IND_EST_CONF, BSC_RLLR_IND_REL_IND, BSC_RLLR_IND_ERR_IND, BSC_RLLR_IND_TIMEOUT, }; int rll_establish(struct gsm_lchan *lchan, uint8_t link_id, void (*cb)(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind), void *data); void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type); #endif /* _BSC_RLL_H */ osmo-bsc-1.3.0/include/osmocom/bsc/bsc_subscr_conn_fsm.h000066400000000000000000000041021332665256100232740ustar00rootroot00000000000000#pragma once #include enum gscon_fsm_event { /* local SCCP stack tells us incoming conn from MSC */ GSCON_EV_A_CONN_IND, /* RSL side requests CONNECT to MSC */ GSCON_EV_A_CONN_REQ, /* MSC confirms the SCCP connection */ GSCON_EV_A_CONN_CFM, /* MSC requests assignment */ GSCON_EV_A_ASSIGNMENT_CMD, /* MSC has sent BSSMAP CLEAR CMD */ GSCON_EV_A_CLEAR_CMD, /* MSC SCCP disconnect indication */ GSCON_EV_A_DISC_IND, /* MSC sends Handover Request (in CR) */ GSCON_EV_A_HO_REQ, /* RR ASSIGNMENT COMPLETE received */ GSCON_EV_RR_ASS_COMPL, /* RR ASSIGNMENT FAIL received */ GSCON_EV_RR_ASS_FAIL, /* RSL RLL Release Indication */ GSCON_EV_RLL_REL_IND, /* RSL CONNection FAILure Indication */ GSCON_EV_RSL_CONN_FAIL, /* RSL/lchan tells us clearing is complete */ GSCON_EV_RSL_CLEAR_COMPL, /* Mobile-originated DTAP (from MS) */ GSCON_EV_MO_DTAP, /* Mobile-terminated DTAP (from MSC) */ GSCON_EV_MT_DTAP, /* Transmit custom SCCP message */ GSCON_EV_TX_SCCP, /* MGW is indicating failure (BTS) */ GSCON_EV_MGW_FAIL_BTS, /* MGW is indicating failure (MSC) */ GSCON_EV_MGW_FAIL_MSC, /* CRCX response received (BTS) */ GSCON_EV_MGW_CRCX_RESP_BTS, /* MDCX response received (BTS) */ GSCON_EV_MGW_MDCX_RESP_BTS, /* CRCX response received (MSC) */ GSCON_EV_MGW_CRCX_RESP_MSC, /* MDCX response received (MSC) - triggered by LCLS */ GSCON_EV_MGW_MDCX_RESP_MSC, /* Internal handover request (intra-BSC handover) */ GSCON_EV_HO_START, /* Handover timed out (T3103 in handover_logic.c) */ GSCON_EV_HO_TIMEOUT, /* Handover failed (handover_logic.c) */ GSCON_EV_HO_FAIL, /* Handover completed successfully (handover_logic.c) */ GSCON_EV_HO_COMPL, /* LCLS child FSM has terminated due to hard failure */ GSCON_EV_LCLS_FAIL, }; struct gsm_subscriber_connection; struct gsm_network; struct mgcp_conn_peer; /* Allocate a subscriber connection and its associated FSM */ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net); void bsc_subscr_pick_codec(struct mgcp_conn_peer *conn_peer, struct gsm_subscriber_connection *conn); osmo-bsc-1.3.0/include/osmocom/bsc/bsc_subscriber.h000066400000000000000000000024131332665256100222570ustar00rootroot00000000000000/* GSM subscriber details for use in BSC land */ #pragma once #include #include #include struct log_target; struct bsc_subscr { struct llist_head entry; int use_count; char imsi[GSM23003_IMSI_MAX_DIGITS+1]; uint32_t tmsi; uint16_t lac; }; const char *bsc_subscr_name(struct bsc_subscr *bsub); struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list, const char *imsi); struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list, uint32_t tmsi); struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list, const char *imsi); struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list, uint32_t tmsi); void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi); struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub, const char *file, int line); struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub, const char *file, int line); #define bsc_subscr_get(bsub) _bsc_subscr_get(bsub, __BASE_FILE__, __LINE__) #define bsc_subscr_put(bsub) _bsc_subscr_put(bsub, __BASE_FILE__, __LINE__) void log_set_filter_bsc_subscr(struct log_target *target, struct bsc_subscr *bsub); osmo-bsc-1.3.0/include/osmocom/bsc/bss.h000066400000000000000000000007311332665256100200550ustar00rootroot00000000000000#ifndef _BSS_H_ #define _BSS_H_ #include struct msgb; /* start and stop network */ extern int bsc_network_alloc(void); extern int bsc_shutdown_net(struct gsm_network *net); /* register all supported BTS */ extern int bts_init(void); extern int bts_model_bs11_init(void); extern int bts_model_rbs2k_init(void); extern int bts_model_nanobts_init(void); extern int bts_model_nokia_site_init(void); extern int bts_model_sysmobts_init(void); #endif osmo-bsc-1.3.0/include/osmocom/bsc/bts_ipaccess_nanobts_omlattr.h000066400000000000000000000023101332665256100252110ustar00rootroot00000000000000/* OML attribute table generator for ipaccess nanobts */ /* (C) 2016 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 . */ #pragma once #include #include struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts); struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts); struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts); struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts); struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts, struct gsm_bts_trx *trx); osmo-bsc-1.3.0/include/osmocom/bsc/chan_alloc.h000066400000000000000000000034761332665256100213620ustar00rootroot00000000000000/* Management functions to allocate/release struct gsm_lchan */ /* (C) 2008 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 . * */ #ifndef _CHAN_ALLOC_H #define _CHAN_ALLOC_H #include "gsm_data.h" struct gsm_subscriber_connection; /* Count number of free TS of given pchan type */ int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan); /* Allocate a logical channel (SDCCH, TCH, ...) */ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger); /* Free a logical channel (SDCCH, TCH, ...) */ void lchan_free(struct gsm_lchan *lchan); void lchan_reset(struct gsm_lchan *lchan); /* Release the given lchan */ int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode release_mode); struct pchan_load { struct load_counter pchan[_GSM_PCHAN_MAX]; }; void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts); void network_chan_load(struct pchan_load *pl, struct gsm_network *net); void bts_update_t3122_chan_load(struct gsm_bts *bts); bool ts_is_usable(const struct gsm_bts_trx_ts *ts); #endif /* _CHAN_ALLOC_H */ osmo-bsc-1.3.0/include/osmocom/bsc/codec_pref.h000066400000000000000000000003671332665256100213640ustar00rootroot00000000000000#pragma once struct gsm_bts; int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, const struct gsm0808_channel_type *ct, const struct gsm0808_speech_codec_list *scl, const struct bsc_msc_data *msc, struct gsm_bts *bts); osmo-bsc-1.3.0/include/osmocom/bsc/ctrl.h000066400000000000000000000003621332665256100202320ustar00rootroot00000000000000#pragma once #include struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, const char *bind_addr, uint16_t port); enum bsc_ctrl_node { CTRL_NODE_MSC = _LAST_CTRL_NODE, _LAST_CTRL_NODE_BSC }; osmo-bsc-1.3.0/include/osmocom/bsc/debug.h000066400000000000000000000004451332665256100203560ustar00rootroot00000000000000#pragma once #include #include #define DEBUG #include /* Debug Areas of the code */ enum { DRLL, DMM, DRR, DRSL, DNM, DPAG, DMEAS, DMSC, DHO, DHODEC, DREF, DNAT, DCTRL, DFILTER, DPCU, DLCLS, Debug_LastEntry, }; osmo-bsc-1.3.0/include/osmocom/bsc/e1_config.h000066400000000000000000000004121332665256100211140ustar00rootroot00000000000000#ifndef _E1_CONFIG_H #define _E1_CONFIG_H struct gsm_bts_trx_ts; struct gsm_bts_trx; struct gsm_bts; int e1_reconfig_ts(struct gsm_bts_trx_ts *ts); int e1_reconfig_trx(struct gsm_bts_trx *trx); int e1_reconfig_bts(struct gsm_bts *bts); #endif /* _E1_CONFIG_H */ osmo-bsc-1.3.0/include/osmocom/bsc/gsm_04_08_utils.h000066400000000000000000000040251332665256100221060ustar00rootroot00000000000000#pragma once void gsm_net_update_ctype(struct gsm_network *network); enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra); int get_reason_by_chreq(uint8_t ra, int neci); int gsm48_send_rr_release(struct gsm_lchan *lchan); int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv); int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct bsc_subscr *bsub); int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan); int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes); int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref); int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command); int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode); int gsm48_rx_rr_modif_ack(struct msgb *msg); int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn); int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value); struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value); int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type); int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, char *mi_string, uint8_t *mi_type); struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); struct msgb *gsm48_create_rr_status(uint8_t cause); int gsm48_tx_rr_status(struct gsm_subscriber_connection *conn, uint8_t cause); #define GSM48_ALLOC_SIZE 2048 #define GSM48_ALLOC_HEADROOM 256 static inline struct msgb *gsm48_msgb_alloc_name(const char *name) { return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, name); } uint64_t str_to_imsi(const char *imsi_str); osmo-bsc-1.3.0/include/osmocom/bsc/gsm_04_80.h000066400000000000000000000003401332665256100206620ustar00rootroot00000000000000#pragma once struct gsm_subscriber_connection; int bsc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text); int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn); osmo-bsc-1.3.0/include/osmocom/bsc/gsm_data.h000066400000000000000000001124731332665256100210540ustar00rootroot00000000000000#ifndef _GSM_DATA_H #define _GSM_DATA_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 #include #include struct mgcp_client_conf; struct mgcp_client; struct mgcp_ctx; /** annotations for msgb ownership */ #define __uses #define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3] struct bsc_subscr; struct gprs_ra_id; #define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] #define tmsi_from_string(str) strtoul(str, NULL, 10) /* 3-bit long values */ #define EARFCN_PRIO_INVALID 8 #define EARFCN_MEAS_BW_INVALID 8 /* 5-bit long values */ #define EARFCN_QRXLV_INVALID 32 #define EARFCN_THRESH_LOW_INVALID 32 struct msgb; typedef int gsm_cbfn(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param); /* Maximum number of neighbor cells whose average we track */ #define MAX_NEIGH_MEAS 10 /* Maximum size of the averaging window for neighbor cells */ #define MAX_WIN_NEIGH_AVG 10 /* Maximum number of report history we store */ #define MAX_MEAS_REP 10 /* processed neighbor measurements for one cell */ struct neigh_meas_proc { uint16_t arfcn; uint8_t bsic; uint8_t rxlev[MAX_WIN_NEIGH_AVG]; unsigned int rxlev_cnt; uint8_t last_seen_nr; }; struct gsm_classmark { bool classmark1_set; struct gsm48_classmark1 classmark1; uint8_t classmark2_len; uint8_t classmark2[3]; uint8_t classmark3_len; uint8_t classmark3[14]; /* if cm3 gets extended by spec, it will be truncated */ }; enum subscr_sccp_state { SUBSCR_SCCP_ST_NONE, SUBSCR_SCCP_ST_WAIT_CONN_CONF, SUBSCR_SCCP_ST_CONNECTED }; /* active radio connection of a mobile subscriber */ struct gsm_subscriber_connection { /* global linked list of subscriber_connections */ struct llist_head entry; /* FSM instance to control the subscriber connection state (RTP, A) */ struct osmo_fsm_inst *fi; /* libbsc subscriber information (if available) */ struct bsc_subscr *bsub; /* back pointers */ struct gsm_network *network; /* the primary / currently active lchan to the BTS/subscriber */ struct gsm_lchan *lchan; /* handover information, if a handover is pending for this conn. */ struct bsc_handover *ho; /* the future allocated but not yet used lchan during ASSIGNMENT */ struct gsm_lchan *secondary_lchan; /* buffer/cache for classmark of the ME of the subscriber */ struct gsm_classmark classmark; /* Cache DTAP messages during handover/assignment (msgb_enqueue()/msgb_dequeue())*/ struct llist_head ho_dtap_cache; unsigned int ho_dtap_cache_len; struct { int failures; struct penalty_timers *penalty_timers; } hodec2; /* "Codec List (MSC Preferred)" as received by the BSSAP Assignment Request. 3GPP 48.008 * 3.2.2.103 says: * The "Codec List (MSC Preferred)" shall not include codecs * that are not supported by the MS. * i.e. by heeding the "Codec list (MSC Preferred)", we inherently heed the MS bearer * capabilities, which the MSC is required to translate into the codec list. */ struct gsm0808_speech_codec_list codec_list; bool codec_list_present; /* flag to prevent multiple simultaneous ciphering commands */ int ciphering_handled; /* state related to welcome USSD */ uint8_t new_subscriber; /* state related to osmo_bsc_filter.c */ struct bsc_filter_state filter_state; /* SCCP connection associatd with this subscriber_connection */ struct { /* for advanced ping/pong */ int send_ping; /* SCCP connection realted */ struct bsc_msc_data *msc; /* Sigtran connection ID */ int conn_id; enum subscr_sccp_state state; } sccp; /* for audio handling */ struct { uint16_t cic; uint32_t rtp_ip; int rtp_port; /* RTP address of the remote end (assigned by MSC through assignment request) */ struct sockaddr_storage aoip_rtp_addr_remote; /* Local RTP address (reported back to the MSC by us with the * assignment complete message) */ struct sockaddr_storage aoip_rtp_addr_local; /* FSM instance to control the BTS sided RTP connection */ struct osmo_fsm_inst *fi_bts; /* FSM instance to control the MSC sided RTP connection */ struct osmo_fsm_inst *fi_msc; /* Endpoint identifier of the MGCP endpoint the connection uses */ char *mgw_endpoint; /* Channel rate flag, FR=1, HR=0, Invalid=-1 */ int full_rate; /* Channel mode flag (signaling or voice channel) */ enum gsm48_chan_mode chan_mode; } user_plane; /* LCLS (local call, local switch) related state */ struct { uint8_t global_call_ref[15]; uint8_t global_call_ref_len; /* length of global_call_ref */ uint8_t config; /* TS 48.008 3.2.2.116 */ uint8_t control;/* TS 48.008 3.2.2.117 */ /* LCLS FSM */ struct osmo_fsm_inst *fi; /* pointer to "other" connection, if Call Leg Relocation was successful */ struct gsm_subscriber_connection *other; } lcls; }; /* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1: 4-bit index is used (2#1111 = 10#15) */ #define SI2Q_MAX_NUM 16 /* length in bits (for single SI2quater message) */ #define SI2Q_MAX_LEN 160 #define SI2Q_MIN_LEN 18 struct osmo_bsc_data; struct osmo_bsc_sccp_con; /* Channel Request reason */ enum gsm_chreq_reason_t { GSM_CHREQ_REASON_EMERG, GSM_CHREQ_REASON_PAG, GSM_CHREQ_REASON_CALL, GSM_CHREQ_REASON_LOCATION_UPD, GSM_CHREQ_REASON_OTHER, GSM_CHREQ_REASON_PDCH, }; /* lchans 0..3 are SDCCH in combined channel configuration, use 4 as magic number for BCCH hack - see osmo-bts-../oml.c:opstart_compl() */ #define CCCH_LCHAN 4 #define TRX_NR_TS 8 #define TS_MAX_LCHAN 8 #define HARDCODED_ARFCN 123 #define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ /* for multi-drop config */ #define HARDCODED_BTS0_TS 1 #define HARDCODED_BTS1_TS 6 #define HARDCODED_BTS2_TS 11 #define MAX_VERSION_LENGTH 64 enum gsm_hooks { GSM_HOOK_NM_SWLOAD, GSM_HOOK_RR_PAGING, GSM_HOOK_RR_SECURITY, }; enum bts_gprs_mode { BTS_GPRS_NONE = 0, BTS_GPRS_GPRS = 1, BTS_GPRS_EGPRS = 2, }; struct gsm_lchan; struct osmo_rtp_socket; struct rtp_socket; /* Network Management State */ struct gsm_nm_state { uint8_t operational; uint8_t administrative; uint8_t availability; }; struct gsm_abis_mo { uint8_t obj_class; uint8_t procedure_pending; struct abis_om_obj_inst obj_inst; const char *name; struct gsm_nm_state nm_state; struct tlv_parsed *nm_attr; struct gsm_bts *bts; }; /* Ericsson OM2000 Managed Object */ struct abis_om2k_mo { uint8_t class; uint8_t bts; uint8_t assoc_so; uint8_t inst; } __attribute__ ((packed)); struct om2k_mo { struct abis_om2k_mo addr; struct osmo_fsm_inst *fsm; }; #define A38_XOR_MIN_KEY_LEN 12 #define A38_XOR_MAX_KEY_LEN 16 #define A38_COMP128_KEY_LEN 16 #define RSL_ENC_ALG_A5(x) (x+1) #define MAX_EARFCN_LIST 32 /* is the data link established? who established it? */ #define LCHAN_SAPI_UNUSED 0 #define LCHAN_SAPI_MS 1 #define LCHAN_SAPI_NET 2 #define LCHAN_SAPI_REL 3 /* state of a logical channel */ enum gsm_lchan_state { LCHAN_S_NONE, /* channel is not active */ LCHAN_S_ACT_REQ, /* channel activation requested */ LCHAN_S_ACTIVE, /* channel is active and operational */ LCHAN_S_REL_REQ, /* channel release has been requested */ LCHAN_S_REL_ERR, /* channel is in an error state */ LCHAN_S_BROKEN, /* channel is somehow unusable */ LCHAN_S_INACTIVE, /* channel is set inactive */ }; /* BTS ONLY */ #define MAX_NUM_UL_MEAS 104 #define LC_UL_M_F_L1_VALID (1 << 0) #define LC_UL_M_F_RES_VALID (1 << 1) struct bts_ul_meas { /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */ uint16_t ber10k; /* timing advance offset (in quarter bits) */ int16_t ta_offs_qbits; /* C/I ratio in dB */ float c_i; /* flags */ uint8_t is_sub:1; /* RSSI in dBm * -1 */ uint8_t inv_rssi; }; struct bts_codec_conf { uint8_t hr; uint8_t efr; uint8_t amr; }; struct amr_mode { uint8_t mode; uint8_t threshold; uint8_t hysteresis; }; struct amr_multirate_conf { uint8_t gsm48_ie[2]; struct amr_mode ms_mode[4]; struct amr_mode bts_mode[4]; uint8_t num_modes; }; /* /BTS ONLY */ enum lchan_csd_mode { LCHAN_CSD_M_NT, LCHAN_CSD_M_T_1200_75, LCHAN_CSD_M_T_600, LCHAN_CSD_M_T_1200, LCHAN_CSD_M_T_2400, LCHAN_CSD_M_T_9600, LCHAN_CSD_M_T_14400, LCHAN_CSD_M_T_29000, LCHAN_CSD_M_T_32000, }; /* State of the SAPIs in the lchan */ enum lchan_sapi_state { LCHAN_SAPI_S_NONE, LCHAN_SAPI_S_REQ, LCHAN_SAPI_S_ASSIGNED, LCHAN_SAPI_S_REL, LCHAN_SAPI_S_ERROR, }; #define MAX_A5_KEY_LEN (128/8) struct gsm_encr { uint8_t alg_id; uint8_t key_len; uint8_t key[MAX_A5_KEY_LEN]; }; #define LOGPLCHAN(lchan, ss, level, fmt, args...) \ LOGP(ss, level, "%s (ss=%d,%s) (%s) " fmt, \ lchan ? gsm_ts_and_pchan_name(lchan->ts) : "-", \ lchan ? lchan->nr : 0, \ lchan ? gsm_lchant_name(lchan->type) : "-", \ bsc_subscr_name(lchan && lchan->conn ? lchan->conn->bsub : NULL), \ ## args) struct gsm_lchan { /* The TS that we're part of */ struct gsm_bts_trx_ts *ts; /* The logical subslot number in the TS */ uint8_t nr; /* The logical channel type */ enum gsm_chan_t type; /* RSL channel mode */ enum rsl_cmod_spd rsl_cmode; /* If TCH, traffic channel mode */ enum gsm48_chan_mode tch_mode; enum lchan_csd_mode csd_mode; /* State */ enum gsm_lchan_state state; const char *broken_reason; /* Power levels for MS and BTS */ uint8_t bs_power; uint8_t ms_power; /* Encryption information */ struct gsm_encr encr; /* AMR bits */ uint8_t mr_ms_lv[7]; uint8_t mr_bts_lv[7]; /* Established data link layer services */ uint8_t sapis[8]; struct { uint32_t bound_ip; uint32_t connect_ip; uint16_t bound_port; uint16_t connect_port; uint16_t conn_id; uint8_t rtp_payload; uint8_t rtp_payload2; uint8_t speech_mode; struct rtp_socket *rtp_socket; /* info we need to postpone the AoIP * assignment completed message */ struct { uint8_t rr_cause; bool valid; } ass_compl; } abis_ip; uint8_t rqd_ta; char *name; struct osmo_timer_list T3101; struct osmo_timer_list T3109; struct osmo_timer_list T3111; struct osmo_timer_list error_timer; struct osmo_timer_list act_timer; struct osmo_timer_list rel_work; uint8_t error_cause; /* table of neighbor cell measurements */ struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; /* cache of last measurement reports on this lchan */ struct gsm_meas_rep meas_rep[MAX_MEAS_REP]; int meas_rep_idx; int meas_rep_count; uint8_t meas_rep_last_seen_nr; /* GSM Random Access data */ struct gsm48_req_ref *rqd_ref; struct gsm_subscriber_connection *conn; struct { /* channel activation type and handover ref */ uint8_t act_type; uint8_t ho_ref; struct gsm48_req_ref *rqd_ref; uint8_t rqd_ta; } dyn; }; enum { TS_F_PDCH_ACTIVE = 0x1000, TS_F_PDCH_ACT_PENDING = 0x2000, TS_F_PDCH_DEACT_PENDING = 0x4000, TS_F_PDCH_PENDING_MASK = (TS_F_PDCH_ACT_PENDING | TS_F_PDCH_DEACT_PENDING), } gsm_bts_trx_ts_flags; /* One Timeslot in a TRX */ struct gsm_bts_trx_ts { struct gsm_bts_trx *trx; bool initialized; /* number of this timeslot at the TRX */ uint8_t nr; enum gsm_phys_chan_config pchan; struct { enum gsm_phys_chan_config pchan_is; enum gsm_phys_chan_config pchan_want; struct msgb *pending_chan_activ; } dyn; unsigned int flags; struct gsm_abis_mo mo; struct tlv_parsed nm_attr; uint8_t nm_chan_comb; int tsc; /* -1 == use BTS TSC */ struct { /* Parameters below are configured by VTY */ int enabled; uint8_t maio; uint8_t hsn; struct bitvec arfcns; uint8_t arfcns_data[1024/8]; /* This is the pre-computed MA for channel assignments */ struct bitvec ma; uint8_t ma_len; /* part of ma_data that is used */ uint8_t ma_data[8]; /* 10.5.2.21: max 8 bytes value part */ } hopping; /* To which E1 subslot are we connected */ struct gsm_e1_subslot e1_link; union { struct { struct om2k_mo om2k_mo; } rbs2000; }; struct gsm_lchan lchan[TS_MAX_LCHAN]; }; /* One TRX in a BTS */ struct gsm_bts_trx { /* list header in bts->trx_list */ struct llist_head list; struct gsm_bts *bts; /* number of this TRX in the BTS */ uint8_t nr; /* human readable name / description */ char *description; /* how do we talk RSL with this TRX? */ struct gsm_e1_subslot rsl_e1_link; uint8_t rsl_tei; struct e1inp_sign_link *rsl_link; /* Timeout for initiating the RSL connection. */ struct osmo_timer_list rsl_connect_timeout; /* Some BTS (specifically Ericsson RBS) have a per-TRX OML Link */ struct e1inp_sign_link *oml_link; struct gsm_abis_mo mo; struct tlv_parsed nm_attr; struct { struct gsm_abis_mo mo; } bb_transc; uint16_t arfcn; int nominal_power; /* in dBm */ unsigned int max_power_red; /* in actual dB */ union { struct { struct { struct gsm_abis_mo mo; } bbsig; struct { struct gsm_abis_mo mo; } pa; } bs11; struct { unsigned int test_state; uint8_t test_nr; struct rxlev_stats rxlev_stat; } ipaccess; struct { struct { struct om2k_mo om2k_mo; } trxc; struct { struct om2k_mo om2k_mo; } rx; struct { struct om2k_mo om2k_mo; } tx; } rbs2000; }; struct gsm_bts_trx_ts ts[TRX_NR_TS]; }; #define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i]) #define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i)) #define GSM_BTS_SI(bts, i) (void *)((bts)->si_buf[i][0]) #define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0]) enum gsm_bts_type { GSM_BTS_TYPE_UNKNOWN, GSM_BTS_TYPE_BS11, GSM_BTS_TYPE_NANOBTS, GSM_BTS_TYPE_RBS2000, GSM_BTS_TYPE_NOKIA_SITE, GSM_BTS_TYPE_OSMOBTS, _NUM_GSM_BTS_TYPE }; enum gsm_bts_type_variant { BTS_UNKNOWN, BTS_OSMO_LITECELL15, BTS_OSMO_OCTPHY, BTS_OSMO_SYSMO, BTS_OSMO_TRX, _NUM_BTS_VARIANT }; /* Used by OML layer for BTS Attribute reporting */ enum bts_attribute { BTS_TYPE_VARIANT, BTS_SUB_MODEL, TRX_PHY_VERSION, }; struct vty; struct gsm_bts_model { struct llist_head list; enum gsm_bts_type type; enum gsm_bts_type_variant variant; const char *name; bool started; int (*start)(struct gsm_network *net); int (*oml_rcvmsg)(struct msgb *msg); char * (*oml_status)(const struct gsm_bts *bts); bool (*oml_is_ts_ready)(const struct gsm_bts_trx_ts *ts); void (*e1line_bind_ops)(struct e1inp_line *line); void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts); void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx); void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts); struct tlv_definition nm_att_tlvdef; /* features of a given BTS model set via gsm_bts_model_register() locally */ struct bitvec features; uint8_t _features_data[MAX_BTS_FEATURES/8]; }; /* * This keeps track of the paging status of one BTS. It * includes a number of pending requests, a back pointer * to the gsm_bts, a timer and some more state. */ struct gsm_bts_paging_state { /* pending requests */ struct llist_head pending_requests; struct gsm_bts *bts; struct osmo_timer_list work_timer; struct osmo_timer_list credit_timer; /* free chans needed */ int free_chans_need; /* load */ uint16_t available_slots; }; struct gsm_envabtse { struct gsm_abis_mo mo; }; struct gsm_bts_gprs_nsvc { struct gsm_bts *bts; /* data read via VTY config file, to configure the BTS * via OML from BSC */ int id; uint16_t nsvci; uint16_t local_port; /* on the BTS */ uint16_t remote_port; /* on the SGSN */ uint32_t remote_ip; /* on the SGSN */ struct gsm_abis_mo mo; }; enum gprs_rlc_par { RLC_T3142, RLC_T3169, RLC_T3191, RLC_T3193, RLC_T3195, RLC_N3101, RLC_N3103, RLC_N3105, CV_COUNTDOWN, T_DL_TBF_EXT, /* ms */ T_UL_TBF_EXT, /* ms */ _NUM_RLC_PAR }; enum gprs_cs { GPRS_CS1, GPRS_CS2, GPRS_CS3, GPRS_CS4, GPRS_MCS1, GPRS_MCS2, GPRS_MCS3, GPRS_MCS4, GPRS_MCS5, GPRS_MCS6, GPRS_MCS7, GPRS_MCS8, GPRS_MCS9, _NUM_GRPS_CS }; struct gprs_rlc_cfg { uint16_t parameter[_NUM_RLC_PAR]; struct { uint16_t repeat_time; /* ms */ uint8_t repeat_count; } paging; uint32_t cs_mask; /* bitmask of gprs_cs */ uint8_t initial_cs; uint8_t initial_mcs; }; enum neigh_list_manual_mode { NL_MODE_AUTOMATIC = 0, NL_MODE_MANUAL = 1, NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */ }; enum bts_loc_fix { BTS_LOC_FIX_INVALID = 0, BTS_LOC_FIX_2D = 1, BTS_LOC_FIX_3D = 2, }; extern const struct value_string bts_loc_fix_names[]; struct bts_location { struct llist_head list; time_t tstamp; enum bts_loc_fix valid; double lat; double lon; double height; }; /* Channel load counter */ struct load_counter { unsigned int total; unsigned int used; }; /* One BTS */ struct gsm_bts { /* list header in net->bts_list */ struct llist_head list; /* Geographical location of the BTS */ struct llist_head loc_list; /* number of ths BTS in network */ uint8_t nr; /* human readable name / description */ char *description; /* Cell Identity */ uint16_t cell_identity; /* location area code of this BTS */ uint16_t location_area_code; /* Base Station Identification Code (BSIC), lower 3 bits is BCC, * which is used as TSC for the CCCH */ uint8_t bsic; /* type of BTS */ enum gsm_bts_type type; enum gsm_bts_type_variant variant; struct gsm_bts_model *model; enum gsm_band band; char version[MAX_VERSION_LENGTH]; char sub_model[MAX_VERSION_LENGTH]; /* features of a given BTS set/reported via OML */ struct bitvec features; uint8_t _features_data[MAX_BTS_FEATURES/8]; /* Connected PCU version (if any) */ char pcu_version[MAX_VERSION_LENGTH]; /* maximum Tx power that the MS is permitted to use in this cell */ int ms_max_power; /* how do we talk OML with this TRX? */ struct gsm_e1_subslot oml_e1_link; uint8_t oml_tei; struct e1inp_sign_link *oml_link; /* when OML link was established */ time_t uptime; /* Abis network management O&M handle */ struct abis_nm_h *nmh; struct gsm_abis_mo mo; /* number of this BTS on given E1 link */ uint8_t bts_nr; /* DTX features of this BTS */ enum gsm48_dtx_mode dtxu; bool dtxd; /* paging state and control */ struct gsm_bts_paging_state paging; /* CCCH is on C0 */ struct gsm_bts_trx *c0; struct { struct gsm_abis_mo mo; } site_mgr; /* bitmask of all SI that are present/valid in si_buf */ uint32_t si_valid; /* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */ uint8_t si2q_index; /* distinguish individual SI2quater messages */ uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */ /* buffers where we put the pre-computed SI */ sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM]; /* offsets used while generating SI2quater */ size_t e_offset; size_t u_offset; /* ip.accesss Unit ID's have Site/BTS/TRX layout */ union { struct { uint16_t site_id; uint16_t bts_id; uint32_t flags; uint32_t rsl_ip; } ip_access; struct { struct { struct gsm_abis_mo mo; } cclk; struct { struct gsm_abis_mo mo; } rack; struct gsm_envabtse envabtse[4]; } bs11; struct { struct { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; struct llist_head conn_groups; } cf; struct { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; struct llist_head conn_groups; } is; struct { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; struct llist_head conn_groups; } con; struct { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; } dp; struct { struct om2k_mo om2k_mo; struct gsm_abis_mo mo; } tf; uint32_t use_superchannel:1; } rbs2000; struct { uint8_t bts_type; unsigned int configured:1, skip_reset:1, no_loc_rel_cnf:1, bts_reset_timer_cnf, did_reset:1, wait_reset:1; struct osmo_timer_list reset_timer; } nokia; }; /* Not entirely sure how ip.access specific this is */ struct { uint8_t supports_egprs_11bit_rach; enum bts_gprs_mode mode; struct { struct gsm_abis_mo mo; uint16_t nsei; uint8_t timer[7]; } nse; struct { struct gsm_abis_mo mo; uint16_t bvci; uint8_t timer[11]; struct gprs_rlc_cfg rlc_cfg; } cell; struct gsm_bts_gprs_nsvc nsvc[2]; uint8_t rac; uint8_t net_ctrl_ord; bool ctrl_ack_type_use_block; } gprs; /* RACH NM values */ int rach_b_thresh; int rach_ldavg_slots; /* transceivers */ int num_trx; struct llist_head trx_list; /* SI related items */ int force_combined_si; int bcch_change_mark; /* Abis NM queue */ struct llist_head abis_queue; int abis_nm_pend; struct gsm_network *network; /* should the channel allocator allocate channels from high TRX to TRX0, * rather than starting from TRX0 and go upwards? */ int chan_alloc_reverse; enum neigh_list_manual_mode neigh_list_manual_mode; /* parameters from which we build SYSTEM INFORMATION */ struct { struct gsm48_rach_control rach_control; uint8_t ncc_permitted; struct gsm48_cell_sel_par cell_sel_par; struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */ struct gsm48_cell_options cell_options; struct gsm48_control_channel_descr chan_desc; struct bitvec neigh_list; struct bitvec cell_alloc; struct bitvec si5_neigh_list; struct osmo_earfcn_si2q si2quater_neigh_list; size_t uarfcn_length; /* index for uarfcn and scramble lists */ struct { /* bitmask large enough for all possible ARFCN's */ uint8_t neigh_list[1024/8]; uint8_t cell_alloc[1024/8]; /* If the user wants a different neighbor list in SI5 than in SI2 */ uint8_t si5_neigh_list[1024/8]; uint8_t meas_bw_list[MAX_EARFCN_LIST]; uint16_t earfcn_list[MAX_EARFCN_LIST]; uint16_t uarfcn_list[MAX_EARFCN_LIST]; uint16_t scramble_list[MAX_EARFCN_LIST]; } data; } si_common; bool early_classmark_allowed; bool early_classmark_allowed_3g; /* for testing only: Have an infinitely long radio link timeout */ bool infinite_radio_link_timeout; /* do we use static (user-defined) system information messages? (bitmask) */ uint32_t si_mode_static; /* access control class ramping */ struct acc_ramp acc_ramp; /* exclude the BTS from the global RF Lock handling */ int excl_from_rf_lock; /* supported codecs beside FR */ struct bts_codec_conf codec; /* BTS dependencies bit field */ uint32_t depends_on[256/(8*4)]; /* full and half rate multirate config */ struct amr_multirate_conf mr_full; struct amr_multirate_conf mr_half; /* PCU socket state */ char *pcu_sock_path; struct pcu_sock_state *pcu_state; struct rate_ctr_group *bts_ctrs; struct osmo_stat_item_group *bts_statg; struct handover_cfg *ho; /* BTS-specific overrides for timer values from struct gsm_network. */ uint8_t T3122; /* ASSIGMENT REJECT wait indication */ /* Periodic channel load measurements are used to maintain T3122. */ struct load_counter chan_load_samples[7]; int chan_load_samples_idx; uint8_t chan_load_avg; /* current channel load average in percent (0 - 100). */ }; struct gsm_network *gsm_network_init(void *ctx); struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num); struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); enum gsm_bts_type str2btstype(const char *arg); const char *btstype2str(enum gsm_bts_type type); enum bts_attribute str2btsattr(const char *s); const char *btsatttr2str(enum bts_attribute v); enum gsm_bts_type_variant str2btsvariant(const char *arg); const char *btsvariant2str(enum gsm_bts_type_variant v); extern const struct value_string gsm_chreq_descs[]; const struct value_string gsm_pchant_names[13]; const struct value_string gsm_pchant_descs[13]; const char *gsm_pchan_name(enum gsm_phys_chan_config c); enum gsm_phys_chan_config gsm_pchan_parse(const char *name); const char *gsm_lchant_name(enum gsm_chan_t c); const char *gsm_chreq_name(enum gsm_chreq_reason_t c); char *gsm_trx_name(const struct gsm_bts_trx *trx); char *gsm_ts_name(const struct gsm_bts_trx_ts *ts); char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts); char *gsm_lchan_name_compute(const struct gsm_lchan *lchan); const char *gsm_lchans_name(enum gsm_lchan_state s); static inline char *gsm_lchan_name(const struct gsm_lchan *lchan) { return lchan->name; } void gsm_abis_mo_reset(struct gsm_abis_mo *mo); struct gsm_nm_state * gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst); void * gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst); /* reset the state of all MO in the BTS */ void gsm_bts_mo_reset(struct gsm_bts *bts); uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan, uint8_t ts_nr, uint8_t lchan_nr); uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan); uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan, enum gsm_phys_chan_config as_pchan); /* return the gsm_lchan for the CBCH (if it exists at all) */ struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts); /* * help with parsing regexps */ int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) __attribute__ ((warn_unused_result)); static inline uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts) { if (ts->tsc != -1) return ts->tsc; else return ts->trx->bts->bsic & 7; } struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, int *rc); enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts); uint8_t ts_subslots(struct gsm_bts_trx_ts *ts); bool ts_is_tch(struct gsm_bts_trx_ts *ts); static inline struct gsm_bts *conn_get_bts(struct gsm_subscriber_connection *conn) { OSMO_ASSERT(conn->lchan); return conn->lchan->ts->trx->bts; } enum { BTS_CTR_CHREQ_TOTAL, BTS_CTR_CHREQ_NO_CHANNEL, BTS_CTR_CHAN_RF_FAIL, BTS_CTR_CHAN_RLL_ERR, BTS_CTR_BTS_OML_FAIL, BTS_CTR_BTS_RSL_FAIL, BTS_CTR_CODEC_AMR_F, BTS_CTR_CODEC_AMR_H, BTS_CTR_CODEC_EFR, BTS_CTR_CODEC_V1_FR, BTS_CTR_CODEC_V1_HR, BTS_CTR_PAGING_ATTEMPTED, BTS_CTR_PAGING_ALREADY, BTS_CTR_PAGING_RESPONDED, BTS_CTR_PAGING_EXPIRED, BTS_CTR_CHAN_ACT_TOTAL, BTS_CTR_CHAN_ACT_NACK, BTS_CTR_RSL_UNKNOWN, BTS_CTR_RSL_IPA_NACK, BTS_CTR_MODE_MODIFY_NACK, }; static const struct rate_ctr_desc bts_ctr_description[] = { [BTS_CTR_CHREQ_TOTAL] = {"chreq:total", "Received channel requests."}, [BTS_CTR_CHREQ_NO_CHANNEL] = {"chreq:no_channel", "Sent to MS no channel available."}, [BTS_CTR_CHAN_RF_FAIL] = {"chan:rf_fail", "Received a RF failure indication from BTS."}, [BTS_CTR_CHAN_RLL_ERR] = {"chan:rll_err", "Received a RLL failure with T200 cause from BTS."}, [BTS_CTR_BTS_OML_FAIL] = {"oml_fail", "Received a TEI down on a OML link."}, [BTS_CTR_BTS_RSL_FAIL] = {"rsl_fail", "Received a TEI down on a OML link."}, [BTS_CTR_CODEC_AMR_F] = {"codec:amr_f", "Count the usage of AMR/F codec by channel mode requested."}, [BTS_CTR_CODEC_AMR_H] = {"codec:amr_h", "Count the usage of AMR/H codec by channel mode requested."}, [BTS_CTR_CODEC_EFR] = {"codec:efr", "Count the usage of EFR codec by channel mode requested."}, [BTS_CTR_CODEC_V1_FR] = {"codec:fr", "Count the usage of FR codec by channel mode requested."}, [BTS_CTR_CODEC_V1_HR] = {"codec:hr", "Count the usage of HR codec by channel mode requested."}, [BTS_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber."}, [BTS_CTR_PAGING_ALREADY] = {"paging:already", "Paging attempts ignored as subsciber was already being paged."}, [BTS_CTR_PAGING_RESPONDED] = {"paging:responded", "Paging attempts with successful paging response."}, [BTS_CTR_PAGING_EXPIRED] = {"paging:expired", "Paging Request expired because of timeout T3113."}, [BTS_CTR_CHAN_ACT_TOTAL] = {"chan_act:total", "Total number of Channel Activations."}, [BTS_CTR_CHAN_ACT_NACK] = {"chan_act:nack", "Number of Channel Activations that the BTS NACKed"}, [BTS_CTR_RSL_UNKNOWN] = {"rsl:unknown", "Number of unknown/unsupported RSL messages received from BTS"}, [BTS_CTR_RSL_IPA_NACK] = {"rsl:ipa_nack", "Number of IPA (RTP/dyn-PDCH) related NACKs received from BTS"}, [BTS_CTR_MODE_MODIFY_NACK] = {"chan:mode_modify_nack", "Number of Channel Mode Modify NACKs received from BTS"}, }; static const struct rate_ctr_group_desc bts_ctrg_desc = { "bts", "base transceiver station", OSMO_STATS_CLASS_GLOBAL, ARRAY_SIZE(bts_ctr_description), bts_ctr_description, }; enum { BTS_STAT_CHAN_LOAD_AVERAGE, BTS_STAT_T3122, }; enum { BSC_CTR_HANDOVER_ATTEMPTED, BSC_CTR_HANDOVER_NO_CHANNEL, BSC_CTR_HANDOVER_TIMEOUT, BSC_CTR_HANDOVER_COMPLETED, BSC_CTR_HANDOVER_FAILED, BSC_CTR_PAGING_ATTEMPTED, BSC_CTR_PAGING_DETACHED, BSC_CTR_PAGING_RESPONDED, BSC_CTR_UNKNOWN_UNIT_ID, }; static const struct rate_ctr_desc bsc_ctr_description[] = { [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover:attempted", "Received handover attempts."}, [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover:no_channel", "Sent no channel available responses."}, [BSC_CTR_HANDOVER_TIMEOUT] = {"handover:timeout", "Timeouts of timer T3103."}, [BSC_CTR_HANDOVER_COMPLETED] = {"handover:completed", "Received handover completed."}, [BSC_CTR_HANDOVER_FAILED] = {"handover:failed", "Received HO FAIL messages."}, [BSC_CTR_PAGING_ATTEMPTED] = {"paging:attempted", "Paging attempts for a subscriber."}, [BSC_CTR_PAGING_DETACHED] = {"paging:detached", "Paging request send failures because no responsible BTS was found."}, [BSC_CTR_PAGING_RESPONDED] = {"paging:responded", "Paging attempts with successful response."}, [BSC_CTR_UNKNOWN_UNIT_ID] = {"abis:unknown_unit_id", "Connection attempts from unknown IPA CCM Unit ID."}, }; static const struct rate_ctr_group_desc bsc_ctrg_desc = { "bsc", "base station controller", OSMO_STATS_CLASS_GLOBAL, ARRAY_SIZE(bsc_ctr_description), bsc_ctr_description, }; #define GSM_T3101_DEFAULT 3 /* s */ #define GSM_T3103_DEFAULT 5 /* s */ #define GSM_T3105_DEFAULT 100 /* ms */ #define GSM_T3107_DEFAULT 5 /* s */ #define GSM_T3109_DEFAULT 5 /* s, must be 2s + radio_link_timeout*0.48 */ #define GSM_T3111_DEFAULT 2 /* s */ #define GSM_T3113_DEFAULT 10 /* s */ #define GSM_T3115_DEFAULT 10 #define GSM_T3117_DEFAULT 10 #define GSM_T3119_DEFAULT 10 #define GSM_T3122_DEFAULT 10 #define GSM_T3141_DEFAULT 10 #define GSM_T10_DEFAULT 6 /* RR Assignment timeout, in seconds */ #define GSM_T7_DEFAULT 10 /* inter-BSC MO Handover first timeout, in seconds */ #define GSM_T8_DEFAULT 10 /* inter-BSC MO Handover second timeout, in seconds */ #define GSM_T101_DEFAULT 10 /* inter-BSC MT Handover timeout, in seconds */ struct gsm_tz { int override; /* if 0, use system's time zone instead. */ int hr; /* hour */ int mn; /* minute */ int dst; /* daylight savings */ }; struct gsm_network { /* TODO MSCSPLIT the gsm_network struct is basically a kitchen sink for * global settings and variables, "madly" mixing BSC and MSC stuff. Split * this in e.g. struct osmo_bsc and struct osmo_msc, with the things * these have in common, like country and network code, put in yet * separate structs and placed as members in osmo_bsc and osmo_msc. */ struct osmo_plmn_id plmn; /* bit-mask of permitted encryption algorithms. LSB=A5/0, MSB=A5/7 */ uint8_t a5_encryption_mask; int neci; struct handover_cfg *ho; struct { unsigned int congestion_check_interval_s; struct osmo_timer_list congestion_check_timer; } hodec2; struct rate_ctr_group *bsc_ctrs; unsigned int num_bts; struct llist_head bts_list; /* timer values */ int T3101; int T3103; /*< Handover timeout */ int T3105; int T3107; int T3109; int T3111; int T3113; int T3115; int T3117; int T3119; int T3122; int T3141; int T10; /*< RR Assignment timeout, in seconds */ int T7; /*< inter-BSC handover MO timeout from Handover Required to Handover Command */ int T8; /*< inter-BSC handover MO timeout from Handover Command to final Clear*/ int T101; /*< inter-BSC handover MT timeout from Handover Request to Handover Accept */ enum gsm_chan_t ctype_by_chreq[_NUM_CHREQ_T]; /* Use a TCH for handling requests of type paging any */ int pag_any_tch; /* MSC data in case we are a true BSC */ struct osmo_bsc_data *bsc_data; /* control interface */ struct ctrl_handle *ctrl; /* Allow or disallow TCH/F on dynamic TCH/F_TCH/H_PDCH; OS#1778 */ bool dyn_ts_allow_tch_f; /* all active subscriber connections. */ struct llist_head subscr_conns; /* if override is nonzero, this timezone data is used for all MM * contexts. */ /* TODO: in OsmoNITB, tz-override used to be BTS-specific. To enable * BTS|RNC specific timezone overrides for multi-tz networks in * OsmoMSC, this should be tied to the location area code (LAC). */ struct gsm_tz tz; /* List of all struct bsc_subscr used in libbsc. This llist_head is * allocated so that the llist_head pointer itself can serve as a * talloc context (useful to not have to pass the entire gsm_network * struct to the bsc_subscr_* API, and for bsc_susbscr unit tests to * not require gsm_data.h). In an MSC-without-BSC environment, this * pointer is NULL to indicate absence of a bsc_subscribers list. */ struct llist_head *bsc_subscribers; /* Periodic location update default value */ uint8_t t3212; /* Timer for periodic channel load measurements to maintain each BTS's T3122. */ struct osmo_timer_list t3122_chan_load_timer; struct { struct mgcp_client_conf *conf; struct mgcp_client *client; } mgw; }; static inline const struct osmo_location_area_id *bts_lai(struct gsm_bts *bts) { static struct osmo_location_area_id lai; lai = (struct osmo_location_area_id){ .plmn = bts->network->plmn, .lac = bts->location_area_code, }; return &lai; } extern void talloc_ctx_init(void *ctx_root); int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); enum gsm_bts_type parse_btstype(const char *arg); const char *btstype2str(enum gsm_bts_type type); struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, struct gsm_bts *start_bts); extern void *tall_bsc_ctx; extern int ipacc_rtp_direct; /* this actaully refers to the IPA transport, not the BTS model */ static inline int is_ipaccess_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: return 1; default: break; } return 0; } static inline int is_sysmobts_v2(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_OSMOBTS: return 1; default: break; } return 0; } static inline int is_siemens_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_BS11: return 1; default: break; } return 0; } static inline int is_nokia_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_NOKIA_SITE: return 1; default: break; } return 0; } static inline int is_e1_bts(struct gsm_bts *bts) { switch (bts->type) { case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_RBS2000: case GSM_BTS_TYPE_NOKIA_SITE: return 1; default: break; } return 0; } extern struct gsm_network *bsc_gsmnet; enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid); const char *bts_gprs_mode_name(enum bts_gprs_mode mode); int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode); void gsm48_ra_id_by_bts(struct gsm48_ra_id *buf, struct gsm_bts *bts); void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); int gsm_bts_model_register(struct gsm_bts_model *model); struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network); struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network); void msc_subscr_con_free(struct gsm_subscriber_connection *conn); struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic); struct gsm_bts *bsc_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic); void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, uint8_t e1_ts, uint8_t e1_ts_ss); void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason); struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx); int gsm_bts_set_system_infos(struct gsm_bts *bts); /* generic E1 line operations for all ISDN-based BTS. */ extern struct e1inp_line_ops bts_isdn_e1inp_line_ops; extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1]; extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1]; char *get_model_oml_status(const struct gsm_bts *bts); unsigned long long bts_uptime(const struct gsm_bts *bts); /* control interface handling */ int bsc_base_ctrl_cmds_install(void); /* dependency handling */ void bts_depend_mark(struct gsm_bts *bts, int dep); void bts_depend_clear(struct gsm_bts *bts, int dep); int bts_depend_check(struct gsm_bts *bts); int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other); int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts); void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value); bool classmark_is_r99(struct gsm_classmark *cm); void gsm_ts_check_init(struct gsm_bts_trx_ts *ts); void gsm_trx_mark_all_ts_uninitialized(struct gsm_bts_trx *trx); void gsm_bts_mark_all_ts_uninitialized(struct gsm_bts *bts); bool trx_is_usable(const struct gsm_bts_trx *trx); bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts); #endif /* _GSM_DATA_H */ osmo-bsc-1.3.0/include/osmocom/bsc/handover.h000066400000000000000000000073341332665256100211020ustar00rootroot00000000000000#pragma once #include #include #include #include struct gsm_lchan; struct gsm_bts; struct gsm_subscriber_connection; struct gsm_meas_rep mr; #define LOGPHOLCHANTOLCHAN(old_lchan, new_lchan, level, fmt, args...) \ LOGP(DHODEC, level, "(BTS %u trx %u arfcn %u ts %u lchan %u %s %s)->(BTS %u trx %u arfcn %u ts %u lchan %u %s) (subscr %s) " fmt, \ old_lchan->ts->trx->bts->nr, \ old_lchan->ts->trx->nr, \ old_lchan->ts->trx->arfcn, \ old_lchan->ts->nr, \ old_lchan->nr, \ gsm_lchant_name(old_lchan->type), \ gsm48_chan_mode_name(old_lchan->tch_mode), \ new_lchan->ts->trx->bts->nr, \ new_lchan->ts->trx->nr, \ new_lchan->ts->trx->arfcn, \ new_lchan->ts->nr, \ new_lchan->nr, \ gsm_pchan_name(new_lchan->ts->pchan), \ bsc_subscr_name(old_lchan->conn? old_lchan->conn->bsub : NULL), \ ## args) #define LOGPHO(struct_bsc_handover, level, fmt, args ...) \ LOGPHOLCHANTOLCHAN(struct_bsc_handover->old_lchan, struct_bsc_handover->new_lchan, level, fmt, ## args) enum hodec_id { HODEC_NONE, HODEC1 = 1, HODEC2 = 2, }; struct bsc_handover { struct llist_head list; /* Initial details of what is requested */ struct gsm_lchan *old_lchan; struct gsm_bts *new_bts; enum gsm_chan_t new_lchan_type; bool async; /* Derived and resulting state */ bool inter_cell; uint8_t ho_ref; enum hodec_id from_hodec_id; struct gsm_lchan *new_lchan; struct osmo_timer_list T3103; }; int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts, enum gsm_chan_t new_lchan_type); int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn); void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan); struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan); int bsc_ho_count(struct gsm_bts *bts, bool inter_cell); /* Handover decision algorithms' actions to take on incoming handover-relevant events. * * All events that are interesting for handover decision are actually communicated by S_LCHAN_* signals, * so theoretically, each handover algorithm could evaluate those. However, handover_logic.c cleans up * handover operation state upon receiving some of these signals. To allow a handover decision algorithm * to take advantage of e.g. the struct bsc_handover before it is discarded, the handover decision event * handler needs to be invoked before handover_logic.c discards the state. For example, if the handover * decision wants to place a penalty timer upon a handover failure, it still needs to know which target * cell the handover failed for; handover_logic.c erases that knowledge on handover failure, since it * needs to clean up the lchan's handover state. * * The most explicit and safest way to ensure the correct order of event handling is to invoke the * handover decision algorithm's actions from handover_logic.c itself, before cleaning up. This struct * provides the callback functions for this purpose. * * For consistency, also handle signals in this way that aren't actually in danger of interference from * handover_logic.c (which also saves repeated lookup of handover state for lchans). Thus, handover * decision algorithms should not register any signal handler at all. */ struct handover_decision_callbacks { struct llist_head entry; int hodec_id; void (*on_measurement_report)(struct gsm_meas_rep *mr); void (*on_ho_chan_activ_nack)(struct bsc_handover *ho); void (*on_ho_failure)(struct bsc_handover *ho); }; void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc); struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id); osmo-bsc-1.3.0/include/osmocom/bsc/handover_cfg.h000066400000000000000000000274241332665256100217230ustar00rootroot00000000000000#pragma once #include #include struct vty; /* handover_cfg is an opaque struct to manage several levels of configuration. There is an overall handover * config on 'network' level and a per-'bts' specific handover config. If the 'bts' level sets no values, * the defaults from 'network' level are used implicitly, and changes take effect immediately. */ struct handover_cfg; #define HO_CFG_CONGESTION_CHECK_DEFAULT 10 struct handover_cfg *ho_cfg_init(void *ctx, struct handover_cfg *higher_level_cfg); #define HO_CFG_STR_HANDOVER1 "Handover options for handover decision algorithm 1\n" #define HO_CFG_STR_HANDOVER2 "Handover options for handover decision algorithm 2\n" #define HO_CFG_STR_WIN "Measurement averaging settings\n" #define HO_CFG_STR_WIN_RXLEV HO_CFG_STR_WIN "Received-Level averaging\n" #define HO_CFG_STR_WIN_RXQUAL HO_CFG_STR_WIN "Received-Quality averaging\n" #define HO_CFG_STR_POWER_BUDGET "Neighbor cell power triggering\n" "Neighbor cell power triggering\n" #define HO_CFG_STR_AVG_COUNT "Number of values to average over\n" #define HO_CFG_STR_2 " (HO algo 2 only)\n" #define HO_CFG_STR_MIN "Minimum Level/Quality thresholds before triggering HO" HO_CFG_STR_2 #define HO_CFG_STR_AFS_BIAS "Configure bias to prefer AFS (AMR on TCH/F) over other codecs" HO_CFG_STR_2 #define HO_CFG_STR_MIN_TCH "Minimum free TCH timeslots before cell is considered congested" HO_CFG_STR_2 #define HO_CFG_STR_PENALTY_TIME "Set penalty times to wait between repeated handovers" HO_CFG_STR_2 #define as_is(x) (x) static inline bool a2bool(const char *arg) { return (bool)(atoi(arg)); } static inline int bool2i(bool arg) { return arg? 1 : 0; } static inline bool a2tdma(const char *arg) { if (!strcmp(arg, "full")) return true; return false; } static inline const char *tdma2a(bool val) { return val? "full" : "subset"; } /* The HO_CFG_ONE_MEMBER macro gets redefined, depending on whether to define struct members, * function declarations or definitions... It is of the format * HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, * VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, * VTY_WRITE_FMT, VTY_WRITE_CONV, * VTY_DOC) * Then using HO_CFG_ALL_MEMBERS can save a lot of code dup in defining API declaration, API * definitions, VTY commands and VTY write code. Of course this doesn't prevent us from adding manual * members as well, in case future additions don't fit in this scheme. * * TYPE: a type name like int. * NAME: a variable name suitable for a struct member. * DEFAULT_VAL: default value, as passed to the VTY, e.g. '0' or 'foo', without quotes. * VTY_CMD_PREFIX: "handover1 ", "handover2 ", ... or just "" for the common general items. * VTY_CMD: a command string for VTY without any arguments. * VTY_CMD_ARG: VTY value range like '<0-23>' or 'foo|bar', will become '(VTY_CMD_ARG|default)'. * VTY_ARG_EVAL: function name for parsing the VTY arg[0], e.g. 'atoi'. * VTY_WRITE_FMT: printf-like string format for vty_out(). * VTY_WRITE_CONV: function name to convert struct value to VTY_WRITE_FMT, e.g. 'as_is'. * VTY_DOC: VTY documentation strings to match VTY_CMD and VTY_CMD_ARGs. */ #define HO_GENERAL_CFG_ALL_MEMBERS \ \ HO_CFG_ONE_MEMBER(bool, ho_active, 0, \ "", "handover", "0|1", a2bool, "%d", bool2i, \ "Handover general config\n" \ "Disable in-call handover\n" \ "Enable in-call handover\n" \ "Enable/disable handover: ") \ \ HO_CFG_ONE_MEMBER(int, algorithm, 1, \ "", "handover algorithm", "1|2", atoi, "%d", as_is, \ "Handover general config\n" \ "Choose algorithm for handover decision\n" \ "Algorithm 1: trigger handover based on comparing current cell and neighbor RxLev and RxQual," \ " only.\n" \ "Algorithm 2: trigger handover on RxLev/RxQual, and also to balance the load across several" \ " cells. Consider available codecs. Prevent repeated handover by penalty timers.\n") \ #define HODEC1_CFG_ALL_MEMBERS \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_rxlev_avg_win, 10, \ "handover1 ", "window rxlev averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ HO_CFG_STR_WIN_RXLEV \ "How many RxLev measurements are used for averaging\n" \ "RxLev averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_rxqual_avg_win, 1, \ "handover1 ", "window rxqual averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ HO_CFG_STR_WIN_RXQUAL \ "How many RxQual measurements are used for averaging\n" \ "RxQual averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_rxlev_neigh_avg_win, 10, \ "handover1 ", "window rxlev neighbor averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ HO_CFG_STR_WIN_RXLEV \ "How many Neighbor RxLev measurements are used for averaging\n" \ "How many Neighbor RxLev measurements are used for averaging\n" \ "Neighbor RxLev averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_pwr_interval, 6, \ "handover1 ", "power budget interval", "<1-99>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ HO_CFG_STR_POWER_BUDGET \ "How often to check for a better cell (SACCH frames)\n" \ "Check for stronger neighbor every N number of SACCH frames\n") \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_pwr_hysteresis, 3, \ "handover1 ", "power budget hysteresis", "<0-999>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ HO_CFG_STR_POWER_BUDGET \ "How many dB stronger must a neighbor be to become a HO candidate\n" \ "Neighbor's strength difference in dB\n") \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec1_max_distance, 9999, \ "handover1 ", "maximum distance" , "<0-9999>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER1 \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n" \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n" \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n") \ #define HODEC2_CFG_ALL_MEMBERS \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_rxlev_avg_win, 10, \ "handover2 ", "window rxlev averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_WIN_RXLEV \ "How many RxLev measurements are used for averaging\n" \ "RxLev averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_rxqual_avg_win, 1, \ "handover2 ", "window rxqual averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_WIN_RXQUAL \ "How many RxQual measurements are used for averaging\n" \ "RxQual averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_rxlev_neigh_avg_win, 10, \ "handover2 ", "window rxlev neighbor averaging", "<1-10>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_WIN_RXLEV \ "How many Neighbor RxLev measurements are used for averaging\n" \ "How many Neighbor RxLev measurements are used for averaging\n" \ "Neighbor RxLev averaging: " HO_CFG_STR_AVG_COUNT) \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_pwr_interval, 6, \ "handover2 ", "power budget interval", "<1-99>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_POWER_BUDGET \ "How often to check for a better cell (SACCH frames)\n" \ "Check for stronger neighbor every N number of SACCH frames\n") \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_pwr_hysteresis, 3, \ "handover2 ", "power budget hysteresis", "<0-999>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_POWER_BUDGET \ "How many dB stronger must a neighbor be to become a HO candidate\n" \ "Neighbor's strength difference in dB\n") \ \ HO_CFG_ONE_MEMBER(unsigned int, hodec2_max_distance, 9999, \ "handover2 ", "maximum distance" , "<0-9999>", atoi, "%u", as_is, \ HO_CFG_STR_HANDOVER2 \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n" \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n" \ "Maximum Timing-Advance value (i.e. MS distance) before triggering HO\n") \ \ HO_CFG_ONE_MEMBER(bool, hodec2_as_active, 0, \ "handover2 ", "assignment", "0|1", a2bool, "%d", bool2i, \ HO_CFG_STR_HANDOVER2 \ "Enable or disable in-call channel re-assignment" HO_CFG_STR_2 \ "Disable in-call assignment\n" \ "Enable in-call assignment\n") \ \ HO_CFG_ONE_MEMBER(bool, hodec2_full_tdma, subset, \ "handover2 ", "tdma-measurement", "full|subset", a2tdma, "%s", tdma2a, \ HO_CFG_STR_HANDOVER2 \ "Define measurement set of TDMA frames" HO_CFG_STR_2 \ "Full set of 102/104 TDMA frames\n" \ "Sub set of 4 TDMA frames (SACCH)\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_min_rxlev, -100, \ "handover2 ", "min rxlev", "<-110--50>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_MIN \ "How weak may RxLev of an MS become before triggering HO\n" \ "minimum RxLev (dBm)\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_min_rxqual, 5, \ "handover2 ", "min rxqual", "<0-7>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_MIN \ "How bad may RxQual of an MS become before triggering HO\n" \ "minimum RxQual\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_afs_bias_rxlev, 0, \ "handover2 ", "afs-bias rxlev", "<0-20>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_AFS_BIAS \ "RxLev improvement bias for AFS over other codecs\n" \ "Virtual RxLev improvement (dB)\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_afs_bias_rxqual, 0, \ "handover2 ", "afs-bias rxqual", "<0-7>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_AFS_BIAS \ "RxQual improvement bias for AFS over other codecs\n" \ "Virtual RxQual improvement\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_tchf_min_slots, 0, \ "handover2 ", "min-free-slots tch/f", "<0-9999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_MIN_TCH \ "Minimum free TCH/F timeslots before cell is considered congested\n" \ "Number of TCH/F slots\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_tchh_min_slots, 0, \ "handover2 ", "min-free-slots tch/h", "<0-9999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_MIN_TCH \ "Minimum free TCH/H timeslots before cell is considered congested\n" \ "Number of TCH/H slots\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_ho_max, 9999, \ "handover2 ", "max-handovers", "<1-9999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ "Maximum number of concurrent handovers allowed per cell" HO_CFG_STR_2 \ "Number\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_penalty_max_dist, 300, \ "handover2 ", "penalty-time max-distance", "<0-99999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_PENALTY_TIME \ "Time to suspend handovers after leaving this cell due to exceeding max distance\n" \ "Seconds\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_penalty_failed_ho, 60, \ "handover2 ", "penalty-time failed-ho", "<0-99999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_PENALTY_TIME \ "Time to suspend handovers after handover failure to this cell\n" \ "Seconds\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_penalty_failed_as, 60, \ "handover2 ", "penalty-time failed-assignment", "<0-99999>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ HO_CFG_STR_PENALTY_TIME \ "Time to suspend handovers after assignment failure in this cell\n" \ "Seconds\n") \ \ HO_CFG_ONE_MEMBER(int, hodec2_retries, 0, \ "handover2 ", "retries", "<0-9>", atoi, "%d", as_is, \ HO_CFG_STR_HANDOVER2 \ "Immediately retry on handover/assignment failure" HO_CFG_STR_2 \ "Number of retries\n") \ #define HO_CFG_ALL_MEMBERS \ HO_GENERAL_CFG_ALL_MEMBERS \ HODEC1_CFG_ALL_MEMBERS \ HODEC2_CFG_ALL_MEMBERS \ /* Declare public API for handover cfg parameters... */ #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY3, VTY4, VTY5, VTY6) \ TYPE ho_get_##NAME(struct handover_cfg *ho); \ void ho_set_##NAME(struct handover_cfg *ho, TYPE val); \ bool ho_isset_##NAME(struct handover_cfg *ho); \ void ho_clear_##NAME(struct handover_cfg *ho); \ bool ho_isset_on_parent_##NAME(struct handover_cfg *ho); HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER osmo-bsc-1.3.0/include/osmocom/bsc/handover_decision.h000066400000000000000000000002241332665256100227460ustar00rootroot00000000000000#pragma once struct gsm_bts *bts_by_arfcn_bsic(const struct gsm_network *net, uint16_t arfcn, uint8_t bsic); void handover_decision_1_init(void); osmo-bsc-1.3.0/include/osmocom/bsc/handover_decision_2.h000066400000000000000000000005061332665256100231720ustar00rootroot00000000000000/* Handover Decision Algorithm 2 for intra-BSC (inter-BTS) handover, public API for OsmoBSC */ #pragma once struct gsm_bts; void hodec2_init(struct gsm_network *net); void hodec2_on_change_congestion_check_interval(struct gsm_network *net, unsigned int new_interval); void hodec2_congestion_check(struct gsm_network *net); osmo-bsc-1.3.0/include/osmocom/bsc/handover_vty.h000066400000000000000000000003441332665256100217760ustar00rootroot00000000000000#pragma once #include #include void ho_vty_init(); void ho_vty_write_net(struct vty *vty, struct gsm_network *net); void ho_vty_write_bts(struct vty *vty, struct gsm_bts *bts); osmo-bsc-1.3.0/include/osmocom/bsc/ipaccess.h000066400000000000000000000023561332665256100210650ustar00rootroot00000000000000#ifndef _IPACCESS_H #define _IPACCESS_H #include #include #include #include struct gsm_bts; struct gsm_bts_trx; struct ipac_msgt_sccp_state { uint8_t src_ref[3]; uint8_t dst_ref[3]; uint8_t trans_id; uint8_t invoke_id; char imsi[GSM23003_IMSI_MAX_DIGITS+1]; uint8_t data[0]; } __attribute__((packed)); /* * @add_remove 0 for remove, 1 for add, 3 to asK * @nr_lacs Number of extra lacs inside this package * @lac One lac entry */ struct ipac_ext_lac_cmd { uint8_t add_remove; uint8_t nr_extra_lacs; uint16_t lac; uint8_t data[0]; } __attribute__((packed)); void ipaccess_drop_oml(struct gsm_bts *bts); void ipaccess_drop_rsl(struct gsm_bts_trx *trx); struct sdp_header_item { struct sdp_header_entry header_entry; struct llist_head entry; off_t absolute_offset; }; struct sdp_header { struct sdp_firmware firmware_info; /* for more_magic a list of sdp_header_entry_list */ struct llist_head header_list; /* the entry of the sdp_header */ struct llist_head entry; }; int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned base_offset, struct llist_head *list); #endif /* _IPACCESS_H */ osmo-bsc-1.3.0/include/osmocom/bsc/meas_feed.h000066400000000000000000000016731332665256100212040ustar00rootroot00000000000000#pragma once #include #include struct meas_feed_hdr { uint8_t msg_type; uint8_t reserved; uint16_t version; }; struct meas_feed_meas { struct meas_feed_hdr hdr; char imsi[15+1]; char name[31+1]; char scenario[31+1]; struct gsm_meas_rep mr; /* The logical channel type, enum gsm_chan_t */ uint8_t lchan_type; /* The physical channel type, enum gsm_phys_chan_config */ uint8_t pchan_type; /* number of ths BTS in network */ uint8_t bts_nr; /* number of this TRX in the BTS */ uint8_t trx_nr; /* number of this timeslot at the TRX */ uint8_t ts_nr; /* The logical subslot number in the TS */ uint8_t ss_nr; }; enum meas_feed_msgtype { MEAS_FEED_MEAS = 0, }; #define MEAS_FEED_VERSION 1 int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port); void meas_feed_scenario_set(const char *name); void meas_feed_cfg_get(char **host, uint16_t *port); const char *meas_feed_scenario_get(void); osmo-bsc-1.3.0/include/osmocom/bsc/meas_rep.h000066400000000000000000000032611332665256100210620ustar00rootroot00000000000000#ifndef _MEAS_REP_H #define _MEAS_REP_H #include #include #define MRC_F_PROCESSED 0x0001 /* extracted from a L3 measurement report IE */ struct gsm_meas_rep_cell { uint8_t rxlev; uint8_t bsic; uint8_t neigh_idx; uint16_t arfcn; unsigned int flags; }; #define MEAS_REP_F_UL_DTX 0x01 #define MEAS_REP_F_DL_VALID 0x02 #define MEAS_REP_F_BA1 0x04 #define MEAS_REP_F_DL_DTX 0x08 #define MEAS_REP_F_MS_TO 0x10 #define MEAS_REP_F_MS_L1 0x20 #define MEAS_REP_F_FPC 0x40 /* parsed uplink and downlink measurement result */ struct gsm_meas_rep { /* back-pointer to the logical channel */ struct gsm_lchan *lchan; /* number of the measurement report */ uint8_t nr; /* flags, see MEAS_REP_F_* */ unsigned int flags; /* uplink and downlink rxlev, rxqual; full and sub */ struct gsm_meas_rep_unidir ul; struct gsm_meas_rep_unidir dl; uint8_t bs_power; /* according to 3GPP TS 48.058 § MS Timing Offset [-63; 192] */ int16_t ms_timing_offset; struct { int8_t pwr; /* MS power in dBm */ uint8_t ta; /* MS timing advance */ } ms_l1; /* neighbor measurement reports for up to 6 cells */ int num_cell; struct gsm_meas_rep_cell cell[6]; }; /* obtain an average over the last 'num' fields in the meas reps */ int get_meas_rep_avg(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int num); /* Check if N out of M last values for FIELD are >= bd */ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int n, unsigned int m, int be); unsigned int calc_initial_idx(unsigned int array_size, unsigned int meas_rep_idx, unsigned int num_values); #endif /* _MEAS_REP_H */ osmo-bsc-1.3.0/include/osmocom/bsc/misdn.h000066400000000000000000000016611332665256100204030ustar00rootroot00000000000000/* (C) 2008 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 . * */ #ifndef MISDN_H #define MISDN_H #include int mi_setup(int cardnr, struct e1inp_line *line, int release_l2); int mi_e1_line_update(struct e1inp_line *line); #endif osmo-bsc-1.3.0/include/osmocom/bsc/network_listen.h000066400000000000000000000007411332665256100223360ustar00rootroot00000000000000#ifndef _OPENBSC_NWL_H #define _OPENBSC_NWL_H #include #include void ipac_nwl_init(void); /* Start a NWL test. It will raise the S_IPAC_TEST_COMPLETE signal. */ int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, const uint8_t *phys_conf, unsigned int phys_conf_len); int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, uint16_t max_num_arfcns); #endif /* _OPENBSC_NWL_H */ osmo-bsc-1.3.0/include/osmocom/bsc/openbscdefines.h000066400000000000000000000020551332665256100222560ustar00rootroot00000000000000/* * (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 . * */ #ifndef OPENBSCDEFINES_H #define OPENBSCDEFINES_H #ifdef BUILDING_ON_WINDOWS #ifdef BUILDING_OPENBSC #define BSC_API __declspec(dllexport) #else #define BSC_API __declspec(dllimport) #endif #else #define BSC_API __attribute__((visibility("default"))) #endif #endif osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc.h000066400000000000000000000024221332665256100210710ustar00rootroot00000000000000/* OpenBSC BSC code */ #ifndef OSMO_BSC_H #define OSMO_BSC_H #include "bsc_api.h" #include "bsc_msg_filter.h" #define BSS_SEND_USSD 1 enum bsc_con { BSC_CON_SUCCESS, BSC_CON_REJECT_NO_LINK, BSC_CON_REJECT_RF_GRACE, BSC_CON_NO_MEM, }; struct bsc_msc_data; struct bsc_api *osmo_bsc_api(); int bsc_queue_for_msc(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_open_connection(struct gsm_subscriber_connection *sccp, struct msgb *msg); enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc, int send_ping); int bsc_delete_connection(struct gsm_subscriber_connection *sccp); struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *); int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); int bsc_handle_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length); int bsc_handle_dt(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int len); int bsc_ctrl_cmds_install(); void bsc_gen_location_state_trap(struct gsm_bts *bts); struct llist_head *bsc_access_lists(void); #endif osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc_grace.h000066400000000000000000000022711332665256100222340ustar00rootroot00000000000000/* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 . * */ #ifndef OSMO_BSC_GRACE_H #define OSMO_BSC_GRACE_H #include #include struct bsc_msc_data; int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts); int bsc_grace_paging_request(enum signal_rf rf_policy, struct bsc_subscr *subscr, int chan_needed, struct bsc_msc_data *msc, struct gsm_bts *bts); #endif osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc_lcls.h000066400000000000000000000022231332665256100221050ustar00rootroot00000000000000#pragma once #include enum lcls_fsm_state { ST_NO_LCLS, ST_NOT_YET_LS, ST_NOT_POSSIBLE_LS, ST_NO_LONGER_LS, ST_REQ_LCLS_NOT_SUPP, ST_LOCALLY_SWITCHED, /* locally switched; received remote break; wait for "local" break */ ST_LOCALLY_SWITCHED_WAIT_BREAK, /* locally switched; received break; wait for "other" break */ ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK, }; enum lcls_event { /* update LCLS config/control based on some BSSMAP signaling */ LCLS_EV_UPDATE_CFG_CSC, /* apply LCLS config/control */ LCLS_EV_APPLY_CFG_CSC, /* we have been identified as the correlation peer of another conn */ LCLS_EV_CORRELATED, /* "other" LCLS connection has enabled local switching */ LCLS_EV_OTHER_ENABLED, /* "other" LCLS connection is breaking local switch */ LCLS_EV_OTHER_BREAK, /* "other" LCLS connection is dying */ LCLS_EV_OTHER_DEAD, }; enum gsm0808_lcls_status lcls_get_status(struct gsm_subscriber_connection *conn); void lcls_update_config(struct gsm_subscriber_connection *conn, const uint8_t *config, const uint8_t *control); void lcls_apply_config(struct gsm_subscriber_connection *conn); extern struct osmo_fsm lcls_fsm; osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc_reset.h000066400000000000000000000024161332665256100222760ustar00rootroot00000000000000/* (C) 2017 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 . * */ /* Create and start state machine which handles the reset/reset-ack procedure */ void start_reset_fsm(struct bsc_msc_data *msc); /* Confirm that we sucessfully received a reset acknowlege message */ void reset_ack_confirm(struct bsc_msc_data *msc); /* Report a failed connection */ void report_conn_fail(struct bsc_msc_data *msc); /* Report a successful connection */ void report_conn_success(struct bsc_msc_data *msc); /* Check if we have a connection to a specified msc */ bool sccp_conn_ready(struct bsc_msc_data *msc); osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc_rf.h000066400000000000000000000033331332665256100215620ustar00rootroot00000000000000#ifndef OSMO_BSC_RF #define OSMO_BSC_RF #include #include #include enum osmo_bsc_rf_opstate { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, OSMO_BSC_RF_OPSTATE_OPERATIONAL, }; enum osmo_bsc_rf_adminstate { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, OSMO_BSC_RF_ADMINSTATE_LOCKED, }; enum osmo_bsc_rf_policy { OSMO_BSC_RF_POLICY_OFF, OSMO_BSC_RF_POLICY_ON, OSMO_BSC_RF_POLICY_GRACE, OSMO_BSC_RF_POLICY_UNKNOWN, }; struct gsm_network; struct osmo_bsc_rf { /* the value of signal.h */ int policy; struct osmo_fd listen; struct gsm_network *gsm_network; const char *last_state_command; char *last_rf_lock_ctrl_command; /* delay the command */ char last_request; struct osmo_timer_list delay_cmd; /* verify that RF is up as it should be */ struct osmo_timer_list rf_check; /* some handling for the automatic grace switch */ struct osmo_timer_list grace_timeout; /* auto RF switch-off due lack of MSC connection */ struct osmo_timer_list auto_off_timer; }; struct osmo_bsc_rf_conn { struct osmo_wqueue queue; struct osmo_bsc_rf *rf; }; const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate); const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate); const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy); enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts); enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts); enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts); struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net); void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd); #endif osmo-bsc-1.3.0/include/osmocom/bsc/osmo_bsc_sigtran.h000066400000000000000000000033721332665256100226250ustar00rootroot00000000000000/* (C) 2017 by Sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 . * */ #pragma once #include #include /* Allocate resources to make a new connection oriented sigtran connection * (not the connection ittself!) */ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc); /* Open a new connection oriented sigtran connection */ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg); /* Send data to MSC */ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg); /* Initalize osmo sigtran backhaul */ int osmo_bsc_sigtran_init(struct llist_head *mscs); /* Close all open sigtran connections and channels */ void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc); /* Send reset-ack to MSC */ void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc); /* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link */ int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg); osmo-bsc-1.3.0/include/osmocom/bsc/osmux.h000066400000000000000000000017431332665256100204450ustar00rootroot00000000000000#ifndef _OPENBSC_OSMUX_H_ #define _OPENBSC_OSMUX_H_ #include #define OSMUX_PORT 1984 enum { OSMUX_ROLE_BSC = 0, OSMUX_ROLE_BSC_NAT, }; int osmux_init(int role, struct mgcp_config *cfg); int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port); void osmux_disable_endpoint(struct mgcp_endpoint *endp); void osmux_allocate_cid(struct mgcp_endpoint *endp); void osmux_release_cid(struct mgcp_endpoint *endp); int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); int osmux_send_dummy(struct mgcp_endpoint *endp); int osmux_get_cid(void); void osmux_put_cid(uint8_t osmux_cid); int osmux_used_cid(void); enum osmux_state { OSMUX_STATE_DISABLED = 0, OSMUX_STATE_NEGOTIATING, OSMUX_STATE_ACTIVATING, OSMUX_STATE_ENABLED, }; enum osmux_usage { OSMUX_USAGE_OFF = 0, OSMUX_USAGE_ON = 1, OSMUX_USAGE_ONLY = 2, }; #endif osmo-bsc-1.3.0/include/osmocom/bsc/paging.h000066400000000000000000000046361332665256100205430ustar00rootroot00000000000000/* Paging helper and manager.... */ /* (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 . * */ #ifndef PAGING_H #define PAGING_H #include #include #include #include #include #include struct bsc_msc_data; /** * A pending paging request */ struct gsm_paging_request { /* list_head for list of all paging requests */ struct llist_head entry; /* the subscriber which we're paging. Later gsm_paging_request * should probably become a part of the bsc_subsrc struct? */ struct bsc_subscr *bsub; /* back-pointer to the BTS on which we are paging */ struct gsm_bts *bts; /* what kind of channel type do we ask the MS to establish */ int chan_type; /* Timer 3113: how long do we try to page? */ struct osmo_timer_list T3113; /* How often did we ask the BTS to page? */ int attempts; /* MSC that has issued this paging */ struct bsc_msc_data *msc; }; /* schedule paging request */ int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, int type, struct bsc_msc_data *msc); /* stop paging requests */ void paging_request_stop(struct llist_head *bts_list, struct gsm_bts *_bts, struct bsc_subscr *bsub, struct gsm_subscriber_connection *conn, struct msgb *msg); /* update paging load */ void paging_update_buffer_space(struct gsm_bts *bts, uint16_t); /* pending paging requests */ unsigned int paging_pending_requests_nr(struct gsm_bts *bts); struct bsc_msc_data *paging_get_msc(struct gsm_bts *bts, struct bsc_subscr *bsub); void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc); void paging_flush_network(struct gsm_network *net, struct bsc_msc_data *msc); #endif osmo-bsc-1.3.0/include/osmocom/bsc/pcu_if.h000066400000000000000000000020171332665256100205320ustar00rootroot00000000000000#ifndef _PCU_IF_H #define _PCU_IF_H #include extern int pcu_direct; struct pcu_sock_state { struct gsm_network *net; struct osmo_fd listen_bfd; /* fd for listen socket */ struct osmo_fd conn_bfd; /* fd for connection to lcr */ struct llist_head upqueue; /* queue for sending messages */ }; /* PCU relevant information has changed; Inform PCU (if connected) */ void pcu_info_update(struct gsm_bts *bts); /* Forward rach indication to PCU */ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, uint8_t is_11bit, enum ph_burst_type burst_type); /* Confirm the sending of an immediate assignment to the pcu */ int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli); /* Confirm the sending of an immediate assignment to the pcu */ int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli); /* Open connection to PCU */ int pcu_sock_init(const char *path, struct gsm_bts *bts); /* Close connection to PCU */ void pcu_sock_exit(struct gsm_bts *bts); #endif /* _PCU_IF_H */ osmo-bsc-1.3.0/include/osmocom/bsc/pcuif_proto.h000066400000000000000000000123171332665256100216220ustar00rootroot00000000000000#ifndef _PCUIF_PROTO_H #define _PCUIF_PROTO_H #include #define PCU_IF_VERSION 0x09 #define TXT_MAX_LEN 128 /* msg_type */ #define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */ #define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */ #define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */ #define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */ #define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */ #define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */ #define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */ #define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */ #define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */ #define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */ #define PCU_IF_MSG_TXT_IND 0x70 /* Text indication for BTS */ /* sapi */ #define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */ #define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */ #define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */ #define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */ #define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */ #define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */ #define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */ #define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */ /* flags */ #define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */ #define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */ #define PCU_IF_FLAG_CS1 (1 << 16) #define PCU_IF_FLAG_CS2 (1 << 17) #define PCU_IF_FLAG_CS3 (1 << 18) #define PCU_IF_FLAG_CS4 (1 << 19) #define PCU_IF_FLAG_MCS1 (1 << 20) #define PCU_IF_FLAG_MCS2 (1 << 21) #define PCU_IF_FLAG_MCS3 (1 << 22) #define PCU_IF_FLAG_MCS4 (1 << 23) #define PCU_IF_FLAG_MCS5 (1 << 24) #define PCU_IF_FLAG_MCS6 (1 << 25) #define PCU_IF_FLAG_MCS7 (1 << 26) #define PCU_IF_FLAG_MCS8 (1 << 27) #define PCU_IF_FLAG_MCS9 (1 << 28) enum gsm_pcu_if_text_type { PCU_VERSION, PCU_OML_ALERT, }; struct gsm_pcu_if_txt_ind { uint8_t type; /* gsm_pcu_if_text_type */ char text[TXT_MAX_LEN]; /* Text to be transmitted to BTS */ } __attribute__ ((packed)); struct gsm_pcu_if_data { uint8_t sapi; uint8_t len; uint8_t data[162]; uint32_t fn; uint16_t arfcn; uint8_t trx_nr; uint8_t ts_nr; uint8_t block_nr; int8_t rssi; uint16_t ber10k; /* !< \brief BER in units of 0.01% */ int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */ int16_t lqual_cb; /* !< \brief Link quality in centiBel */ } __attribute__ ((packed)); /* data confirmation with direct tlli (instead of raw mac block with tlli) */ struct gsm_pcu_if_data_cnf_dt { uint8_t sapi; uint32_t tlli; uint32_t fn; uint16_t arfcn; uint8_t trx_nr; uint8_t ts_nr; uint8_t block_nr; int8_t rssi; uint16_t ber10k; /* !< \brief BER in units of 0.01% */ int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */ int16_t lqual_cb; /* !< \brief Link quality in centiBel */ } __attribute__ ((packed)); struct gsm_pcu_if_rts_req { uint8_t sapi; uint8_t spare[3]; uint32_t fn; uint16_t arfcn; uint8_t trx_nr; uint8_t ts_nr; uint8_t block_nr; } __attribute__ ((packed)); struct gsm_pcu_if_rach_ind { uint8_t sapi; uint16_t ra; int16_t qta; uint32_t fn; uint16_t arfcn; uint8_t is_11bit; uint8_t burst_type; } __attribute__ ((packed)); struct gsm_pcu_if_info_trx { uint16_t arfcn; uint8_t pdch_mask; /* PDCH channels per TS */ uint8_t spare; uint8_t tsc[8]; /* TSC per channel */ uint32_t hlayer1; } __attribute__ ((packed)); struct gsm_pcu_if_info_ind { uint32_t version; uint32_t flags; struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */ uint8_t bsic; /* RAI */ uint16_t mcc, mnc; uint8_t mnc_3_digits; uint16_t lac, rac; /* NSE */ uint16_t nsei; uint8_t nse_timer[7]; uint8_t cell_timer[11]; /* cell */ uint16_t cell_id; uint16_t repeat_time; uint8_t repeat_count; uint16_t bvci; uint8_t t3142; uint8_t t3169; uint8_t t3191; uint8_t t3193_10ms; uint8_t t3195; uint8_t n3101; uint8_t n3103; uint8_t n3105; uint8_t cv_countdown; uint16_t dl_tbf_ext; uint16_t ul_tbf_ext; uint8_t initial_cs; uint8_t initial_mcs; /* NSVC */ uint16_t nsvci[2]; uint16_t local_port[2]; uint16_t remote_port[2]; uint32_t remote_ip[2]; } __attribute__ ((packed)); struct gsm_pcu_if_act_req { uint8_t activate; uint8_t trx_nr; uint8_t ts_nr; uint8_t spare; } __attribute__ ((packed)); struct gsm_pcu_if_time_ind { uint32_t fn; } __attribute__ ((packed)); struct gsm_pcu_if_pag_req { uint8_t sapi; uint8_t chan_needed; uint8_t identity_lv[9]; } __attribute__ ((packed)); struct gsm_pcu_if { /* context based information */ uint8_t msg_type; /* message type */ uint8_t bts_nr; /* bts number */ uint8_t spare[2]; union { struct gsm_pcu_if_data data_req; struct gsm_pcu_if_data data_cnf; struct gsm_pcu_if_data_cnf_dt data_cnf_dt; struct gsm_pcu_if_data data_ind; struct gsm_pcu_if_rts_req rts_req; struct gsm_pcu_if_rach_ind rach_ind; struct gsm_pcu_if_txt_ind txt_ind; struct gsm_pcu_if_info_ind info_ind; struct gsm_pcu_if_act_req act_req; struct gsm_pcu_if_time_ind time_ind; struct gsm_pcu_if_pag_req pag_req; } u; } __attribute__ ((packed)); #endif /* _PCUIF_PROTO_H */ osmo-bsc-1.3.0/include/osmocom/bsc/penalty_timers.h000066400000000000000000000045121332665256100223260ustar00rootroot00000000000000/* Manage a list of penalty timers per BTS; * initially used by handover algorithm 2 to keep per-BTS timers for each subscriber connection. */ #pragma once /* Opaque struct to manage penalty timers */ struct penalty_timers; /* Initialize a list of penalty timers. * param ctx: talloc context to allocate in. * returns an empty struct penalty_timers. */ struct penalty_timers *penalty_timers_init(void *ctx); /* Add a penalty timer for an arbitary object. * Note: the ownership of for_object remains with the caller; it is handled as a mere void* value, so * invalid pointers can be handled without problems, while common sense dictates that invalidated * pointers (freed objects) should probably be removed from this list. More importantly, the pointer must * match any pointers used to query penalty timers, so for_object should reference some global/singleton * object that tends to stay around longer than the penalty timers. * param pt: penalty timers list as from penalty_timers_init(). * param for_object: arbitrary pointer reference to store a penalty timer for (passing NULL is possible, * but note that penalty_timers_clear() will clear all timers if given for_object=NULL). * param timeout: penalty time in seconds. */ void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int timeout); /* Return the amount of penalty time remaining for an object. * param pt: penalty timers list as from penalty_timers_init(). * param for_object: arbitrary pointer reference to query penalty timers for. * returns seconds remaining until all penalty time has expired. */ unsigned int penalty_timers_remaining(struct penalty_timers *pt, const void *for_object); /* Clear penalty timers for one or all objects. * param pt: penalty timers list as from penalty_timers_init(). * param for_object: arbitrary pointer reference to clear penalty time for, * or NULL to clear all timers. */ void penalty_timers_clear(struct penalty_timers *pt, const void *for_object); /* Free a struct as returned from penalty_timers_init(). * Clear all timers from the list, deallocate the list and set the pointer to NULL. * param pt: pointer-to-pointer which references a struct penalty_timers as returned by * penalty_timers_init(); *pt_p will be set to NULL. */ void penalty_timers_free(struct penalty_timers **pt_p); osmo-bsc-1.3.0/include/osmocom/bsc/rest_octets.h000066400000000000000000000057241332665256100216330ustar00rootroot00000000000000#ifndef _REST_OCTETS_H #define _REST_OCTETS_H #include #include struct gsm_bts; /* generate SI1 rest octets */ int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net); int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts); int rest_octets_si2ter(uint8_t *data); int rest_octets_si2bis(uint8_t *data); int rest_octets_si6(uint8_t *data, bool is1800_net); struct gsm48_si_selection_params { uint16_t penalty_time:5, temp_offs:3, cell_resel_off:6, cbq:1, present:1; }; struct gsm48_si_power_offset { uint8_t power_offset:2, present:1; }; struct gsm48_si3_gprs_ind { uint8_t si13_position:1, ra_colour:3, present:1; }; struct gsm48_lsa_params { uint32_t prio_thr:3, lsa_offset:3, mcc:12, mnc:12; unsigned int present; }; struct gsm48_si_ro_info { struct gsm48_si_selection_params selection_params; struct gsm48_si_power_offset power_offset; bool si2ter_indicator; bool early_cm_ctrl; struct { uint8_t where:3, present:1; } scheduling; struct gsm48_si3_gprs_ind gprs_ind; /* SI 3 specific */ bool early_cm_restrict_3g; bool si2quater_indicator; /* SI 4 specific */ struct gsm48_lsa_params lsa_params; uint16_t cell_id; uint8_t break_ind; /* do we have SI7 + SI8 ? */ }; /* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3); /* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len); /* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ enum gprs_nmo { GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ GPRS_NMO_II = 1, /* all paging on CCCH */ GPRS_NMO_III = 2, /* no paging coordination */ }; /* TS 04.60 12.24 */ struct gprs_cell_options { enum gprs_nmo nmo; /* T3168: wait for packet uplink assignment message */ uint32_t t3168; /* in milliseconds */ /* T3192: wait for release of the TBF after reception of the final block */ uint32_t t3192; /* in milliseconds */ uint32_t drx_timer_max;/* in seconds */ uint32_t bs_cv_max; uint8_t supports_egprs_11bit_rach; bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */ uint8_t ext_info_present; struct { uint8_t egprs_supported; uint8_t use_egprs_p_ch_req; uint8_t bep_period; uint8_t pfc_supported; uint8_t dtm_supported; uint8_t bss_paging_coordination; } ext_info; }; /* TS 04.60 Table 12.9.2 */ struct gprs_power_ctrl_pars { uint8_t alpha; uint8_t t_avg_w; uint8_t t_avg_t; uint8_t pc_meas_chan; uint8_t n_avg_i; }; struct gsm48_si13_info { struct gprs_cell_options cell_opts; struct gprs_power_ctrl_pars pwr_ctrl_pars; uint8_t bcch_change_mark; uint8_t si_change_field; uint8_t rac; uint8_t spgc_ccch_sup; uint8_t net_ctrl_ord; uint8_t prio_acc_thr; }; /* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13); #endif /* _REST_OCTETS_H */ osmo-bsc-1.3.0/include/osmocom/bsc/rs232.h000066400000000000000000000002741332665256100201430ustar00rootroot00000000000000#ifndef _RS232_H #define _RS232_H int rs232_setup(const char *serial_port, unsigned int delay_ms, struct gsm_bts *bts); int handle_serial_msg(struct msgb *msg); #endif /* _RS232_H */ osmo-bsc-1.3.0/include/osmocom/bsc/signal.h000066400000000000000000000110041332665256100205360ustar00rootroot00000000000000/* Generic signalling/notification infrastructure */ /* (C) 2009-2010, 2015 by Holger Hans Peter Freyther * (C) 2009 by Harald Welte * (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 . * */ #ifndef OPENBSC_SIGNAL_H #define OPENBSC_SIGNAL_H #include #include #include #include /* * Signalling subsystems */ enum signal_subsystems { SS_PAGING, SS_ABISIP, SS_NM, SS_LCHAN, SS_CHALLOC, SS_IPAC_NWL, SS_RF, SS_MSC, SS_HO, SS_CCCH, }; /* SS_PAGING signals */ enum signal_paging { S_PAGING_SUCCEEDED, S_PAGING_EXPIRED, }; /* SS_ABISIP signals */ enum signal_abisip { S_ABISIP_CRCX_ACK, S_ABISIP_MDCX_ACK, S_ABISIP_DLCX_IND, }; /* SS_NM signals */ enum signal_nm { S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ S_NM_FAIL_REP, /* GSM 12.21 failure event report */ S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ S_NM_TEST_REP, /* GSM 12.21 Test Report */ S_NM_STATECHG_OPER, /* Operational State changed*/ S_NM_STATECHG_ADM, /* Administrative State changed */ S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */ S_NM_OPSTART_ACK, /* Received OPSTART ACK, arg is struct msgb *oml_msg */ }; /* SS_LCHAN signals */ enum signal_lchan { /* * The lchan got freed with an use_count != 0 and error * recovery needs to be carried out from within the * signal handler. */ S_LCHAN_UNEXPECTED_RELEASE, S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ S_LCHAN_ASSIGNMENT_COMPL, /* 04.08 Assignment Completed */ S_LCHAN_ASSIGNMENT_FAIL, /* 04.08 Assignment Failed */ S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ }; /* SS_CHALLOC signals */ enum signal_challoc { S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */ S_CHALLOC_FREED, /* lchan has been successfully freed */ }; /* SS_IPAC_NWL signals */ enum signal_ipaccess { S_IPAC_NWL_COMPLETE, }; enum signal_global { S_GLOBAL_BTS_CLOSE_OM, }; /* SS_RF signals */ enum signal_rf { S_RF_OFF, S_RF_ON, S_RF_GRACE, }; struct ipacc_ack_signal_data { struct gsm_bts_trx *trx; uint8_t msg_type; }; struct abis_om2k_mo; struct nm_statechg_signal_data { struct gsm_bts *bts; uint8_t obj_class; void *obj; struct gsm_nm_state *old_state; struct gsm_nm_state *new_state; /* This pointer is vaold for TS 12.21 MO */ struct abis_om_obj_inst *obj_inst; /* This pointer is vaold for RBS2000 MO */ struct abis_om2k_mo *om2k_mo; }; struct nm_om2k_signal_data { struct gsm_bts *bts; void *obj; struct abis_om2k_mo *om2k_mo; uint8_t accordance_ind; }; struct nm_nack_signal_data { struct msgb *msg; struct gsm_bts *bts; uint8_t mt; }; struct challoc_signal_data { struct gsm_bts *bts; struct gsm_lchan *lchan; enum gsm_chan_t type; }; struct rf_signal_data { struct gsm_network *net; }; struct lchan_signal_data { /* The lchan the signal happened on */ struct gsm_lchan *lchan; /* Measurement reports on this lchan */ struct gsm_meas_rep *mr; }; /* MSC signals */ enum signal_msc { S_MSC_LOST, S_MSC_CONNECTED, S_MSC_AUTHENTICATED, }; struct bsc_msc_data; struct msc_signal_data { struct bsc_msc_data *data; }; /* SS_CCCH signals */ enum signal_ccch { S_CCCH_PAGING_LOAD, S_CCCH_RACH_LOAD, }; struct ccch_signal_data { struct gsm_bts *bts; uint16_t pg_buf_space; uint16_t rach_slot_count; uint16_t rach_busy_count; uint16_t rach_access_count; }; #endif osmo-bsc-1.3.0/include/osmocom/bsc/system_information.h000066400000000000000000000014541332665256100232220ustar00rootroot00000000000000#ifndef _SYSTEM_INFO_H #define _SYSTEM_INFO_H #include #include struct gsm_bts; int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type); size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e); unsigned range1024_p(unsigned n); unsigned range512_q(unsigned m); int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w, int f0, uint8_t *chan_list); uint8_t si2q_num(struct gsm_bts *bts); int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio, uint8_t qrx, uint8_t meas_bw); int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble); int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity); #endif osmo-bsc-1.3.0/include/osmocom/bsc/ussd.h000066400000000000000000000003241332665256100202420ustar00rootroot00000000000000#ifndef _USSD_H #define _USSD_H /* Handler function for mobile-originated USSD messages */ #include int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg); #endif osmo-bsc-1.3.0/include/osmocom/bsc/vty.h000066400000000000000000000013771332665256100201170ustar00rootroot00000000000000#ifndef OPENBSC_VTY_H #define OPENBSC_VTY_H #include #include #include struct gsm_network; struct vty; void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); extern struct cmd_element cfg_description_cmd; extern struct cmd_element cfg_no_description_cmd; enum bsc_vty_node { GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, BTS_NODE, TRX_NODE, TS_NODE, OML_NODE, NAT_NODE, NAT_BSC_NODE, MSC_NODE, OM2K_NODE, OM2K_CON_GROUP_NODE, BSC_NODE, }; struct log_info; int bsc_vty_init(struct gsm_network *network); int bsc_vty_init_extra(void); struct gsm_network *gsmnet_from_vty(struct vty *vty); #endif osmo-bsc-1.3.0/m4/000077500000000000000000000000001332665256100135665ustar00rootroot00000000000000osmo-bsc-1.3.0/m4/README000066400000000000000000000002431332665256100144450ustar00rootroot00000000000000We want to avoid creating too many external build-time dependencies like this one to autoconf-archive. This directory provides a local copy of required m4 rules. osmo-bsc-1.3.0/m4/ax_check_compile_flag.m4000066400000000000000000000064021332665256100203000ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # 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 . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 4 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS osmo-bsc-1.3.0/osmoappdesc.py000066400000000000000000000020271332665256100161360ustar00rootroot00000000000000#!/usr/bin/env python # (C) 2013 by Katerina Barone-Adesi # 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 app_configs = { "osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg", "doc/examples/osmo-bsc/osmo-bsc_custom-sccp.cfg"] } apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc") ] vty_command = ["./src/osmo-bsc/osmo-bsc", "-c", "doc/examples/osmo-bsc/osmo-bsc.cfg"] vty_app = apps[0] osmo-bsc-1.3.0/src/000077500000000000000000000000001332665256100140355ustar00rootroot00000000000000osmo-bsc-1.3.0/src/Makefile.am000066400000000000000000000006251332665256100160740ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(COVERAGE_LDFLAGS) \ $(NULL) SUBDIRS = \ libfilter \ osmo-bsc \ utils \ ipaccess \ $(NULL) osmo-bsc-1.3.0/src/ipaccess/000077500000000000000000000000001332665256100156275ustar00rootroot00000000000000osmo-bsc-1.3.0/src/ipaccess/Makefile.am000066400000000000000000000024201332665256100176610ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBOSMOMGCPCLIENT_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) OSMO_LIBS = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOMGCPCLIENT_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(NULL) bin_PROGRAMS = \ abisip-find \ ipaccess-config \ ipaccess-proxy \ $(NULL) abisip_find_LDADD = \ $(OSMO_LIBS) \ $(NULL) abisip_find_SOURCES = \ abisip-find.c \ stubs.c \ $(NULL) ipaccess_config_SOURCES = \ ipaccess-config.c \ ipaccess-firmware.c \ network_listen.c \ stubs.c \ $(NULL) # FIXME: resolve the bogus dependencies patched around here: ipaccess_config_LDADD = \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \ $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(OSMO_LIBS) \ $(NULL) ipaccess_proxy_SOURCES = \ ipaccess-proxy.c \ stubs.c \ $(top_srcdir)/src/osmo-bsc/gsm_data.c \ $(NULL) ipaccess_proxy_LDADD = \ $(OSMO_LIBS) \ $(NULL) osmo-bsc-1.3.0/src/ipaccess/abisip-find.c000066400000000000000000000257561332665256100201770ustar00rootroot00000000000000/* ip.access nanoBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * (C) 2017 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 #include #include static struct { const char *ifname; const char *bind_ip; int send_interval; bool list_view; time_t list_view_timeout; bool format_json; } cmdline_opts = { .ifname = NULL, .bind_ip = NULL, .send_interval = 5, .list_view = false, .list_view_timeout = 10, .format_json = false, }; static void print_help() { printf("\n"); printf("Usage: abisip-find [-l] []\n"); printf(" Specify the outgoing network interface,\n" " e.g. 'eth0'\n"); printf(" -b --bind-ip Specify the local IP to bind to,\n" " e.g. '192.168.1.10'\n"); printf(" -i --interval Send broadcast frames every seconds.\n"); printf(" -l --list-view Instead of printing received responses,\n" " output a sorted list of currently present\n" " base stations and change events.\n"); printf(" -t --timeout Drop base stations after seconds of\n" " receiving no more replies from it.\n" " Implies --list-view.\n"); printf(" -j --format-json Print BTS information using json syntax.\n"); } static void handle_options(int argc, char **argv) { while (1) { int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"bind-ip", 1, 0, 'b'}, {"send-interval", 1, 0, 'i'}, {"list-view", 0, 0, 'l'}, {"timeout", 1, 0, 't'}, {"format-json", 0, 0, 'j'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hb:i:lt:j", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_help(); exit(EXIT_SUCCESS); case 'b': cmdline_opts.bind_ip = optarg; break; case 'i': errno = 0; cmdline_opts.send_interval = strtoul(optarg, NULL, 10); if (errno || cmdline_opts.send_interval < 1) { fprintf(stderr, "Invalid interval value: %s\n", optarg); exit(EXIT_FAILURE); } break; case 't': errno = 0; cmdline_opts.list_view_timeout = strtoul(optarg, NULL, 10); if (errno) { fprintf(stderr, "Invalid timeout value: %s\n", optarg); exit(EXIT_FAILURE); } /* fall through to imply list-view: */ case 'l': cmdline_opts.list_view = true; break; case 'j': cmdline_opts.format_json = true; break; default: /* catch unknown options *as well as* missing arguments. */ fprintf(stderr, "Error in command line options. Exiting. Try --help.\n"); exit(EXIT_FAILURE); break; } } if (argc - optind > 0) cmdline_opts.ifname = argv[optind++]; if (argc - optind > 0) { fprintf(stderr, "Error: too many arguments\n"); print_help(); exit(EXIT_FAILURE); } } static int udp_sock(const char *ifname, const char *bind_ip) { int fd, rc, bc = 1; struct sockaddr_in sa; fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) return fd; if (ifname) { #ifdef __FreeBSD__ rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname, strlen(ifname)); #else rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); #endif if (rc < 0) goto err; } memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(3006); if (bind_ip) { rc = inet_pton(AF_INET, bind_ip, &sa.sin_addr); if (rc != 1) { fprintf(stderr, "bind ip addr: inet_pton failed, returned %d\n", rc); goto err; } } else { sa.sin_addr.s_addr = INADDR_ANY; } rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); if (rc < 0) goto err; rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); if (rc < 0) goto err; #if 0 /* we cannot bind, since the response packets don't come from * the broadcast address */ sa.sin_family = AF_INET; sa.sin_port = htons(3006); inet_aton("255.255.255.255", &sa.sin_addr); rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); if (rc < 0) goto err; #endif return fd; err: close(fd); return rc; } const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, IPAC_MSGT_ID_GET, 0x01, IPAC_IDTAG_MACADDR, 0x01, IPAC_IDTAG_IPADDR, 0x01, IPAC_IDTAG_UNIT, 0x01, IPAC_IDTAG_LOCATION1, 0x01, IPAC_IDTAG_LOCATION2, 0x01, IPAC_IDTAG_EQUIPVERS, 0x01, IPAC_IDTAG_SWVERSION, 0x01, IPAC_IDTAG_UNITNAME, 0x01, IPAC_IDTAG_SERNR, }; static int bcast_find(int fd) { struct sockaddr_in sa; sa.sin_family = AF_INET; sa.sin_port = htons(3006); inet_aton("255.255.255.255", &sa.sin_addr); return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); } static char *parse_response(void *ctx, unsigned char *buf, int len) { unsigned int out_len; uint8_t t_len; uint8_t t_tag; uint8_t *cur = buf; char *out = talloc_zero_size(ctx, 512); if (cmdline_opts.format_json) out = talloc_asprintf_append(out,"{ "); while (cur < buf + len) { t_len = *cur++; t_tag = *cur++; if (cmdline_opts.format_json) out = talloc_asprintf_append(out, "\"%s\": \"%s\", ", ipa_ccm_idtag_name(t_tag), cur); else out = talloc_asprintf_append(out, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); cur += t_len; } if (cmdline_opts.format_json) { out_len = strlen(out); if (out[out_len-2] == ',') out[out_len-2] = ' '; out[out_len-1] = '}'; } return out; } struct base_station { struct llist_head entry; char *line; time_t timestamp; }; LLIST_HEAD(base_stations); void *ctx = NULL; void print_timestamp() { time_t now = time(NULL); printf("\n\n----- %s\n", ctime(&now)); } struct base_station *base_station_parse(unsigned char *buf, int len) { struct base_station *new_bs = talloc_zero(ctx, struct base_station); new_bs->line = parse_response(new_bs, buf, len); new_bs->timestamp = time(NULL); return new_bs; } bool base_stations_add(struct base_station *new_bs) { struct base_station *bs; llist_for_each_entry(bs, &base_stations, entry) { int c = strcmp(new_bs->line, bs->line); if (!c) { /* entry already exists. */ bs->timestamp = new_bs->timestamp; return false; } if (c < 0) { /* found the place to add the entry */ break; } } print_timestamp(); printf("New:\n%s\n", new_bs->line); llist_add_tail(&new_bs->entry, &bs->entry); return true; } bool base_stations_timeout() { struct base_station *bs, *next_bs; time_t now = time(NULL); bool changed = false; llist_for_each_entry_safe(bs, next_bs, &base_stations, entry) { if (now - bs->timestamp < cmdline_opts.list_view_timeout) continue; print_timestamp(); printf("LOST:\n%s\n", bs->line); llist_del(&bs->entry); talloc_free(bs); changed = true; } return changed; } void base_stations_print() { struct base_station *bs; int count = 0; print_timestamp(); if (cmdline_opts.format_json) printf("["); llist_for_each_entry(bs, &base_stations, entry) { if (cmdline_opts.format_json) { if (count) printf(","); printf("\n%s", bs->line); } else { printf("%3d: %s\n", count, bs->line); } count++; } if (cmdline_opts.format_json) printf("%c]\n", count ? '\n': ' '); printf("\nTotal: %d\n", count); } static void base_stations_bump(bool known_changed) { bool changed = known_changed; if (base_stations_timeout()) changed = true; if (changed) base_stations_print(); } static void handle_response(unsigned char *buf, int len) { static unsigned int responses = 0; responses++; if (cmdline_opts.list_view) { bool changed = false; struct base_station *bs = base_station_parse(buf, len); if (base_stations_add(bs)) changed = true; else talloc_free(bs); base_stations_bump(changed); printf("RX: %u \r", responses); } else { printf("%s\n", parse_response(ctx, buf, len)); } fflush(stdout); } static int read_response(int fd) { unsigned char buf[255]; struct sockaddr_in sa; int len; socklen_t sa_len = sizeof(sa); len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); if (len < 0) return len; /* 2 bytes length, 1 byte protocol */ if (buf[2] != IPAC_PROTO_IPACCESS) return 0; if (buf[4] != IPAC_MSGT_ID_RESP) return 0; handle_response(buf+6, len-6); return 0; } static int bfd_cb(struct osmo_fd *bfd, unsigned int flags) { if (flags & BSC_FD_READ) return read_response(bfd->fd); if (flags & BSC_FD_WRITE) { bfd->when &= ~BSC_FD_WRITE; return bcast_find(bfd->fd); } return 0; } static struct osmo_timer_list timer; static void timer_cb(void *_data) { struct osmo_fd *bfd = _data; bfd->when |= BSC_FD_WRITE; base_stations_bump(false); osmo_timer_schedule(&timer, cmdline_opts.send_interval, 0); } int main(int argc, char **argv) { struct osmo_fd bfd; int rc; printf("abisip-find (C) 2009-2010 by Harald Welte\n"); printf(" (C) 2017 by sysmocom - s.f.m.c. GmbH\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); handle_options(argc, argv); if (!cmdline_opts.ifname && !cmdline_opts.bind_ip) fprintf(stdout, "- You might need to specify the outgoing network interface,\n" " e.g. ``%s eth0'' (requires root permissions),\n" " or alternatively use -b to bind to the source address\n" " assigned to that interface\n", argv[0]); if (!cmdline_opts.list_view) fprintf(stdout, "- You may find the --list-view option convenient.\n"); else if (cmdline_opts.send_interval >= cmdline_opts.list_view_timeout) fprintf(stdout, "\nWARNING: the --timeout should be larger than --interval.\n\n"); bfd.cb = bfd_cb; bfd.when = BSC_FD_READ | BSC_FD_WRITE; bfd.fd = udp_sock(cmdline_opts.ifname, cmdline_opts.bind_ip); if (bfd.fd < 0) { perror("Cannot create local socket for broadcast udp"); exit(1); } rc = osmo_fd_register(&bfd); if (rc < 0) { fprintf(stderr, "Cannot register FD\n"); exit(1); } osmo_timer_setup(&timer, timer_cb, &bfd); osmo_timer_schedule(&timer, cmdline_opts.send_interval, 0); printf("Trying to find ip.access BTS by broadcast UDP...\n"); while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } exit(0); } osmo-bsc-1.3.0/src/ipaccess/ipaccess-config.c000066400000000000000000000672421332665256100210430ustar00rootroot00000000000000/* ip.access nanoBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2010 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct gsm_network *bsc_gsmnet; static int net_listen_testnr; static int restart; static char *prim_oml_ip; static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; static char *unit_id; static uint16_t nv_flags; static uint16_t nv_mask; static char *software = NULL; static int sw_load_state = 0; static int oml_state = 0; static int dump_files = 0; static char *firmware_analysis = NULL; static int found_trx = 0; static int loop_tests = 0; static void *tall_ctx_config = NULL; static struct abis_nm_sw_desc *sw_load1 = NULL; static struct abis_nm_sw_desc *sw_load2 = NULL; /* static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; */ extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); extern struct e1inp_line_ops ipaccess_e1inp_line_ops; /* Actively connect to a BTS. Currently used by ipaccess-config.c */ static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) { struct e1inp_ts *e1i_ts = &line->ts[0]; struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; int ret, on = 1; bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ | BSC_FD_WRITE; bfd->data = line; bfd->priv_nr = E1INP_SIGN_OML; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n"); return -EIO; } ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not set socket option\n"); close(bfd->fd); return -EIO; } ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "could not connect socket\n"); close(bfd->fd); return ret; } ret = osmo_fd_register(bfd); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "unable to register socket fd\n"); close(bfd->fd); return ret; } return ret; //return e1inp_line_register(line); } /* configure pseudo E1 line in ip.access style and connect to BTS */ static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) { struct e1inp_line *line; struct e1inp_ts *sign_ts, *rsl_ts; struct e1inp_sign_link *oml_link, *rsl_link; line = talloc_zero(tall_bsc_ctx, struct e1inp_line); if (!line) return -ENOMEM; line->driver = e1inp_driver_find("ipa"); if (!line->driver) { fprintf(stderr, "cannot `ipa' driver, giving up.\n"); return -EINVAL; } line->ops = &ipaccess_e1inp_line_ops; /* create E1 timeslots for signalling and TRAU frames */ e1inp_ts_config_sign(&line->ts[1-1], line); e1inp_ts_config_sign(&line->ts[2-1], line); /* create signalling links for TS1 */ sign_ts = &line->ts[1-1]; rsl_ts = &line->ts[2-1]; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, bts->c0, 0xff, 0); rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, bts->c0, 0, 0); /* create back-links from bts/trx */ bts->oml_link = oml_link; bts->c0->rsl_link = rsl_link; /* default port at BTS for incoming connections is 3006 */ if (sin->sin_port == 0) sin->sin_port = htons(3006); return ipaccess_connect(line, sin); } /* * Callback function for NACK on the OML NM * * Currently we send the config requests but don't check the * result. The nanoBTS will send us a NACK when we did something the * BTS didn't like. */ static int ipacc_msg_nack(uint8_t mt) { fprintf(stderr, "Failure to set attribute. This seems fatal\n"); exit(-1); return 0; } static void check_restart_or_exit(struct gsm_bts_trx *trx) { if (restart) { abis_nm_ipaccess_restart(trx); } else { exit(0); } } static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx) { if (sw_load_state == 1) { fprintf(stderr, "The new software is activaed.\n"); check_restart_or_exit(trx); } else if (oml_state == 1) { fprintf(stderr, "Set the NV Attributes.\n"); check_restart_or_exit(trx); } return 0; } static const uint8_t phys_conf_min[] = { 0x02 }; static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) { uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); int num_arfcn; unsigned int arfcnlist_size; /* Create whitelist from rxlevels */ physconf_buf[0] = phys_conf_min[0]; physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); arfcnlist_size = num_arfcn * 2; *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4)); return arfcnlist_size+4; } static int nwl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts_trx *trx; uint8_t physconf_buf[2*NUM_ARFCNS+16]; uint16_t physconf_len; switch (signal) { case S_IPAC_NWL_COMPLETE: trx = signal_data; DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); switch (trx->ipaccess.test_nr) { case NM_IPACC_TESTNO_CHAN_USAGE: /* Dump RxLev results */ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); /* Create whitelist from results */ physconf_len = build_physconf(physconf_buf, &trx->ipaccess.rxlev_stat); /* Start next test abbout BCCH channel usage */ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, physconf_buf, physconf_len); break; case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: /* Dump BCCH RxLev results */ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); /* Create whitelist from results */ physconf_len = build_physconf(physconf_buf, &trx->ipaccess.rxlev_stat); /* Start next test about BCCH info */ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, physconf_buf, physconf_len); break; case NM_IPACC_TESTNO_BCCH_INFO: /* re-start full process with CHAN_USAGE */ if (loop_tests) { DEBUGP(DNM, "starting next test cycle\n"); ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, sizeof(phys_conf_min)); } else { exit(0); } break; } break; } return 0; } static int nm_state_event(int evt, uint8_t obj_class, void *obj, struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, struct abis_om_obj_inst *obj_inst); static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ipacc_ack_signal_data *ipacc_data; struct nm_statechg_signal_data *nsd; switch (signal) { case S_NM_IPACC_NACK: ipacc_data = signal_data; return ipacc_msg_nack(ipacc_data->msg_type); case S_NM_IPACC_ACK: ipacc_data = signal_data; return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); case S_NM_IPACC_RESTART_ACK: printf("The BTS has acked the restart. Exiting.\n"); exit(0); break; case S_NM_IPACC_RESTART_NACK: printf("The BTS has nacked the restart. Exiting.\n"); exit(0); break; case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: nsd = signal_data; nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, nsd->new_state, nsd->obj_inst); break; default: break; } return 0; } /* callback function passed to the ABIS OML code */ static int percent; static int percent_old; static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, void *data, void *param) { struct msgb *msg; struct gsm_bts_trx *trx; if (hook != GSM_HOOK_NM_SWLOAD) return 0; trx = (struct gsm_bts_trx *) data; switch (event) { case NM_MT_LOAD_INIT_ACK: fprintf(stdout, "Software Load Initiate ACK\n"); break; case NM_MT_LOAD_INIT_NACK: fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); exit(5); break; case NM_MT_LOAD_END_ACK: fprintf(stderr, "LOAD END ACK..."); /* now make it the default */ sw_load_state = 1; msg = msgb_alloc(1024, "sw: nvattr"); msg->l2h = msgb_put(msg, 3); msg->l3h = &msg->l2h[3]; /* activate software */ if (sw_load1) abis_nm_put_sw_desc(msg, sw_load1, true); if (sw_load2) abis_nm_put_sw_desc(msg, sw_load2, true); /* fill in the data */ msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; msg->l2h[1] = msgb_l3len(msg) >> 8; msg->l2h[2] = msgb_l3len(msg) & 0xff; printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); msgb_free(msg); break; case NM_MT_LOAD_END_NACK: fprintf(stderr, "ERROR: Software Load End NACK\n"); exit(3); break; case NM_MT_ACTIVATE_SW_NACK: fprintf(stderr, "ERROR: Activate Software NACK\n"); exit(4); break; case NM_MT_ACTIVATE_SW_ACK: break; case NM_MT_LOAD_SEG_ACK: percent = abis_nm_software_load_status(trx->bts); if (percent > percent_old) printf("Software Download Progress: %d%%\n", percent); percent_old = percent; break; case NM_MT_LOAD_ABORT: fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); exit(6); break; } return 0; } static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) { msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); msgb_put_u32(nmsg, ip); msgb_put_u32(nmsg, mask); } static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) { msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); msgb_put_u32(nmsg, addr); msgb_put_u32(nmsg, mask); msgb_put_u32(nmsg, gw); } static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) { msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, (const uint8_t *)unit_id); } static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) { int len; /* 0x88 + IP + port */ len = 1 + sizeof(ip) + sizeof(port); msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); msgb_put_u16(nmsg, len); msgb_put_u8(nmsg, 0x88); /* IP address */ msgb_put_u32(nmsg, ip); /* port number */ msgb_put_u16(nmsg, port); } static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) { msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); msgb_put_u8(nmsg, nv_flags & 0xff); msgb_put_u8(nmsg, nv_mask & 0xff); msgb_put_u8(nmsg, nv_flags >> 8); msgb_put_u8(nmsg, nv_mask >> 8); } /* human-readable test names for the ip.access tests */ static const struct value_string ipa_test_strs[] = { { 64, "ccch-usage" }, { 65, "bcch-usage" }, { 66, "freq-sync" }, { 67, "rtp-usage" }, { 68, "rtp-perf" }, { 69, "gprs-ccch" }, { 70, "pccch-usage" }, { 71, "gprs-usage" }, { 72, "esta-mf" }, { 73, "uplink-mf" }, { 74, "dolink-mf" }, { 75, "tbf-details" }, { 76, "tbf-usage" }, { 77, "llc-data" }, { 78, "pdch-usage" }, { 79, "power-control" }, { 80, "link-adaption" }, { 81, "tch-usage" }, { 82, "amr-mf" }, { 83, "rtp-multiplex-perf" }, { 84, "rtp-multiplex-usage" }, { 85, "srtp-multiplex-usage" }, { 86, "abis-traffic" }, { 89, "gprs-multiplex-perf" }, { 90, "gprs-multiplex-usage" }, { 0, NULL }, }; /* human-readable names for the ip.access nanoBTS NVRAM Flags */ static const struct value_string ipa_nvflag_strs[] = { { 0x0001, "static-ip" }, { 0x0002, "static-gw" }, { 0x0004, "no-dhcp-vsi" }, { 0x0008, "dhcp-enabled" }, { 0x0040, "led-disabled" }, { 0x0100, "secondary-oml-enabled" }, { 0x0200, "diag-enabled" }, { 0x0400, "cli-enabled" }, { 0x0800, "http-enabled" }, { 0x1000, "post-enabled" }, { 0x2000, "snmp-enabled" }, { 0, NULL } }; /* set the flags in flags/mask according to a string-identified flag and 'enable' */ static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) { int rc; rc = get_string_value(ipa_nvflag_strs, name); if (rc < 0) return rc; *mask |= rc; if (en) *flags |= rc; else *flags &= ~rc; return 0; } static void bootstrap_om(struct gsm_bts_trx *trx) { struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); int need_to_set_attr = 0; int len; printf("OML link established using TRX %d\n", trx->nr); if (unit_id) { len = strlen(unit_id); if (len > nmsg->data_len-10) goto out_err; printf("setting Unit ID to '%s'\n", unit_id); nv_put_unit_id(nmsg, unit_id); need_to_set_attr = 1; } if (prim_oml_ip) { struct in_addr ia; if (!inet_aton(prim_oml_ip, &ia)) { fprintf(stderr, "invalid IP address: %s\n", prim_oml_ip); goto out_err; } printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); need_to_set_attr = 1; } if (nv_mask) { printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", nv_flags, nv_mask); nv_put_flags(nmsg, nv_flags, nv_mask); need_to_set_attr = 1; } if (bts_ip_addr && bts_ip_mask) { struct in_addr ia_addr, ia_mask; if (!inet_aton(bts_ip_addr, &ia_addr)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_addr); goto out_err; } if (!inet_aton(bts_ip_mask, &ia_mask)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_mask); goto out_err; } printf("setting static IP Address/Mask\n"); nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); need_to_set_attr = 1; } if (bts_ip_gw) { struct in_addr ia_gw; if (!inet_aton(bts_ip_gw, &ia_gw)) { fprintf(stderr, "invalid IP address: %s\n", bts_ip_gw); goto out_err; } printf("setting static IP Gateway\n"); /* we only set the default gateway with zero addr/mask */ nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); need_to_set_attr = 1; } if (need_to_set_attr) { abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); oml_state = 1; } if (restart && !prim_oml_ip && !software) { printf("restarting BTS\n"); abis_nm_ipaccess_restart(trx); } out_err: msgb_free(nmsg); } static int nm_state_event(int evt, uint8_t obj_class, void *obj, struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, struct abis_om_obj_inst *obj_inst) { if (obj_class == NM_OC_BASEB_TRANSC) { if (!found_trx && obj_inst->trx_nr != 0xff) { struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); bootstrap_om(trx); found_trx = 1; } } else if (evt == S_NM_STATECHG_OPER && obj_class == NM_OC_RADIO_CARRIER && new_state->availability == 3) { struct gsm_bts_trx *trx = obj; if (net_listen_testnr) ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, sizeof(phys_conf_min)); else if (software) { int rc; printf("Attempting software upload with '%s'\n", software); rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); if (rc < 0) { fprintf(stderr, "Failed to start software load\n"); exit(-3); } } } return 0; } static struct abis_nm_sw_desc *create_swload(struct sdp_header *header) { struct abis_nm_sw_desc *load; load = talloc_zero(tall_ctx_config, struct abis_nm_sw_desc); osmo_strlcpy((char *)load->file_id, header->firmware_info.sw_part, sizeof(load->file_id)); load->file_id_len = strlen((char*)load->file_id) + 1; osmo_strlcpy((char *)load->file_version, header->firmware_info.version, sizeof(load->file_version)); load->file_version_len = strlen((char*)load->file_version) + 1; return load; } static int find_sw_load_params(const char *filename) { struct stat stat; struct sdp_header *header; struct llist_head *entry; int fd; void *tall_firm_ctx = 0; entry = talloc_zero(tall_firm_ctx, struct llist_head); INIT_LLIST_HEAD(entry); fd = open(filename, O_RDONLY); if (!fd) { perror("nada"); return -1; } /* verify the file */ if (fstat(fd, &stat) == -1) { perror("Can not stat the file"); close(fd); return -1; } ipaccess_analyze_file(fd, stat.st_size, 0, entry); if (close(fd) != 0) { perror("Close failed.\n"); return -1; } /* try to find what we are looking for */ llist_for_each_entry(header, entry, entry) { if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { sw_load1 = create_swload(header); } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { sw_load2 = create_swload(header); } } if (!sw_load1 || !sw_load2) { fprintf(stderr, "Did not find data.\n"); talloc_free(tall_firm_ctx); return -1; } talloc_free(tall_firm_ctx); return 0; } static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) { int out_fd; int copied; char filename[4096]; off_t target; if (!dump_files) return; if (sub_entry->header_entry.something1 == 0) return; snprintf(filename, sizeof(filename), "part.%d", part++); out_fd = open(filename, O_WRONLY | O_CREAT, 0660); if (out_fd < 0) { perror("Can not dump firmware"); return; } target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; if (lseek(fd, target, SEEK_SET) != target) { perror("seek failed"); close(out_fd); return; } for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { char c; if (read(fd, &c, sizeof(c)) != sizeof(c)) { perror("copy failed"); break; } if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { perror("write failed"); break; } } close(out_fd); } static void analyze_firmware(const char *filename) { struct stat stat; struct sdp_header *header; struct sdp_header_item *sub_entry; struct llist_head *entry; int fd; void *tall_firm_ctx = 0; int part = 0; entry = talloc_zero(tall_firm_ctx, struct llist_head); INIT_LLIST_HEAD(entry); printf("Opening possible firmware '%s'\n", filename); fd = open(filename, O_RDONLY); if (!fd) { perror("nada"); return; } /* verify the file */ if (fstat(fd, &stat) == -1) { perror("Can not stat the file"); close(fd); return; } ipaccess_analyze_file(fd, stat.st_size, 0, entry); llist_for_each_entry(header, entry, entry) { printf("Printing header information:\n"); printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); printf("sw_part: %.20s\n", header->firmware_info.sw_part); printf("text1: %.64s\n", header->firmware_info.text1); printf("time: %.12s\n", header->firmware_info.time); printf("date: %.14s\n", header->firmware_info.date); printf("text2: %.10s\n", header->firmware_info.text2); printf("version: %.20s\n", header->firmware_info.version); printf("subitems...\n"); llist_for_each_entry(sub_entry, &header->header_list, entry) { printf("\tsomething1: %u\n", sub_entry->header_entry.something1); printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); printf("\ttime: %.12s\n", sub_entry->header_entry.time); printf("\tdate: %.14s\n", sub_entry->header_entry.date); printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); printf("\tversion: %.20s\n", sub_entry->header_entry.version); printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); printf("\n\n"); dump_entry(sub_entry, part++, fd); } printf("\n\n"); } if (close(fd) != 0) { perror("Close failed.\n"); return; } talloc_free(tall_firm_ctx); } static bool check_unitid_fmt(const char* unit_id) { const char *p = unit_id; bool must_digit = true; uint8_t remain_slash = 2; if (strlen(unit_id) < 5) goto wrong_fmt; while (*p != '\0') { if (*p != '/' && !isdigit(*p)) goto wrong_fmt; if (*p == '/' && must_digit) goto wrong_fmt; if (*p == '/') { must_digit = true; remain_slash--; if (remain_slash < 0) goto wrong_fmt; } else { must_digit = false; } p++; } if (*(p-1) == '/') goto wrong_fmt; return true; wrong_fmt: fprintf(stderr, "ERROR: unit-id wrong format. Must be '\\d+/\\d+/\\d+'\n"); return false; } static void print_usage(void) { printf("Usage: ipaccess-config IP_OF_BTS\n"); } static void print_help(void) { #if 0 printf("Commands for reading from the BTS:\n"); printf(" -D --dump\t\t\tDump the BTS configuration\n"); printf("\n"); #endif printf("Commands for writing to the BTS:\n"); printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); printf(" -l --listen TESTNR\t\tPerform specified test number\n"); printf(" -L --Listen TEST_NAME\t\tPerform specified test\n"); printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); printf("\n"); printf("Miscellaneous commands:\n"); printf(" -h --help\t\t\tthis text\n"); printf(" -H --HELP\t\t\tPrint parameter details.\n"); printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n"); } static void print_value_string(const struct value_string *val, int size) { int i; for (i = 0; i < size - 1; ++i) { char sep = val[i + 1].str == NULL ? '.' : ','; printf("%s%c ", val[i].str, sep); } printf("\n"); } static void print_options(void) { printf("Options for NVRAM (-S,-U):\n "); print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs)); printf("Options for Tests (-L):\n "); print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs)); } static const struct log_info_cat log_categories[] = { [DNM] = { .name = "DNM", .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .loglevel = LOGL_DEBUG, .enabled = 1, }, }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { struct gsm_bts *bts; struct sockaddr_in sin; char *bts_ip; int rc, option_index = 0, stream_id = 0xff; tall_ctx_config = talloc_named_const(NULL, 0, "ipaccess-config"); tall_bsc_ctx = tall_ctx_config; msgb_talloc_ctx_init(tall_ctx_config, 0); osmo_init_logging2(tall_ctx_config, &log_info); bts_model_nanobts_init(); printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); while (1) { int c; unsigned long ul; char *slash; static struct option long_options[] = { { "unit-id", 1, 0, 'u' }, { "oml-ip", 1, 0, 'o' }, { "ip-address", 1, 0, 'i' }, { "ip-gateway", 1, 0, 'g' }, { "restart", 0, 0, 'r' }, { "nvram-flags", 1, 0, 'n' }, { "nvattr-set", 1, 0, 'S' }, { "nvattr-unset", 1, 0, 'U' }, { "help", 0, 0, 'h' }, { "HELP", 0, 0, 'H' }, { "listen", 1, 0, 'l' }, { "Listen", 1, 0, 'L' }, { "stream-id", 1, 0, 's' }, { "software", 1, 0, 'd' }, { "firmware", 1, 0, 'f' }, { "write-firmware", 0, 0, 'w' }, { "disable-color", 0, 0, 'c'}, { "loop", 0, 0, 'p' }, { 0, 0, 0, 0 }, }; c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options, &option_index); if (c == -1) break; switch (c) { case 'u': if (!check_unitid_fmt(optarg)) exit(2); unit_id = optarg; break; case 'o': prim_oml_ip = optarg; break; case 'i': slash = strchr(optarg, '/'); if (!slash) exit(2); bts_ip_addr = optarg; *slash = 0; bts_ip_mask = slash+1; break; case 'g': bts_ip_gw = optarg; break; case 'r': restart = 1; break; case 'n': slash = strchr(optarg, '/'); if (!slash) exit(2); ul = strtoul(optarg, NULL, 16); nv_flags = ul & 0xffff; ul = strtoul(slash+1, NULL, 16); nv_mask = ul & 0xffff; break; case 'S': if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) exit(2); break; case 'U': if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) exit(2); break; case 'l': net_listen_testnr = atoi(optarg); break; case 'L': net_listen_testnr = get_string_value(ipa_test_strs, optarg); if (net_listen_testnr < 0) { fprintf(stderr, "The test '%s' is not known. Use -H to" " see available tests.\n", optarg); exit(2); } break; case 's': stream_id = atoi(optarg); break; case 'd': software = strdup(optarg); if (find_sw_load_params(optarg) != 0) exit(0); break; case 'f': firmware_analysis = optarg; break; case 'w': dump_files = 1; break; case 'c': log_set_use_color(osmo_stderr_target, 0); break; case 'p': loop_tests = 1; break; case 'h': print_usage(); print_help(); exit(0); case 'H': print_options(); exit(0); } }; if (firmware_analysis) { analyze_firmware(firmware_analysis); if (argc == optind) /* Nothing more to do, exit successfully */ exit(EXIT_SUCCESS); } if (argc - optind != 1) { fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); exit(2); } bts_ip = argv[optind++]; libosmo_abis_init(tall_ctx_config); bsc_gsmnet = gsm_network_init(tall_ctx_config); if (!bsc_gsmnet) exit(1); bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_BSIC); /* ip.access supports up to 4 chained TRX */ gsm_bts_trx_alloc(bts); gsm_bts_trx_alloc(bts); gsm_bts_trx_alloc(bts); bts->oml_tei = stream_id; osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); ipac_nwl_init(); printf("Trying to connect to ip.access BTS %s...\n", bts_ip); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bts_ip, &sin.sin_addr); rc = ia_config_connect(bts, &sin); if (rc < 0) { perror("Error connecting to the BTS"); exit(1); } bts->oml_link->ts->sign.delay = 10; bts->c0->rsl_link->ts->sign.delay = 10; while (1) { rc = osmo_select_main(0); if (rc < 0) exit(3); } exit(0); } /* Stub */ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } /* Stub */ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } osmo-bsc-1.3.0/src/ipaccess/ipaccess-firmware.c000066400000000000000000000075541332665256100214120ustar00rootroot00000000000000/* Routines for parsing an ipacces SDP firmware file */ /* (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 . * */ #include #include #include #include #include #include #include #define PART_LENGTH 138 osmo_static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); osmo_static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length); /* more magic, the second "int" in the header */ static char more_magic[] = { 0x10, 0x02 }; int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) { struct sdp_firmware *firmware_header = 0; struct sdp_header *header; char buf[4096]; int rc, i; uint16_t table_size; uint16_t table_offset; off_t table_start; rc = read(fd, buf, sizeof(*firmware_header)); if (rc < 0) { perror("Can not read header start."); return -1; } firmware_header = (struct sdp_firmware *) &buf[0]; if (strncmp(firmware_header->magic, " SDP", 4) != 0) { fprintf(stderr, "Wrong magic.\n"); return -1; } if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { fprintf(stderr, "Wrong more magic. Got: 0x%x 0x%x vs. 0x%x 0x%x\n", firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, more_magic[0], more_magic[1]); return -1; } if (ntohl(firmware_header->file_length) != st_size) { fprintf(stderr, "The filesize and the header do not match.\n"); return -1; } /* add the firmware */ header = talloc_zero(list, struct sdp_header); header->firmware_info = *firmware_header; INIT_LLIST_HEAD(&header->header_list); llist_add(&header->entry, list); table_offset = ntohs(firmware_header->table_offset); table_start = lseek(fd, table_offset, SEEK_CUR); if (table_start == -1) { fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset); return -1; } if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) { fprintf(stderr, "The table size could not be read.\n"); return -1; } table_size = ntohs(table_size); if (table_size % PART_LENGTH != 0) { fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size); return -1; } /* look into each firmware now */ for (i = 0; i < table_size / PART_LENGTH; ++i) { struct sdp_header_entry entry; struct sdp_header_item *header_entry; unsigned int offset = table_start + 2; offset += i * 138; if (lseek(fd, offset, SEEK_SET) != offset) { fprintf(stderr, "Can not seek to the offset: %u.\n", offset); return -1; } rc = read(fd, &entry, sizeof(entry)); if (rc != sizeof(entry)) { fprintf(stderr, "Can not read the header entry.\n"); return -1; } header_entry = talloc_zero(header, struct sdp_header_item); header_entry->header_entry = entry; header_entry->absolute_offset = base_offset; llist_add(&header_entry->entry, &header->header_list); /* now we need to find the SDP file... */ offset = ntohl(entry.start) + 4 + base_offset; if (lseek(fd, offset, SEEK_SET) != offset) { perror("can't seek to sdp"); return -1; } ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); } return 0; } osmo-bsc-1.3.0/src/ipaccess/ipaccess-proxy.c000066400000000000000000000773671332665256100207700ustar00rootroot00000000000000/* OpenBSC Abis/IP proxy ip.access nanoBTS */ /* (C) 2009 by Harald Welte * (C) 2010 by On-Waves * (C) 2010 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include /* one instance of an ip.access protocol proxy */ struct ipa_proxy { /* socket where we listen for incoming OML from BTS */ struct osmo_fd oml_listen_fd; /* socket where we listen for incoming RSL from BTS */ struct osmo_fd rsl_listen_fd; /* list of BTS's (struct ipa_bts_conn */ struct llist_head bts_list; /* the BSC reconnect timer */ struct osmo_timer_list reconn_timer; /* global GPRS NS data */ struct in_addr gprs_addr; struct in_addr listen_addr; }; /* global pointer to the proxy structure */ static struct ipa_proxy *ipp; struct ipa_proxy_conn { struct osmo_fd fd; struct llist_head tx_queue; struct ipa_bts_conn *bts_conn; }; #define MAX_TRX 4 /* represents a particular BTS in our proxy */ struct ipa_bts_conn { /* list of BTS's (ipa_proxy->bts_list) */ struct llist_head list; /* back pointer to the proxy which we belong to */ struct ipa_proxy *ipp; /* the unit ID as determined by CCM */ struct { uint16_t site_id; uint16_t bts_id; } unit_id; /* incoming connections from BTS */ struct ipa_proxy_conn *oml_conn; struct ipa_proxy_conn *rsl_conn[MAX_TRX]; /* outgoing connections to BSC */ struct ipa_proxy_conn *bsc_oml_conn; struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; /* UDP sockets for BTS and BSC injection */ struct osmo_fd udp_bts_fd; struct osmo_fd udp_bsc_fd; /* NS data */ struct in_addr bts_addr; struct osmo_fd gprs_ns_fd; int gprs_local_port; uint16_t gprs_orig_port; uint32_t gprs_orig_ip; char *id_tags[256]; uint8_t *id_resp; unsigned int id_resp_len; }; enum ipp_fd_type { OML_FROM_BTS = 1, RSL_FROM_BTS = 2, OML_TO_BSC = 3, RSL_TO_BSC = 4, UDP_TO_BTS = 5, UDP_TO_BSC = 6, }; extern void *tall_bsc_ctx; static char *listen_ipaddr; static char *bsc_ipaddr; static char *gprs_ns_ipaddr; static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what); #define PROXY_ALLOC_SIZE 1200 static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, uint16_t site_id, uint16_t bts_id) { struct ipa_bts_conn *ipbc; llist_for_each_entry(ipbc, &ipp->bts_list, list) { if (ipbc->unit_id.site_id == site_id && ipbc->unit_id.bts_id == bts_id) return ipbc; } return NULL; } struct ipa_proxy_conn *alloc_conn(void) { struct ipa_proxy_conn *ipc; ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); if (!ipc) return NULL; INIT_LLIST_HEAD(&ipc->tx_queue); return ipc; } static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) { unsigned int i, len; for (i = 0; i <= 0xff; i++) { if (!TLVP_PRESENT(tlvp, i)) continue; len = TLVP_LEN(tlvp, i); #if 0 if (!ipbc->id_tags[i]) ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); else #endif ipbc->id_tags[i] = talloc_realloc_size(ipbc, ipbc->id_tags[i], len); if (!ipbc->id_tags[i]) return -ENOMEM; memset(ipbc->id_tags[i], 0, len); //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); } return 0; } static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); #define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, struct ipa_bts_conn *ipbc, uint8_t trx_id) { if (ipbc) logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, ipbc->unit_id.bts_id, trx_id); else logp2(ss, lvl, file, line, 0, "unknown "); } static int handle_udp_read(struct osmo_fd *bfd) { struct ipa_bts_conn *ipbc = bfd->data; struct ipa_proxy_conn *other_conn = NULL; struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); struct ipaccess_head *hh; int ret; /* with UDP sockets, we cannot read partial packets but have to read * all of it in one go */ hh = (struct ipaccess_head *) msg->data; ret = recv(bfd->fd, msg->data, msg->data_len, 0); if (ret < 0) { if (errno != EAGAIN) LOGP(DLINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); msgb_free(msg); return ret; } if (ret == 0) { DEBUGP(DLINP, "UDP peer disappeared, dead socket\n"); osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; msgb_free(msg); return -EIO; } if (ret < sizeof(*hh)) { DEBUGP(DLINP, "could not even read header!?!\n"); msgb_free(msg); return -EIO; } msgb_put(msg, ret); msg->l2h = msg->data + sizeof(*hh); DEBUGP(DLMI, "UDP RX: %s\n", osmo_hexdump(msg->data, msg->len)); if (hh->len != msg->len - sizeof(*hh)) { DEBUGP(DLINP, "length (%u/%u) disagrees with header(%u)\n", msg->len, msg->len - 3, hh->len); msgb_free(msg); return -EIO; } switch (bfd->priv_nr & 0xff) { case UDP_TO_BTS: /* injection towards BTS */ switch (hh->proto) { case IPAC_PROTO_RSL: /* FIXME: what to do about TRX > 0 */ other_conn = ipbc->rsl_conn[0]; break; default: DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " "OML FD\n", hh->proto); /* fall through */ case IPAC_PROTO_IPACCESS: case IPAC_PROTO_OML: other_conn = ipbc->oml_conn; break; } break; case UDP_TO_BSC: /* injection towards BSC */ switch (hh->proto) { case IPAC_PROTO_RSL: /* FIXME: what to do about TRX > 0 */ other_conn = ipbc->bsc_rsl_conn[0]; break; default: DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " "OML FD\n", hh->proto); /* fall through */ case IPAC_PROTO_IPACCESS: case IPAC_PROTO_OML: other_conn = ipbc->bsc_oml_conn; break; } break; default: DEBUGP(DLINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); break; } if (other_conn) { /* enqueue the message for TX on the respective FD */ msgb_enqueue(&other_conn->tx_queue, msg); other_conn->fd.when |= BSC_FD_WRITE; } else msgb_free(msg); return 0; } static int handle_udp_write(struct osmo_fd *bfd) { /* not implemented yet */ bfd->when &= ~BSC_FD_WRITE; return -EIO; } /* callback from select.c in case one of the fd's can be read/written */ static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) rc = handle_udp_read(bfd); if (what & BSC_FD_WRITE) rc = handle_udp_write(bfd); return rc; } static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct osmo_fd *bfd, uint16_t site_id, uint16_t bts_id, uint16_t trx_id, struct tlv_parsed *tlvp, struct msgb *msg) { struct ipa_bts_conn *ipbc; uint16_t udp_port; int ret = 0; struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); DEBUGP(DLINP, "(%u/%u/%u) New BTS connection: ", site_id, bts_id, trx_id); /* OML needs to be established before RSL */ if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { DEBUGPC(DLINP, "Not a OML connection ?!?\n"); return -EIO; } /* allocate new BTS connection data structure */ ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); if (!ipbc) { ret = -ENOMEM; goto err_out; } DEBUGPC(DLINP, "Created BTS Conn data structure\n"); ipbc->ipp = ipp; ipbc->unit_id.site_id = site_id; ipbc->unit_id.bts_id = bts_id; ipbc->oml_conn = ipc; ipc->bts_conn = ipbc; /* store the content of the ID TAGS for later reference */ store_idtags(ipbc, tlvp); ipbc->id_resp_len = msg->len; ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); /* Create OML TCP connection towards BSC */ sin.sin_port = htons(IPA_TCP_PORT_OML); ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); if (!ipbc->bsc_oml_conn) { ret = -EIO; goto err_bsc_conn; } DEBUGP(DLINP, "(%u/%u/%u) OML Connected to BSC\n", site_id, bts_id, trx_id); /* Create UDP socket for BTS packet injection */ udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); ipbc->udp_bts_fd.priv_nr = UDP_TO_BTS; ipbc->udp_bts_fd.cb = udp_fd_cb; ipbc->udp_bts_fd.data = ipbc; ret = osmo_sock_init_ofd(&ipbc->udp_bts_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, udp_port, OSMO_SOCK_F_BIND); if (ret < 0) goto err_udp_bts; DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); /* Create UDP socket for BSC packet injection */ udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); ipbc->udp_bsc_fd.priv_nr = UDP_TO_BSC; ipbc->udp_bsc_fd.cb = udp_fd_cb; ipbc->udp_bsc_fd.data = ipbc; ret = osmo_sock_init_ofd(&ipbc->udp_bsc_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, udp_port, OSMO_SOCK_F_BIND); if (ret < 0) goto err_udp_bsc; DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); /* GPRS NS related code */ if (gprs_ns_ipaddr) { struct sockaddr_in sock; socklen_t len = sizeof(sock); ipbc->gprs_ns_fd.priv_nr = 0; ipbc->gprs_ns_fd.cb = gprs_ns_cb; ipbc->gprs_ns_fd.data = ipbc; ret = osmo_sock_init_ofd(&ipbc->gprs_ns_fd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, listen_ipaddr, 0, OSMO_SOCK_F_BIND); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Creating the GPRS socket failed.\n"); goto err_udp_bsc; } ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len); ipbc->gprs_local_port = ntohs(sock.sin_port); LOGP(DLINP, LOGL_NOTICE, "Created GPRS NS Socket. Listening on: %s:%d\n", inet_ntoa(sock.sin_addr), ipbc->gprs_local_port); ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len); ipbc->bts_addr = sock.sin_addr; } llist_add(&ipbc->list, &ipp->bts_list); return 0; err_udp_bsc: osmo_fd_unregister(&ipbc->udp_bts_fd); err_udp_bts: osmo_fd_unregister(&ipbc->bsc_oml_conn->fd); close(ipbc->bsc_oml_conn->fd.fd); talloc_free(ipbc->bsc_oml_conn); ipbc->bsc_oml_conn = NULL; err_bsc_conn: talloc_free(ipbc->id_resp); talloc_free(ipbc); #if 0 osmo_fd_unregister(bfd); close(bfd->fd); talloc_free(bfd); #endif err_out: return ret; } static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, struct osmo_fd *bfd) { struct tlv_parsed tlvp; uint8_t msg_type = *(msg->l2h); struct ipaccess_unit unit_data; struct ipa_bts_conn *ipbc; int ret = 0; switch (msg_type) { case IPAC_MSGT_PING: ret = ipa_ccm_send_pong(bfd->fd); break; case IPAC_MSGT_PONG: DEBUGP(DLMI, "PONG!\n"); break; case IPAC_MSGT_ID_RESP: DEBUGP(DLMI, "ID_RESP "); /* parse tags, search for Unit ID */ ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2, msgb_l2len(msg)-2); DEBUGP(DLMI, "\n"); if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { LOGP(DLINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); return -EIO; } /* lookup BTS, create sign_link, ... */ memset(&unit_data, 0, sizeof(unit_data)); ipa_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), &unit_data); ipbc = find_bts_by_unitid(ipp, unit_data.site_id, unit_data.bts_id); if (!ipbc) { /* We have not found an ipbc (per-bts proxy instance) * for this BTS yet. The first connection of a new BTS must * be a OML connection. We allocate the associated data structures, * and try to connect to the remote end */ return ipbc_alloc_connect(ipc, bfd, unit_data.site_id, unit_data.bts_id, unit_data.trx_id, &tlvp, msg); /* if this fails, the caller will clean up bfd */ } else { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", unit_data.site_id, unit_data.bts_id, unit_data.trx_id); if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { LOGP(DLINP, LOGL_ERROR, "Second OML connection from " "same BTS ?!?\n"); return 0; } if (unit_data.trx_id >= MAX_TRX) { LOGP(DLINP, LOGL_ERROR, "We don't support more " "than %u TRX\n", MAX_TRX); return -EINVAL; } ipc->bts_conn = ipbc; /* store TRX number in higher 8 bit of the bfd private number */ bfd->priv_nr |= unit_data.trx_id << 8; ipbc->rsl_conn[unit_data.trx_id] = ipc; /* Create RSL TCP connection towards BSC */ sin.sin_port = htons(IPA_TCP_PORT_RSL); ipbc->bsc_rsl_conn[unit_data.trx_id] = connect_bsc(&sin, RSL_TO_BSC | (unit_data.trx_id << 8), ipbc); if (!ipbc->bsc_oml_conn) return -EIO; DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n", unit_data.site_id, unit_data.bts_id, unit_data.trx_id); } break; case IPAC_MSGT_ID_GET: DEBUGP(DLMI, "ID_GET\n"); if ((bfd->priv_nr & 0xff) != OML_TO_BSC && (bfd->priv_nr & 0xff) != RSL_TO_BSC) { DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n"); return -EIO; } ipbc = ipc->bts_conn; if (!ipbc) { DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); return -EIO; } ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); if (ret != ipbc->id_resp_len) { LOGP(DLINP, LOGL_ERROR, "Partial write: %d of %d\n", ret, ipbc->id_resp_len); return -EIO; } ret = 0; break; case IPAC_MSGT_ID_ACK: DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); ret = ipa_ccm_send_id_ack(bfd->fd); break; default: LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); return 1; break; } return ret; } struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error) { struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); struct ipaccess_head *hh; int len, ret = 0; if (!msg) { *error = -ENOMEM; return NULL; } /* first read our 3-byte header */ hh = (struct ipaccess_head *) msg->data; ret = recv(bfd->fd, msg->data, 3, 0); if (ret < 0) { if (errno != EAGAIN) LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); msgb_free(msg); *error = ret; return NULL; } else if (ret == 0) { msgb_free(msg); *error = ret; return NULL; } msgb_put(msg, ret); /* then read te length as specified in header */ msg->l2h = msg->data + sizeof(*hh); len = ntohs(hh->len); ret = recv(bfd->fd, msg->l2h, len, 0); if (ret < len) { LOGP(DLINP, LOGL_ERROR, "short read!\n"); msgb_free(msg); *error = -EIO; return NULL; } msgb_put(msg, ret); return msg; } static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, unsigned int priv_nr) { struct ipa_proxy_conn *bsc_conn; unsigned int trx_id = priv_nr >> 8; switch (priv_nr & 0xff) { case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ bsc_conn = ipbc->bsc_oml_conn; break; case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ bsc_conn = ipbc->bsc_rsl_conn[trx_id]; break; case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ bsc_conn = ipbc->oml_conn; break; case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ bsc_conn = ipbc->rsl_conn[trx_id]; break; default: bsc_conn = NULL; break; } return bsc_conn; } static void reconn_tmr_cb(void *data) { struct ipa_proxy *ipp = data; struct ipa_bts_conn *ipbc; struct sockaddr_in sin; int i; DEBUGP(DLINP, "Running reconnect timer\n"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inet_aton(bsc_ipaddr, &sin.sin_addr); llist_for_each_entry(ipbc, &ipp->bts_list, list) { /* if OML to BSC is dead, try to restore it */ if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { sin.sin_port = htons(IPA_TCP_PORT_OML); logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n"); ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); if (!ipbc->bsc_oml_conn) goto reschedule; logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n"); } /* if we (still) don't have a OML connection, skip RSL */ if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) continue; for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { unsigned int priv_nr; /* don't establish RSL links which we don't have */ if (!ipbc->rsl_conn[i]) continue; if (ipbc->bsc_rsl_conn[i]) continue; priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; priv_nr &= ~0xff; priv_nr |= RSL_TO_BSC; sin.sin_port = htons(IPA_TCP_PORT_RSL); logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); if (!ipbc->bsc_rsl_conn[i]) goto reschedule; logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n"); } } return; reschedule: osmo_timer_schedule(&ipp->reconn_timer, 5, 0); } static void handle_dead_socket(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ struct ipa_proxy_conn *bsc_conn; /* remote conn */ struct ipa_bts_conn *ipbc = ipc->bts_conn; unsigned int trx_id = bfd->priv_nr >> 8; struct msgb *msg, *msg2; osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; /* FIXME: clear tx_queue, remove all references, etc. */ llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) msgb_free(msg); switch (bfd->priv_nr & 0xff) { case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ /* The BTS started a connection with us but we got no * IPAC_MSGT_ID_RESP message yet, in that scenario we did not * allocate the ipa_bts_conn structure. */ if (ipbc == NULL) break; ipbc->oml_conn = NULL; bsc_conn = ipbc->bsc_oml_conn; /* close the connection to the BSC */ osmo_fd_unregister(&bsc_conn->fd); close(bsc_conn->fd.fd); llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) msgb_free(msg); talloc_free(bsc_conn); ipbc->bsc_oml_conn = NULL; /* FIXME: do we need to delete the entire ipbc ? */ break; case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ ipbc->rsl_conn[trx_id] = NULL; bsc_conn = ipbc->bsc_rsl_conn[trx_id]; /* close the connection to the BSC */ osmo_fd_unregister(&bsc_conn->fd); close(bsc_conn->fd.fd); llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) msgb_free(msg); talloc_free(bsc_conn); ipbc->bsc_rsl_conn[trx_id] = NULL; break; case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ ipbc->bsc_oml_conn = NULL; bsc_conn = ipbc->oml_conn; /* start reconnect timer */ osmo_timer_schedule(&ipp->reconn_timer, 5, 0); break; case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ ipbc->bsc_rsl_conn[trx_id] = NULL; bsc_conn = ipbc->rsl_conn[trx_id]; /* start reconnect timer */ osmo_timer_schedule(&ipp->reconn_timer, 5, 0); break; default: bsc_conn = NULL; break; } talloc_free(ipc); } static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) { uint8_t *nsvci; if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) return; if (msgb_l2len(msg) != 39) return; /* * Check if this is a IPA Set Attribute or IPA Set Attribute ACK * and if the FOM Class is GPRS NSVC0 and then we will patch it. * * The patch assumes the message looks like the one from the trace * but we only match messages with a specific size anyway... So * this hack should work just fine. */ if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { nsvci = &msg->l2h[23]; ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8); ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10); *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { nsvci = &msg->l2h[23]; *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port; *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip; } } static int handle_tcp_read(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; struct ipa_bts_conn *ipbc = ipc->bts_conn; struct ipa_proxy_conn *bsc_conn; struct msgb *msg; struct ipaccess_head *hh; int ret = 0; char *btsbsc; if ((bfd->priv_nr & 0xff) <= 2) btsbsc = "BTS"; else btsbsc = "BSC"; msg = ipaccess_proxy_read_msg(bfd, &ret); if (!msg) { if (ret == 0) { logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, " "dead socket\n", btsbsc); handle_dead_socket(bfd); } return ret; } msgb_put(msg, ret); logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len)); hh = (struct ipaccess_head *) msg->data; if (hh->proto == IPAC_PROTO_IPACCESS) { ret = ipaccess_rcvmsg(ipc, msg, bfd); if (ret < 0) { osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; talloc_free(bfd); msgb_free(msg); return ret; } else if (ret == 0) { /* we do not forward parts of the CCM protocol * through the proxy but rather terminate it ourselves. */ msgb_free(msg); return ret; } } if (!ipbc) { LOGP(DLINP, LOGL_ERROR, "received %s packet but no ipc->bts_conn?!?\n", btsbsc); msgb_free(msg); return -EIO; } bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); if (bsc_conn) { if (gprs_ns_ipaddr) patch_gprs_msg(ipbc, bfd->priv_nr, msg); /* enqueue packet towards BSC */ msgb_enqueue(&bsc_conn->tx_queue, msg); /* mark respective filedescriptor as 'we want to write' */ bsc_conn->fd.when |= BSC_FD_WRITE; } else { logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, " "since remote connection is dead\n", btsbsc); msgb_free(msg); } return ret; } /* a TCP socket is ready to be written to */ static int handle_tcp_write(struct osmo_fd *bfd) { struct ipa_proxy_conn *ipc = bfd->data; struct ipa_bts_conn *ipbc = ipc->bts_conn; struct llist_head *lh; struct msgb *msg; char *btsbsc; int ret; if ((bfd->priv_nr & 0xff) <= 2) btsbsc = "BTS"; else btsbsc = "BSC"; /* get the next msg for this timeslot */ if (llist_empty(&ipc->tx_queue)) { bfd->when &= ~BSC_FD_WRITE; return 0; } lh = ipc->tx_queue.next; llist_del(lh); msg = llist_entry(lh, struct msgb, list); logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr, osmo_hexdump(msg->data, msg->len)); ret = send(bfd->fd, msg->data, msg->len, 0); msgb_free(msg); if (ret == 0) { logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); handle_dead_socket(bfd); } return ret; } /* callback from select.c in case one of the fd's can be read/written */ static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what) { int rc = 0; if (what & BSC_FD_READ) { rc = handle_tcp_read(bfd); if (rc < 0) return rc; } if (what & BSC_FD_WRITE) rc = handle_tcp_write(bfd); return rc; } /* callback of the listening filedescriptor */ static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) { int ret; struct ipa_proxy_conn *ipc; struct osmo_fd *bfd; struct sockaddr_in sa; socklen_t sa_len = sizeof(sa); if (!(what & BSC_FD_READ)) return 0; ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); if (ret < 0) { perror("accept"); return ret; } DEBUGP(DLINP, "accept()ed new %s link from %s\n", (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", inet_ntoa(sa.sin_addr)); ipc = alloc_conn(); if (!ipc) { close(ret); return -ENOMEM; } bfd = &ipc->fd; bfd->fd = ret; bfd->data = ipc; bfd->priv_nr = listen_bfd->priv_nr; bfd->cb = proxy_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"); close(bfd->fd); talloc_free(ipc); return ret; } /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ ret = ipa_ccm_send_id_req(bfd->fd); return 0; } static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port) { int ret; struct sockaddr_in addr; socklen_t len = sizeof(addr); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr = ip; ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to forward GPRS message.\n"); } } static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what) { struct ipa_bts_conn *bts; char buf[4096]; int ret; struct sockaddr_in sock; socklen_t len = sizeof(sock); /* 1. get the data... */ ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno)); return -1; } bts = bfd->data; /* 2. figure out where to send it to */ if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) { LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from network.\n"); send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000); } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) { LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n"); send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000); } else { LOGP(DLINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr)); } return 0; } /* Actively connect to a BSC. */ static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) { struct ipa_proxy_conn *ipc; struct osmo_fd *bfd; int ret, on = 1; ipc = alloc_conn(); if (!ipc) return NULL; ipc->bts_conn = data; bfd = &ipc->fd; bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); bfd->cb = ipaccess_fd_cb; bfd->when = BSC_FD_READ | BSC_FD_WRITE; bfd->data = ipc; bfd->priv_nr = priv_nr; if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n", strerror(errno)); talloc_free(ipc); return NULL; } ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Could not set socket option\n"); close(bfd->fd); talloc_free(ipc); return NULL; } ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); if (ret < 0) { LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n", inet_ntoa(sa->sin_addr)); close(bfd->fd); talloc_free(ipc); return NULL; } /* pre-fill tx_queue with identity request */ ret = osmo_fd_register(bfd); if (ret < 0) { close(bfd->fd); talloc_free(ipc); return NULL; } return ipc; } static int ipaccess_proxy_setup(void) { int ret; ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); if (!ipp) return -ENOMEM; INIT_LLIST_HEAD(&ipp->bts_list); osmo_timer_setup(&ipp->reconn_timer, reconn_tmr_cb, ipp); /* Listen for OML connections */ ipp->oml_listen_fd.priv_nr = OML_FROM_BTS; ipp->oml_listen_fd.cb = listen_fd_cb; ret = osmo_sock_init_ofd(&ipp->oml_listen_fd, AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, IPA_TCP_PORT_OML, OSMO_SOCK_F_BIND); if (ret < 0) return ret; /* Listen for RSL connections */ ipp->rsl_listen_fd.priv_nr = RSL_FROM_BTS; ipp->rsl_listen_fd.cb = listen_fd_cb; ret = osmo_sock_init_ofd(&ipp->rsl_listen_fd, AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, IPA_TCP_PORT_RSL, OSMO_SOCK_F_BIND); if (ret < 0) return ret; /* Connect the GPRS NS Socket */ if (gprs_ns_ipaddr) { inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); inet_aton(listen_ipaddr, &ipp->listen_addr); } return ret; } static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGABRT: /* in case of abort, we want to obtain a talloc report * and then return to the caller, who will abort the process */ case SIGUSR1: talloc_report_full(tall_bsc_ctx, stderr); break; default: break; } } static void print_help(void) { printf(" ipaccess-proxy is a proxy BTS.\n"); printf(" -h --help. This help text.\n"); printf(" -l --listen IP. The ip to listen to.\n"); printf(" -b --bsc IP. The BSC IP address.\n"); printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); printf("\n"); printf(" -s --disable-color. Disable the color inside the logging message.\n"); printf(" -e --log-level number. Set the global loglevel.\n"); printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); printf(" -V --version. Print the version of OpenBSC.\n"); } static void print_usage(void) { printf("Usage: ipaccess-proxy [options]\n"); } enum { IPA_PROXY_OPT_LISTEN_NONE = 0, IPA_PROXY_OPT_LISTEN_IP = (1 << 0), IPA_PROXY_OPT_BSC_IP = (1 << 1), }; static void handle_options(int argc, char** argv) { int options_mask = 0; /* disable explicit missing arguments error output from getopt_long */ opterr = 0; while (1) { int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"disable-color", 0, 0, 's'}, {"timestamp", 0, 0, 'T'}, {"log-level", 1, 0, 'e'}, {"listen", 1, 0, 'l'}, {"bsc", 1, 0, 'b'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hsTe:l:b:g:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_usage(); print_help(); exit(0); case 'l': listen_ipaddr = optarg; options_mask |= IPA_PROXY_OPT_LISTEN_IP; break; case 'b': bsc_ipaddr = optarg; options_mask |= IPA_PROXY_OPT_BSC_IP; break; case 'g': gprs_ns_ipaddr = optarg; break; case 's': log_set_use_color(osmo_stderr_target, 0); break; case 'T': log_set_print_timestamp(osmo_stderr_target, 1); break; case 'e': log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case '?': if (optopt) { printf("ERROR: missing mandatory argument " "for `%s' option\n", argv[optind-1]); } else { printf("ERROR: unknown option `%s'\n", argv[optind-1]); } print_usage(); print_help(); exit(EXIT_FAILURE); break; default: /* ignore */ break; } } if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) { printf("ERROR: You have to specify `--listen' and `--bsc' " "options at least.\n"); print_usage(); print_help(); exit(EXIT_FAILURE); } } static const struct log_info_cat log_categories[] = { [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { int rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); msgb_talloc_ctx_init(tall_bsc_ctx, 0); osmo_init_logging2(tall_bsc_ctx, &log_info); log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI"); handle_options(argc, argv); rc = ipaccess_proxy_setup(); if (rc < 0) exit(1); signal(SIGUSR1, &signal_handler); signal(SIGABRT, &signal_handler); osmo_init_ignore_signals(); while (1) { osmo_select_main(0); } } osmo-bsc-1.3.0/src/ipaccess/network_listen.c000066400000000000000000000165331332665256100210520ustar00rootroot00000000000000/* ip.access nanoBTS network listen mode */ /* (C) 2009-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include #define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1) int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, uint16_t max_num_arfcns) { int i; unsigned int num_arfcn = 0; for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) { int16_t arfcn = -1; while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { *buf++ = htons(arfcn); num_arfcn++; } if (num_arfcn > max_num_arfcns) break; } return num_arfcn; } enum ipac_test_state { IPAC_TEST_S_IDLE, IPAC_TEST_S_RQD, IPAC_TEST_S_EXEC, IPAC_TEST_S_PARTIAL, }; int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, const uint8_t *phys_conf, unsigned int phys_conf_len) { struct msgb *msg; if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) { fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state); return -EINVAL; } switch (testnr) { case NM_IPACC_TESTNO_CHAN_USAGE: case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: rxlev_stat_reset(&trx->ipaccess.rxlev_stat); break; } msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML"); if (phys_conf && phys_conf_len) { uint8_t *payload; /* first put the phys conf header */ msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len); payload = msgb_put(msg, phys_conf_len); memcpy(payload, phys_conf, phys_conf_len); } abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff, testnr, 1, msg); trx->ipaccess.test_nr = testnr; /* FIXME: start safety timer until when test is supposed to complete */ return 0; } static uint16_t last_arfcn; static struct gsm_sysinfo_freq nwl_si_freq[1024]; #define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ #define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ #define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ struct ipacc_ferr_elem { int16_t freq_err; uint8_t freq_qual; uint8_t arfcn; } __attribute__((packed)); struct ipacc_cusage_elem { uint16_t arfcn:10, rxlev:6; } __attribute__ ((packed)); static int test_rep(void *_msg) { struct msgb *msg = _msg; struct abis_om_fom_hdr *foh = msgb_l3(msg); uint16_t test_rep_len, ferr_list_len; struct ipacc_ferr_elem *ife; struct ipac_bcch_info binfo; struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; int i, rc; DEBUGP(DNM, "TEST REPORT: "); if (foh->data[0] != NM_ATT_TEST_NO || foh->data[2] != NM_ATT_TEST_REPORT) return -EINVAL; DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); /* data[2] == NM_ATT_TEST_REPORT */ /* data[3..4]: test_rep_len */ memcpy(&test_rep_len, &foh->data[3], sizeof(uint16_t)); test_rep_len = ntohs(test_rep_len); /* data[5]: ip.access test result */ DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5])); /* data[6]: ip.access nested IE. 3 == freq_err_list */ switch (foh->data[6]) { case NM_IPAC_EIE_FREQ_ERR_LIST: /* data[7..8]: length of ferr_list */ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); ferr_list_len = ntohs(ferr_list_len); /* data[9...]: frequency error list elements */ for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", ife->arfcn, ntohs(ife->freq_err)); } break; case NM_IPAC_EIE_CHAN_USE_LIST: /* data[7..8]: length of ferr_list */ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); ferr_list_len = ntohs(ferr_list_len); /* data[9...]: channel usage list elements */ for (i = 0; i < ferr_list_len; i+= 2) { uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i); uint16_t cu = ntohs(*cu_ptr); uint16_t arfcn = cu & 0x3ff; uint8_t rxlev = cu >> 10; DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev); rxlev_stat_input(&sign_link->trx->ipaccess.rxlev_stat, arfcn, rxlev); } break; case NM_IPAC_EIE_BCCH_INFO_TYPE: break; case NM_IPAC_EIE_BCCH_INFO: rc = ipac_parse_bcch_info(&binfo, foh->data+6); if (rc < 0) { DEBUGP(DNM, "BCCH Info parsing failed\n"); break; } DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %s, LAC %d CI %d BSIC %u\n", binfo.arfcn, binfo.rx_lev, binfo.rx_qual, osmo_plmn_name(&binfo.cgi.lai.plmn), binfo.cgi.lai.lac, binfo.cgi.cell_identity, binfo.bsic); if (binfo.arfcn != last_arfcn) { /* report is on a new arfcn, need to clear channel list */ memset(nwl_si_freq, 0, sizeof(nwl_si_freq)); last_arfcn = binfo.arfcn; } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) { DEBUGP(DNM, "BA SI2: %s\n", osmo_hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2), 0x8c, FREQ_TYPE_NCELL_2); } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) { DEBUGP(DNM, "BA SI2bis: %s\n", osmo_hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis), 0x8e, FREQ_TYPE_NCELL_2bis); } if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) { DEBUGP(DNM, "BA SI2ter: %s\n", osmo_hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter))); gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter), 0x8e, FREQ_TYPE_NCELL_2ter); } for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) { if (nwl_si_freq[i].mask) DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i); } break; default: break; } switch (foh->data[5]) { case NM_IPACC_TESTRES_SUCCESS: case NM_IPACC_TESTRES_STOPPED: case NM_IPACC_TESTRES_TIMEOUT: case NM_IPACC_TESTRES_NO_CHANS: sign_link->trx->ipaccess.test_state = IPAC_TEST_S_IDLE; /* Send signal to notify higher layers of test completion */ DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n"); osmo_signal_dispatch(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE, sign_link->trx); break; case NM_IPACC_TESTRES_PARTIAL: sign_link->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL; break; } return 0; } static int nwl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { switch (signal) { case S_NM_TEST_REP: return test_rep(signal_data); default: break; } return 0; } void ipac_nwl_init(void) { osmo_signal_register_handler(SS_NM, nwl_sig_cb, NULL); } osmo-bsc-1.3.0/src/ipaccess/stubs.c000066400000000000000000000022221332665256100171310ustar00rootroot00000000000000/* Stubs required for linking */ /* (C) 2018 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 struct gsm_bts; struct gsm_bts_trx_ts; struct msgb; struct bsc_msc_data; bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { /* No TS init required here. */ return true; } int abis_rsl_rcvmsg(struct msgb *msg) { /* No RSL handling here */ return 0; } void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc) { /* No paging flushing */ } osmo-bsc-1.3.0/src/libfilter/000077500000000000000000000000001332665256100160115ustar00rootroot00000000000000osmo-bsc-1.3.0/src/libfilter/Makefile.am000066400000000000000000000006671332665256100200560ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMOLEGACYMGCP_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) noinst_LIBRARIES = \ libfilter.a \ $(NULL) libfilter_a_SOURCES = \ bsc_msg_filter.c \ bsc_msg_acc.c \ bsc_msg_vty.c \ $(NULL) osmo-bsc-1.3.0/src/libfilter/bsc_msg_acc.c000066400000000000000000000072441332665256100204070ustar00rootroot00000000000000/* * (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2011 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 static const struct rate_ctr_desc acc_list_ctr_description[] = { [ACC_LIST_LOCAL_FILTER] = { "access-list:local-filter", "Rejected by rule for local"}, [ACC_LIST_GLOBAL_FILTER]= { "access-list:global-filter", "Rejected by rule for global"}, }; static const struct rate_ctr_group_desc bsc_cfg_acc_list_desc = { .group_name_prefix = "nat:filter", .group_description = "NAT Access-List Statistics", .num_ctr = ARRAY_SIZE(acc_list_ctr_description), .ctr_desc = acc_list_ctr_description, .class_id = OSMO_STATS_CLASS_GLOBAL, }; /*! Find an unused index for this rate counter group. * \param[in] head List of allocated ctr groups of the same type * \returns the largest used index number + 1, or 0 if none exist yet. */ static unsigned int rate_ctr_get_unused_idx(struct llist_head *head) { unsigned int idx = 0; struct bsc_msg_acc_lst *lst; llist_for_each_entry(lst, head, list) { if (idx <= lst->stats->idx) idx = lst->stats->idx + 1; } return idx; } int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *mi_string) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (!entry->imsi_allow) continue; if (regexec(&entry->imsi_allow_re, mi_string, 0, NULL, 0) == 0) return 0; } return 1; } struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *head, const char *name) { struct bsc_msg_acc_lst *lst; if (!name) return NULL; llist_for_each_entry(lst, head, list) if (strcmp(lst->name, name) == 0) return lst; return NULL; } struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *head, const char *name) { struct bsc_msg_acc_lst *lst; unsigned int new_idx; lst = bsc_msg_acc_lst_find(head, name); if (lst) return lst; lst = talloc_zero(ctx, struct bsc_msg_acc_lst); if (!lst) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate access list\n"); return NULL; } new_idx = rate_ctr_get_unused_idx(head); lst->stats = rate_ctr_group_alloc(lst, &bsc_cfg_acc_list_desc, new_idx); if (!lst->stats) { talloc_free(lst); return NULL; } INIT_LLIST_HEAD(&lst->fltr_list); lst->name = talloc_strdup(lst, name); llist_add_tail(&lst->list, head); return lst; } void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst) { llist_del(&lst->list); rate_ctr_group_free(lst->stats); talloc_free(lst); } struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *lst) { struct bsc_msg_acc_lst_entry *entry; entry = talloc_zero(lst, struct bsc_msg_acc_lst_entry); if (!entry) return NULL; entry->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; entry->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; llist_add_tail(&entry->list, &lst->fltr_list); return entry; } osmo-bsc-1.3.0/src/libfilter/bsc_msg_filter.c000066400000000000000000000216131332665256100211420ustar00rootroot00000000000000/* * Access filtering */ /* * (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2012 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 #include #include #include #include #include #include #include #include #include static int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) { struct bsc_filter_barr_entry *n; n = rb_entry(root->rb_node, struct bsc_filter_barr_entry, node); while (n) { int rc = strcmp(imsi, n->imsi); if (rc == 0) { *cm = n->cm_reject_cause; *lu = n->lu_reject_cause; return 1; } n = rb_entry( (rc < 0) ? n->node.rb_left : n->node.rb_right, struct bsc_filter_barr_entry, node); }; return 0; } static int lst_check_deny(struct bsc_msg_acc_lst *lst, const char *mi_string, int *cm_cause, int *lu_cause) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (!entry->imsi_deny) continue; if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) { *cm_cause = entry->cm_reject_cause; *lu_cause = entry->lu_reject_cause; return 0; } } return 1; } /* apply white/black list */ static int auth_imsi(struct bsc_filter_request *req, const char *imsi, struct bsc_filter_reject_cause *cause) { /* * Now apply blacklist/whitelist of the BSC and the NAT. * 1.) Check the global IMSI barr list * 2.) Allow directly if the IMSI is allowed at the BSC * 3.) Reject if the IMSI is not allowed at the BSC * 4.) Allow directly if the IMSI is allowed at the global level * 5.) Reject if the IMSI not allowed at the global level. */ int cm, lu; struct bsc_msg_acc_lst *nat_lst = NULL; struct bsc_msg_acc_lst *bsc_lst = NULL; /* 1. global check for barred imsis */ if (req->black_list && bsc_filter_barr_find(req->black_list, imsi, &cm, &lu)) { cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; LOGP(DFILTER, LOGL_DEBUG, "Blocking subscriber IMSI %s with CM: %d LU: %d\n", imsi, cm, lu); return -4; } bsc_lst = bsc_msg_acc_lst_find(req->access_lists, req->local_lst_name); nat_lst = bsc_msg_acc_lst_find(req->access_lists, req->global_lst_name); if (bsc_lst) { /* 2. BSC allow */ if (bsc_msg_acc_lst_check_allow(bsc_lst, imsi) == 0) return 1; /* 3. BSC deny */ if (lst_check_deny(bsc_lst, imsi, &cm, &lu) == 0) { LOGP(DFILTER, LOGL_ERROR, "Filtering %s by imsi_deny on config nr: %d.\n", imsi, req->bsc_nr); rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_LOCAL_FILTER]); cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; return -2; } } if (nat_lst) { /* 4. global allow */ if (bsc_msg_acc_lst_check_allow(nat_lst, imsi) == 0) return 1; /* 5. global deny */ if (lst_check_deny(nat_lst, imsi, &cm, &lu) == 0) { LOGP(DFILTER, LOGL_ERROR, "Filtering %s global imsi_deny on bsc nr: %d.\n", imsi, req->bsc_nr); rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_GLOBAL_FILTER]); cause->cm_reject_cause = cm; cause->lu_reject_cause = lu; return -3; } } return 1; } static int _cr_check_loc_upd(void *ctx, uint8_t *data, unsigned int length, char **imsi) { uint8_t mi_type; struct gsm48_loc_upd_req *lu; char mi_string[GSM48_MI_SIZE]; if (length < sizeof(*lu)) { LOGP(DFILTER, LOGL_ERROR, "LU does not fit. Length is %d \n", length); return -1; } lu = (struct gsm48_loc_upd_req *) data; mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; /* * We can only deal with the IMSI. This will fail for a phone that * will send the TMSI of a previous network to us. */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _cr_check_cm_serv_req(void *ctx, uint8_t *data, unsigned int length, int *con_type, char **imsi) { static const uint32_t classmark_offset = offsetof(struct gsm48_service_request, classmark); char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; int rc; struct gsm48_service_request *req; /* unfortunately in Phase1 the classmark2 length is variable */ if (length < sizeof(*req)) { LOGP(DFILTER, LOGL_ERROR, "CM Serv Req does not fit. Length is %d\n", length); return -1; } req = (struct gsm48_service_request *) data; if (req->cm_service_type == 0x8) *con_type = FLT_CON_TYPE_SSA; rc = gsm48_extract_mi((uint8_t *) &req->classmark, length - classmark_offset, mi_string, &mi_type); if (rc < 0) { LOGP(DFILTER, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc); return -1; } /* we have to let the TMSI or such pass */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _cr_check_pag_resp(void *ctx, uint8_t *data, unsigned int length, char **imsi) { struct gsm48_pag_resp *resp; char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; if (length < sizeof(*resp)) { LOGP(DFILTER, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length); return -1; } resp = (struct gsm48_pag_resp *) data; if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) { LOGP(DFILTER, LOGL_ERROR, "Failed to extract the MI.\n"); return -1; } /* we need to let it pass for now */ if (mi_type != GSM_MI_TYPE_IMSI) return 0; *imsi = talloc_strdup(ctx, mi_string); return 1; } static int _dt_check_id_resp(struct bsc_filter_request *req, uint8_t *data, unsigned int length, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause) { char mi_string[GSM48_MI_SIZE]; uint8_t mi_type; if (length < 2) { LOGP(DFILTER, LOGL_ERROR, "mi does not fit.\n"); return -1; } if (data[0] < length - 1) { LOGP(DFILTER, LOGL_ERROR, "mi length too big.\n"); return -2; } mi_type = data[1] & GSM_MI_TYPE_MASK; gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]); if (mi_type != GSM_MI_TYPE_IMSI) return 0; state->imsi_checked = 1; state->imsi = talloc_strdup(req->ctx, mi_string); return auth_imsi(req, mi_string, cause); } /* Filter out CR data... */ int bsc_msg_filter_initial(struct gsm48_hdr *hdr48, size_t hdr48_len, struct bsc_filter_request *req, int *con_type, char **imsi, struct bsc_filter_reject_cause *cause) { int ret = 0; uint8_t msg_type, proto; *con_type = FLT_CON_TYPE_NONE; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; *imsi = NULL; proto = gsm48_hdr_pdisc(hdr48); msg_type = gsm48_hdr_msg_type(hdr48); if (proto == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { *con_type = FLT_CON_TYPE_LU; ret = _cr_check_loc_upd(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); } else if (proto == GSM48_PDISC_MM && msg_type == GSM48_MT_MM_CM_SERV_REQ) { *con_type = FLT_CON_TYPE_CM_SERV_REQ; ret = _cr_check_cm_serv_req(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), con_type, imsi); } else if (proto == GSM48_PDISC_RR && msg_type == GSM48_MT_RR_PAG_RESP) { *con_type = FLT_CON_TYPE_PAG_RESP; ret = _cr_check_pag_resp(req->ctx, &hdr48->data[0], hdr48_len - sizeof(*hdr48), imsi); } else { /* We only want to filter the above, let other things pass */ *con_type = FLT_CON_TYPE_OTHER; return 0; } /* check if we are done */ if (ret != 1) return ret; /* the memory allocation failed */ if (!*imsi) return -1; /* now check the imsi */ return auth_imsi(req, *imsi, cause); } int bsc_msg_filter_data(struct gsm48_hdr *hdr48, size_t len, struct bsc_filter_request *req, struct bsc_filter_state *state, struct bsc_filter_reject_cause *cause) { uint8_t msg_type, proto; cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; if (state->imsi_checked) return 0; proto = gsm48_hdr_pdisc(hdr48); msg_type = gsm48_hdr_msg_type(hdr48); if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) return 0; return _dt_check_id_resp(req, &hdr48->data[0], len - sizeof(*hdr48), state, cause); } osmo-bsc-1.3.0/src/libfilter/bsc_msg_vty.c000066400000000000000000000077731332665256100205120ustar00rootroot00000000000000/* (C) 2010-2015 by Holger Hans Peter Freyther * (C) 2010-2013 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 #include #include #include static struct llist_head *_acc_lst; static void *_ctx; static void bsc_msg_acc_lst_write_one(struct vty *vty, struct bsc_msg_acc_lst *lst) { struct bsc_msg_acc_lst_entry *entry; llist_for_each_entry(entry, &lst->fltr_list, list) { if (entry->imsi_allow) vty_out(vty, " access-list %s imsi-allow %s%s", lst->name, entry->imsi_allow, VTY_NEWLINE); if (entry->imsi_deny) vty_out(vty, " access-list %s imsi-deny %s %d %d%s", lst->name, entry->imsi_deny, entry->cm_reject_cause, entry->lu_reject_cause, VTY_NEWLINE); } } DEFUN(cfg_lst_no, cfg_lst_no_cmd, "no access-list NAME", NO_STR "Remove an access-list by name\n" "The access-list to remove\n") { struct bsc_msg_acc_lst *acc; acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); if (!acc) return CMD_WARNING; bsc_msg_acc_lst_delete(acc); return CMD_SUCCESS; } DEFUN(show_acc_lst, show_acc_lst_cmd, "show access-list NAME", SHOW_STR "IMSI access list\n" "Name of the access list\n") { struct bsc_msg_acc_lst *acc; acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); if (!acc) return CMD_WARNING; vty_out(vty, "access-list %s%s", acc->name, VTY_NEWLINE); bsc_msg_acc_lst_write_one(vty, acc); vty_out_rate_ctr_group(vty, " ", acc->stats); return CMD_SUCCESS; } DEFUN(cfg_lst_imsi_allow, cfg_lst_imsi_allow_cmd, "access-list NAME imsi-allow [REGEXP]", "Access list commands\n" "Name of the access list\n" "Add allowed IMSI to the list\n" "Regexp for IMSIs\n") { struct bsc_msg_acc_lst *acc; struct bsc_msg_acc_lst_entry *entry; acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); if (!acc) return CMD_WARNING; entry = bsc_msg_acc_lst_entry_create(acc); if (!entry) return CMD_WARNING; if (gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_lst_imsi_deny, cfg_lst_imsi_deny_cmd, "access-list NAME imsi-deny [REGEXP] (<0-256>) (<0-256>)", "Access list commands\n" "Name of the access list\n" "Add denied IMSI to the list\n" "Regexp for IMSIs\n" "CM Service Reject reason\n" "LU Reject reason\n") { struct bsc_msg_acc_lst *acc; struct bsc_msg_acc_lst_entry *entry; acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); if (!acc) return CMD_WARNING; entry = bsc_msg_acc_lst_entry_create(acc); if (!entry) return CMD_WARNING; if (gsm_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0) return CMD_WARNING; if (argc >= 3) entry->cm_reject_cause = atoi(argv[2]); if (argc >= 4) entry->lu_reject_cause = atoi(argv[3]); return CMD_SUCCESS; } void bsc_msg_acc_lst_write(struct vty *vty) { struct bsc_msg_acc_lst *lst; llist_for_each_entry(lst, _acc_lst, list) { bsc_msg_acc_lst_write_one(vty, lst); } } void bsc_msg_acc_lst_vty_init(void *ctx, struct llist_head *lst, int node) { _ctx = ctx; _acc_lst = lst; install_element_ve(&show_acc_lst_cmd); /* access-list */ install_element(node, &cfg_lst_imsi_allow_cmd); install_element(node, &cfg_lst_imsi_deny_cmd); install_element(node, &cfg_lst_no_cmd); } osmo-bsc-1.3.0/src/osmo-bsc/000077500000000000000000000000001332665256100155575ustar00rootroot00000000000000osmo-bsc-1.3.0/src/osmo-bsc/Makefile.am000066400000000000000000000033561332665256100176220ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOCTRL_CFLAGS) \ $(LIBOSMONETIF_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMOMGCPCLIENT_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) bin_PROGRAMS = \ osmo-bsc \ $(NULL) osmo_bsc_SOURCES = \ a_reset.c \ abis_nm.c \ abis_nm_vty.c \ abis_om2000.c \ abis_om2000_vty.c \ abis_rsl.c \ acc_ramp.c \ arfcn_range_encode.c \ bsc_api.c \ bsc_ctrl_commands.c \ bsc_ctrl_lookup.c \ bsc_dyn_ts.c \ bsc_init.c \ bsc_rf_ctrl.c \ bsc_rll.c \ bsc_subscr_conn_fsm.c \ bsc_subscriber.c \ bsc_vty.c \ bts_ericsson_rbs2000.c \ bts_init.c \ bts_ipaccess_nanobts.c \ bts_ipaccess_nanobts_omlattr.c \ bts_nokia_site.c \ bts_siemens_bs11.c \ bts_sysmobts.c \ bts_unknown.c \ chan_alloc.c \ codec_pref.c \ e1_config.c \ gsm_04_08_utils.c \ gsm_04_80_utils.c \ gsm_data.c \ handover_cfg.c \ handover_decision.c \ handover_decision_2.c \ handover_logic.c \ handover_vty.c \ meas_feed.c \ meas_rep.c \ net_init.c \ osmo_bsc_api.c \ osmo_bsc_audio.c \ osmo_bsc_bssap.c \ osmo_bsc_ctrl.c \ osmo_bsc_filter.c \ osmo_bsc_grace.c \ osmo_bsc_lcls.c \ osmo_bsc_main.c \ osmo_bsc_msc.c \ osmo_bsc_sigtran.c \ osmo_bsc_vty.c \ paging.c \ pcu_sock.c \ penalty_timers.c \ rest_octets.c \ system_information.c \ $(NULL) osmo_bsc_LDADD = \ $(top_builddir)/src/libfilter/libfilter.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOCTRL_LIBS) \ $(COVERAGE_LDFLAGS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBOSMOMGCPCLIENT_LIBS) \ $(NULL) osmo-bsc-1.3.0/src/osmo-bsc/a_reset.c000066400000000000000000000136151332665256100173530ustar00rootroot00000000000000/* (C) 2017 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 #define RESET_RESEND_INTERVAL 2 /* sec */ #define RESET_RESEND_TIMER_NO 4 /* See also 3GPP TS 48.008 Chapter 3.1.4.1.3.1 */ #define BAD_CONNECTION_THRESOLD 3 /* connection failures */ /* Reset context data (callbacks, state machine etc...) */ struct reset_ctx { /* Connection failure counter. When this counter * reaches a certain threshold, the reset procedure * will be triggered */ int conn_loss_counter; /* Callback function to be called when a connection * failure is detected and a rest must occur */ void (*cb)(void *priv); /* Privated data for the callback function */ void *priv; }; enum reset_fsm_states { ST_DISC, /* Disconnected from remote end */ ST_CONN, /* We have a confirmed connection */ }; enum reset_fsm_evt { EV_RESET_ACK, /* got reset acknowlegement from remote end */ EV_N_DISCONNECT, /* lost a connection */ EV_N_CONNECT, /* made a successful connection */ }; static const struct value_string fsm_event_names[] = { OSMO_VALUE_STRING(EV_RESET_ACK), OSMO_VALUE_STRING(EV_N_DISCONNECT), OSMO_VALUE_STRING(EV_N_CONNECT), {0, NULL} }; /* Disconnected state */ static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; OSMO_ASSERT(reset_ctx); LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection succeded.\n"); reset_ctx->conn_loss_counter = 0; osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0); } /* Connected state */ static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; OSMO_ASSERT(reset_ctx); switch (event) { case EV_N_DISCONNECT: if (reset_ctx->conn_loss_counter >= BAD_CONNECTION_THRESOLD) { LOGPFSML(fi, LOGL_NOTICE, "SIGTRAN connection down, reconnecting...\n"); osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); } else reset_ctx->conn_loss_counter++; break; case EV_N_CONNECT: reset_ctx->conn_loss_counter = 0; break; } } /* Timer callback to retransmit the reset signal */ static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi) { struct reset_ctx *reset_ctx = (struct reset_ctx *)fi->priv; OSMO_ASSERT(reset_ctx); LOGPFSML(fi, LOGL_NOTICE, "(re)sending BSSMAP RESET message...\n"); reset_ctx->cb(reset_ctx->priv); osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); return 0; } static struct osmo_fsm_state reset_fsm_states[] = { [ST_DISC] = { .in_event_mask = (1 << EV_RESET_ACK), .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), .name = "DISC", .action = fsm_disc_cb, }, [ST_CONN] = { .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT), .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN), .name = "CONN", .action = fsm_conn_cb, }, }; /* State machine definition */ static struct osmo_fsm fsm = { .name = "A-RESET", .states = reset_fsm_states, .num_states = ARRAY_SIZE(reset_fsm_states), .log_subsys = DMSC, .timer_cb = fsm_reset_ack_timeout_cb, .event_names = fsm_event_names, }; /* Create and start state machine which handles the reset/reset-ack procedure */ struct osmo_fsm_inst *a_reset_alloc(void *ctx, const char *name, void *cb, void *priv) { OSMO_ASSERT(name); struct reset_ctx *reset_ctx; struct osmo_fsm_inst *reset_fsm; /* Register the fsm description (if not already done) */ if (osmo_fsm_find_by_name(fsm.name) != &fsm) osmo_fsm_register(&fsm); /* Allocate and configure a new fsm instance */ reset_ctx = talloc_zero(ctx, struct reset_ctx); OSMO_ASSERT(reset_ctx); reset_ctx->priv = priv; reset_ctx->cb = cb; reset_ctx->conn_loss_counter = 0; reset_fsm = osmo_fsm_inst_alloc(&fsm, ctx, reset_ctx, LOGL_DEBUG, name); OSMO_ASSERT(reset_fsm); /* kick off reset-ack sending mechanism */ osmo_fsm_inst_state_chg(reset_fsm, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO); return reset_fsm; } /* Confirm that we sucessfully received a reset acknowlege message */ void a_reset_ack_confirm(struct osmo_fsm_inst *reset_fsm) { OSMO_ASSERT(reset_fsm); osmo_fsm_inst_dispatch(reset_fsm, EV_RESET_ACK, NULL); } /* Report a failed connection */ void a_reset_conn_fail(struct osmo_fsm_inst *reset_fsm) { /* If no reset context is supplied, just drop the info */ if (!reset_fsm) return; osmo_fsm_inst_dispatch(reset_fsm, EV_N_DISCONNECT, NULL); } /* Report a successful connection */ void a_reset_conn_success(struct osmo_fsm_inst *reset_fsm) { /* If no reset context is supplied, just drop the info */ if (!reset_fsm) return; osmo_fsm_inst_dispatch(reset_fsm, EV_N_CONNECT, NULL); } /* Check if we have a connection to a specified msc */ bool a_reset_conn_ready(struct osmo_fsm_inst *reset_fsm) { /* If no reset context is supplied, we assume that * the connection can't be ready! */ if (!reset_fsm) return false; if (reset_fsm->state == ST_CONN) return true; return false; } osmo-bsc-1.3.0/src/osmo-bsc/abis_bs11.c000066400000000000000000000014521332665256100174710ustar00rootroot00000000000000/* Siemens BS11 specific Abis implementations */ /* (C) 2008-2018 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 . * */ osmo-bsc-1.3.0/src/osmo-bsc/abis_nm.c000066400000000000000000002416311332665256100173420ustar00rootroot00000000000000/* GSM Network Management (OML) messages on the A-bis interface * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ /* (C) 2008-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 #define IPACC_SEGMENT_SIZE 245 #define LOGPFOH(ss, lvl, foh, fmt, args ...) LOGP(ss, lvl, "%s: " fmt, abis_nm_dump_foh(foh), ## args) #define DEBUGPFOH(ss, foh, fmt, args ...) LOGPFOH(ss, LOGL_DEBUG, foh, fmt, ## args) int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len) { if (!bts->model) return -EIO; return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); } static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) { int i; for (i = 0; i < size; i++) { if (arr[i] == mt) return 1; } return 0; } #if 0 /* is this msgtype the usual ACK/NACK type ? */ static int is_ack_nack(enum abis_nm_msgtype mt) { return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); } #endif /* is this msgtype a report ? */ static int is_report(enum abis_nm_msgtype mt) { return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports)); } #define MT_ACK(x) (x+1) #define MT_NACK(x) (x+2) 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 struct abis_om_fom_hdr *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; return foh; } static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); } int _abis_nm_sendmsg(struct msgb *msg) { msg->l2h = msg->data; if (!msg->dst) { LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); return -EINVAL; } return abis_sendmsg(msg); } /* Send a OML NM Message from BSC to BTS */ static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) { msg->dst = bts->oml_link; /* queue OML messages */ if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); return _abis_nm_sendmsg(msg); } else { msgb_enqueue(&bts->abis_queue, msg); return 0; } } int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) { OBSC_NM_W_ACK_CB(msg) = 1; return abis_nm_queue_msg(bts, msg); } static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) { OBSC_NM_W_ACK_CB(msg) = 0; return abis_nm_queue_msg(bts, msg); } static int abis_nm_rcvmsg_sw(struct msgb *mb); /* Update the administrative state of a given object in our in-memory data * structures and send an event to the higher layer */ static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, struct abis_om_obj_inst *obj_inst, uint8_t adm_state) { struct gsm_nm_state *nm_state, new_state; struct nm_statechg_signal_data nsd; memset(&nsd, 0, sizeof(nsd)); nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); if (!nsd.obj) return -EINVAL; nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); if (!nm_state) return -1; new_state = *nm_state; new_state.administrative = adm_state; nsd.bts = bts; nsd.obj_class = obj_class; nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.obj_inst = obj_inst; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); nm_state->administrative = adm_state; return 0; } static int abis_nm_rx_statechg_rep(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct tlv_parsed tp; struct gsm_nm_state *nm_state, new_state; memset(&new_state, 0, sizeof(new_state)); nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); if (!nm_state) { LOGPFOH(DNM, LOGL_ERROR, foh, "unknown managed object\n"); return -EINVAL; } new_state = *nm_state; DEBUGPFOH(DNM, foh, "STATE CHG: "); abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); DEBUGPC(DNM, "OP_STATE=%s ", abis_nm_opstate_name(new_state.operational)); } if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) new_state.availability = 0xff; else new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); DEBUGPC(DNM, "AVAIL=%s(%02x) ", abis_nm_avail_name(new_state.availability), new_state.availability); } else new_state.availability = 0xff; if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); DEBUGPC(DNM, "ADM=%2s ", get_value_string(abis_nm_adm_state_names, new_state.administrative)); } DEBUGPC(DNM, "\n"); if ((new_state.administrative != 0 && nm_state->administrative == 0) || new_state.operational != nm_state->operational || new_state.availability != nm_state->availability) { /* Update the operational state of a given object in our in-memory data * structures and send an event to the higher layer */ struct nm_statechg_signal_data nsd; nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); nsd.obj_class = foh->obj_class; nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.obj_inst = &foh->obj_inst; nsd.bts = bts; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); nm_state->operational = new_state.operational; nm_state->availability = new_state.availability; if (nm_state->administrative == 0) nm_state->administrative = new_state.administrative; } #if 0 if (op_state == 1) { /* try to enable objects that are disabled */ abis_nm_opstart(bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); } #endif return 0; } static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type, const char *severity, const uint8_t *p_val, const char *text) { enum abis_nm_pcause_type pcause = p_val[0]; enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); LOGPC(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr); if (type) LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type); if (severity) LOGPC(DNM, LOGL_ERROR, "Severity=%s, ", severity); LOGPC(DNM, LOGL_ERROR, "Probable cause=%s: ", get_value_string(abis_nm_pcause_type_names, pcause)); if (pcause == NM_PCAUSE_T_MANUF) LOGPC(DNM, LOGL_ERROR, "%s, ", get_value_string(abis_mm_event_cause_names, cause)); else LOGPC(DNM, LOGL_ERROR, "%02X %02X ", p_val[1], p_val[2]); if (text) { LOGPC(DNM, LOGL_ERROR, "Additional Text=%s. ", text); } LOGPC(DNM, LOGL_ERROR, "\n"); } static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_val, const char *type, const char *severity, const char *text) { enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); switch (cause) { case OSMO_EVT_PCU_VERS: if (text) { LOGPC(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text); osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version)); } else { LOGPC(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr); bts->pcu_version[0] = '\0'; } break; default: log_oml_fail_rep(bts, type, severity, p_val, text); }; } static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; int rc = 0; const uint8_t *p_val = NULL; char *p_text = NULL; const char *e_type = NULL, *severity = NULL; abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); } if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) e_type = abis_nm_event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE)); if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) severity = abis_nm_severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY)); if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); switch (p_val[0]) { case NM_PCAUSE_T_MANUF: handle_manufact_report(bts, p_val, e_type, severity, p_text); break; default: log_oml_fail_rep(bts, e_type, severity, p_val, p_text); }; } else { LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: Failure Event Report without " "Probable Cause?!\n", bts->nr); rc = -EINVAL; } if (p_text) talloc_free(p_text); return rc; } static int abis_nm_rcvmsg_report(struct msgb *mb, struct gsm_bts *bts) { struct abis_om_fom_hdr *foh = msgb_l3(mb); uint8_t mt = foh->msg_type; switch (mt) { case NM_MT_STATECHG_EVENT_REP: return abis_nm_rx_statechg_rep(mb); break; case NM_MT_SW_ACTIVATED_REP: DEBUGPFOH(DNM, foh, "Software Activated Report\n"); osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); break; case NM_MT_FAILURE_EVENT_REP: rx_fail_evt_rep(mb, bts); osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); break; case NM_MT_TEST_REP: DEBUGPFOH(DNM, foh, "Test Report\n"); osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); break; default: LOGPFOH(DNM, LOGL_NOTICE, foh, "unknown NM report MT 0x%02x\n", mt); break; }; return 0; } /* Activate the specified software into the BTS */ static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, const struct abis_nm_sw_desc *sw_desc) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint16_t len = abis_nm_sw_desc_len(sw_desc, true); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); abis_nm_put_sw_desc(msg, sw_desc, true); return abis_nm_sendmsg(bts, msg); } int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw_descr, const size_t size) { int res = 0; int i; for (i = 1; i < size; ++i) { if (memcmp(sw_descr[res].file_version, sw_descr[i].file_version, OSMO_MIN(sw_descr[i].file_version_len, sw_descr[res].file_version_len)) < 0) { res = i; } } return res; } static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id, uint8_t *val, uint8_t len) { switch (id) { case BTS_TYPE_VARIANT: LOGP(DNM, LOGL_NOTICE, "BTS%u reported variant: %s\n", bts->nr, val); break; case BTS_SUB_MODEL: LOGP(DNM, LOGL_NOTICE, "BTS%u reported submodel: %s\n", bts->nr, val); break; default: return false; } return true; } /* Parse Attribute Response Info - return pointer to the actual content */ static inline const uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, const uint8_t *ari, uint16_t ari_len, uint16_t *out_len) { uint8_t num_unreported = ari[0], i; DEBUGP(DNM, "BTS%u Get Attributes Response Info: %u bytes total with %u unreported attributes\n", bts_nr, ari_len, num_unreported); /* +1 because we have to account for number of unreported attributes, prefixing the list: */ for (i = 0; i < num_unreported; i++) LOGP(DNM, LOGL_ERROR, "BTS%u Attribute %s is unreported\n", bts_nr, get_value_string(abis_nm_att_names, ari[i + 1])); /* the data starts right after the list of unreported attributes + space for length of that list */ *out_len = ari_len - (num_unreported + 2); return ari + num_unreported + 1; /* we have to account for 1st byte with number of unreported attributes */ } /* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */ static inline const uint8_t *parse_attr_resp_info_manuf_id(struct gsm_bts *bts, const uint8_t *data, uint16_t *data_len) { struct tlv_parsed tp; uint16_t m_id_len = 0; uint8_t adjust = 0, i; abis_nm_tlv_parse(&tp, bts, data, *data_len); if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_ID, 2)) { m_id_len = TLVP_LEN(&tp, NM_ATT_MANUF_ID); /* log potential BTS feature vector overflow */ if (m_id_len > sizeof(bts->_features_data)) LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: feature vector is truncated to %u bytes\n", bts->nr, MAX_BTS_FEATURES/8); /* check that max. expected BTS attribute is above given feature vector length */ if (m_id_len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT)) LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: reported unexpectedly long (%u bytes) " "feature vector - most likely it was compiled against newer BSC headers. " "Consider upgrading your BSC to later version.\n", bts->nr, m_id_len); memcpy(bts->_features_data, TLVP_VAL(&tp, NM_ATT_MANUF_ID), sizeof(bts->_features_data)); adjust = m_id_len + 3; /* adjust for parsed TL16V struct */ for (i = 0; i < _NUM_BTS_FEAT; i++) if (osmo_bts_has_feature(&bts->features, i) != osmo_bts_has_feature(&bts->model->features, i)) LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically " "set feature: %u != %u. Please fix.\n", bts->nr, get_value_string(osmo_bts_features_descs, i), osmo_bts_has_feature(&bts->features, i), osmo_bts_has_feature(&bts->model->features, i)); } *data_len -= adjust; return data + adjust; } /* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.28 Manufacturer Dependent State */ static inline const uint8_t *parse_attr_resp_info_manuf_state(const struct gsm_bts_trx *trx, const uint8_t *data, uint16_t *data_len) { struct tlv_parsed tp; const uint8_t *power; uint8_t adjust = 0; if (!trx) /* this attribute does not make sense on BTS level, only on TRX level */ return data; abis_nm_tlv_parse(&tp, trx->bts, data, *data_len); if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_STATE, 1)) { power = TLVP_VAL(&tp, NM_ATT_MANUF_STATE); LOGP(DNM, LOGL_NOTICE, "%s Get Attributes Response: nominal power is %u\n", gsm_trx_name(trx), *power); adjust = 2; /* adjust for parsed TV struct */ } *data_len -= adjust; return data + adjust; } /* Handle 3GPP TS 52.021 §9.4.64 Get Attribute Response Info */ static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *trx) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts *bts = trx ? trx->bts : sign_link->trx->bts; struct tlv_parsed tp; const uint8_t *data; int i; uint16_t data_len; int rc; struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; DEBUGPFOH(DNM, foh, "Get Attributes Response for BTS%u\n", bts->nr); abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (!TLVP_PRES_LEN(&tp, NM_ATT_GET_ARI, 1)) { LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: Get Attr Response without Response Info?!\n", bts->nr); return -EINVAL; } data = parse_attr_resp_info_unreported(bts->nr, TLVP_VAL(&tp, NM_ATT_GET_ARI), TLVP_LEN(&tp, NM_ATT_GET_ARI), &data_len); data = parse_attr_resp_info_manuf_state(trx, data, &data_len); data = parse_attr_resp_info_manuf_id(bts, data, &data_len); /* after parsing manufacturer-specific attributes there's list of replies in form of sw-conf structure: */ rc = abis_nm_get_sw_conf(data, data_len, &sw_descr[0], ARRAY_SIZE(sw_descr)); if (rc > 0) { for (i = 0; i < rc; i++) { if (!handle_attr(bts, str2btsattr((const char *)sw_descr[i].file_id), sw_descr[i].file_version, sw_descr[i].file_version_len)) LOGPFOH(DNM, LOGL_NOTICE, foh, "BTS%u: ARI reported sw[%d/%d]: %s " "is %s\n", bts->nr, i, rc, sw_descr[i].file_id, sw_descr[i].file_version); } } else { LOGPFOH(DNM, LOGL_ERROR, foh, "BTS%u: failed to parse SW-Config part of " "Get Attribute Response Info: %s\n", bts->nr, strerror(-rc)); } return 0; } /* 3GPP TS 52.021 §6.2.5 */ static int abis_nm_rx_sw_act_req(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; const uint8_t *sw_config; int ret, sw_config_len, len; struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; DEBUGPFOH(DNM, foh, "Software Activate Request, ACKing and Activating\n"); ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, 0, foh->data, oh->length-sizeof(*foh)); if (ret != 0) { LOGPFOH(DNM, LOGL_ERROR, foh, "Sending SW ActReq ACK failed: %d\n", ret); return ret; } abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { LOGPFOH(DNM, LOGL_ERROR, foh, "SW config not found! Can't continue.\n"); return -EINVAL; } else { DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); } /* Parse up to two sw descriptions from the data */ len = abis_nm_get_sw_conf(sw_config, sw_config_len, &sw_descr[0], ARRAY_SIZE(sw_descr)); if (len <= 0) { LOGPFOH(DNM, LOGL_ERROR, foh, "Failed to parse SW Config.\n"); return -EINVAL; } ret = abis_nm_select_newest_sw(&sw_descr[0], len); DEBUGPFOH(DNM, foh, "Selected sw description %d of %d\n", ret, len); return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr, &sw_descr[ret]); } /* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; uint8_t adm_state; abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) return -EINVAL; adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); } static int abis_nm_rx_lmt_event(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct tlv_parsed tp; DEBUGPFOH(DNM, foh, "LMT Event "); abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); } if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); DEBUGPC(DNM, "Level=%u ", level); } if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); DEBUGPC(DNM, "Username=%s ", name); } DEBUGPC(DNM, "\n"); /* FIXME: parse LMT LOGON TIME */ return 0; } /* From a received OML message, determine the matching struct gsm_bts_trx_ts instance. * Note that the BTS-TRX-TS numbers received in the FOM header do not correspond to the local bts->nr and * bts->trx->nr. Rather, the trx is identified by the e1inp_sign_link* found in msg->dst, and the TS is * then obtained by the FOM header's TS number. */ struct gsm_bts_trx_ts *abis_nm_get_ts(const struct msgb *oml_msg) { struct abis_om_fom_hdr *foh = msgb_l3(oml_msg); struct e1inp_sign_link *sign_link = oml_msg->dst; struct gsm_bts_trx *trx = sign_link->trx; uint8_t ts_nr = foh->obj_inst.ts_nr; if (!trx) { LOGP(DNM, LOGL_ERROR, "%s Channel OPSTART ACK for sign_link without trx\n", abis_nm_dump_foh(foh)); return NULL; } if (ts_nr >= ARRAY_SIZE(trx->ts)) { LOGP(DNM, LOGL_ERROR, "bts%u-trx%u %s Channel OPSTART ACK for non-existent TS\n", trx->bts->nr, trx->nr, abis_nm_dump_foh(foh)); return NULL; } return &trx->ts[ts_nr]; } static int abis_nm_rx_opstart_ack(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts_trx *trx = sign_link->trx; DEBUGPFOH(DNM, foh, "bts=%u trx=%u Opstart ACK\n", trx->bts->nr, trx->nr); osmo_signal_dispatch(SS_NM, S_NM_OPSTART_ACK, mb); return 0; } bool all_trx_rsl_connected_unlocked(const struct gsm_bts *bts) { const struct gsm_bts_trx *trx; if (bts->mo.nm_state.administrative == NM_STATE_LOCKED) return false; if (bts->gprs.mode != BTS_GPRS_NONE) { if (bts->gprs.cell.mo.nm_state.administrative == NM_STATE_LOCKED) return false; if (bts->gprs.nse.mo.nm_state.administrative == NM_STATE_LOCKED) return false; if (bts->gprs.nsvc[0].mo.nm_state.administrative == NM_STATE_LOCKED && bts->gprs.nsvc[1].mo.nm_state.administrative == NM_STATE_LOCKED) return false; } llist_for_each_entry(trx, &bts->trx_list, list) { if (!trx->rsl_link) return false; if (!trx_is_usable(trx)) return false; if (trx->mo.nm_state.administrative == NM_STATE_LOCKED) return false; } return true; } char *get_model_oml_status(const struct gsm_bts *bts) { if (bts->model->oml_status) return bts->model->oml_status(bts); return "unknown"; } void abis_nm_queue_send_next(struct gsm_bts *bts) { int wait = 0; struct msgb *msg; /* the queue is empty */ while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); wait = OBSC_NM_W_ACK_CB(msg); _abis_nm_sendmsg(msg); if (wait) break; } bts->abis_nm_pend = wait; } /* Receive a OML NM Message from BTS */ static int abis_nm_rcvmsg_fom(struct msgb *mb) { struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; uint8_t mt = foh->msg_type; /* sign_link might get deleted via osmo_signal_dispatch -> save bts */ struct gsm_bts *bts = sign_link->trx->bts; int ret = 0; /* check for unsolicited message */ if (is_report(mt)) return abis_nm_rcvmsg_report(mb, bts); if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) return abis_nm_rcvmsg_sw(mb); if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { struct nm_nack_signal_data nack_data; struct tlv_parsed tp; LOGPFOH(DNM, LOGL_NOTICE, foh, "%s NACK ", abis_nm_nack_name(mt)); abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) DEBUGPC(DNM, "CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else DEBUGPC(DNM, "\n"); nack_data.msg = mb; nack_data.mt = mt; nack_data.bts = bts; osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); abis_nm_queue_send_next(bts); return 0; } #if 0 /* check if last message is to be acked */ if (is_ack_nack(nmh->last_msgtype)) { if (mt == MT_ACK(nmh->last_msgtype)) { DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); /* we got our ACK, continue sending the next msg */ } else if (mt == MT_NACK(nmh->last_msgtype)) { /* we got a NACK, signal this to the caller */ DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); /* FIXME: somehow signal this to the caller */ } else { /* really strange things happen */ return -EINVAL; } } #endif switch (mt) { case NM_MT_CHG_ADM_STATE_ACK: ret = abis_nm_rx_chg_adm_state_ack(mb); break; case NM_MT_SW_ACT_REQ: ret = abis_nm_rx_sw_act_req(mb); break; case NM_MT_BS11_LMT_SESSION: ret = abis_nm_rx_lmt_event(mb); break; case NM_MT_OPSTART_ACK: abis_nm_rx_opstart_ack(mb); break; case NM_MT_SET_CHAN_ATTR_ACK: DEBUGPFOH(DNM, foh, "Set Channel Attributes ACK\n"); break; case NM_MT_SET_RADIO_ATTR_ACK: DEBUGPFOH(DNM, foh, "Set Radio Carrier Attributes ACK\n"); break; case NM_MT_CONN_MDROP_LINK_ACK: DEBUGPFOH(DNM, foh, "CONN MDROP LINK ACK\n"); break; case NM_MT_IPACC_RESTART_ACK: DEBUGPFOH(DNM, foh, "IPA Restart ACK\n"); osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); break; case NM_MT_IPACC_RESTART_NACK: LOGPFOH(DNM, LOGL_NOTICE, foh, "IPA Restart NACK\n"); osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); break; case NM_MT_SET_BTS_ATTR_ACK: DEBUGPFOH(DNM, foh, "Set BTS Attribute ACK\n"); break; case NM_MT_GET_ATTR_RESP: ret = abis_nm_rx_get_attr_resp(mb, gsm_bts_trx_num(bts, (foh)->obj_inst.trx_nr)); break; default: LOGPFOH(DNM, LOGL_ERROR, foh, "Unhandled message %s\n", get_value_string(abis_nm_msgtype_names, mt)); } abis_nm_queue_send_next(bts); return ret; } static int abis_nm_rx_ipacc(struct msgb *mb); static int abis_nm_rcvmsg_manuf(struct msgb *mb) { int rc; struct e1inp_sign_link *sign_link = mb->dst; int bts_type = sign_link->trx->bts->type; switch (bts_type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: rc = abis_nm_rx_ipacc(mb); abis_nm_queue_send_next(sign_link->trx->bts); break; default: LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " "BTS type (%u)\n", bts_type); rc = 0; break; } return rc; } /* High-Level API */ /* Entry-point where L2 OML from BTS enters the NM code */ int abis_nm_rcvmsg(struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { rc = -EINVAL; goto err; } } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); rc = -EINVAL; goto err; } #if 0 unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); if (oh->length + hlen > l2_len) { LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", oh->length + sizeof(*oh), l2_len); return -EINVAL; } if (oh->length + hlen < l2_len) LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); #endif msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: rc = abis_nm_rcvmsg_fom(msg); break; case ABIS_OM_MDISC_MANUF: rc = abis_nm_rcvmsg_manuf(msg); break; case ABIS_OM_MDISC_MMI: case ABIS_OM_MDISC_TRAU: LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", oh->mdisc); break; default: LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", oh->mdisc); rc = -EINVAL; break; } err: msgb_free(msg); return rc; } #if 0 /* initialized all resources */ struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) { struct abis_nm_h *nmh; nmh = malloc(sizeof(*nmh)); if (!nmh) return NULL; nmh->cfg = cfg; return nmh; } /* free all resources */ void abis_nm_fini(struct abis_nm_h *nmh) { free(nmh); } #endif /* Here we are trying to define a high-level API that can be used by * the actual BSC implementation. However, the architecture is currently * still under design. Ideally the calls to this API would be synchronous, * while the underlying stack behind the APi runs in a traditional select * based state machine. */ /* 6.2 Software Load: */ enum sw_state { SW_STATE_NONE, SW_STATE_WAIT_INITACK, SW_STATE_WAIT_SEGACK, SW_STATE_WAIT_ENDACK, SW_STATE_WAIT_ACTACK, SW_STATE_ERROR, }; struct abis_nm_sw { struct gsm_bts *bts; int trx_nr; gsm_cbfn *cbfn; void *cb_data; int forced; /* this will become part of the SW LOAD INITIATE */ uint8_t obj_class; uint8_t obj_instance[3]; uint8_t file_id[255]; uint8_t file_id_len; uint8_t file_version[255]; uint8_t file_version_len; uint8_t window_size; uint8_t seg_in_window; int fd; FILE *stream; enum sw_state state; int last_seg; }; static struct abis_nm_sw g_sw; static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) { if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { msgb_v_put(msg, NM_ATT_SW_DESCR); msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); } else { LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); } } /* 6.2.1 / 8.3.1: Load Data Initiate */ static int sw_load_init(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); sw_add_file_id_and_ver(sw, msg); msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); return abis_nm_sendmsg(sw->bts, msg); } static int is_last_line(FILE *stream) { char next_seg_buf[256]; long pos; /* check if we're sending the last line */ pos = ftell(stream); /* Did ftell fail? Then we are at the end for sure */ if (pos < 0) return 1; if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { int rc = fseek(stream, pos, SEEK_SET); if (rc < 0) return rc; return 1; } fseek(stream, pos, SEEK_SET); return 0; } /* 6.2.2 / 8.3.2 Load Data Segment */ static int sw_load_segment(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); char seg_buf[256]; char *line_buf = seg_buf+2; unsigned char *tlv; int len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { perror("fgets reading segment"); return -EINVAL; } seg_buf[0] = 0x00; /* check if we're sending the last line */ sw->last_seg = is_last_line(sw->stream); if (sw->last_seg) seg_buf[1] = 0; else seg_buf[1] = 1 + sw->seg_in_window++; len = strlen(line_buf) + 2; tlv = msgb_put(msg, TLV_GROSS_LEN(len)); tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); /* BS11 wants CR + LF in excess of the TLV length !?! */ tlv[1] -= 2; /* we only now know the exact length for the OM hdr */ len = strlen(line_buf)+2; break; case GSM_BTS_TYPE_NANOBTS: { osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); if (len < 0) { perror("read failed"); return -EINVAL; } if (len != IPACC_SEGMENT_SIZE) sw->last_seg = 1; ++sw->seg_in_window; msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); len += 3; break; } default: LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); /* FIXME: Other BTS types */ return -1; } fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); return abis_nm_sendmsg_direct(sw->bts, msg); } /* 6.2.4 / 8.3.4 Load Data End */ static int sw_load_end(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); sw_add_file_id_and_ver(sw, msg); return abis_nm_sendmsg(sw->bts, msg); } /* Activate the specified software into the BTS */ static int sw_activate(struct abis_nm_sw *sw) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, sw->obj_instance[0], sw->obj_instance[1], sw->obj_instance[2]); /* FIXME: this is BS11 specific format */ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, sw->file_version); return abis_nm_sendmsg(sw->bts, msg); } struct sdp_firmware { char magic[4]; char more_magic[4]; unsigned int header_length; unsigned int file_length; } __attribute__ ((packed)); static int parse_sdp_header(struct abis_nm_sw *sw) { struct sdp_firmware firmware_header; int rc; struct stat stat; rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); if (rc != sizeof(firmware_header)) { LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); return -1; } if (strncmp(firmware_header.magic, " SDP", 4) != 0) { LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); return -1; } if (firmware_header.more_magic[0] != 0x10 || firmware_header.more_magic[1] != 0x02 || firmware_header.more_magic[2] != 0x00 || firmware_header.more_magic[3] != 0x00) { LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); return -1; } if (fstat(sw->fd, &stat) == -1) { LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); return -1; } if (ntohl(firmware_header.file_length) != stat.st_size) { LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); return -1; } /* go back to the start as we checked the whole filesize.. */ lseek(sw->fd, 0l, SEEK_SET); LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood." " There might be checksums in the file that are not" " verified and incomplete firmware might be flashed." " There is absolutely no WARRANTY that flashing will" " work.\n"); return 0; } static int sw_open_file(struct abis_nm_sw *sw, const char *fname) { char file_id[12+1]; char file_version[80+1]; int rc; sw->fd = open(fname, O_RDONLY); if (sw->fd < 0) return sw->fd; switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: sw->stream = fdopen(sw->fd, "r"); if (!sw->stream) { perror("fdopen"); return -1; } /* read first line and parse file ID and VERSION */ rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", file_id, file_version); if (rc != 2) { perror("parsing header line of software file"); return -1; } strcpy((char *)sw->file_id, file_id); sw->file_id_len = strlen(file_id); strcpy((char *)sw->file_version, file_version); sw->file_version_len = strlen(file_version); /* rewind to start of file */ rewind(sw->stream); break; case GSM_BTS_TYPE_NANOBTS: /* TODO: extract that from the filename or content */ rc = parse_sdp_header(sw); if (rc < 0) { fprintf(stderr, "Could not parse the ipaccess SDP header\n"); return -1; } strcpy((char *)sw->file_id, "id"); sw->file_id_len = 3; strcpy((char *)sw->file_version, "version"); sw->file_version_len = 8; break; default: /* We don't know how to treat them yet */ close(sw->fd); return -EINVAL; } return 0; } static void sw_close_file(struct abis_nm_sw *sw) { switch (sw->bts->type) { case GSM_BTS_TYPE_BS11: fclose(sw->stream); break; default: close(sw->fd); break; } } /* Fill the window */ static int sw_fill_window(struct abis_nm_sw *sw) { int rc; while (sw->seg_in_window < sw->window_size) { rc = sw_load_segment(sw); if (rc < 0) return rc; if (sw->last_seg) break; } return 0; } /* callback function from abis_nm_rcvmsg() handler */ static int abis_nm_rcvmsg_sw(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; int rc = -1; struct abis_nm_sw *sw = &g_sw; enum sw_state old_state = sw->state; //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); switch (sw->state) { case SW_STATE_WAIT_INITACK: switch (foh->msg_type) { case NM_MT_LOAD_INIT_ACK: /* fill window with segments */ if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_ACK, mb, sw->cb_data, NULL); rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_INIT_NACK: if (sw->forced) { DEBUGPFOH(DNM, foh, "FORCED: Ignoring Software Load Init NACK\n"); if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_ACK, mb, sw->cb_data, NULL); rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; } else { LOGPFOH(DNM, LOGL_NOTICE, foh, "Software Load Init NACK\n"); /* FIXME: cause */ if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_INIT_NACK, mb, sw->cb_data, NULL); sw->state = SW_STATE_ERROR; } abis_nm_queue_send_next(sign_link->trx->bts); break; } break; case SW_STATE_WAIT_SEGACK: switch (foh->msg_type) { case NM_MT_LOAD_SEG_ACK: if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_SEG_ACK, mb, sw->cb_data, NULL); sw->seg_in_window = 0; if (!sw->last_seg) { /* fill window with more segments */ rc = sw_fill_window(sw); sw->state = SW_STATE_WAIT_SEGACK; } else { /* end the transfer */ sw->state = SW_STATE_WAIT_ENDACK; rc = sw_load_end(sw); } abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_ABORT: if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_ABORT, mb, sw->cb_data, NULL); break; } break; case SW_STATE_WAIT_ENDACK: switch (foh->msg_type) { case NM_MT_LOAD_END_ACK: sw_close_file(sw); DEBUGPFOH(DNM, foh, "Software Load End (BTS %u)\n", sw->bts->nr); sw->state = SW_STATE_NONE; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_ACK, mb, sw->cb_data, NULL); rc = 0; abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_LOAD_END_NACK: if (sw->forced) { DEBUGPFOH(DNM, foh, "FORCED: Ignoring Software Load End NACK\n"); sw->state = SW_STATE_NONE; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_ACK, mb, sw->cb_data, NULL); } else { LOGPFOH(DNM, LOGL_NOTICE, foh, "Software Load End NACK\n"); /* FIXME: cause */ sw->state = SW_STATE_ERROR; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_LOAD_END_NACK, mb, sw->cb_data, NULL); } abis_nm_queue_send_next(sign_link->trx->bts); break; } case SW_STATE_WAIT_ACTACK: switch (foh->msg_type) { case NM_MT_ACTIVATE_SW_ACK: /* we're done */ LOGPFOH(DNM, LOGL_INFO, foh, "Activate Software DONE!\n"); sw->state = SW_STATE_NONE; rc = 0; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_ACTIVATE_SW_ACK, mb, sw->cb_data, NULL); abis_nm_queue_send_next(sign_link->trx->bts); break; case NM_MT_ACTIVATE_SW_NACK: LOGPFOH(DNM, LOGL_ERROR, foh, "Activate Software NACK\n"); /* FIXME: cause */ sw->state = SW_STATE_ERROR; if (sw->cbfn) sw->cbfn(GSM_HOOK_NM_SWLOAD, NM_MT_ACTIVATE_SW_NACK, mb, sw->cb_data, NULL); abis_nm_queue_send_next(sign_link->trx->bts); break; } case SW_STATE_NONE: switch (foh->msg_type) { case NM_MT_ACTIVATE_SW_ACK: rc = 0; break; } break; case SW_STATE_ERROR: break; } if (rc) LOGPFOH(DNM, LOGL_ERROR, foh, "unexpected NM MT 0x%02x in state %u -> %u\n", foh->msg_type, old_state, sw->state); return rc; } /* Load the specified software into the BTS */ int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn, void *cb_data) { struct abis_nm_sw *sw = &g_sw; int rc; DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", bts->nr, fname); if (sw->state != SW_STATE_NONE) return -EBUSY; sw->bts = bts; sw->trx_nr = trx_nr; switch (bts->type) { case GSM_BTS_TYPE_BS11: sw->obj_class = NM_OC_SITE_MANAGER; sw->obj_instance[0] = 0xff; sw->obj_instance[1] = 0xff; sw->obj_instance[2] = 0xff; break; case GSM_BTS_TYPE_NANOBTS: sw->obj_class = NM_OC_BASEB_TRANSC; sw->obj_instance[0] = sw->bts->nr; sw->obj_instance[1] = sw->trx_nr; sw->obj_instance[2] = 0xff; break; case GSM_BTS_TYPE_UNKNOWN: default: LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); return -1; break; } sw->window_size = win_size; sw->state = SW_STATE_WAIT_INITACK; sw->cbfn = cbfn; sw->cb_data = cb_data; sw->forced = forced; rc = sw_open_file(sw, fname); if (rc < 0) { sw->state = SW_STATE_NONE; return rc; } return sw_load_init(sw); } int abis_nm_software_load_status(struct gsm_bts *bts) { struct abis_nm_sw *sw = &g_sw; struct stat st; int rc, percent; rc = fstat(sw->fd, &st); if (rc < 0) { perror("ERROR during stat"); return rc; } if (sw->stream) percent = (ftell(sw->stream) * 100) / st.st_size; else percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; return percent; } /* Activate the specified software into the BTS */ int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, gsm_cbfn *cbfn, void *cb_data) { struct abis_nm_sw *sw = &g_sw; int rc; DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", bts->nr, fname); if (sw->state != SW_STATE_NONE) return -EBUSY; sw->bts = bts; sw->obj_class = NM_OC_SITE_MANAGER; sw->obj_instance[0] = 0xff; sw->obj_instance[1] = 0xff; sw->obj_instance[2] = 0xff; sw->state = SW_STATE_WAIT_ACTACK; sw->cbfn = cbfn; sw->cb_data = cb_data; /* Open the file in order to fill some sw struct members */ rc = sw_open_file(sw, fname); if (rc < 0) { sw->state = SW_STATE_NONE; return rc; } sw_close_file(sw); return sw_activate(sw); } static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, uint8_t ts_nr, uint8_t subslot_nr) { ch->attrib = NM_ATT_ABIS_CHANNEL; ch->bts_port = bts_port; ch->timeslot = ts_nr; ch->subslot = subslot_nr; } int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei) { struct abis_om_hdr *oh; struct abis_nm_channel *ch; uint8_t len = sizeof(*ch) + 2; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, bts->bts_nr, trx_nr, 0xff); msgb_tv_put(msg, NM_ATT_TEI, tei); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } /* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) { struct gsm_bts *bts = trx->bts; struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } #if 0 int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, struct abis_nm_abis_channel *chan) { } #endif int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) { struct gsm_bts *bts = ts->trx->bts; struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", gsm_ts_name(ts), e1_port, e1_timeslot, e1_subslot); return abis_nm_sendmsg(bts, msg); } #if 0 int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, struct abis_nm_abis_channel *chan, uint8_t subchan) { } #endif /* 3GPP TS 52.021 § 8.11.1 */ int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, const uint8_t *attr, uint8_t attr_len) { struct abis_om_hdr *oh; struct msgb *msg; if (bts->type != GSM_BTS_TYPE_OSMOBTS) { LOGPC(DNM, LOGL_NOTICE, "Getting attributes from BTS%d type %s is not supported.\n", bts->nr, btstype2str(bts->type)); return -EINVAL; } DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, bts_nr, trx_nr, ts_nr); msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.6.1 */ int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.6.2 */ int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, trx->bts->bts_nr, trx->nr, 0xff); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_update_max_power_red(struct gsm_bts_trx *trx) { uint8_t attr[] = { NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2 }; return abis_nm_set_radio_attr(trx, attr, ARRAY_SIZE(attr)); } static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, const char **reason) { int i; *reason = "Reason unknown"; /* As it turns out, the BS-11 has some very peculiar restrictions * on the channel combinations it allows */ switch (ts->trx->bts->type) { case GSM_BTS_TYPE_BS11: switch (chan_comb) { case NM_CHANC_TCHHalf: case NM_CHANC_TCHHalf2: case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: /* not supported */ *reason = "TCH/H is not supported."; return -EINVAL; case NM_CHANC_SDCCH: /* only one SDCCH/8 per TRX */ for (i = 0; i < TRX_NR_TS; i++) { if (i == ts->nr) continue; if (ts->trx->ts[i].nm_chan_comb == NM_CHANC_SDCCH) { *reason = "Only one SDCCH/8 per TRX allowed."; return -EINVAL; } } /* not allowed for TS0 of BCCH-TRX */ if (ts->trx == ts->trx->bts->c0 && ts->nr == 0) { *reason = "SDCCH/8 must be on TS0."; return -EINVAL; } /* not on the same TRX that has a BCCH+SDCCH4 * combination */ if (ts->trx != ts->trx->bts->c0 && (ts->trx->ts[0].nm_chan_comb == 5 || ts->trx->ts[0].nm_chan_comb == 8)) { *reason = "SDCCH/8 and BCCH must be on the same TRX."; return -EINVAL; } break; case NM_CHANC_mainBCCH: case NM_CHANC_BCCHComb: /* allowed only for TS0 of C0 */ if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { *reason = "Main BCCH must be on TS0."; return -EINVAL; } break; case NM_CHANC_BCCH: /* allowed only for TS 2/4/6 of C0 */ if (ts->trx != ts->trx->bts->c0) { *reason = "BCCH must be on C0."; return -EINVAL; } if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { *reason = "BCCH must be on TS 2/4/6."; return -EINVAL; } break; case 8: /* this is not like 08.58, but in fact * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ /* FIXME: only one CBCH allowed per cell */ break; } break; case GSM_BTS_TYPE_NANOBTS: switch (ts->nr) { case 0: if (ts->trx->nr == 0) { /* only on TRX0 */ switch (chan_comb) { case NM_CHANC_BCCH: case NM_CHANC_mainBCCH: case NM_CHANC_BCCHComb: return 0; break; default: *reason = "TS0 of TRX0 must carry a BCCH."; return -EINVAL; } } else { switch (chan_comb) { case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; default: *reason = "TS0 must carry a TCH/F or TCH/H."; return -EINVAL; } } break; case 1: if (ts->trx->nr == 0) { switch (chan_comb) { case NM_CHANC_SDCCH_CBCH: if (ts->trx->ts[0].nm_chan_comb == NM_CHANC_mainBCCH) return 0; *reason = "TS0 must be the main BCCH for CBCH."; return -EINVAL; case NM_CHANC_SDCCH: case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: case NM_CHANC_IPAC_TCHFull_PDCH: case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: return 0; default: *reason = "TS1 must carry a CBCH, SDCCH or TCH."; return -EINVAL; } } else { switch (chan_comb) { case NM_CHANC_SDCCH: case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; default: *reason = "TS1 must carry a SDCCH or TCH."; return -EINVAL; } } break; case 2: case 3: case 4: case 5: case 6: case 7: switch (chan_comb) { case NM_CHANC_TCHFull: case NM_CHANC_TCHHalf: case NM_CHANC_IPAC_TCHFull_TCHHalf: return 0; case NM_CHANC_IPAC_PDCH: case NM_CHANC_IPAC_TCHFull_PDCH: case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: if (ts->trx->nr == 0) return 0; else { *reason = "PDCH must be on TRX0."; return -EINVAL; } } break; } *reason = "Unknown combination"; return -EINVAL; case GSM_BTS_TYPE_OSMOBTS: /* no known restrictions */ return 0; default: /* unknown BTS type */ return 0; } return 0; } /* Chapter 8.6.3 */ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) { struct gsm_bts *bts = ts->trx->bts; struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; uint8_t zero = 0x00; struct msgb *msg = nm_msgb_alloc(); uint8_t len = 2 + 2; const char *reason = NULL; if (bts->type == GSM_BTS_TYPE_BS11) len += 4 + 2 + 2 + 3; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); foh = fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); DEBUGPFOH(DNM, foh, "Set Chan Attr %s\n", gsm_ts_name(ts)); if (verify_chan_comb(ts, chan_comb, &reason) < 0) { msgb_free(msg); LOGPFOH(DNM, LOGL_ERROR, foh, "Invalid Channel Combination %d on %s. Reason: %s\n", chan_comb, gsm_ts_name(ts), reason); return -EINVAL; } ts->nm_chan_comb = chan_comb; msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); if (ts->hopping.enabled) { unsigned int i; uint8_t *len; msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); /* build the ARFCN list */ msgb_put_u8(msg, NM_ATT_ARFCN_LIST); len = msgb_put(msg, 1); *len = 0; for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { msgb_put_u16(msg, i); /* At least BS-11 wants a TLV16 here */ if (bts->type == GSM_BTS_TYPE_BS11) *len += 1; else *len += sizeof(uint16_t); } } } msgb_tv_put(msg, NM_ATT_TSC, gsm_ts_tsc(ts)); /* training sequence */ if (bts->type == GSM_BTS_TYPE_BS11) msgb_tlv_put(msg, 0x59, 1, &zero); DEBUGPFOH(DNM, foh, "%s(): sending %s\n", __func__, msgb_hexdump(msg)); return abis_nm_sendmsg(bts, msg); } int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, uint8_t i2, uint8_t i3, int nack, 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; uint8_t len = att_len; if (nack) { len += 2; msgtype = NM_MT_SW_ACT_REQ_NACK; } 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); } if (nack) msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); return abis_nm_sendmsg_direct(bts, msg); } int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *rawmsg) { struct msgb *msg = nm_msgb_alloc(); struct abis_om_hdr *oh; uint8_t *data; oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); fill_om_hdr(oh, len); data = msgb_put(msg, len); memcpy(data, rawmsg, len); return abis_nm_sendmsg(bts, msg); } /* Siemens specific commands */ static int __simple_cmd(struct gsm_bts *bts, uint8_t msg_type) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.9.2 */ int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2) { struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); foh = fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); DEBUGPFOH(DNM, foh, "Sending OPSTART\n"); return abis_nm_sendmsg(bts, msg); } /* Chapter 8.8.5 */ int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); return abis_nm_sendmsg(bts, msg); } int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, uint8_t e1_port1, uint8_t ts1) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *attr; DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", e1_port0, ts0, e1_port1, ts1); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); attr = msgb_put(msg, 3); attr[0] = NM_ATT_MDROP_LINK; attr[1] = e1_port0; attr[2] = ts0; attr = msgb_put(msg, 3); attr[0] = NM_ATT_MDROP_NEXT; attr[1] = e1_port1; attr[2] = ts1; return abis_nm_sendmsg(bts, msg); } /* Chapter 8.7.1 */ int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t test_nr, uint8_t auton_report, struct msgb *msg) { struct abis_om_hdr *oh; DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); if (!msg) msg = nm_msgb_alloc(); msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, obj_class, bts_nr, trx_nr, ts_nr); return abis_nm_sendmsg(bts, msg); } int abis_nm_event_reports(struct gsm_bts *bts, int on) { if (on == 0) return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); else return __simple_cmd(bts, NM_MT_REST_EVENT_REP); } /* Siemens (or BS-11) specific commands */ int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) { if (reconnect == 0) return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); else return __simple_cmd(bts, NM_MT_BS11_RECONNECT); } int abis_nm_bs11_restart(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_RESTART); } struct bs11_date_time { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t min; uint8_t sec; } __attribute__((packed)); void get_bs11_date_time(struct bs11_date_time *aet) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); aet->sec = tm->tm_sec; aet->min = tm->tm_min; aet->hour = tm->tm_hour; aet->day = tm->tm_mday; aet->month = tm->tm_mon; aet->year = htons(1900 + tm->tm_year); } int abis_nm_bs11_reset_resource(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); } int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) { if (begin) return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); else return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); } int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx, uint8_t attr_len, const uint8_t *attr) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t *cur; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11, type, 0, idx); cur = msgb_put(msg, attr_len); memcpy(cur, attr, attr_len); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_delete_object(struct gsm_bts *bts, enum abis_bs11_objtype type, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11, type, 0, idx); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t zero = 0x00; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_ENVABTSE, 0, idx, 0xff); msgb_tlv_put(msg, 0x99, 1, &zero); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, idx, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, idx, 0xff, 0xff); return abis_nm_sendmsg(bts, msg); } static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); return abis_nm_sendmsg(bts, msg); } /* like abis_nm_conn_terr_traf + set_tei */ int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei) { struct abis_om_hdr *oh; struct abis_nm_channel *ch; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); msgb_tv_put(msg, NM_ATT_TEI, tei); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr = NM_ATT_BS11_TXPWR; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); return abis_nm_sendmsg(trx->bts, msg); } int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_cclk(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, NM_ATT_BS11_CCLK_TYPE }; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); return abis_nm_sendmsg(bts, msg); } //static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) { return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); } int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) { return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); } int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time bdt; get_bs11_date_time(&bdt); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); if (on) { uint8_t len = 3*2 + sizeof(bdt) + 1 + strlen(name); fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, sizeof(bdt), (uint8_t *) &bdt); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, 1, &level); msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, strlen(name), (uint8_t *)name); } else { fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); } return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) { struct abis_om_hdr *oh; struct msgb *msg; if (strlen(password) != 10) return -EINVAL; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); return abis_nm_sendmsg(bts, msg); } /* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) { struct abis_om_hdr *oh; struct msgb *msg; uint8_t tlv_value; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); if (locked) tlv_value = BS11_LI_PLL_LOCKED; else tlv_value = BS11_LI_PLL_STANDALONE; msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); return abis_nm_sendmsg(bts, msg); } /* Set the calibration value of the PLL (work value/set value) * It depends on the login which one is changed */ int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) { struct abis_om_hdr *oh; struct msgb *msg; uint8_t tlv_value[2]; msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); tlv_value[0] = value>>8; tlv_value[1] = value&0xff; msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_state(struct gsm_bts *bts) { return __simple_cmd(bts, NM_MT_BS11_GET_STATE); } /* BS11 SWL */ void *tall_fle_ctx = NULL; struct abis_nm_bs11_sw { struct gsm_bts *bts; char swl_fname[PATH_MAX]; uint8_t win_size; int forced; struct llist_head file_list; gsm_cbfn *user_cb; /* specified by the user */ }; static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; struct file_list_entry { struct llist_head list; char fname[PATH_MAX]; }; struct file_list_entry *fl_dequeue(struct llist_head *queue) { struct llist_head *lh; if (llist_empty(queue)) return NULL; lh = queue->next; llist_del(lh); return llist_entry(lh, struct file_list_entry, list); } static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) { char linebuf[255]; struct llist_head *lh, *lh2; FILE *swl; int rc = 0; swl = fopen(bs11_sw->swl_fname, "r"); if (!swl) return -ENODEV; /* zero the stale file list, if any */ llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { llist_del(lh); talloc_free(lh); } while (fgets(linebuf, sizeof(linebuf), swl)) { char file_id[12+1]; char file_version[80+1]; struct file_list_entry *fle; static char dir[PATH_MAX]; if (strlen(linebuf) < 4) continue; rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); if (rc < 0) { perror("ERR parsing SWL file"); rc = -EINVAL; goto out; } if (rc < 2) continue; fle = talloc_zero(tall_fle_ctx, struct file_list_entry); if (!fle) { rc = -ENOMEM; goto out; } /* construct new filename */ osmo_strlcpy(dir, bs11_sw->swl_fname, sizeof(dir)); strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); strcat(fle->fname, "/"); strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); llist_add_tail(&fle->list, &bs11_sw->file_list); } out: fclose(swl); return rc; } /* bs11 swload specific callback, passed to abis_nm core swload */ static int bs11_swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) { struct abis_nm_bs11_sw *bs11_sw = data; struct file_list_entry *fle; int rc = 0; switch (event) { case NM_MT_LOAD_END_ACK: fle = fl_dequeue(&bs11_sw->file_list); if (fle) { /* start download the next file of our file list */ rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, bs11_sw->win_size, bs11_sw->forced, &bs11_swload_cbfn, bs11_sw); talloc_free(fle); } else { /* activate the SWL */ rc = abis_nm_software_activate(bs11_sw->bts, bs11_sw->swl_fname, bs11_swload_cbfn, bs11_sw); } break; case NM_MT_LOAD_SEG_ACK: case NM_MT_LOAD_END_NACK: case NM_MT_LOAD_INIT_ACK: case NM_MT_LOAD_INIT_NACK: case NM_MT_ACTIVATE_SW_NACK: case NM_MT_ACTIVATE_SW_ACK: default: /* fallthrough to the user callback */ if (bs11_sw->user_cb) rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); break; } return rc; } /* Siemens provides a SWL file that is a mere listing of all the other * files that are part of a software release. We need to upload first * the list file, and then each file that is listed in the list file */ int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, uint8_t win_size, int forced, gsm_cbfn *cbfn) { struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; struct file_list_entry *fle; int rc = 0; INIT_LLIST_HEAD(&bs11_sw->file_list); bs11_sw->bts = bts; bs11_sw->win_size = win_size; bs11_sw->user_cb = cbfn; bs11_sw->forced = forced; osmo_strlcpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); rc = bs11_read_swl_file(bs11_sw); if (rc < 0) return rc; /* dequeue next item in file list */ fle = fl_dequeue(&bs11_sw->file_list); if (!fle) return -EINVAL; /* start download the next file of our file list */ rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, bs11_swload_cbfn, bs11_sw); talloc_free(fle); return rc; } #if 0 static uint8_t req_attr_btse[] = { NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, NM_ATT_BS11_LMT_USER_NAME, 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, NM_ATT_BS11_SW_LOAD_STORED }; static uint8_t req_attr_btsm[] = { NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; #endif static uint8_t req_attr[] = { NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; int abis_nm_bs11_get_serno(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); /* SiemensHW CCTRL object */ fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, 0x03, 0x00, 0x00); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time aet; get_bs11_date_time(&aet); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); /* SiemensHW CCTRL object */ fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); uint8_t attr = NM_ATT_BS11_LINE_CFG; oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, NM_OC_BS11_BPORT, bport, 0xff, 0x02); msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); return abis_nm_sendmsg(bts, msg); } int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); struct bs11_date_time aet; get_bs11_date_time(&aet); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, bport, 0xff, 0x02); msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); return abis_nm_sendmsg(bts, msg); } /* ip.access nanoBTS specific commands */ static const char ipaccess_magic[] = "com.ipaccess"; static int abis_nm_rx_ipacc(struct msgb *msg) { struct in_addr addr; struct abis_om_hdr *oh = msgb_l2(msg); struct abis_om_fom_hdr *foh; uint8_t idstrlen = oh->data[0]; struct tlv_parsed tp; struct ipacc_ack_signal_data signal; struct e1inp_sign_link *sign_link = msg->dst; foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { LOGPFOH(DNM, LOGL_ERROR, foh, "id string is not com.ipaccess !?!\n"); return -EINVAL; } abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); DEBUGPFOH(DNM, foh, "IPACCESS(0x%02x): ", foh->msg_type); switch (foh->msg_type) { case NM_MT_IPACC_RSL_CONNECT_ACK: DEBUGPC(DNM, "RSL CONNECT ACK "); if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { memcpy(&addr, TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); } if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) DEBUGPC(DNM, "PORT=%u ", ntohs(*((uint16_t *) TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) DEBUGPC(DNM, "STREAM=0x%02x ", *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); DEBUGPC(DNM, "\n"); osmo_timer_del(&sign_link->trx->rsl_connect_timeout); break; case NM_MT_IPACC_RSL_CONNECT_NACK: LOGPFOH(DNM, LOGL_ERROR, foh, "RSL CONNECT NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); osmo_timer_del(&sign_link->trx->rsl_connect_timeout); break; case NM_MT_IPACC_SET_NVATTR_ACK: DEBUGPFOH(DNM, foh, "SET NVATTR ACK\n"); /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_SET_NVATTR_NACK: LOGPFOH(DNM, LOGL_ERROR, foh, "SET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_GET_NVATTR_ACK: DEBUGPFOH(DNM, foh, "GET NVATTR ACK\n"); /* FIXME: decode and show the actual attributes */ break; case NM_MT_IPACC_GET_NVATTR_NACK: LOGPFOH(DNM, LOGL_ERROR, foh, "GET NVATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; case NM_MT_IPACC_SET_ATTR_ACK: DEBUGPFOH(DNM, foh, "SET ATTR ACK\n"); break; case NM_MT_IPACC_SET_ATTR_NACK: LOGPFOH(DNM, LOGL_ERROR, foh, "SET ATTR NACK "); if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); else LOGPC(DNM, LOGL_ERROR, "\n"); break; default: DEBUGPC(DNM, "unknown\n"); break; } /* signal handling */ switch (foh->msg_type) { case NM_MT_IPACC_RSL_CONNECT_NACK: case NM_MT_IPACC_SET_NVATTR_NACK: case NM_MT_IPACC_GET_NVATTR_NACK: signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); break; case NM_MT_IPACC_SET_NVATTR_ACK: signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); signal.msg_type = foh->msg_type; osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); break; default: break; } return 0; } /* send an ip-access manufacturer specific message */ int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, int attr_len) { struct msgb *msg = nm_msgb_alloc(); struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; uint8_t *data; /* construct the 12.21 OM header, observe the erroneous length */ oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); fill_om_hdr(oh, sizeof(*foh) + attr_len); oh->mdisc = ABIS_OM_MDISC_MANUF; /* add the ip.access magic */ data = msgb_put(msg, sizeof(ipaccess_magic)+1); *data++ = sizeof(ipaccess_magic); memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); /* fill the 12.21 FOM header */ foh = (struct abis_om_fom_hdr *) msgb_put(msg, 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; if (attr && attr_len) { data = msgb_put(msg, attr_len); memcpy(data, attr, attr_len); } return abis_nm_sendmsg(bts, msg); } /* set some attributes in NVRAM */ int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) { return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, attr_len); } static void rsl_connect_timeout(void *data) { struct gsm_bts_trx *trx = data; struct ipacc_ack_signal_data signal; LOGP(DRSL, LOGL_NOTICE, "(bts=%d,trx=%d) RSL connection request timed out\n", trx->bts->nr, trx->nr); /* Fake an RSL CONECT NACK message from the BTS. */ signal.trx = trx; signal.msg_type = NM_MT_IPACC_RSL_CONNECT_NACK; osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); } int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, uint32_t ip, uint16_t port, uint8_t stream) { struct in_addr ia; uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, NM_ATT_IPACC_DST_IP_PORT, 0, 0, NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; int attr_len = sizeof(attr); int error; osmo_timer_setup(&trx->rsl_connect_timeout, rsl_connect_timeout, trx); ia.s_addr = htonl(ip); attr[1] = stream; attr[3] = port >> 8; attr[4] = port & 0xff; memcpy(attr + 6, &ia.s_addr, sizeof(uint32_t)); /* if ip == 0, we use the default IP */ if (ip == 0) attr_len -= 5; LOGP(DNM, LOGL_INFO, "IPA RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", inet_ntoa(ia), port, stream); error = abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, NM_OC_BASEB_TRANSC, trx->bts->bts_nr, trx->nr, 0xff, attr, attr_len); if (error == 0) osmo_timer_schedule(&trx->rsl_connect_timeout, 60, 0); return error; } /* restart / reboot an ip.access nanoBTS */ int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) { struct abis_om_hdr *oh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, trx->bts->nr, trx->nr, 0xff); return abis_nm_sendmsg_direct(trx->bts, msg); } int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, uint8_t *attr, uint8_t attr_len) { return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, obj_class, bts_nr, trx_nr, ts_nr, attr, attr_len); } void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) { struct gsm48_ra_id *_buf = (struct gsm48_ra_id*)buf; uint16_t ci = htons(bts->cell_identity); /* we simply reuse the GSM48 function and write the Cell ID over the position where the RAC * starts */ gsm48_ra_id_by_bts(_buf, bts); memcpy(&_buf->rac, &ci, sizeof(ci)); } void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason) { uint8_t new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; if (!trx->bts || !trx->bts->oml_link) { /* Set initial state which will be sent when BTS connects. */ trx->mo.nm_state.administrative = new_state; return; } LOGP(DNM, LOGL_NOTICE, "(bts=%d,trx=%d) Requesting administrative state change %s -> %s [%s]\n", trx->bts->nr, trx->nr, get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative), get_value_string(abis_nm_adm_state_names, new_state), reason); abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, trx->bts->bts_nr, trx->nr, 0xff, new_state); } static const struct value_string ipacc_testres_names[] = { { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, { 0, NULL } }; const char *ipacc_testres_name(uint8_t res) { return get_value_string(ipacc_testres_names, res); } void ipac_parse_cgi(struct osmo_cell_global_id *cid, const uint8_t *buf) { osmo_plmn_from_bcd(buf, &cid->lai.plmn); cid->lai.lac = ntohs(*((uint16_t *)&buf[3])); cid->cell_identity = ntohs(*((uint16_t *)&buf[5])); } /* parse BCCH information IEI from wire format to struct ipac_bcch_info */ int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) { uint8_t *cur = buf; uint16_t len __attribute__((unused)); memset(binf, 0, sizeof(*binf)); if (cur[0] != NM_IPAC_EIE_BCCH_INFO) return -EINVAL; cur++; len = ntohs(*(uint16_t *)cur); cur += 2; binf->info_type = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) binf->freq_qual = *cur >> 2; binf->arfcn = (*cur++ & 3) << 8; binf->arfcn |= *cur++; if (binf->info_type & IPAC_BINF_RXLEV) binf->rx_lev = *cur & 0x3f; cur++; if (binf->info_type & IPAC_BINF_RXQUAL) binf->rx_qual = *cur & 0x7; cur++; if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) binf->freq_err = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FRAME_OFFSET) binf->frame_offset = ntohs(*(uint16_t *)cur); cur += 2; if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) binf->frame_nr_offset = ntohl(*(uint32_t *)cur); cur += 4; #if 0 /* Somehow this is not set correctly */ if (binf->info_type & IPAC_BINF_BSIC) #endif binf->bsic = *cur & 0x3f; cur++; ipac_parse_cgi(&binf->cgi, cur); cur += 7; if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); cur += sizeof(binf->ba_list_si2); } if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { memcpy(binf->ba_list_si2bis, cur, sizeof(binf->ba_list_si2bis)); cur += sizeof(binf->ba_list_si2bis); } if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { memcpy(binf->ba_list_si2ter, cur, sizeof(binf->ba_list_si2ter)); cur += sizeof(binf->ba_list_si2ter); } return 0; } void abis_nm_clear_queue(struct gsm_bts *bts) { struct msgb *msg; while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); msgb_free(msg); } bts->abis_nm_pend = 0; } osmo-bsc-1.3.0/src/osmo-bsc/abis_nm_ipaccess.c000066400000000000000000000050551332665256100212120ustar00rootroot00000000000000/* GSM Network Management (OML) messages on the A-bis interface * Extensions for the ip.access A-bis over IP protocol*/ /* (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 /* A list of all the 'embedded' attributes of ip.access */ enum ipa_embedded_att { IPA_ATT_ARFCN_WHITELIST = 0x01, IPA_ATT_ARFCN_BLACKLIST = 0x02, IPA_ATT_FREQ_ERR_LIST = 0x03, IPA_ATT_CHAN_USAGE_LIST = 0x04, IPA_ATT_BCCH_INF_TYPE = 0x05, IPA_ATT_BCCH_INF = 0x06, IPA_ATT_CONFIG = 0x07, IPA_ATT_RESULT_DETAILS = 0x08, IPA_ATT_RXLEV_THRESH = 0x09, IPA_ATT_FREQ_SYNC_OPT = 0x0a, IPA_ATT_MAC_ADDR = 0x0b, IPA_ATT_HW_SW_COMPAT_NR = 0x0c, IPA_ATT_MANUF_SER_NR = 0x0d, IPA_ATT_OEM_ID = 0x0e, IPA_ATT_DATETIME_MANUF = 0x0f, IPA_ATT_DATETIME_CALIB = 0x10, IPA_ATT_BEACON_INF = 0x11, IPA_ATT_FREQ_ERR = 0x12, IPA_ATT_SNMP_COMM_STRING = 0x13, IPA_ATT_SNMP_TRAP_ADDR = 0x14, IPA_ATT_SNMP_TRAP_PORT = 0x15, IPA_ATT_SNMP_MAN_ADDR = 0x16, IPA_ATT_SNMP_SYS_CONTACT = 0x17, IPA_ATT_FACTORY_ID = 0x18, IPA_ATT_FACTORY_SERIAL = 0x19, IPA_ATT_LOGGED_EVT_IND = 0x1a, IPA_ATT_LOCAL_ADD_TEXT = 0x1b, IPA_ATT_FREQ_BANDS = 0x1c, IPA_ATT_MAX_TA = 0x1d, IPA_ATT_CIPH_ALG = 0x1e, IPA_ATT_CHAN_TYPES = 0x1f, IPA_ATT_CHAN_MODES = 0x20, IPA_ATT_GPRS_CODING_SCHEMES = 0x21, IPA_ATT_RTP_FEATURES = 0x22, IPA_ATT_RSL_FEATURES = 0x23, IPA_ATT_BTS_HW_CLASS = 0x24, IPA_ATT_BTS_ID = 0x25, IPA_ATT_BCAST_L2_MSG = 0x26, }; /* append an ip.access channel list to the given msgb */ static int ipa_chan_list_append(struct msgb *msg, uint8_t ie, uint16_t *arfcns, int arfcn_count) { int i; uint8_t *u8; uint16_t *u16; /* tag */ u8 = msgb_push(msg, 1); *u8 = ie; /* length in octets */ u16 = msgb_push(msg, 2); *u16 = htons(arfcn_count * 2); for (i = 0; i < arfcn_count; i++) { u16 = msgb_push(msg, 2); *u16 = htons(arfcns[i]); } return 0; } osmo-bsc-1.3.0/src/osmo-bsc/abis_nm_vty.c000066400000000000000000000122051332665256100202350ustar00rootroot00000000000000/* VTY interface for A-bis OML (Netowrk Management) */ /* (C) 2009-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct cmd_node oml_node = { OML_NODE, "%s(oml)# ", 1, }; struct oml_node_state { struct gsm_bts *bts; uint8_t obj_class; uint8_t obj_inst[3]; }; static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } /* FIXME: auto-generate those strings from the value_string lists */ #define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)" #define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \ "BTS Object\n" \ "Radio Carrier Object\n" \ "Baseband Transceiver Object\n" \ "Channel (Timeslot) Object\n" \ "Adjacent Object (Siemens)\n" \ "Handover Object (Siemens)\n" \ "Power Control Object (Siemens)\n" \ "BTSE Object (Siemens)\n" \ "Rack Object (Siemens)\n" \ "Test Object (Siemens)\n" \ "ENVABTSE Object (Siemens)\n" \ "BPORT Object (Siemens)\n" \ "GPRS NSE Object (ip.access/osmo-bts)\n" \ "GPRS Cell Object (ip.acecss/osmo-bts)\n" \ "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \ "SIEMENSHW Object (Siemens)\n" DEFUN(oml_class_inst, oml_class_inst_cmd, "bts <0-255> oml class " NM_OBJCLASS_VTY " instance <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" NM_OBJCLASS_VTY_HELP "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); oms->obj_inst[0] = atoi(argv[2]); oms->obj_inst[1] = atoi(argv[3]); oms->obj_inst[2] = atoi(argv[4]); vty->index = oms; vty->node = OML_NODE; return CMD_SUCCESS; } DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" "Object Class\n" "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->obj_class = atoi(argv[1]); oms->obj_inst[0] = atoi(argv[2]); oms->obj_inst[1] = atoi(argv[3]); oms->obj_inst[2] = atoi(argv[4]); vty->index = oms; vty->node = OML_NODE; return CMD_SUCCESS; } DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, "change-adm-state (locked|unlocked|shutdown|null)", "Change the Administrative State\n" "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") { struct oml_node_state *oms = vty->index; enum abis_nm_adm_state state; state = get_string_value(abis_nm_adm_state_names, argv[0]); abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], oms->obj_inst[1], oms->obj_inst[2], state); return CMD_SUCCESS; } DEFUN(oml_opstart, oml_opstart_cmd, "opstart", "Send an OPSTART message to the object") { struct oml_node_state *oms = vty->index; abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], oms->obj_inst[1], oms->obj_inst[2]); return CMD_SUCCESS; } int abis_nm_vty_init(void) { install_element(ENABLE_NODE, &oml_class_inst_cmd); install_element(ENABLE_NODE, &oml_classnum_inst_cmd); install_node(&oml_node, dummy_config_write); install_element(OML_NODE, &oml_chg_adm_state_cmd); install_element(OML_NODE, &oml_opstart_cmd); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/abis_om2000.c000066400000000000000000002332171332665256100176460ustar00rootroot00000000000000/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface * implemented based on protocol trace analysis, no formal documentation */ /* (C) 2010-2011,2016 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FIXME: move to libosmocore */ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, struct osmo_fsm_inst *parent, uint32_t parent_term_event, const char *id) { struct osmo_fsm_inst *fi; fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, id ? id : parent->id); if (!fi) { /* indicate immediate termination to caller */ osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); return NULL; } LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); fi->proc.parent = parent; fi->proc.parent_term_event = parent_term_event; llist_add(&fi->proc.child, &parent->proc.children); return fi; } #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 #define OM2K_TIMEOUT 10 #define TRX_FSM_TIMEOUT 60 #define BTS_FSM_TIMEOUT 60 /* use following functions from abis_nm.c: * om2k_msgb_alloc() * abis_om2k_sendmsg() */ struct abis_om2k_hdr { struct abis_om_hdr om; uint16_t msg_type; struct abis_om2k_mo mo; uint8_t data[0]; } __attribute__ ((packed)); enum abis_om2k_msgtype { OM2K_MSGT_ABORT_SP_CMD = 0x0000, OM2K_MSGT_ABORT_SP_COMPL = 0x0002, OM2K_MSGT_ALARM_REP_ACK = 0x0004, OM2K_MSGT_ALARM_REP_NACK = 0x0005, OM2K_MSGT_ALARM_REP = 0x0006, OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, OM2K_MSGT_ALARM_STATUS_RES = 0x000e, OM2K_MSGT_CAL_TIME_RESP = 0x0010, OM2K_MSGT_CAL_TIME_REJ = 0x0011, OM2K_MSGT_CAL_TIME_REQ = 0x0012, OM2K_MSGT_CON_CONF_REQ = 0x0014, OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, OM2K_MSGT_CON_CONF_RES = 0x001a, OM2K_MSGT_CONNECT_CMD = 0x001c, OM2K_MSGT_CONNECT_COMPL = 0x001e, OM2K_MSGT_CONNECT_REJ = 0x001f, OM2K_MSGT_DISABLE_REQ = 0x0028, OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, OM2K_MSGT_DISABLE_RES_ACK = 0x002c, OM2K_MSGT_DISABLE_RES_NACK = 0x002d, OM2K_MSGT_DISABLE_RES = 0x002e, OM2K_MSGT_DISCONNECT_CMD = 0x0030, OM2K_MSGT_DISCONNECT_COMPL = 0x0032, OM2K_MSGT_DISCONNECT_REJ = 0x0033, OM2K_MSGT_ENABLE_REQ = 0x0034, OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, OM2K_MSGT_ENABLE_RES_ACK = 0x0038, OM2K_MSGT_ENABLE_RES_NACK = 0x0039, OM2K_MSGT_ENABLE_RES = 0x003a, OM2K_MSGT_FAULT_REP_ACK = 0x0040, OM2K_MSGT_FAULT_REP_NACK = 0x0041, OM2K_MSGT_FAULT_REP = 0x0042, OM2K_MSGT_IS_CONF_REQ = 0x0060, OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, OM2K_MSGT_IS_CONF_RES = 0x0066, OM2K_MSGT_OP_INFO = 0x0074, OM2K_MSGT_OP_INFO_ACK = 0x0076, OM2K_MSGT_OP_INFO_REJ = 0x0077, OM2K_MSGT_RESET_CMD = 0x0078, OM2K_MSGT_RESET_COMPL = 0x007a, OM2K_MSGT_RESET_REJ = 0x007b, OM2K_MSGT_RX_CONF_REQ = 0x007c, OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, OM2K_MSGT_RX_CONF_RES = 0x0082, OM2K_MSGT_START_REQ = 0x0084, OM2K_MSGT_START_REQ_ACK = 0x0086, OM2K_MSGT_START_REQ_REJ = 0x0087, OM2K_MSGT_START_RES_ACK = 0x0088, OM2K_MSGT_START_RES_NACK = 0x0089, OM2K_MSGT_START_RES = 0x008a, OM2K_MSGT_STATUS_REQ = 0x008c, OM2K_MSGT_STATUS_RESP = 0x008e, OM2K_MSGT_STATUS_REJ = 0x008f, OM2K_MSGT_TEST_REQ = 0x0094, OM2K_MSGT_TEST_REQ_ACK = 0x0096, OM2K_MSGT_TEST_REQ_REJ = 0x0097, OM2K_MSGT_TEST_RES_ACK = 0x0098, OM2K_MSGT_TEST_RES_NACK = 0x0099, OM2K_MSGT_TEST_RES = 0x009a, OM2K_MSGT_TF_CONF_REQ = 0x00a0, OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, OM2K_MSGT_TF_CONF_RES = 0x00a6, OM2K_MSGT_TS_CONF_REQ = 0x00a8, OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, OM2K_MSGT_TS_CONF_RES = 0x00ae, OM2K_MSGT_TX_CONF_REQ = 0x00b0, OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, OM2K_MSGT_TX_CONF_RES = 0x00b6, OM2K_MSGT_CAPA_REQ = 0x00e8, OM2K_MSGT_CAPA_REQ_ACK = 0x00ea, OM2K_MSGT_CAPA_REQ_REJ = 0x00eb, OM2K_MSGT_CAPA_RES = 0x00ee, OM2K_MSGT_CAPA_RES_ACK = 0x00ec, OM2K_MSGT_CAPA_RES_NACK = 0x00ed, OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, OM2K_MSGT_NEGOT_REQ = 0x0106, }; enum abis_om2k_dei { OM2K_DEI_ACCORDANCE_IND = 0x00, OM2K_DEI_BCC = 0x06, OM2K_DEI_BS_AG_BKS_RES = 0x07, OM2K_DEI_BSIC = 0x09, OM2K_DEI_BA_PA_MFRMS = 0x0a, OM2K_DEI_CBCH_INDICATOR = 0x0b, OM2K_DEI_CCCH_OPTIONS = 0x0c, OM2K_DEI_CAL_TIME = 0x0d, OM2K_DEI_COMBINATION = 0x0f, OM2K_DEI_CON_CONN_LIST = 0x10, OM2K_DEI_DRX_DEV_MAX = 0x12, OM2K_DEI_END_LIST_NR = 0x13, OM2K_DEI_EXT_COND_MAP_1 = 0x14, OM2K_DEI_EXT_COND_MAP_2 = 0x15, OM2K_DEI_FILLING_MARKER = 0x1c, OM2K_DEI_FN_OFFSET = 0x1d, OM2K_DEI_FREQ_LIST = 0x1e, OM2K_DEI_FREQ_SPEC_RX = 0x1f, OM2K_DEI_FREQ_SPEC_TX = 0x20, OM2K_DEI_HSN = 0x21, OM2K_DEI_ICM_INDICATOR = 0x22, OM2K_DEI_INT_FAULT_MAP_1A = 0x23, OM2K_DEI_INT_FAULT_MAP_1B = 0x24, OM2K_DEI_INT_FAULT_MAP_2A = 0x25, OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, OM2K_DEI_IS_CONN_LIST = 0x27, OM2K_DEI_LIST_NR = 0x28, OM2K_DEI_LOCAL_ACCESS = 0x2a, OM2K_DEI_MAIO = 0x2b, OM2K_DEI_MO_STATE = 0x2c, OM2K_DEI_NY1 = 0x2d, OM2K_DEI_OP_INFO = 0x2e, OM2K_DEI_POWER = 0x2f, OM2K_DEI_REASON_CODE = 0x32, OM2K_DEI_RX_DIVERSITY = 0x33, OM2K_DEI_REPL_UNIT_MAP = 0x34, OM2K_DEI_RESULT_CODE = 0x35, OM2K_DEI_T3105 = 0x38, OM2K_DEI_TF_MODE = 0x3a, OM2K_DEI_TS_NR = 0x3c, OM2K_DEI_TSC = 0x3d, OM2K_DEI_BTS_VERSION = 0x40, OM2K_DEI_OML_IWD_VERSION = 0x41, OM2K_DEI_RSL_IWD_VERSION = 0x42, OM2K_DEI_OML_FUNC_MAP_1 = 0x43, OM2K_DEI_OML_FUNC_MAP_2 = 0x44, OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, OM2K_DEI_EXT_RANGE = 0x47, OM2K_DEI_REQ_IND = 0x48, OM2K_DEI_REPL_UNIT_MAP_EXT = 0x50, OM2K_DEI_ICM_BOUND_PARAMS = 0x74, OM2K_DEI_LSC = 0x79, OM2K_DEI_LSC_FILT_TIME = 0x7a, OM2K_DEI_CALL_SUPV_TIME = 0x7b, OM2K_DEI_ICM_CHAN_RATE = 0x7e, OM2K_DEI_HW_INFO_SIG = 0x84, OM2K_DEI_TF_SYNC_SRC = 0x86, OM2K_DEI_TTA = 0x87, OM2K_DEI_CAPA_SIG = 0x8a, OM2K_DEI_NEGOT_REC1 = 0x90, OM2K_DEI_NEGOT_REC2 = 0x91, OM2K_DEI_ENCR_ALG = 0x92, OM2K_DEI_INTERF_REJ_COMB = 0x94, OM2K_DEI_FS_OFFSET = 0x98, OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, OM2K_DEI_TSS_MO_STATE = 0x9d, }; const struct tlv_definition om2k_att_tlvdef = { .def = { [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, [OM2K_DEI_BCC] = { TLV_TYPE_TV }, [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_HSN] = { TLV_TYPE_TV }, [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, [OM2K_DEI_NY1] = { TLV_TYPE_TV }, [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, [OM2K_DEI_POWER] = { TLV_TYPE_TV }, [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, [OM2K_DEI_T3105] = { TLV_TYPE_TV }, [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, [OM2K_DEI_TSC] = { TLV_TYPE_TV }, [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, [OM2K_DEI_REPL_UNIT_MAP_EXT] = {TLV_TYPE_FIXED, 6}, [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, [OM2K_DEI_LSC] = { TLV_TYPE_TV }, [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, [OM2K_DEI_TTA] = { TLV_TYPE_TV }, [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, [OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 }, }, }; static const struct value_string om2k_msgcode_vals[] = { { 0x0000, "Abort SP Command" }, { 0x0002, "Abort SP Complete" }, { 0x0004, "Alarm Report ACK" }, { 0x0005, "Alarm Report NACK" }, { 0x0006, "Alarm Report" }, { 0x0008, "Alarm Status Request" }, { 0x000a, "Alarm Status Request Accept" }, { 0x000b, "Alarm Status Request Reject" }, { 0x000c, "Alarm Status Result ACK" }, { 0x000d, "Alarm Status Result NACK" }, { 0x000e, "Alarm Status Result" }, { 0x0010, "Calendar Time Response" }, { 0x0011, "Calendar Time Reject" }, { 0x0012, "Calendar Time Request" }, { 0x0014, "CON Configuration Request" }, { 0x0016, "CON Configuration Request Accept" }, { 0x0017, "CON Configuration Request Reject" }, { 0x0018, "CON Configuration Result ACK" }, { 0x0019, "CON Configuration Result NACK" }, { 0x001a, "CON Configuration Result" }, { 0x001c, "Connect Command" }, { 0x001e, "Connect Complete" }, { 0x001f, "Connect Reject" }, { 0x0028, "Disable Request" }, { 0x002a, "Disable Request Accept" }, { 0x002b, "Disable Request Reject" }, { 0x002c, "Disable Result ACK" }, { 0x002d, "Disable Result NACK" }, { 0x002e, "Disable Result" }, { 0x0030, "Disconnect Command" }, { 0x0032, "Disconnect Complete" }, { 0x0033, "Disconnect Reject" }, { 0x0034, "Enable Request" }, { 0x0036, "Enable Request Accept" }, { 0x0037, "Enable Request Reject" }, { 0x0038, "Enable Result ACK" }, { 0x0039, "Enable Result NACK" }, { 0x003a, "Enable Result" }, { 0x003c, "Escape Downlink Normal" }, { 0x003d, "Escape Downlink NACK" }, { 0x003e, "Escape Uplink Normal" }, { 0x003f, "Escape Uplink NACK" }, { 0x0040, "Fault Report ACK" }, { 0x0041, "Fault Report NACK" }, { 0x0042, "Fault Report" }, { 0x0044, "File Package End Command" }, { 0x0046, "File Package End Result" }, { 0x0047, "File Package End Reject" }, { 0x0048, "File Relation Request" }, { 0x004a, "File Relation Response" }, { 0x004b, "File Relation Request Reject" }, { 0x004c, "File Segment Transfer" }, { 0x004e, "File Segment Transfer Complete" }, { 0x004f, "File Segment Transfer Reject" }, { 0x0050, "HW Information Request" }, { 0x0052, "HW Information Request Accept" }, { 0x0053, "HW Information Request Reject" }, { 0x0054, "HW Information Result ACK" }, { 0x0055, "HW Information Result NACK" }, { 0x0056, "HW Information Result" }, { 0x0060, "IS Configuration Request" }, { 0x0062, "IS Configuration Request Accept" }, { 0x0063, "IS Configuration Request Reject" }, { 0x0064, "IS Configuration Result ACK" }, { 0x0065, "IS Configuration Result NACK" }, { 0x0066, "IS Configuration Result" }, { 0x0068, "Load Data End" }, { 0x006a, "Load Data End Result" }, { 0x006b, "Load Data End Reject" }, { 0x006c, "Load Data Init" }, { 0x006e, "Load Data Init Accept" }, { 0x006f, "Load Data Init Reject" }, { 0x0070, "Loop Control Command" }, { 0x0072, "Loop Control Complete" }, { 0x0073, "Loop Control Reject" }, { 0x0074, "Operational Information" }, { 0x0076, "Operational Information Accept" }, { 0x0077, "Operational Information Reject" }, { 0x0078, "Reset Command" }, { 0x007a, "Reset Complete" }, { 0x007b, "Reset Reject" }, { 0x007c, "RX Configuration Request" }, { 0x007e, "RX Configuration Request Accept" }, { 0x007f, "RX Configuration Request Reject" }, { 0x0080, "RX Configuration Result ACK" }, { 0x0081, "RX Configuration Result NACK" }, { 0x0082, "RX Configuration Result" }, { 0x0084, "Start Request" }, { 0x0086, "Start Request Accept" }, { 0x0087, "Start Request Reject" }, { 0x0088, "Start Result ACK" }, { 0x0089, "Start Result NACK" }, { 0x008a, "Start Result" }, { 0x008c, "Status Request" }, { 0x008e, "Status Response" }, { 0x008f, "Status Reject" }, { 0x0094, "Test Request" }, { 0x0096, "Test Request Accept" }, { 0x0097, "Test Request Reject" }, { 0x0098, "Test Result ACK" }, { 0x0099, "Test Result NACK" }, { 0x009a, "Test Result" }, { 0x00a0, "TF Configuration Request" }, { 0x00a2, "TF Configuration Request Accept" }, { 0x00a3, "TF Configuration Request Reject" }, { 0x00a4, "TF Configuration Result ACK" }, { 0x00a5, "TF Configuration Result NACK" }, { 0x00a6, "TF Configuration Result" }, { 0x00a8, "TS Configuration Request" }, { 0x00aa, "TS Configuration Request Accept" }, { 0x00ab, "TS Configuration Request Reject" }, { 0x00ac, "TS Configuration Result ACK" }, { 0x00ad, "TS Configuration Result NACK" }, { 0x00ae, "TS Configuration Result" }, { 0x00b0, "TX Configuration Request" }, { 0x00b2, "TX Configuration Request Accept" }, { 0x00b3, "TX Configuration Request Reject" }, { 0x00b4, "TX Configuration Result ACK" }, { 0x00b5, "TX Configuration Result NACK" }, { 0x00b6, "TX Configuration Result" }, { 0x00bc, "DIP Alarm Report ACK" }, { 0x00bd, "DIP Alarm Report NACK" }, { 0x00be, "DIP Alarm Report" }, { 0x00c0, "DIP Alarm Status Request" }, { 0x00c2, "DIP Alarm Status Response" }, { 0x00c3, "DIP Alarm Status Reject" }, { 0x00c4, "DIP Quality Report I ACK" }, { 0x00c5, "DIP Quality Report I NACK" }, { 0x00c6, "DIP Quality Report I" }, { 0x00c8, "DIP Quality Report II ACK" }, { 0x00c9, "DIP Quality Report II NACK" }, { 0x00ca, "DIP Quality Report II" }, { 0x00dc, "DP Configuration Request" }, { 0x00de, "DP Configuration Request Accept" }, { 0x00df, "DP Configuration Request Reject" }, { 0x00e0, "DP Configuration Result ACK" }, { 0x00e1, "DP Configuration Result NACK" }, { 0x00e2, "DP Configuration Result" }, { 0x00e4, "Capabilities HW Info Report ACK" }, { 0x00e5, "Capabilities HW Info Report NACK" }, { 0x00e6, "Capabilities HW Info Report" }, { 0x00e8, "Capabilities Request" }, { 0x00ea, "Capabilities Request Accept" }, { 0x00eb, "Capabilities Request Reject" }, { 0x00ec, "Capabilities Result ACK" }, { 0x00ed, "Capabilities Result NACK" }, { 0x00ee, "Capabilities Result" }, { 0x00f0, "FM Configuration Request" }, { 0x00f2, "FM Configuration Request Accept" }, { 0x00f3, "FM Configuration Request Reject" }, { 0x00f4, "FM Configuration Result ACK" }, { 0x00f5, "FM Configuration Result NACK" }, { 0x00f6, "FM Configuration Result" }, { 0x00f8, "FM Report Request" }, { 0x00fa, "FM Report Response" }, { 0x00fb, "FM Report Reject" }, { 0x00fc, "FM Start Command" }, { 0x00fe, "FM Start Complete" }, { 0x00ff, "FM Start Reject" }, { 0x0100, "FM Stop Command" }, { 0x0102, "FM Stop Complete" }, { 0x0103, "FM Stop Reject" }, { 0x0104, "Negotiation Request ACK" }, { 0x0105, "Negotiation Request NACK" }, { 0x0106, "Negotiation Request" }, { 0x0108, "BTS Initiated Request ACK" }, { 0x0109, "BTS Initiated Request NACK" }, { 0x010a, "BTS Initiated Request" }, { 0x010c, "Radio Channels Release Command" }, { 0x010e, "Radio Channels Release Complete" }, { 0x010f, "Radio Channels Release Reject" }, { 0x0118, "Feature Control Command" }, { 0x011a, "Feature Control Complete" }, { 0x011b, "Feature Control Reject" }, { 0, NULL } }; /* TS 12.21 Section 9.4: Attributes */ static const struct value_string om2k_attr_vals[] = { { 0x00, "Accordance indication" }, { 0x01, "Alarm Id" }, { 0x02, "Alarm Data" }, { 0x03, "Alarm Severity" }, { 0x04, "Alarm Status" }, { 0x05, "Alarm Status Type" }, { 0x06, "BCC" }, { 0x07, "BS_AG_BKS_RES" }, { 0x09, "BSIC" }, { 0x0a, "BA_PA_MFRMS" }, { 0x0b, "CBCH Indicator" }, { 0x0c, "CCCH Options" }, { 0x0d, "Calendar Time" }, { 0x0f, "Channel Combination" }, { 0x10, "CON Connection List" }, { 0x11, "Data End Indication" }, { 0x12, "DRX_DEV_MAX" }, { 0x13, "End List Number" }, { 0x14, "External Condition Map Class 1" }, { 0x15, "External Condition Map Class 2" }, { 0x16, "File Relation Indication" }, { 0x17, "File Revision" }, { 0x18, "File Segment Data" }, { 0x19, "File Segment Length" }, { 0x1a, "File Segment Sequence Number" }, { 0x1b, "File Size" }, { 0x1c, "Filling Marker" }, { 0x1d, "FN Offset" }, { 0x1e, "Frequency List" }, { 0x1f, "Frequency Specifier RX" }, { 0x20, "Frequency Specifier TX" }, { 0x21, "HSN" }, { 0x22, "ICM Indicator" }, { 0x23, "Internal Fault Map Class 1A" }, { 0x24, "Internal Fault Map Class 1B" }, { 0x25, "Internal Fault Map Class 2A" }, { 0x26, "Internal Fault Map Class 2A Extension" }, { 0x27, "IS Connection List" }, { 0x28, "List Number" }, { 0x29, "File Package State Indication" }, { 0x2a, "Local Access State" }, { 0x2b, "MAIO" }, { 0x2c, "MO State" }, { 0x2d, "Ny1" }, { 0x2e, "Operational Information" }, { 0x2f, "Power" }, { 0x30, "RU Position Data" }, { 0x31, "Protocol Error" }, { 0x32, "Reason Code" }, { 0x33, "Receiver Diversity" }, { 0x34, "Replacement Unit Map" }, { 0x35, "Result Code" }, { 0x36, "RU Revision Data" }, { 0x38, "T3105" }, { 0x39, "Test Loop Setting" }, { 0x3a, "TF Mode" }, { 0x3b, "TF Compensation Value" }, { 0x3c, "Time Slot Number" }, { 0x3d, "TSC" }, { 0x3e, "RU Logical Id" }, { 0x3f, "RU Serial Number Data" }, { 0x40, "BTS Version" }, { 0x41, "OML IWD Version" }, { 0x42, "RWL IWD Version" }, { 0x43, "OML Function Map 1" }, { 0x44, "OML Function Map 2" }, { 0x45, "RSL Function Map 1" }, { 0x46, "RSL Function Map 2" }, { 0x47, "Extended Range Indicator" }, { 0x48, "Request Indicators" }, { 0x49, "DIP Alarm Condition Map" }, { 0x4a, "ES Incoming" }, { 0x4b, "ES Outgoing" }, { 0x4e, "SES Incoming" }, { 0x4f, "SES Outgoing" }, { 0x50, "Replacement Unit Map Extension" }, { 0x52, "UAS Incoming" }, { 0x53, "UAS Outgoing" }, { 0x58, "DF Incoming" }, { 0x5a, "DF Outgoing" }, { 0x5c, "SF" }, { 0x60, "S Bits Setting" }, { 0x61, "CRC-4 Use Option" }, { 0x62, "T Parameter" }, { 0x63, "N Parameter" }, { 0x64, "N1 Parameter" }, { 0x65, "N3 Parameter" }, { 0x66, "N4 Parameter" }, { 0x67, "P Parameter" }, { 0x68, "Q Parameter" }, { 0x69, "BI_Q1" }, { 0x6a, "BI_Q2" }, { 0x74, "ICM Boundary Parameters" }, { 0x77, "AFT" }, { 0x78, "AFT RAI" }, { 0x79, "Link Supervision Control" }, { 0x7a, "Link Supervision Filtering Time" }, { 0x7b, "Call Supervision Time" }, { 0x7c, "Interval Length UAS Incoming" }, { 0x7d, "Interval Length UAS Outgoing" }, { 0x7e, "ICM Channel Rate" }, { 0x7f, "Attribute Identifier" }, { 0x80, "FM Frequency List" }, { 0x81, "FM Frequency Report" }, { 0x82, "FM Percentile" }, { 0x83, "FM Clear Indication" }, { 0x84, "HW Info Signature" }, { 0x85, "MO Record" }, { 0x86, "TF Synchronisation Source" }, { 0x87, "TTA" }, { 0x88, "End Segment Number" }, { 0x89, "Segment Number" }, { 0x8a, "Capabilities Signature" }, { 0x8c, "File Relation List" }, { 0x90, "Negotiation Record I" }, { 0x91, "Negotiation Record II" }, { 0x92, "Encryption Algorithm" }, { 0x94, "Interference Rejection Combining" }, { 0x95, "Dedication Information" }, { 0x97, "Feature Code" }, { 0x98, "FS Offset" }, { 0x99, "ESB Timeslot" }, { 0x9a, "Master TG Instance" }, { 0x9b, "Master TX Chain Delay" }, { 0x9c, "External Condition Class 2 Extension" }, { 0x9d, "TSs MO State" }, { 0, NULL } }; const struct value_string om2k_mo_class_short_vals[] = { { 0x01, "TRXC" }, { 0x03, "TS" }, { 0x04, "TF" }, { 0x05, "IS" }, { 0x06, "CON" }, { 0x07, "DP" }, { 0x0a, "CF" }, { 0x0b, "TX" }, { 0x0c, "RX" }, { 0, NULL } }; const struct value_string om2k_result_strings[] = { { 0x02, "Wrong state or out of sequence" }, { 0x03, "File error" }, { 0x04, "Fault, unspecified" }, { 0x05, "Tuning fault" }, { 0x06, "Protocol error" }, { 0x07, "MO not connected" }, { 0x08, "Parameter error" }, { 0x09, "Optional function not supported" }, { 0x0a, "Local access state LOCALLY DISCONNECTED" }, { 0, NULL } }; const struct value_string om2k_accordance_strings[] = { { 0x00, "Data according to request" }, { 0x01, "Data not according to request" }, { 0x02, "Inconsistent MO data" }, { 0x03, "Capability constraint violation" }, { 0, NULL } }; const struct value_string om2k_mostate_vals[] = { { 0x00, "RESET" }, { 0x01, "STARTED" }, { 0x02, "ENABLED" }, { 0x03, "DISABLED" }, { 0, NULL } }; /* entire decoded OM2K message (header + parsed TLV) */ struct om2k_decoded_msg { struct abis_om2k_hdr o2h; uint16_t msg_type; struct tlv_parsed tp; }; /* resolve the OM2000 Managed Object by BTS + MO Address */ static struct om2k_mo * get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) { struct om2k_mo *mo = NULL; struct gsm_bts_trx *trx; switch (abis_mo->class) { case OM2K_MO_CLS_CF: mo = &bts->rbs2000.cf.om2k_mo; break; case OM2K_MO_CLS_CON: mo = &bts->rbs2000.con.om2k_mo; break; case OM2K_MO_CLS_IS: mo = &bts->rbs2000.is.om2k_mo; break; case OM2K_MO_CLS_TF: mo = &bts->rbs2000.tf.om2k_mo; break; case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_num(bts, abis_mo->inst); if (!trx) return NULL; mo = &trx->rbs2000.trxc.om2k_mo; break; case OM2K_MO_CLS_TX: trx = gsm_bts_trx_num(bts, abis_mo->inst); if (!trx) return NULL; mo = &trx->rbs2000.tx.om2k_mo; break; case OM2K_MO_CLS_RX: trx = gsm_bts_trx_num(bts, abis_mo->inst); if (!trx) return NULL; mo = &trx->rbs2000.rx.om2k_mo; break; case OM2K_MO_CLS_TS: trx = gsm_bts_trx_num(bts, abis_mo->assoc_so); if (!trx) return NULL; if (abis_mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; mo = &trx->ts[abis_mo->inst].rbs2000.om2k_mo; break; default: return NULL; }; return mo; } static struct msgb *om2k_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OM2000"); } static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) { return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); } static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) { return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); } /* decode/parse the message */ static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg) { struct abis_om2k_hdr *o2h = msgb_l2(msg); odm->msg_type = ntohs(o2h->msg_type); odm->o2h = *o2h; return abis_om2k_msg_tlv_parse(&odm->tp, o2h); } static char *om2k_mo_name(const struct abis_om2k_mo *mo) { static char mo_buf[64]; memset(mo_buf, 0, sizeof(mo_buf)); snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", get_value_string(om2k_mo_class_short_vals, mo->class), mo->bts, mo->assoc_so, mo->inst); return mo_buf; } /* resolve the gsm_nm_state data structure for a given MO */ static struct gsm_nm_state * mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; struct gsm_nm_state *nm_state = NULL; switch (mo->class) { case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_num(bts, mo->inst); if (!trx) return NULL; nm_state = &trx->mo.nm_state; break; case OM2K_MO_CLS_TS: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; if (mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; nm_state = &trx->ts[mo->inst].mo.nm_state; break; case OM2K_MO_CLS_TF: nm_state = &bts->rbs2000.tf.mo.nm_state; break; case OM2K_MO_CLS_IS: nm_state = &bts->rbs2000.is.mo.nm_state; break; case OM2K_MO_CLS_CON: nm_state = &bts->rbs2000.con.mo.nm_state; break; case OM2K_MO_CLS_DP: nm_state = &bts->rbs2000.con.mo.nm_state; break; case OM2K_MO_CLS_CF: nm_state = &bts->mo.nm_state; break; case OM2K_MO_CLS_TX: trx = gsm_bts_trx_num(bts, mo->inst); if (!trx) return NULL; /* FIXME */ break; case OM2K_MO_CLS_RX: trx = gsm_bts_trx_num(bts, mo->inst); if (!trx) return NULL; /* FIXME */ break; } return nm_state; } static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; switch (mo->class) { case OM2K_MO_CLS_TX: case OM2K_MO_CLS_RX: case OM2K_MO_CLS_TRXC: return gsm_bts_trx_num(bts, mo->inst); case OM2K_MO_CLS_TS: trx = gsm_bts_trx_num(bts, mo->assoc_so); if (!trx) return NULL; if (mo->inst >= ARRAY_SIZE(trx->ts)) return NULL; return &trx->ts[mo->inst]; case OM2K_MO_CLS_TF: case OM2K_MO_CLS_IS: case OM2K_MO_CLS_CON: case OM2K_MO_CLS_DP: case OM2K_MO_CLS_CF: return bts; } return NULL; } static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct gsm_nm_state new_state; struct nm_statechg_signal_data nsd; if (!nm_state) return; new_state = *nm_state; /* NOTICE: 12.21 Availability state values != OM2000 */ new_state.availability = mo_state; memset(&nsd, 0, sizeof(nsd)); nsd.bts = bts; nsd.obj = mo2obj(bts, mo); nsd.old_state = nm_state; nsd.new_state = &new_state; nsd.om2k_mo = mo; osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); nm_state->availability = new_state.availability; } static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); struct gsm_nm_state new_state; if (!nm_state) return; new_state = *nm_state; switch (op_state) { case 1: new_state.operational = NM_OPSTATE_ENABLED; break; case 0: new_state.operational = NM_OPSTATE_DISABLED; break; default: new_state.operational = NM_OPSTATE_NULL; break; } nm_state->operational = new_state.operational; } static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) { struct abis_om2k_hdr *o2h; struct gsm_bts_trx *trx; msg->l2h = msg->data; o2h = (struct abis_om2k_hdr *) msg->l2h; /* Compute the length in the OML header */ o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); switch (o2h->mo.class) { case OM2K_MO_CLS_TRXC: case OM2K_MO_CLS_TX: case OM2K_MO_CLS_RX: /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); if (!trx) { LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " "non-existing TRX\n", om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; break; case OM2K_MO_CLS_TS: /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); if (!trx) { LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " "non-existing TRX\n", om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; break; default: /* Route through the IXU/DXU OML Link */ msg->dst = bts->oml_link; break; } return _abis_nm_sendmsg(msg); } static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, uint16_t msg_type) { o2h->om.mdisc = ABIS_OM_MDISC_FOM; o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; o2h->om.sequence = 0; /* We fill o2h->om.length later during om2k_sendmsg() */ o2h->msg_type = htons(msg_type); memcpy(&o2h->mo, mo, sizeof(o2h->mo)); } static int abis_om2k_cal_time_resp(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; time_t tm_t; struct tm *tm; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr, OM2K_MSGT_CAL_TIME_RESP); tm_t = time(NULL); tm = localtime(&tm_t); msgb_put_u8(msg, OM2K_DEI_CAL_TIME); msgb_put_u8(msg, tm->tm_year % 100); msgb_put_u8(msg, tm->tm_mon + 1); msgb_put_u8(msg, tm->tm_mday); msgb_put_u8(msg, tm->tm_hour); msgb_put_u8(msg, tm->tm_min); msgb_put_u8(msg, tm->tm_sec); return abis_om2k_sendmsg(bts, msg); } static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t msg_type) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, msg_type); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, msg_type)); return abis_om2k_sendmsg(bts, msg); } int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); } int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); } int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); } int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); } int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); } int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); } int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); } int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); } int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); /* we update the state here... and send the signal at ACK */ update_op_state(bts, mo, operational); return abis_om2k_sendmsg(bts, msg); } int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CAPA_REQ); } static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, uint16_t icp2, uint8_t cont_idx) { grp->icp1 = htons(icp1); grp->icp2 = htons(icp2); grp->cont_idx = cont_idx; } int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct is_conn_group *grp; unsigned int num_grps = 0, i = 0; struct om2k_is_conn_grp *cg; /* count number of groups in linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) num_grps++; if (!num_grps) return -EINVAL; /* allocate buffer for oml group array */ cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); /* fill array with data from linked list */ llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr, OM2K_MSGT_IS_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, num_grps * sizeof(*cg), (uint8_t *)cg); talloc_free(cg); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } int abis_om2k_tx_con_conf_req(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct con_group *grp; unsigned int num_grps = 0; /* count number of groups in linked list */ llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) num_grps++; if (!num_grps) return -EINVAL; /* first build the value part of the OM2K_DEI_CON_CONN_LIST DEI */ msgb_put_u8(msg, num_grps); llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) { struct con_path *cp; unsigned int num_paths = 0; llist_for_each_entry(cp, &grp->paths, list) num_paths++; msgb_put_u8(msg, num_paths); llist_for_each_entry(cp, &grp->paths, list) { struct om2k_con_path *om2k_cp; om2k_cp = (struct om2k_con_path *) msgb_put(msg, sizeof(*om2k_cp)); om2k_cp->ccp = htons(cp->ccp); om2k_cp->ci = cp->ci; om2k_cp->tag = cp->tag; om2k_cp->tei = cp->tei; } } msgb_push_u8(msg, msgb_length(msg)); msgb_push_u8(msg, OM2K_DEI_CON_CONN_LIST); /* pre-pend the list number DEIs */ msgb_tv_push(msg, OM2K_DEI_END_LIST_NR, 1); msgb_tv_push(msg, OM2K_DEI_LIST_NR, 1); /* pre-pend the OM2K header */ o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, OM2K_MSGT_CON_CONF_REQ); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } static void om2k_trx_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx *trx, enum abis_om2k_mo_cls cls) { mo->class = cls; mo->bts = 0; mo->inst = trx->nr; mo->assoc_so = 255; } static void om2k_ts_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx_ts *ts) { mo->class = OM2K_MO_CLS_TS; mo->bts = 0; mo->inst = ts->nr; mo->assoc_so = ts->trx->nr; } /* Configure a Receiver MO */ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ return abis_om2k_sendmsg(trx->bts, msg); } /* Configure a Transmitter MO */ int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); /* Dedication Information is optional */ return abis_om2k_sendmsg(trx->bts, msg); } enum abis_om2k_tf_mode { OM2K_TF_MODE_MASTER = 0x00, OM2K_TF_MODE_STANDALONE = 0x01, OM2K_TF_MODE_SLAVE = 0x02, OM2K_TF_MODE_UNDEFINED = 0xff, }; static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr, OM2K_MSGT_TF_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, sizeof(fs_offset_undef), fs_offset_undef); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) { switch (pchan) { case GSM_PCHAN_CCCH: return 4; case GSM_PCHAN_CCCH_SDCCH4: return 5; case GSM_PCHAN_SDCCH8_SACCH8C: return 3; case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: case GSM_PCHAN_PDCH: case GSM_PCHAN_TCH_F_PDCH: case GSM_PCHAN_TCH_F_TCH_H_PDCH: return 8; default: return 0; } } static uint8_t ts2comb(struct gsm_bts_trx_ts *ts) { switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use" " with OM2000, use %s instead\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH), gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); /* If we allowed initialization of TCH/F_PDCH, it would fail * when we try to send the ip.access specific RSL PDCH Act * message for it. Rather fail completely right now: */ return 0; case GSM_PCHAN_TCH_F_TCH_H_PDCH: return pchan2comb(GSM_PCHAN_TCH_F); default: return pchan2comb(ts->pchan); } } static int put_freq_list(uint8_t *buf, uint16_t arfcn) { buf[0] = 0x00; /* TX/RX address */ buf[1] = (arfcn >> 8); buf[2] = (arfcn & 0xff); return 3; } /* Compute a frequency list in OM2000 fomrmat */ static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) { uint8_t *cur = list; int len; if (ts->hopping.enabled) { unsigned int i; for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) cur += put_freq_list(cur, i); } } else cur += put_freq_list(cur, ts->trx->arfcn); len = cur - list; return len; } const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; struct abis_om2k_mo mo; uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ int freq_list_len; om2k_ts_to_mo(&mo, ts); memset(freq_list, 0, sizeof(freq_list)); freq_list_len = om2k_gen_freq_list(freq_list, ts); if (freq_list_len < 0) return freq_list_len; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_COMBINATION, ts2comb(ts)); msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ /* Optional: Interference Rejection Combining */ msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); switch (ts->pchan) { case GSM_PCHAN_CCCH: msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); break; case GSM_PCHAN_CCCH_SDCCH4: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); break; case GSM_PCHAN_SDCCH8_SACCH8C: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); /* Disable RF RESOURCE INDICATION on idle channels */ msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); break; default: msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); msgb_tv_put(msg, OM2K_DEI_NY1, 35); msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); /* Disable RF RESOURCE INDICATION on idle channels */ msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, sizeof(icm_bound_params), icm_bound_params); msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ if (ts->pchan == GSM_PCHAN_TCH_H) msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ else msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); /* Not sure what those below mean */ msgb_tv_put(msg, 0x9e, 0x00); msgb_tv_put(msg, 0x9f, 0x37); msgb_tv_put(msg, 0xa0, 0x01); break; } DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ)); return abis_om2k_sendmsg(ts->trx->bts, msg); } /*********************************************************************** * OM2000 Managed Object (MO) FSM ***********************************************************************/ #define S(x) (1 << (x)) enum om2k_event_name { OM2K_MO_EVT_START, OM2K_MO_EVT_RX_CONN_COMPL, OM2K_MO_EVT_RX_RESET_COMPL, OM2K_MO_EVT_RX_START_REQ_ACCEPT, OM2K_MO_EVT_RX_START_RES, OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, OM2K_MO_EVT_RX_CFG_RES, OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, OM2K_MO_EVT_RX_ENA_RES, OM2K_MO_EVT_RX_OPINFO_ACC, }; static const struct value_string om2k_event_names[] = { { OM2K_MO_EVT_START, "START" }, { OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" }, { OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" }, { OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" }, { OM2K_MO_EVT_RX_START_RES, "RX-START-RESULT" }, { OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, "RX-CFG-REQ-ACCEPT" }, { OM2K_MO_EVT_RX_CFG_RES, "RX-CFG-RESULT" }, { OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, "RX-ENABLE-REQ-ACCEPT" }, { OM2K_MO_EVT_RX_ENA_RES, "RX-ENABLE-RESULT" }, { OM2K_MO_EVT_RX_OPINFO_ACC, "RX-OPINFO-ACCEPT" }, { 0, NULL } }; enum om2k_mo_fsm_state { OM2K_ST_INIT, OM2K_ST_WAIT_CONN_COMPL, OM2K_ST_WAIT_RES_COMPL, OM2K_ST_WAIT_START_ACCEPT, OM2K_ST_WAIT_START_RES, OM2K_ST_WAIT_CFG_ACCEPT, OM2K_ST_WAIT_CFG_RES, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_ST_WAIT_ENABLE_RES, OM2K_ST_WAIT_OPINFO_ACCEPT, OM2K_ST_DONE, OM2K_ST_ERROR, }; struct om2k_mo_fsm_priv { struct gsm_bts_trx *trx; struct om2k_mo *mo; uint8_t ts_nr; }; static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; OSMO_ASSERT(event == OM2K_MO_EVT_START); switch (omfp->mo->addr.class) { case OM2K_MO_CLS_CF: /* no Connect required, is always connected */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); break; case OM2K_MO_CLS_TRXC: /* no Connect required, start with Reset */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, OM2K_TIMEOUT, 0); abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); break; default: /* start with Connect */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL, OM2K_TIMEOUT, 0); abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr); break; } } static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; switch (omfp->mo->addr.class) { #if 0 case OM2K_MO_CLS_TF: /* skip the reset, hope that helps */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); break; #endif default: osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, OM2K_TIMEOUT, 0); abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); break; } } static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); } static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_decoded_msg *omd = data; switch (omd->msg_type) { case OM2K_MSGT_START_REQ_ACK: osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, OM2K_TIMEOUT, 0); break; case OM2K_MSGT_START_REQ_REJ: osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); break; } } static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; struct gsm_bts_trx_ts *ts; switch (omfp->mo->addr.class) { case OM2K_MO_CLS_CF: case OM2K_MO_CLS_TRXC: /* Transition directly to Operational Info */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); return; case OM2K_MO_CLS_DP: /* Transition directoy to WAIT_ENABLE_ACCEPT */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); return; #if 0 case OM2K_MO_CLS_TF: /* skip the config, hope that helps speeding things up */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); return; #endif } osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_ACCEPT, OM2K_TIMEOUT, 0); switch (omfp->mo->addr.class) { case OM2K_MO_CLS_TF: abis_om2k_tx_tf_conf_req(omfp->trx->bts); break; case OM2K_MO_CLS_IS: abis_om2k_tx_is_conf_req(omfp->trx->bts); break; case OM2K_MO_CLS_CON: abis_om2k_tx_con_conf_req(omfp->trx->bts); break; case OM2K_MO_CLS_TX: abis_om2k_tx_tx_conf_req(omfp->trx); break; case OM2K_MO_CLS_RX: abis_om2k_tx_rx_conf_req(omfp->trx); break; case OM2K_MO_CLS_TS: ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); abis_om2k_tx_ts_conf_req(ts); break; } } static void om2k_mo_st_wait_cfg_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; uint32_t timeout = OM2K_TIMEOUT; if (omfp->mo->addr.class == OM2K_MO_CLS_TF) timeout = 600; osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_RES, timeout, 0); } static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; struct om2k_decoded_msg *omd = data; uint8_t accordance; if (!TLVP_PRESENT(&omd->tp, OM2K_DEI_ACCORDANCE_IND)) { osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); return; } accordance = *TLVP_VAL(&omd->tp, OM2K_DEI_ACCORDANCE_IND); if (accordance != 0) { /* accordance not OK */ osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); return; } osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); } static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; struct om2k_decoded_msg *omd = data; switch (omd->msg_type) { case OM2K_MSGT_ENABLE_REQ_REJ: osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); break; case OM2K_MSGT_ENABLE_REQ_ACK: if (omfp->mo->addr.class == OM2K_MO_CLS_IS && omfp->trx->bts->rbs2000.use_superchannel) e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1); osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, OM2K_TIMEOUT, 0); } } static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_mo_fsm_priv *omfp = fi->priv; //struct om2k_decoded_msg *omd = data; /* TODO: check if state is actually enabled now? */ osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, OM2K_TIMEOUT, 0); abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); } static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) { osmo_fsm_inst_state_chg(fi, OM2K_ST_DONE, 0, 0); } static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct om2k_mo_fsm_priv *omfp = fi->priv; omfp->mo->fsm = NULL; osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); } static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct om2k_mo_fsm_priv *omfp = fi->priv; omfp->mo->fsm = NULL; osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); } static const struct osmo_fsm_state om2k_is_states[] = { [OM2K_ST_INIT] = { .name = "INIT", .in_event_mask = S(OM2K_MO_EVT_START), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CONN_COMPL) | S(OM2K_ST_WAIT_START_ACCEPT) | S(OM2K_ST_WAIT_RES_COMPL), .action = om2k_mo_st_init, }, [OM2K_ST_WAIT_CONN_COMPL] = { .name = "WAIT-CONN-COMPL", .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_ACCEPT) | S(OM2K_ST_WAIT_RES_COMPL), .action = om2k_mo_st_wait_conn_compl, }, [OM2K_ST_WAIT_RES_COMPL] = { .name = "WAIT-RES-COMPL", .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_ACCEPT), .action = om2k_mo_st_wait_res_compl, }, [OM2K_ST_WAIT_START_ACCEPT] = { .name = "WAIT-START-ACCEPT", .in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_RES), .action =om2k_mo_st_wait_start_accept, }, [OM2K_ST_WAIT_START_RES] = { .name = "WAIT-START-RES", .in_event_mask = S(OM2K_MO_EVT_RX_START_RES), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CFG_ACCEPT) | S(OM2K_ST_WAIT_OPINFO_ACCEPT), .action = om2k_mo_st_wait_start_res, }, [OM2K_ST_WAIT_CFG_ACCEPT] = { .name = "WAIT-CFG-ACCEPT", .in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CFG_RES), .action = om2k_mo_st_wait_cfg_accept, }, [OM2K_ST_WAIT_CFG_RES] = { .name = "WAIT-CFG-RES", .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_ENABLE_ACCEPT), .action = om2k_mo_st_wait_cfg_res, }, [OM2K_ST_WAIT_ENABLE_ACCEPT] = { .name = "WAIT-ENABLE-ACCEPT", .in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_ENABLE_RES), .action = om2k_mo_st_wait_enable_accept, }, [OM2K_ST_WAIT_ENABLE_RES] = { .name = "WAIT-ENABLE-RES", .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_OPINFO_ACCEPT), .action = om2k_mo_st_wait_enable_res, }, [OM2K_ST_WAIT_OPINFO_ACCEPT] = { .name = "WAIT-OPINFO-ACCEPT", .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC), .out_state_mask = S(OM2K_ST_DONE) | S(OM2K_ST_ERROR), .action = om2k_mo_st_wait_opinfo_accept, }, [OM2K_ST_DONE] = { .name = "DONE", .in_event_mask = 0, .out_state_mask = 0, .onenter = om2k_mo_s_done_onenter, }, [OM2K_ST_ERROR] = { .name = "ERROR", .in_event_mask = 0, .out_state_mask = 0, .onenter = om2k_mo_s_error_onenter, }, }; static int om2k_mo_timer_cb(struct osmo_fsm_inst *fi) { osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); return 0; } static struct osmo_fsm om2k_mo_fsm = { .name = "OM2000-MO", .states = om2k_is_states, .num_states = ARRAY_SIZE(om2k_is_states), .log_subsys = DNM, .event_names = om2k_event_names, .timer_cb = om2k_mo_timer_cb, }; struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, uint32_t term_event, struct gsm_bts_trx *trx, struct om2k_mo *mo) { struct osmo_fsm_inst *fi; struct om2k_mo_fsm_priv *omfp; char idbuf[64]; snprintf(idbuf, sizeof(idbuf), "%s-%s", parent->id, om2k_mo_name(&mo->addr)); fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, term_event, idbuf); if (!fi) return NULL; mo->fsm = fi; omfp = talloc_zero(fi, struct om2k_mo_fsm_priv); omfp->mo = mo; omfp->trx = trx; fi->priv = omfp; osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL); return fi; } int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, struct om2k_decoded_msg *odm) { switch (odm->msg_type) { case OM2K_MSGT_CONNECT_COMPL: case OM2K_MSGT_CONNECT_REJ: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CONN_COMPL, odm); break; case OM2K_MSGT_RESET_COMPL: case OM2K_MSGT_RESET_REJ: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_RESET_COMPL, odm); break; case OM2K_MSGT_START_REQ_ACK: case OM2K_MSGT_START_REQ_REJ: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm); break; case OM2K_MSGT_START_RES: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_START_RES, odm); break; case OM2K_MSGT_CON_CONF_REQ_ACK: case OM2K_MSGT_IS_CONF_REQ_ACK: case OM2K_MSGT_RX_CONF_REQ_ACK: case OM2K_MSGT_TF_CONF_REQ_ACK: case OM2K_MSGT_TS_CONF_REQ_ACK: case OM2K_MSGT_TX_CONF_REQ_ACK: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm); break; case OM2K_MSGT_CON_CONF_RES: case OM2K_MSGT_IS_CONF_RES: case OM2K_MSGT_RX_CONF_RES: case OM2K_MSGT_TF_CONF_RES: case OM2K_MSGT_TS_CONF_RES: case OM2K_MSGT_TX_CONF_RES: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CFG_RES, odm); break; case OM2K_MSGT_ENABLE_REQ_ACK: case OM2K_MSGT_ENABLE_REQ_REJ: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm); break; case OM2K_MSGT_ENABLE_RES: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_ENA_RES, odm); break; case OM2K_MSGT_OP_INFO_ACK: case OM2K_MSGT_OP_INFO_REJ: osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_OPINFO_ACC, odm); break; default: return -1; } return 0; } /*********************************************************************** * OM2000 TRX Finite State Machine, initializes TRXC and all siblings ***********************************************************************/ enum om2k_trx_event { OM2K_TRX_EVT_START, OM2K_TRX_EVT_TRXC_DONE, OM2K_TRX_EVT_TX_DONE, OM2K_TRX_EVT_RX_DONE, OM2K_TRX_EVT_TS_DONE, OM2K_TRX_EVT_STOP, }; static struct value_string om2k_trx_events[] = { { OM2K_TRX_EVT_START, "START" }, { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" }, { OM2K_TRX_EVT_TX_DONE, "TX-DONE" }, { OM2K_TRX_EVT_RX_DONE, "RX-DONE" }, { OM2K_TRX_EVT_TS_DONE, "TS-DONE" }, { OM2K_TRX_EVT_STOP, "STOP" }, { 0, NULL } }; enum om2k_trx_state { OM2K_TRX_S_INIT, OM2K_TRX_S_WAIT_TRXC, OM2K_TRX_S_WAIT_TX, OM2K_TRX_S_WAIT_RX, OM2K_TRX_S_WAIT_TS, OM2K_TRX_S_DONE, OM2K_TRX_S_ERROR }; struct om2k_trx_fsm_priv { struct gsm_bts_trx *trx; uint8_t next_ts_nr; }; static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_trx_fsm_priv *otfp = fi->priv; /* First initialize TRXC */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, TRX_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx, &otfp->trx->rbs2000.trxc.om2k_mo); } static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_trx_fsm_priv *otfp = fi->priv; /* Initialize TX after TRXC */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, TRX_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx, &otfp->trx->rbs2000.tx.om2k_mo); } static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_trx_fsm_priv *otfp = fi->priv; /* Initialize RX after TX */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, TRX_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx, &otfp->trx->rbs2000.rx.om2k_mo); } static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_trx_fsm_priv *otfp = fi->priv; struct gsm_bts_trx_ts *ts; /* Initialize Timeslots after TX */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, TRX_FSM_TIMEOUT, 0); otfp->next_ts_nr = 0; ts = &otfp->trx->ts[otfp->next_ts_nr++]; om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, &ts->rbs2000.om2k_mo); } static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_trx_fsm_priv *otfp = fi->priv; struct gsm_bts_trx_ts *ts; if (otfp->next_ts_nr < 8) { /* iterate to the next timeslot */ ts = &otfp->trx->ts[otfp->next_ts_nr++]; om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, &ts->rbs2000.om2k_mo); } else { /* only after all 8 TS */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0); } } static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct om2k_trx_fsm_priv *otfp = fi->priv; gsm_bts_trx_set_system_infos(otfp->trx); osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); } static const struct osmo_fsm_state om2k_trx_states[] = { [OM2K_TRX_S_INIT] = { .in_event_mask = S(OM2K_TRX_EVT_START), .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC), .name = "INIT", .action = om2k_trx_s_init, }, [OM2K_TRX_S_WAIT_TRXC] = { .in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE), .out_state_mask = S(OM2K_TRX_S_ERROR) | S(OM2K_TRX_S_WAIT_TX), .name = "WAIT-TRXC", .action = om2k_trx_s_wait_trxc, }, [OM2K_TRX_S_WAIT_TX] = { .in_event_mask = S(OM2K_TRX_EVT_TX_DONE), .out_state_mask = S(OM2K_TRX_S_ERROR) | S(OM2K_TRX_S_WAIT_RX), .name = "WAIT-TX", .action = om2k_trx_s_wait_tx, }, [OM2K_TRX_S_WAIT_RX] = { .in_event_mask = S(OM2K_TRX_EVT_RX_DONE), .out_state_mask = S(OM2K_TRX_S_ERROR) | S(OM2K_TRX_S_WAIT_TS), .name = "WAIT-RX", .action = om2k_trx_s_wait_rx, }, [OM2K_TRX_S_WAIT_TS] = { .in_event_mask = S(OM2K_TRX_EVT_TS_DONE), .out_state_mask = S(OM2K_TRX_S_ERROR) | S(OM2K_TRX_S_DONE), .name = "WAIT-TS", .action = om2k_trx_s_wait_ts, }, [OM2K_TRX_S_DONE] = { .name = "DONE", .onenter = om2k_trx_s_done_onenter, }, [OM2K_TRX_S_ERROR] = { .name = "ERROR", }, }; static int om2k_trx_timer_cb(struct osmo_fsm_inst *fi) { osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0); return 0; } static struct osmo_fsm om2k_trx_fsm = { .name = "OM2000-TRX", .states = om2k_trx_states, .num_states = ARRAY_SIZE(om2k_trx_states), .log_subsys = DNM, .event_names = om2k_trx_events, .timer_cb = om2k_trx_timer_cb, }; struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent, struct gsm_bts_trx *trx, uint32_t term_event) { struct osmo_fsm_inst *fi; struct om2k_trx_fsm_priv *otfp; char idbuf[32]; snprintf(idbuf, sizeof(idbuf), "%u/%u", trx->bts->nr, trx->nr); fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event, idbuf); if (!fi) return NULL; otfp = talloc_zero(fi, struct om2k_trx_fsm_priv); otfp->trx = trx; fi->priv = otfp; osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL); return fi; } /*********************************************************************** * OM2000 BTS Finite State Machine, initializes CF and all siblings ***********************************************************************/ enum om2k_bts_event { OM2K_BTS_EVT_START, OM2K_BTS_EVT_CF_DONE, OM2K_BTS_EVT_IS_DONE, OM2K_BTS_EVT_CON_DONE, OM2K_BTS_EVT_TF_DONE, OM2K_BTS_EVT_TRX_DONE, OM2K_BTS_EVT_STOP, }; static const struct value_string om2k_bts_events[] = { { OM2K_BTS_EVT_START, "START" }, { OM2K_BTS_EVT_CF_DONE, "CF-DONE" }, { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, { OM2K_BTS_EVT_CON_DONE, "CON-DONE" }, { OM2K_BTS_EVT_TF_DONE, "TF-DONE" }, { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" }, { OM2K_BTS_EVT_STOP, "STOP" }, { 0, NULL } }; enum om2k_bts_state { OM2K_BTS_S_INIT, OM2K_BTS_S_WAIT_CF, OM2K_BTS_S_WAIT_IS, OM2K_BTS_S_WAIT_CON, OM2K_BTS_S_WAIT_TF, OM2K_BTS_S_WAIT_TRX, OM2K_BTS_S_DONE, OM2K_BTS_S_ERROR, }; struct om2k_bts_fsm_priv { struct gsm_bts *bts; uint8_t next_trx_nr; }; static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; struct gsm_bts *bts = obfp->bts; OSMO_ASSERT(event == OM2K_BTS_EVT_START); osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, BTS_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0, &bts->rbs2000.cf.om2k_mo); } static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; struct gsm_bts *bts = obfp->bts; OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE); /* TF can take a long time to initialize, wait for 10min */ osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0); om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0, &bts->rbs2000.tf.om2k_mo); } static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; struct gsm_bts *bts = obfp->bts; OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE); osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, BTS_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0, &bts->rbs2000.con.om2k_mo); } static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; struct gsm_bts *bts = obfp->bts; OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE); osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, BTS_FSM_TIMEOUT, 0); om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0, &bts->rbs2000.is.om2k_mo); } static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; struct gsm_bts_trx *trx; OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE); osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, BTS_FSM_TIMEOUT, 0); obfp->next_trx_nr = 0; trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); } static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct om2k_bts_fsm_priv *obfp = fi->priv; OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_DONE); if (obfp->next_trx_nr < obfp->bts->num_trx) { struct gsm_bts_trx *trx; trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); } else { osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0); } } static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); } static const struct osmo_fsm_state om2k_bts_states[] = { [OM2K_BTS_S_INIT] = { .in_event_mask = S(OM2K_BTS_EVT_START), .out_state_mask = S(OM2K_BTS_S_WAIT_CF), .name = "INIT", .action = om2k_bts_s_init, }, [OM2K_BTS_S_WAIT_CF] = { .in_event_mask = S(OM2K_BTS_EVT_CF_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_WAIT_TF), .name = "WAIT-CF", .action = om2k_bts_s_wait_cf, }, [OM2K_BTS_S_WAIT_TF] = { .in_event_mask = S(OM2K_BTS_EVT_TF_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_WAIT_CON), .name = "WAIT-TF", .action = om2k_bts_s_wait_tf, }, [OM2K_BTS_S_WAIT_CON] = { .in_event_mask = S(OM2K_BTS_EVT_CON_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_WAIT_IS), .name = "WAIT-CON", .action = om2k_bts_s_wait_con, }, [OM2K_BTS_S_WAIT_IS] = { .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_WAIT_TRX), .name = "WAIT-IS", .action = om2k_bts_s_wait_is, }, [OM2K_BTS_S_WAIT_TRX] = { .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_DONE), .name = "WAIT-TRX", .action = om2k_bts_s_wait_trx, }, [OM2K_BTS_S_DONE] = { .name = "DONE", .onenter = om2k_bts_s_done_onenter, }, [OM2K_BTS_S_ERROR] = { .name = "ERROR", }, }; static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi) { osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); return 0; } static struct osmo_fsm om2k_bts_fsm = { .name = "OM2000-BTS", .states = om2k_bts_states, .num_states = ARRAY_SIZE(om2k_bts_states), .log_subsys = DNM, .event_names = om2k_bts_events, .timer_cb = om2k_bts_timer_cb, }; struct osmo_fsm_inst * om2k_bts_fsm_start(struct gsm_bts *bts) { struct osmo_fsm_inst *fi; struct om2k_bts_fsm_priv *obfp; char idbuf[16]; snprintf(idbuf, sizeof(idbuf), "%u", bts->nr); fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, LOGL_DEBUG, idbuf); if (!fi) return NULL; fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv); obfp->bts = bts; osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL); return fi; } /*********************************************************************** * OM2000 Negotiation ***********************************************************************/ static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t *data, unsigned int len) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); return abis_om2k_sendmsg(bts, msg); } struct iwd_version { uint8_t gen_char[3+1]; uint8_t rev_char[3+1]; }; struct iwd_type { uint8_t num_vers; struct iwd_version v[8]; }; static int om2k_rx_negot_req(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct abis_om2k_hdr *o2h = msgb_l2(msg); struct iwd_type iwd_types[16]; uint8_t num_iwd_types = o2h->data[2]; uint8_t *cur = o2h->data+3; unsigned int i, v; uint8_t out_buf[1024]; uint8_t *out_cur = out_buf+1; uint8_t out_num_types = 0; memset(iwd_types, 0, sizeof(iwd_types)); /* Parse the RBS-supported IWD versions into iwd_types array */ for (i = 0; i < num_iwd_types; i++) { uint8_t num_versions = *cur++; uint8_t iwd_type = *cur++; iwd_types[iwd_type].num_vers = num_versions; for (v = 0; v < num_versions; v++) { struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; memcpy(iwd_v->gen_char, cur, 3); cur += 3; memcpy(iwd_v->rev_char, cur, 3); cur += 3; DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, iwd_v->gen_char, iwd_v->rev_char); } } /* Select the last version for each IWD type */ for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { struct iwd_type *type = &iwd_types[i]; struct iwd_version *last_v; if (type->num_vers == 0) continue; out_num_types++; last_v = &type->v[type->num_vers-1]; *out_cur++ = i; memcpy(out_cur, last_v->gen_char, 3); out_cur += 3; memcpy(out_cur, last_v->rev_char, 3); out_cur += 3; } out_buf[0] = out_num_types; return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); } /*********************************************************************** * OM2000 Receive Message Handler ***********************************************************************/ static int om2k_rx_nack(struct msgb *msg) { struct abis_om2k_hdr *o2h = msgb_l2(msg); uint16_t msg_type = ntohs(o2h->msg_type); struct tlv_parsed tp; LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type)); abis_om2k_msg_tlv_parse(&tp, o2h); if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) LOGPC(DNM, LOGL_ERROR, ", Result %s", get_value_string(om2k_result_strings, *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); LOGPC(DNM, LOGL_ERROR, "\n"); return 0; } static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) { uint8_t mo_state; if (!TLVP_PRESENT(&odm->tp, OM2K_DEI_MO_STATE)) return -EIO; mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE); LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", om2k_mo_name(&odm->o2h.mo), get_value_string(om2k_msgcode_vals, odm->msg_type), get_value_string(om2k_mostate_vals, mo_state)); /* Throw error message in case we see an enable rsponse that does * not yield an enabled mo-state */ if (odm->msg_type == OM2K_MSGT_ENABLE_RES && mo_state != OM2K_MO_S_ENABLED) { LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s Failed to enable MO State!\n", om2k_mo_name(&odm->o2h.mo), get_value_string(om2k_msgcode_vals, odm->msg_type)); } update_mo_state(bts, &odm->o2h.mo, mo_state); return 0; } /* Display fault report bits (helper function of display_fault_maps()) */ static bool display_fault_bits(const uint8_t *vect, uint16_t len, uint8_t dei, const struct abis_om2k_mo *mo) { uint16_t i; int k; bool faults_present = false; int first = 1; char string[255]; /* Check if errors are present at all */ for (i = 0; i < len; i++) if (vect[i]) faults_present = true; if (!faults_present) return false; sprintf(string, "Fault Report: %s (", get_value_string(om2k_attr_vals, dei)); for (i = 0; i < len; i++) { for (k = 0; k < 8; k++) { if ((vect[i] >> k) & 1) { if (!first) sprintf(string + strlen(string), ","); sprintf(string + strlen(string), "%d", k + i*8); first = 0; } } } sprintf(string + strlen(string), ")\n"); DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string); return true; } /* Display fault report maps */ static void display_fault_maps(const uint8_t *src, unsigned int src_len, const struct abis_om2k_mo *mo) { uint8_t tag; uint16_t tag_len; const uint8_t *val; int src_pos = 0; int rc; int tlv_count = 0; uint16_t msg_code; bool faults_present = false; /* Chop off header */ src+=4; src_len-=4; /* Check message type */ msg_code = (*src & 0xff) << 8; src++; src_len--; msg_code |= (*src & 0xff); src++; src_len--; if (msg_code != OM2K_MSGT_FAULT_REP) { LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", om2k_mo_name(mo)); return; } /* Chop off mo-interface */ src += 4; src_len -= 4; /* Iterate over each TLV element */ while (1) { /* Bail if an the maximum number of TLV fields * have been parsed */ if (tlv_count >= 11) { LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: too many tlv elements!\n", om2k_mo_name(mo)); return; } /* Parse TLV field */ rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, src + src_pos, src_len - src_pos); if (rc > 0) src_pos += rc; else { LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: invalid tlv element!\n", om2k_mo_name(mo)); return; } switch (tag) { case OM2K_DEI_INT_FAULT_MAP_1A: case OM2K_DEI_INT_FAULT_MAP_1B: case OM2K_DEI_INT_FAULT_MAP_2A: case OM2K_DEI_EXT_COND_MAP_1: case OM2K_DEI_EXT_COND_MAP_2: case OM2K_DEI_REPL_UNIT_MAP: case OM2K_DEI_INT_FAULT_MAP_2A_EXT: case OM2K_DEI_EXT_COND_MAP_2_EXT: case OM2K_DEI_REPL_UNIT_MAP_EXT: faults_present |= display_fault_bits(val, tag_len, tag, mo); break; } /* Stop when no further TLV elements can be expected */ if (src_len - src_pos < 2) break; tlv_count++; } if (!faults_present) { DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", om2k_mo_name(mo)); } } int abis_om2k_rcvmsg(struct msgb *msg) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_om2k_hdr *o2h = msgb_l2(msg); struct abis_om_hdr *oh = &o2h->om; uint16_t msg_type = ntohs(o2h->msg_type); struct om2k_decoded_msg odm; struct om2k_mo *mo; int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) return -EINVAL; } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); return -EINVAL; } msg->l3h = (unsigned char *)o2h + sizeof(*o2h); if (oh->mdisc != ABIS_OM_MDISC_FOM) { LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", oh->mdisc); return -EINVAL; } DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type), osmo_hexdump(msg->l2h, msgb_l2len(msg))); om2k_decode_msg(&odm, msg); process_mo_state(bts, &odm); switch (msg_type) { case OM2K_MSGT_CAL_TIME_REQ: rc = abis_om2k_cal_time_resp(bts); break; case OM2K_MSGT_FAULT_REP: display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo); rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); break; case OM2K_MSGT_NEGOT_REQ: rc = om2k_rx_negot_req(msg); break; case OM2K_MSGT_START_RES: /* common processing here */ rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); /* below we dispatch into MO */ break; case OM2K_MSGT_IS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); break; case OM2K_MSGT_CON_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); break; case OM2K_MSGT_TX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); break; case OM2K_MSGT_RX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); break; case OM2K_MSGT_TS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); break; case OM2K_MSGT_TF_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); break; case OM2K_MSGT_ENABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); break; case OM2K_MSGT_DISABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); break; case OM2K_MSGT_TEST_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); break; case OM2K_MSGT_CAPA_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CAPA_RES_ACK); break; /* ERrors */ case OM2K_MSGT_START_REQ_REJ: case OM2K_MSGT_CONNECT_REJ: case OM2K_MSGT_OP_INFO_REJ: case OM2K_MSGT_DISCONNECT_REJ: case OM2K_MSGT_TEST_REQ_REJ: case OM2K_MSGT_CON_CONF_REQ_REJ: case OM2K_MSGT_IS_CONF_REQ_REJ: case OM2K_MSGT_TX_CONF_REQ_REJ: case OM2K_MSGT_RX_CONF_REQ_REJ: case OM2K_MSGT_TS_CONF_REQ_REJ: case OM2K_MSGT_TF_CONF_REQ_REJ: case OM2K_MSGT_ENABLE_REQ_REJ: case OM2K_MSGT_ALARM_STATUS_REQ_REJ: case OM2K_MSGT_DISABLE_REQ_REJ: rc = om2k_rx_nack(msg); break; } /* Resolve the MO for this message */ mo = get_om2k_mo(bts, &o2h->mo); if (!mo) { LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg " "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg)); return 0; } if (!mo->fsm) { LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL " "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg)); return 0; } /* Dispatch message to that MO */ om2k_mo_fsm_recvmsg(bts, mo, &odm); msgb_free(msg); return rc; } static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, uint8_t bts_nr, uint8_t assoc_so, uint8_t inst) { mo->addr.class = class; mo->addr.bts = bts_nr; mo->addr.assoc_so = assoc_so; mo->addr.inst = inst; } /* initialize the OM2K_MO members of gsm_bts_trx and its timeslots */ void abis_om2k_trx_init(struct gsm_bts_trx *trx) { struct gsm_bts *bts = trx->bts; unsigned int i; OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, bts->nr, 255, trx->nr); om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, bts->nr, 255, trx->nr); om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, bts->nr, 255, trx->nr); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS, bts->nr, trx->nr, i); gsm_ts_check_init(ts); } } /* initialize the OM2K_MO members of gsm_bts */ void abis_om2k_bts_init(struct gsm_bts *bts) { OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, bts->nr, 0xFF, 0); om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, bts->nr, 0xFF, 0); om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, bts->nr, 0xFF, 0); om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP, bts->nr, 0xFF, 0); om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, bts->nr, 0xFF, 0); } static __attribute__((constructor)) void abis_om2k_init(void) { osmo_fsm_register(&om2k_mo_fsm); osmo_fsm_register(&om2k_bts_fsm); osmo_fsm_register(&om2k_trx_fsm); } osmo-bsc-1.3.0/src/osmo-bsc/abis_om2000_vty.c000066400000000000000000000347651332665256100205570ustar00rootroot00000000000000/* VTY interface for A-bis OM2000 */ /* (C) 2010-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct cmd_node om2k_node = { OM2K_NODE, "%s(om2k)# ", 1, }; static struct cmd_node om2k_con_group_node = { OM2K_CON_GROUP_NODE, "%s(om2k-con-group)# ", 1, }; struct con_group; struct oml_node_state { struct gsm_bts *bts; struct abis_om2k_mo mo; struct con_group *cg; }; static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } /* FIXME: auto-generate those strings from the value_string lists */ #define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" #define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ "Timeslot\n" \ "Timing Function\n" \ "Interface Switch\n" \ "Abis Concentrator\n" \ "Digital Path\n" \ "Central Function\n" \ "Transmitter\n" \ "Receiver\n" DEFUN(om2k_class_inst, om2k_class_inst_cmd, "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY " <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OM2000 managed objects\n" "Object Class\n" OM2K_OBJCLASS_VTY_HELP "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% BTS %d not an Ericsson RBS%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", "BTS related commands\n" "BTS Number\n" "Manipulate the OML managed objects\n" "Object Class\n" "Object Class\n" "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") { struct gsm_bts *bts; struct oml_node_state *oms; int bts_nr = atoi(argv[0]); bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); if (!oms) return CMD_WARNING; oms->bts = bts; oms->mo.class = atoi(argv[1]); oms->mo.bts = atoi(argv[2]); oms->mo.assoc_so = atoi(argv[3]); oms->mo.inst = atoi(argv[4]); vty->index = oms; vty->node = OM2K_NODE; return CMD_SUCCESS; } DEFUN(om2k_reset, om2k_reset_cmd, "reset-command", "Reset the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_start, om2k_start_cmd, "start-request", "Start the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_start_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_status, om2k_status_cmd, "status-request", "Get the MO Status\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_status_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_connect, om2k_connect_cmd, "connect-command", "Connect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disconnect, om2k_disconnect_cmd, "disconnect-command", "Disconnect the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_enable, om2k_enable_cmd, "enable-request", "Enable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_enable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_disable, om2k_disable_cmd, "disable-request", "Disable the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_disable_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_op_info, om2k_op_info_cmd, "operational-info <0-1>", "Set operational information\n" "Set operational info to 0 or 1\n") { struct oml_node_state *oms = vty->index; int oper = atoi(argv[0]); abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); return CMD_SUCCESS; } DEFUN(om2k_test, om2k_test_cmd, "test-request", "Test the MO\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_test_req(oms->bts, &oms->mo); return CMD_SUCCESS; } DEFUN(om2k_cap_req, om2k_cap_req_cmd, "capabilities-request", "Request MO capabilities\n") { struct oml_node_state *oms = vty->index; abis_om2k_tx_cap_req(oms->bts, &oms->mo); return CMD_SUCCESS; } static struct con_group *con_group_find_or_create(struct gsm_bts *bts, uint8_t cg) { struct con_group *ent; llist_for_each_entry(ent, &bts->rbs2000.con.conn_groups, list) { if (ent->cg == cg) return ent; } ent = talloc_zero(bts, struct con_group); ent->bts = bts; ent->cg = cg; INIT_LLIST_HEAD(&ent->paths); llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); return ent; } static int con_group_del(struct gsm_bts *bts, uint8_t cg_id) { struct con_group *cg, *cg2; llist_for_each_entry_safe(cg, cg2, &bts->rbs2000.con.conn_groups, list) { if (cg->cg == cg_id) { llist_del(&cg->list); talloc_free(cg); return 0; }; } return -ENOENT; } static void con_group_add_path(struct con_group *cg, uint16_t ccp, uint8_t ci, uint8_t tag, uint8_t tei) { struct con_path *cp = talloc_zero(cg, struct con_path); cp->ccp = ccp; cp->ci = ci; cp->tag = tag; cp->tei = tei; llist_add(&cp->list, &cg->paths); } static int con_group_del_path(struct con_group *cg, uint16_t ccp, uint8_t ci, uint8_t tag, uint8_t tei) { struct con_path *cp, *cp2; llist_for_each_entry_safe(cp, cp2, &cg->paths, list) { if (cp->ccp == ccp && cp->ci == ci && cp->tag == tag && cp->tei == tei) { llist_del(&cp->list); talloc_free(cp); return 0; } } return -ENOENT; } DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd, "con-connection-group <1-31>", "Configure a CON (Concentrator) Connection Group\n" "CON Connection Group Number\n") { struct gsm_bts *bts = vty->index; struct con_group *cg; uint8_t cgid = atoi(argv[0]); if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% CON MO only exists in RBS2000%s", VTY_NEWLINE); return CMD_WARNING; } cg = con_group_find_or_create(bts, cgid); if (!cg) { vty_out(vty, "%% Cannot create CON Group%s", VTY_NEWLINE); return CMD_WARNING; } vty->node = OM2K_CON_GROUP_NODE; vty->index = cg; return CMD_SUCCESS; } DEFUN(del_om2k_con_group, del_om2k_con_group_cmd, "del-connection-group <1-31>", "Delete a CON (Concentrator) Connection Group\n" "CON Connection Group Number\n") { struct gsm_bts *bts = vty->index; int rc; uint8_t cgid = atoi(argv[0]); if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% CON MO only exists in RBS2000%s", VTY_NEWLINE); return CMD_WARNING; } rc = con_group_del(bts, cgid); if (rc != 0) { vty_out(vty, "%% Cannot delete CON Group%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define CON_PATH_HELP "CON Path (In/Out)\n" \ "Add CON Path to Concentration Group\n" \ "Delete CON Path from Concentration Group\n" \ "CON Conection Point\n" \ "Contiguity Index\n" \ DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd, "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>", CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n") { struct con_group *cg = vty->index; uint16_t ccp = atoi(argv[1]); uint8_t ci = atoi(argv[2]); uint8_t tei = atoi(argv[3]); if (!strcmp(argv[0], "add")) con_group_add_path(cg, ccp, ci, 0, tei); else { if (con_group_del_path(cg, ccp, ci, 0, tei) < 0) { vty_out(vty, "%% No matching CON Path%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd, "con-path (add|del) <0-2047> <0-255> concentrated <1-16>", CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n") { struct con_group *cg = vty->index; uint16_t ccp = atoi(argv[1]); uint8_t ci = atoi(argv[2]); uint8_t tag = atoi(argv[3]); if (!strcmp(argv[0], "add")) con_group_add_path(cg, ccp, ci, tag, 0xff); else { if (con_group_del_path(cg, ccp, ci, tag, 0xff) < 0) { vty_out(vty, "%% No matching CON list entry%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd, "abis-lower-transport (single-timeslot|super-channel)", "Configure thee Abis Lower Transport\n" "Single Timeslot (classic Abis)\n" "SuperChannel (Packet Abis)\n") { struct gsm_bts *bts = vty->index; if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% Command only works for RBS2000%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "super-channel")) bts->rbs2000.use_superchannel = 1; else bts->rbs2000.use_superchannel = 0; return CMD_SUCCESS; } DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", "Interface Switch Connection List\n" "Add to IS list\n" "Delete from IS list\n" "ICP1\n" "ICP2\n" "Contiguity Index\n") { struct gsm_bts *bts = vty->index; uint16_t icp1 = atoi(argv[1]); uint16_t icp2 = atoi(argv[2]); uint8_t ci = atoi(argv[3]); struct is_conn_group *grp, *grp2; if (bts->type != GSM_BTS_TYPE_RBS2000) { vty_out(vty, "%% IS MO only exists in RBS2000%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "add")) { grp = talloc_zero(bts, struct is_conn_group); grp->icp1 = icp1; grp->icp2 = icp2; grp->ci = ci; llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); } else { llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { if (grp->icp1 == icp1 && grp->icp2 == icp2 && grp->ci == ci) { llist_del(&grp->list); talloc_free(grp); return CMD_SUCCESS; } } vty_out(vty, "%% No matching IS Conn Group found!%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(om2k_conf_req, om2k_conf_req_cmd, "configuration-request", "Send the configuration request for current MO\n") { struct oml_node_state *oms = vty->index; struct gsm_bts *bts = oms->bts; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; switch (oms->mo.class) { case OM2K_MO_CLS_IS: abis_om2k_tx_is_conf_req(bts); break; case OM2K_MO_CLS_TS: trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); if (!trx) { vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, oms->mo.assoc_so, VTY_NEWLINE); return CMD_WARNING; } if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { vty_out(vty, "%% Timeslot %u out of range%s", oms->mo.inst, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[oms->mo.inst]; abis_om2k_tx_ts_conf_req(ts); break; case OM2K_MO_CLS_RX: case OM2K_MO_CLS_TX: case OM2K_MO_CLS_TRXC: trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); if (!trx) { vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, oms->mo.inst, VTY_NEWLINE); return CMD_WARNING; } switch (oms->mo.class) { case OM2K_MO_CLS_RX: abis_om2k_tx_rx_conf_req(trx); break; case OM2K_MO_CLS_TX: abis_om2k_tx_tx_conf_req(trx); break; default: break; } break; case OM2K_MO_CLS_TF: abis_om2k_tx_tf_conf_req(bts); break; default: vty_out(vty, "%% Don't know how to configure MO%s", VTY_NEWLINE); } return CMD_SUCCESS; } static void dump_con_group(struct vty *vty, struct con_group *cg) { struct con_path *cp; llist_for_each_entry(cp, &cg->paths, list) { vty_out(vty, " con-path add %u %u ", cp->ccp, cp->ci); if (cp->tei == 0xff) { vty_out(vty, "concentrated %u%s", cp->tag, VTY_NEWLINE); } else { vty_out(vty, "deconcentrated %u%s", cp->tei, VTY_NEWLINE); } } } void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) { struct is_conn_group *igrp; struct con_group *cgrp; llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) vty_out(vty, " is-connection-list add %u %u %u%s", igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { vty_out(vty, " con-connection-group %u%s", cgrp->cg, VTY_NEWLINE); dump_con_group(vty, cgrp); } if (bts->rbs2000.use_superchannel) vty_out(vty, " abis-lower-transport super-channel%s", VTY_NEWLINE); } int abis_om2k_vty_init(void) { install_element(ENABLE_NODE, &om2k_class_inst_cmd); install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); install_node(&om2k_node, dummy_config_write); install_element(OM2K_NODE, &om2k_reset_cmd); install_element(OM2K_NODE, &om2k_start_cmd); install_element(OM2K_NODE, &om2k_status_cmd); install_element(OM2K_NODE, &om2k_connect_cmd); install_element(OM2K_NODE, &om2k_disconnect_cmd); install_element(OM2K_NODE, &om2k_enable_cmd); install_element(OM2K_NODE, &om2k_disable_cmd); install_element(OM2K_NODE, &om2k_op_info_cmd); install_element(OM2K_NODE, &om2k_test_cmd); install_element(OM2K_NODE, &om2k_cap_req_cmd); install_element(OM2K_NODE, &om2k_conf_req_cmd); install_node(&om2k_con_group_node, dummy_config_write); install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_dec_cmd); install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_conc_cmd); install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); install_element(BTS_NODE, &cfg_bts_alt_mode_cmd); install_element(BTS_NODE, &cfg_om2k_con_group_cmd); install_element(BTS_NODE, &del_om2k_con_group_cmd); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/abis_rsl.c000066400000000000000000002537071332665256100175370ustar00rootroot00000000000000/* GSM Radio Signalling Link messages on the A-bis interface * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (C) 2008-2010 by Harald Welte * (C) 2012 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 #include #include #include #include #include #include #include #include #include #include #define RSL_ALLOC_SIZE 1024 #define RSL_ALLOC_HEADROOM 128 enum sacch_deact { SACCH_NONE, SACCH_DEACTIVATE, }; static int rsl_send_imm_assignment(struct gsm_lchan *lchan); static void error_timeout_cb(void *data); static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts); static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc); static void dyn_ts_switchover_complete(struct gsm_lchan *lchan); static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan, struct gsm_meas_rep *resp) { struct lchan_signal_data sig; sig.lchan = lchan; sig.mr = resp; osmo_signal_dispatch(SS_LCHAN, sig_no, &sig); } static void do_lchan_free(struct gsm_lchan *lchan) { /* We start the error timer to make the channel available again */ if (lchan->state == LCHAN_S_REL_ERR) { osmo_timer_setup(&lchan->error_timer, error_timeout_cb, lchan); osmo_timer_schedule(&lchan->error_timer, lchan->ts->trx->bts->network->T3111 + 2, 0); } else { rsl_lchan_set_state(lchan, LCHAN_S_NONE); } lchan_free(lchan); } static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan) { OSMO_ASSERT(bts); if (lchan->type == GSM_LCHAN_TCH_H) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_AMR: rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_H]); break; case GSM48_CMODE_SPEECH_V1: rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_V1_HR]); break; default: break; } } else if (lchan->type == GSM_LCHAN_TCH_F) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_AMR: rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_F]); break; case GSM48_CMODE_SPEECH_V1: rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_V1_FR]); break; case GSM48_CMODE_SPEECH_EFR: rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_EFR]); break; default: break; } } } static uint8_t mdisc_by_msgtype(uint8_t msg_type) { /* mask off the transparent bit ? */ msg_type &= 0xfe; if ((msg_type & 0xf0) == 0x00) return ABIS_RSL_MDISC_RLL; if ((msg_type & 0xf0) == 0x10) { if (msg_type >= 0x19 && msg_type <= 0x22) return ABIS_RSL_MDISC_TRX; else return ABIS_RSL_MDISC_COM_CHAN; } if ((msg_type & 0xe0) == 0x20) return ABIS_RSL_MDISC_DED_CHAN; return ABIS_RSL_MDISC_LOC; } static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, uint8_t msg_type) { dh->c.msg_discr = mdisc_by_msgtype(msg_type); dh->c.msg_type = msg_type; dh->ie_chan = RSL_IE_CHAN_NR; } /* call rsl_lchan_lookup and set the log context */ static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, const char *log_name) { int rc; struct gsm_lchan *lchan = rsl_lchan_lookup(trx, chan_nr, &rc); if (!lchan) { LOGP(DRSL, LOGL_ERROR, "%sunknown chan_nr=0x%02x\n", log_name, chan_nr); return NULL; } if (rc < 0) LOGP(DRSL, LOGL_ERROR, "%s %smismatching chan_nr=0x%02x\n", gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr); return lchan; } static struct msgb *rsl_msgb_alloc(void) { return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, "RSL"); } static void pad_macblock(uint8_t *out, const uint8_t *in, int len) { memcpy(out, in, len); if (len < GSM_MACBLOCK_LEN) memset(out+len, 0x2b, GSM_MACBLOCK_LEN - len); } /* Chapter 9.3.7: Encryption Information */ static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) { *out++ = lchan->encr.alg_id & 0xff; if (lchan->encr.key_len) memcpy(out, lchan->encr.key, lchan->encr.key_len); return lchan->encr.key_len + 1; } static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) { int i; LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", cause_v[0], rsl_err_name(cause_v[0])); for (i = 1; i < cause_len-1; i++) LOGPC(DRSL, lvl, "%02x ", cause_v[i]); } static void lchan_act_tmr_cb(void *data) { struct gsm_lchan *lchan = data; rsl_lchan_mark_broken(lchan, "activation timeout"); lchan_free(lchan); } static void lchan_deact_tmr_cb(void *data) { struct gsm_lchan *lchan = data; rsl_lchan_mark_broken(lchan, "de-activation timeout"); lchan_free(lchan); } /* Send a BCCH_INFO message as per Chapter 8.5.1 */ int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; const struct gsm_bts *bts = trx->bts; struct msgb *msg = rsl_msgb_alloc(); uint8_t type = osmo_sitype2rsl(si_type); if (bts->c0 != trx) LOGP(DRR, LOGL_ERROR, "Attempting to set BCCH SI%s on wrong BTS%u/TRX%u\n", get_value_string(osmo_sitype_strs, si_type), bts->nr, trx->nr); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); init_dchan_hdr(dh, RSL_MT_BCCH_INFO); dh->chan_nr = RSL_CHAN_BCCH; if (trx->bts->type == GSM_BTS_TYPE_RBS2000 && type == RSL_SYSTEM_INFO_13) { /* Ericsson proprietary encoding of SI13 */ msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, RSL_ERIC_SYSTEM_INFO_13); if (data) msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); msgb_tv_put(msg, RSL_IE_ERIC_BCCH_MAPPING, 0x00); } else { /* Normal encoding */ msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); if (data) msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); } msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, const uint8_t *data, int len) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = RSL_MT_SACCH_FILL; msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); if (data) msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); uint8_t chan_nr = gsm_lchan2chan_nr(lchan); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); if (data) msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); db = abs(db); if (db > 30) return -EINVAL; msg = rsl_msgb_alloc(); lchan->bs_power = db/2; if (fpc) lchan->bs_power |= 0x10; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); int ctl_lvl; ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); if (ctl_lvl < 0) return ctl_lvl; msg = rsl_msgb_alloc(); lchan->ms_power = ctl_lvl; if (fpc) lchan->ms_power |= 0x20; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, struct gsm_lchan *lchan) { memset(cm, 0, sizeof(*cm)); /* FIXME: what to do with data calls ? */ cm->dtx_dtu = 0; if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) cm->dtx_dtu |= RSL_CMOD_DTXu; if (lchan->ts->trx->bts->dtxd) cm->dtx_dtu |= RSL_CMOD_DTXd; /* set TCH Speech/Data */ cm->spd_ind = lchan->rsl_cmode; if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && lchan->tch_mode != GSM48_CMODE_SIGN) LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " "but tch_mode != signalling\n"); switch (lchan->type) { case GSM_LCHAN_SDCCH: cm->chan_rt = RSL_CMOD_CRT_SDCCH; break; case GSM_LCHAN_TCH_F: cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; break; case GSM_LCHAN_TCH_H: cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; break; case GSM_LCHAN_NONE: case GSM_LCHAN_UNKNOWN: default: LOGP(DRSL, LOGL_ERROR, "unsupported activation lchan->type %u %s\n", lchan->type, gsm_lchant_name(lchan->type)); return -EINVAL; } switch (lchan->tch_mode) { case GSM48_CMODE_SIGN: cm->chan_rate = 0; break; case GSM48_CMODE_SPEECH_V1: cm->chan_rate = RSL_CMOD_SP_GSM1; break; case GSM48_CMODE_SPEECH_EFR: cm->chan_rate = RSL_CMOD_SP_GSM2; break; case GSM48_CMODE_SPEECH_AMR: cm->chan_rate = RSL_CMOD_SP_GSM3; break; case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: switch (lchan->csd_mode) { case LCHAN_CSD_M_NT: /* non-transparent CSD with RLP */ switch (lchan->tch_mode) { case GSM48_CMODE_DATA_14k5: cm->chan_rate = RSL_CMOD_SP_NT_14k5; break; case GSM48_CMODE_DATA_12k0: cm->chan_rate = RSL_CMOD_SP_NT_12k0; break; case GSM48_CMODE_DATA_6k0: cm->chan_rate = RSL_CMOD_SP_NT_6k0; break; default: LOGP(DRSL, LOGL_ERROR, "unsupported lchan->tch_mode %u\n", lchan->tch_mode); return -EINVAL; } break; /* transparent data services below */ case LCHAN_CSD_M_T_1200_75: cm->chan_rate = RSL_CMOD_CSD_T_1200_75; break; case LCHAN_CSD_M_T_600: cm->chan_rate = RSL_CMOD_CSD_T_600; break; case LCHAN_CSD_M_T_1200: cm->chan_rate = RSL_CMOD_CSD_T_1200; break; case LCHAN_CSD_M_T_2400: cm->chan_rate = RSL_CMOD_CSD_T_2400; break; case LCHAN_CSD_M_T_9600: cm->chan_rate = RSL_CMOD_CSD_T_9600; break; case LCHAN_CSD_M_T_14400: cm->chan_rate = RSL_CMOD_CSD_T_14400; break; case LCHAN_CSD_M_T_29000: cm->chan_rate = RSL_CMOD_CSD_T_29000; break; case LCHAN_CSD_M_T_32000: cm->chan_rate = RSL_CMOD_CSD_T_32000; break; default: LOGP(DRSL, LOGL_ERROR, "unsupported lchan->csd_mode %u\n", lchan->csd_mode); return -EINVAL; } break; default: LOGP(DRSL, LOGL_ERROR, "unsupported lchan->tch_mode %u\n", lchan->tch_mode); return -EINVAL; } return 0; } static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg) { if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], lchan->mr_bts_lv + 1); } static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type) { switch (type) { case GSM_LCHAN_TCH_F: return GSM_PCHAN_TCH_F; case GSM_LCHAN_TCH_H: return GSM_PCHAN_TCH_H; case GSM_LCHAN_NONE: case GSM_LCHAN_PDTCH: /* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only * used in osmo-bts. Maybe set PDTCH and drop the NONE case * here. */ return GSM_PCHAN_PDCH; default: return GSM_PCHAN_UNKNOWN; } } /*! Tx simplified channel activation message for non-standard PDCH type. */ static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan) { struct msgb *msg; struct abis_rsl_dchan_hdr *dh; /* This might be called after release of the second lchan of a TCH/H, * but PDCH activation must always happen on the first lchan. Make sure * the calling code passes the correct lchan. */ OSMO_ASSERT(lchan == lchan->ts->lchan); rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH); msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH); if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_RBS2000 && lchan->ts->trx->bts->rbs2000.use_superchannel) { const uint8_t eric_pgsl_tmr[] = { 30, 1 }; msgb_tv_fixed_put(msg, RSL_IE_ERIC_PGSL_TIMERS, sizeof(eric_pgsl_tmr), eric_pgsl_tmr); } msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.1 */ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; uint8_t *len; uint8_t ta; struct rsl_ie_chan_mode cm; struct gsm48_chan_desc cd; /* If a TCH_F/PDCH TS is in PDCH mode, deactivate PDCH first. */ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH && (lchan->ts->flags & TS_F_PDCH_ACTIVE)) { /* store activation type and handover reference */ lchan->dyn.act_type = act_type; lchan->dyn.ho_ref = ho_ref; return rsl_ipacc_pdch_activate(lchan->ts, 0); } /* * If necessary, release PDCH on dynamic TS. Note that sending a * release here is only necessary when in PDCH mode; for TCH types, an * RSL RF Chan Release is initiated by the BTS when a voice call ends, * so when we reach this, it will already be released. If a dyn TS is * in PDCH mode, it is still active and we need to initiate a release * from the BSC side here. * * If pchan_is != pchan_want, the PDCH has already been taken down and * the switchover now needs to enable the TCH lchan. * * To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send * a chan activ with the new lchan type, because it will already be * released. */ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) { enum gsm_phys_chan_config pchan_want; pchan_want = pchan_for_lchant(lchan->type); if (lchan->ts->dyn.pchan_is != pchan_want) { /* * Make sure to record on lchan[0] so that we'll find * it after the PDCH release. */ struct gsm_lchan *lchan0 = lchan->ts->lchan; lchan0->dyn.act_type = act_type, lchan0->dyn.ho_ref = ho_ref; lchan0->dyn.rqd_ref = lchan->rqd_ref; lchan0->dyn.rqd_ta = lchan->rqd_ta; lchan->rqd_ref = NULL; lchan->rqd_ta = 0; DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n", gsm_lchan_name(lchan0), lchan0->rqd_ref, lchan0->rqd_ta); return dyn_ts_switchover_start(lchan->ts, pchan_want); } } DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n", gsm_ts_and_pchan_name(lchan->ts), rsl_act_type_name(act_type)); if (act_type == RSL_ACT_OSMO_PDCH) { if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) { LOGP(DRSL, LOGL_ERROR, "%s PDCH channel activation only allowed on %s\n", gsm_ts_and_pchan_name(lchan->ts), gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); return -EINVAL; } return rsl_chan_activate_lchan_as_pdch(lchan); } if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) { LOGP(DRSL, LOGL_ERROR, "%s Expected PDCH activation kind\n", gsm_ts_and_pchan_name(lchan->ts)); return -EINVAL; } rc = channel_mode_from_lchan(&cm, lchan); if (rc < 0) { LOGP(DRSL, LOGL_ERROR, "%s Cannot find channel mode from lchan type\n", gsm_ts_and_pchan_name(lchan->ts)); return rc; } rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); ta = lchan->rqd_ta; /* BS11 requires TA shifted by 2 bits */ if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11) ta <<= 2; memset(&cd, 0, sizeof(cd)); gsm48_lchan2chan_desc(&cd, lchan); msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) dh->chan_nr = gsm_lchan_as_pchan2chan_nr( lchan, lchan->ts->dyn.pchan_want); else dh->chan_nr = gsm_lchan2chan_nr(lchan); msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), (uint8_t *) &cm); /* * The Channel Identification is needed for Phase1 phones * and it contains the GSM48 Channel Description and the * Mobile Allocation. The GSM 08.58 asks for the Mobile * Allocation to have a length of zero. We are using the * msgb_l3len to calculate the length of both messages. */ msgb_v_put(msg, RSL_IE_CHAN_IDENT); len = msgb_put(msg, 1); msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); if (lchan->ts->hopping.enabled) msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, lchan->ts->hopping.ma_data); else msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); /* update the calculated size */ msg->l3h = len + 1; *len = msgb_l3len(msg); if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { uint8_t encr_info[MAX_A5_KEY_LEN+2]; rc = build_encr_info(encr_info, lchan); if (rc > 0) msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } switch (act_type) { case RSL_ACT_INTER_ASYNC: case RSL_ACT_INTER_SYNC: msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); break; default: break; } msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); mr_config_for_bts(lchan, msg); msg->dst = lchan->ts->trx->rsl_link; rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_TOTAL]); rc = abis_rsl_sendmsg(msg); if (!rc) rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); return rc; } /* Chapter 8.4.9: Modify channel mode on BTS side */ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); struct rsl_ie_chan_mode cm; rc = channel_mode_from_lchan(&cm, lchan); if (rc < 0) return rc; msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); dh->chan_nr = chan_nr; msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), (uint8_t *) &cm); if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { uint8_t encr_info[MAX_A5_KEY_LEN+2]; rc = build_encr_info(encr_info, lchan); if (rc > 0) msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); } mr_config_for_bts(lchan, msg); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.6: Send the encryption command with given L3 info */ int rsl_encryption_cmd(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh; struct gsm_lchan *lchan = msg->lchan; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t encr_info[MAX_A5_KEY_LEN+2]; uint8_t l3_len = msg->len; int rc; /* First push the L3 IE tag and length */ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); /* then the link identifier (SAPI0, main sign link) */ msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); /* then encryption information */ rc = build_encr_info(encr_info, lchan); if (rc <= 0) return rc; msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); /* and finally the DCHAN header */ dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_ENCR_CMD); dh->chan_nr = chan_nr; msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ int rsl_deact_sacch(struct gsm_lchan *lchan) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); dh->chan_nr = gsm_lchan2chan_nr(lchan); msg->lchan = lchan; msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); return abis_rsl_sendmsg(msg); } static bool dyn_ts_should_switch_to_pdch(struct gsm_bts_trx_ts *ts) { int ss; if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) return false; if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) return false; /* Already in PDCH mode? */ if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) return false; /* See if all lchans are released. */ for (ss = 0; ss < ts_subslots(ts); ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->state != LCHAN_S_NONE) { DEBUGP(DRSL, "%s lchan %u still in use" " (type=%s,state=%s)\n", gsm_ts_and_pchan_name(ts), lc->nr, gsm_lchant_name(lc->type), gsm_lchans_name(lc->state)); /* An lchan is still used. */ return false; } } /* All channels are released, go to PDCH mode. */ DEBUGP(DRSL, "%s back to PDCH\n", gsm_ts_and_pchan_name(ts)); return true; } static void error_timeout_cb(void *data) { struct gsm_lchan *lchan = data; if (lchan->state != LCHAN_S_REL_ERR) { LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", gsm_lchan_name(lchan), lchan->state); return; } /* go back to the none state */ LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan)); rsl_lchan_set_state(lchan, LCHAN_S_NONE); /* Put PDCH channel back into PDCH mode, if GPRS is enabled */ if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH && lchan->ts->trx->bts->gprs.mode != BTS_GPRS_NONE) rsl_ipacc_pdch_activate(lchan->ts, 1); if (dyn_ts_should_switch_to_pdch(lchan->ts)) dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); } static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); /* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, enum sacch_deact deact_sacch) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg; int rc; /* Stop timers that should lead to a channel release */ osmo_timer_del(&lchan->T3109); if (lchan->state == LCHAN_S_REL_ERR) { LOGP(DRSL, LOGL_NOTICE, "%s is in error state, not sending release.\n", gsm_lchan_name(lchan)); return -1; } msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); dh->chan_nr = gsm_lchan2chan_nr(lchan); msg->lchan = lchan; msg->dst = lchan->ts->trx->rsl_link; if (error) DEBUGP(DRSL, "%s RF Channel Release due to error: %d\n", gsm_lchan_name(lchan), error); else DEBUGP(DRSL, "%s RF Channel Release\n", gsm_lchan_name(lchan)); if (error) { /* * FIXME: GSM 04.08 gives us two options for the abnormal * chanel release. This can be either like in the non-existent * sub-lcuase 3.5.1 or for the main signalling link deactivate * the SACCH, start timer T3109 and consider the channel as * released. * * This code is doing the later for all raido links and not * only the main link. Right now all SAPIs are released on the * local end, the SACCH will be de-activated and right now the * T3111 will be started. First T3109 should be started and then * the T3111. * * TODO: Move this out of the function. */ /* * sacch de-activate and "local end release" */ if (deact_sacch == SACCH_DEACTIVATE) rsl_deact_sacch(lchan); rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); /* * TODO: start T3109 now. */ rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); } /* Start another timer or assume the BTS sends a ACK/NACK? */ osmo_timer_setup(&lchan->act_timer, lchan_deact_tmr_cb, lchan); osmo_timer_schedule(&lchan->act_timer, 4, 0); rc = abis_rsl_sendmsg(msg); /* BTS will respond by RF CHAN REL ACK */ return rc; } /* * Special handling for channel releases in the error case. */ static int rsl_rf_chan_release_err(struct gsm_lchan *lchan) { enum sacch_deact sacch_deact; if (lchan->state != LCHAN_S_ACTIVE) return 0; switch (ts_pchan(lchan->ts)) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4_CBCH: case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: sacch_deact = SACCH_DEACTIVATE; break; default: sacch_deact = SACCH_NONE; break; } return rsl_rf_chan_release(lchan, 1, sacch_deact); } static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) { struct gsm_bts_trx_ts *ts = lchan->ts; DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); /* Stop all pending timers */ osmo_timer_del(&lchan->act_timer); osmo_timer_del(&lchan->T3111); /* * The BTS didn't respond within the timeout to our channel * release request and we have marked the channel as broken. * Now we do receive an ACK and let's be conservative. If it * is a sysmoBTS we know that only one RF Channel Release ACK * will be sent. So let's "repair" the channel. */ if (lchan->state == LCHAN_S_BROKEN) { int do_free = is_sysmobts_v2(ts->trx->bts); LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK for broken channel. %s.\n", gsm_lchan_name(lchan), do_free ? "Releasing it" : "Keeping it broken"); if (do_free) do_lchan_free(lchan); if (dyn_ts_should_switch_to_pdch(lchan->ts)) dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); return 0; } if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); do_lchan_free(lchan); /* * Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending * transitions in these cases: * * a) after PDCH was released due to switchover request, activate TCH. * BSC initiated this switchover, so dyn.pchan_is != pchan_want and * lchan->type has been set to the desired GSM_LCHAN_*. * * b) Voice call ended and a TCH is released. If the TS is now unused, * switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because * we're only just notified and may decide to switch to PDCH now. */ if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n", gsm_ts_and_pchan_name(ts), lchan->nr); /* (a) */ if (ts->dyn.pchan_is != ts->dyn.pchan_want) return dyn_ts_switchover_continue(ts); /* (b) */ if (dyn_ts_should_switch_to_pdch(ts)) return dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); } /* * Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was * released successfully. If in error, the PDCH ACT will follow after * T3111 in error_timeout_cb(). * * Any state other than LCHAN_S_REL_ERR became LCHAN_S_NONE after above * do_lchan_free(). Assert this, because that's what ensures a PDCH ACT * on a TCH/F_PDCH TS in all cases. * * If GPRS is disabled, always skip the PDCH ACT. */ OSMO_ASSERT(lchan->state == LCHAN_S_NONE || lchan->state == LCHAN_S_REL_ERR); if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) return 0; if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && lchan->state == LCHAN_S_NONE) return rsl_ipacc_pdch_activate(ts, 1); return 0; } int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs) { struct abis_rsl_dchan_hdr *dh; struct msgb *msg = rsl_msgb_alloc(); dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_PAGING_CMD); dh->chan_nr = RSL_CHAN_PCH_AGCH; msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); /* Ericsson wants to have this IE in case a paging message * relates to packet paging */ if (bts->type == GSM_BTS_TYPE_RBS2000 && is_gprs) msgb_tv_put(msg, RSL_IE_ERIC_PACKET_PAG_IND, 0); msg->dst = bts->c0->rsl_link; return abis_rsl_sendmsg(msg); } int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) { int i, len = strlen(str_in); for (i = 0; i < len; i++) { int num = str_in[i] - 0x30; if (num < 0 || num > 9) return -1; if (i % 2 == 0) bcd_out[i/2] = num; else bcd_out[i/2] |= (num << 4); } return 0; } /* Chapter 8.5.6 */ struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t *val) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint8_t buf[GSM_MACBLOCK_LEN]; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); dh->chan_nr = RSL_CHAN_PCH_AGCH; switch (bts->type) { case GSM_BTS_TYPE_BS11: msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); break; default: /* If phase 2, construct a FULL_IMM_ASS_INFO */ pad_macblock(buf, val, len); msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, GSM_MACBLOCK_LEN, buf); break; } msg->dst = bts->c0->rsl_link; return msg; } /* Chapter 8.5.6 */ int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) { struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); if (!msg) return 1; return abis_rsl_sendmsg(msg); } /* Chapter 8.5.6 */ int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val) { struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); if (!msg) return 1; /* ericsson can handle a reference at the end of the message which is used in * the confirm message. The confirm message is only sent if the trailer is present */ msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID); msgb_put_u32(msg, tlli); return abis_rsl_sendmsg(msg); } /* Send Siemens specific MS RF Power Capability Indication */ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->chan_nr = gsm_lchan2chan_nr(lchan); msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", gsm_lchan_name(lchan), *(uint8_t *)mrpci); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Send "DATA REQUEST" message with given L3 Info payload */ /* Chapter 8.3.1 */ int rsl_data_request(struct msgb *msg, uint8_t link_id) { if (msg->lchan == NULL) { LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); return -EINVAL; } rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), link_id, 1); msg->dst = msg->lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /* Send "ESTABLISH REQUEST" message with given L3 Info payload */ /* Chapter 8.3.1 */ int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) { struct msgb *msg; msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), link_id, 0); msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", gsm_lchan_name(lchan), link_id); return abis_rsl_sendmsg(msg); } static void rsl_handle_release(struct gsm_lchan *lchan); /* Special work handler to handle missing RSL_MT_REL_CONF message from * Nokia InSite BTS */ static void lchan_rel_work_cb(void *data) { struct gsm_lchan *lchan = data; int sapi; for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { if (lchan->sapis[sapi] == LCHAN_SAPI_REL) lchan->sapis[sapi] = LCHAN_SAPI_UNUSED; } rsl_handle_release(lchan); } /* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. This is what higher layers should call. The BTS then responds with RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls lchan_free() */ int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, enum rsl_rel_mode release_mode) { struct msgb *msg; msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan), link_id, 0); /* 0 is normal release, 1 is local end */ msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode); /* FIXME: start some timer in case we don't receive a REL ACK ? */ msg->dst = lchan->ts->trx->rsl_link; DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", gsm_lchan_name(lchan), link_id, release_mode); abis_rsl_sendmsg(msg); /* Do not wait for Nokia BTS to send the confirm. */ if (is_nokia_bts(lchan->ts->trx->bts) && lchan->ts->trx->bts->nokia.no_loc_rel_cnf && release_mode == RSL_REL_LOCAL_END) { DEBUGP(DRLL, "Scheduling release, becasuse Nokia InSite BTS does not send a RELease CONFirm.\n"); lchan->sapis[link_id & 0x7] = LCHAN_SAPI_REL; osmo_timer_setup(&lchan->rel_work, lchan_rel_work_cb, lchan); osmo_timer_schedule(&lchan->rel_work, 0, 0); } return 0; } int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason) { LOGP(DRSL, LOGL_ERROR, "%s %s lchan broken: %s\n", gsm_lchan_name(lchan), gsm_lchant_name(lchan->type), reason); rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); lchan->broken_reason = reason; return 0; } int rsl_lchan_set_state_with_log(struct gsm_lchan *lchan, enum gsm_lchan_state state, const char *file, unsigned line) { if (lchan->state != state) LOGPSRC(DRSL, LOGL_DEBUG, file, line, "%s state %s -> %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state), gsm_lchans_name(state)); lchan->state = state; return 0; } /* Chapter 8.4.2: Channel Activate Acknowledge */ static int rsl_rx_chan_act_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; struct gsm_bts_trx_ts *ts = lchan->ts; /* BTS has confirmed channel activation, we now need * to assign the activated channel to the MS */ if (rslh->ie_chan != RSL_IE_CHAN_NR) return -EINVAL; osmo_timer_del(&lchan->act_timer); if (lchan->state == LCHAN_S_BROKEN) { int do_release = is_sysmobts_v2(ts->trx->bts); LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel. %s\n", gsm_lchan_name(lchan), do_release ? "Releasing it" : "Keeping it broken"); if (do_release) { talloc_free(lchan->rqd_ref); lchan->rqd_ref = NULL; lchan->rqd_ta = 0; rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { /* * lchan_act_tmr_cb() already called * lchan_free() and cleared the lchan->type, so * calling dyn_ts_switchover_complete() here * would not have the desired effect of * mimicking an activated lchan that we can * release. Instead hack the dyn ts state to * make sure that rsl_rx_rf_chan_rel_ack() will * switch back to PDCH, i.e. have pchan_is == * pchan_want, both != GSM_PCHAN_PDCH: */ ts->dyn.pchan_is = GSM_PCHAN_NONE; ts->dyn.pchan_want = GSM_PCHAN_NONE; } rsl_rf_chan_release(msg->lchan, 0, SACCH_NONE); } return 0; } if (lchan->state != LCHAN_S_ACT_REQ) LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); rsl_lchan_set_state(lchan, LCHAN_S_ACTIVE); if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) dyn_ts_switchover_complete(lchan); if (lchan->rqd_ref) { rsl_send_imm_assignment(lchan); talloc_free(lchan->rqd_ref); lchan->rqd_ref = NULL; lchan->rqd_ta = 0; } send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL); /* Update bts attributes inside the PCU */ if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH || ts->pchan == GSM_PCHAN_TCH_F_PDCH || ts->pchan == GSM_PCHAN_PDCH) pcu_info_update(ts->trx->bts); return 0; } /* Chapter 8.4.3: Channel Activate NACK */ static int rsl_rx_chan_act_nack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; osmo_timer_del(&msg->lchan->act_timer); rate_ctr_inc(&msg->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_ACT_NACK]); if (msg->lchan->state == LCHAN_S_BROKEN) { LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK for broken channel.\n", gsm_lchan_name(msg->lchan)); return -1; } /* BTS has rejected channel activation ?!? */ if (dh->ie_chan != RSL_IE_CHAN_NR) { LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK, and chan nr mismatches\n", gsm_lchan_name(msg->lchan)); return -EINVAL; } rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK: ", gsm_lchan_name(msg->lchan)); print_rsl_cause(LOGL_ERROR, cause, TLVP_LEN(&tp, RSL_IE_CAUSE)); LOGPC(DRSL, LOGL_ERROR, "\n"); msg->lchan->error_cause = *cause; if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) { rsl_lchan_mark_broken(msg->lchan, "NACK on activation"); } else rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); } else { LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK, no cause IE\n", gsm_lchan_name(msg->lchan)); rsl_lchan_mark_broken(msg->lchan, "NACK on activation no IE"); } send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); return 0; } /* Chapter 8.4.4: Connection Failure Indication */ static int rsl_rx_conn_fail(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; struct tlv_parsed tp; uint8_t cause = 0; LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL in state %s ", gsm_lchan_name(msg->lchan), gsm_lchans_name(msg->lchan->state)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); cause = *TLVP_VAL(&tp, RSL_IE_CAUSE); } LOGPC(DRSL, LOGL_NOTICE, "\n"); rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL]); if (lchan->conn) osmo_fsm_inst_dispatch(lchan->conn->fi, GSCON_EV_RSL_CONN_FAIL, &cause); return 0; } static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, const char *prefix) { DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", prefix, rxlev2dbm(mru->full.rx_lev), prefix, rxlev2dbm(mru->sub.rx_lev)); DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); } static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) { int i; const char *name = ""; if (lchan && lchan->conn) { if (lchan->conn->bsub) name = bsc_subscr_name(lchan->conn->bsub); else name = lchan->name; } DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); if (mr->flags & MEAS_REP_F_DL_DTX) DEBUGPC(DMEAS, "DTXd "); print_meas_rep_uni(&mr->ul, "ul"); DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); if (mr->flags & MEAS_REP_F_MS_TO) DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); if (mr->flags & MEAS_REP_F_MS_L1) { DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); DEBUGPC(DMEAS, "L1_FPC=%u ", mr->flags & MEAS_REP_F_FPC ? 1 : 0); DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); } if (mr->flags & MEAS_REP_F_UL_DTX) DEBUGPC(DMEAS, "DTXu "); if (mr->flags & MEAS_REP_F_BA1) DEBUGPC(DMEAS, "BA1 "); if (!(mr->flags & MEAS_REP_F_DL_VALID)) DEBUGPC(DMEAS, "NOT VALID "); else print_meas_rep_uni(&mr->dl, "dl"); DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); if (mr->num_cell == 7) return; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); } } static struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) { struct gsm_meas_rep *meas_rep; meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; memset(meas_rep, 0, sizeof(*meas_rep)); meas_rep->lchan = lchan; lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) % ARRAY_SIZE(lchan->meas_rep); return meas_rep; } static int rsl_rx_meas_res(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); uint8_t len; const uint8_t *val; int rc; /* check if this channel is actually active */ /* FIXME: maybe this check should be way more generic/centralized */ if (msg->lchan->state != LCHAN_S_ACTIVE) { LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", gsm_lchan_name(msg->lchan)); return 0; } memset(mr, 0, sizeof(*mr)); mr->lchan = msg->lchan; rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) { LOGP(DRSL, LOGL_ERROR, "%s Measurement Report lacks mandatory IEs\n", gsm_lchan_name(mr->lchan)); return -EIO; } /* Mandatory Parts */ mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); if (len >= 3) { if (val[0] & 0x40) mr->flags |= MEAS_REP_F_DL_DTX; mr->ul.full.rx_lev = val[0] & 0x3f; mr->ul.sub.rx_lev = val[1] & 0x3f; mr->ul.full.rx_qual = val[2]>>3 & 0x7; mr->ul.sub.rx_qual = val[2] & 0x7; } mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); /* Optional Parts */ if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) { /* According to 3GPP TS 48.058 § MS Timing Offset = Timing Offset field - 63 */ mr->ms_timing_offset = *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET) - 63; mr->flags |= MEAS_REP_F_MS_TO; } if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { struct e1inp_sign_link *sign_link = msg->dst; val = TLVP_VAL(&tp, RSL_IE_L1_INFO); mr->flags |= MEAS_REP_F_MS_L1; mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); if (val[0] & 0x04) mr->flags |= MEAS_REP_F_FPC; mr->ms_l1.ta = val[1]; /* BS11 and Nokia reports TA shifted by 2 bits */ if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) mr->ms_l1.ta >>= 2; /* store TA for next assignment/handover */ mr->lchan->rqd_ta = mr->ms_l1.ta; } if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); rc = gsm48_parse_meas_rep(mr, msg); if (rc < 0) return rc; } mr->lchan->meas_rep_count++; mr->lchan->meas_rep_last_seen_nr = mr->nr; LOGP(DRSL, LOGL_DEBUG, "%s: meas_rep_count++=%d meas_rep_last_seen_nr=%u\n", gsm_lchan_name(mr->lchan), mr->lchan->meas_rep_count, mr->lchan->meas_rep_last_seen_nr); print_meas_rep(msg->lchan, mr); send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); return 0; } /* Chapter 8.4.7 */ static int rsl_rx_hando_det(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tp; DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) DEBUGPC(DRSL, "access delay = %u\n", *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); else DEBUGPC(DRSL, "\n"); send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); return 0; } static bool lchan_may_change_pdch(struct gsm_lchan *lchan, bool pdch_act) { struct gsm_bts_trx_ts *ts; OSMO_ASSERT(lchan); ts = lchan->ts; OSMO_ASSERT(ts); OSMO_ASSERT(ts->trx); OSMO_ASSERT(ts->trx->bts); if (lchan->ts->pchan != GSM_PCHAN_TCH_F_PDCH) { LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" " for channel that is no TCH/F_PDCH\n", gsm_lchan_name(lchan), gsm_pchan_name(ts->pchan), pdch_act? "ACT" : "DEACT"); return false; } if (lchan->state != LCHAN_S_NONE) { LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" " in unexpected state: %s\n", gsm_lchan_name(lchan), gsm_pchan_name(ts->pchan), pdch_act? "ACT" : "DEACT", gsm_lchans_name(lchan->state)); return false; } return true; } static int rsl_rx_pdch_act_ack(struct msgb *msg) { if (!lchan_may_change_pdch(msg->lchan, true)) return -EINVAL; msg->lchan->ts->flags |= TS_F_PDCH_ACTIVE; msg->lchan->ts->flags &= ~TS_F_PDCH_ACT_PENDING; return 0; } static int rsl_rx_pdch_deact_ack(struct msgb *msg) { if (!lchan_may_change_pdch(msg->lchan, false)) return -EINVAL; msg->lchan->ts->flags &= ~TS_F_PDCH_ACTIVE; msg->lchan->ts->flags &= ~TS_F_PDCH_DEACT_PENDING; rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn.act_type, msg->lchan->dyn.ho_ref); return 0; } static int abis_rsl_rx_dchan(struct msgb *msg) { struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); int rc = 0; char *ts_name; struct e1inp_sign_link *sign_link = msg->dst; msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, "Abis RSL rx DCHAN: "); if (!msg->lchan) return -1; ts_name = gsm_lchan_name(msg->lchan); switch (rslh->c.msg_type) { case RSL_MT_CHAN_ACTIV_ACK: DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); rc = rsl_rx_chan_act_ack(msg); count_codecs(sign_link->trx->bts, msg->lchan); break; case RSL_MT_CHAN_ACTIV_NACK: rc = rsl_rx_chan_act_nack(msg); break; case RSL_MT_CONN_FAIL: rc = rsl_rx_conn_fail(msg); break; case RSL_MT_MEAS_RES: rc = rsl_rx_meas_res(msg); break; case RSL_MT_HANDO_DET: rc = rsl_rx_hando_det(msg); break; case RSL_MT_RF_CHAN_REL_ACK: rc = rsl_rx_rf_chan_rel_ack(msg->lchan); break; case RSL_MT_MODE_MODIFY_ACK: count_codecs(sign_link->trx->bts, msg->lchan); DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); break; case RSL_MT_MODE_MODIFY_NACK: LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_MODE_MODIFY_NACK]); break; case RSL_MT_IPAC_PDCH_ACT_ACK: DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); rc = rsl_rx_pdch_act_ack(msg); break; case RSL_MT_IPAC_PDCH_ACT_NACK: LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]); break; case RSL_MT_IPAC_PDCH_DEACT_ACK: DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); rc = rsl_rx_pdch_deact_ack(msg); break; case RSL_MT_IPAC_PDCH_DEACT_NACK: LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]); break; case RSL_MT_PHY_CONTEXT_CONF: case RSL_MT_PREPROC_MEAS_RES: case RSL_MT_TALKER_DET: case RSL_MT_LISTENER_DET: case RSL_MT_REMOTE_CODEC_CONF_REP: case RSL_MT_MR_CODEC_MOD_ACK: case RSL_MT_MR_CODEC_MOD_NACK: case RSL_MT_MR_CODEC_MOD_PER: LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " "msg 0x%02x\n", ts_name, rslh->c.msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); break; default: LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", ts_name, rslh->c.msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); return -EINVAL; } return rc; } static int rsl_rx_error_rep(struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct tlv_parsed tp; struct e1inp_sign_link *sign_link = msg->dst; LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), TLVP_LEN(&tp, RSL_IE_CAUSE)); LOGPC(DRSL, LOGL_ERROR, "\n"); return 0; } static int abis_rsl_rx_trx(struct msgb *msg) { struct abis_rsl_common_hdr *rslh = msgb_l2(msg); struct e1inp_sign_link *sign_link = msg->dst; int rc = 0; switch (rslh->msg_type) { case RSL_MT_ERROR_REPORT: rc = rsl_rx_error_rep(msg); break; case RSL_MT_RF_RES_IND: /* interference on idle channels of TRX */ //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); break; case RSL_MT_OVERLOAD: /* indicate CCCH / ACCH / processor overload */ LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", gsm_trx_name(sign_link->trx)); break; case 0x42: /* Nokia specific: SI End ACK */ LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); break; case 0x43: /* Nokia specific: SI End NACK */ LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); break; default: LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); return -EINVAL; } return rc; } /* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ static void t3101_expired(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_NOTICE, "%s T3101 expired: no response to IMMEDIATE ASSIGN\n", gsm_lchan_name(lchan)); rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); } /* If T3111 expires, we will send the RF Channel Request */ static void t3111_expired(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_NOTICE, "%s T3111 expired: releasing RF Channel\n", gsm_lchan_name(lchan)); rsl_rf_chan_release(lchan, 0, SACCH_NONE); } /* If T3109 expires the MS has not send a UA/UM do the error release */ static void t3109_expired(void *data) { struct gsm_lchan *lchan = data; LOGP(DRSL, LOGL_ERROR, "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); rsl_rf_chan_release(lchan, 1, SACCH_NONE); } /* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ static int rsl_send_imm_ass_rej(struct gsm_bts *bts, struct gsm48_req_ref *rqd_ref, uint8_t wait_ind) { uint8_t buf[GSM_MACBLOCK_LEN]; struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; /* create IMMEDIATE ASSIGN REJECT 04.08 message */ memset(iar, 0, sizeof(*iar)); iar->proto_discr = GSM48_PDISC_RR; iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; iar->page_mode = GSM48_PM_SAME; /* * Set all request references and wait indications to the same value. * 3GPP TS 44.018 v4.5.0 release 4 (section 9.1.20.2) requires that * we duplicate reference and wait indication to fill the message. * The BTS will aggregate up to 4 of our ASS REJ messages if possible. */ memcpy(&iar->req_ref1, rqd_ref, sizeof(iar->req_ref1)); iar->wait_ind1 = wait_ind; memcpy(&iar->req_ref2, rqd_ref, sizeof(iar->req_ref2)); iar->wait_ind2 = wait_ind; memcpy(&iar->req_ref3, rqd_ref, sizeof(iar->req_ref3)); iar->wait_ind3 = wait_ind; memcpy(&iar->req_ref4, rqd_ref, sizeof(iar->req_ref4)); iar->wait_ind4 = wait_ind; /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); } /* Handle packet channel rach requests */ static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts) { struct gsm48_req_ref *rqd_ref; struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; uint8_t ra = rqd_ref->ra; uint8_t t1, t2, t3; uint32_t fn; uint8_t rqd_ta; uint8_t is_11bit; /* Process rach request and forward contained information to PCU */ if (ra == 0x7F) { is_11bit = 1; /* FIXME: Also handle 11 bit rach requests */ LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr); return -EINVAL; } else { is_11bit = 0; t1 = rqd_ref->t1; t2 = rqd_ref->t2; t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3); fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1); rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; } return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit, GSM_L1_BURST_TYPE_ACCESS_0); } /* MS has requested a channel on the RACH */ static int rsl_rx_chan_rqd(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); struct gsm48_req_ref *rqd_ref; enum gsm_chan_t lctype; enum gsm_chreq_reason_t chreq_reason; struct gsm_lchan *lchan; uint8_t rqd_ta; uint16_t arfcn; uint8_t subch; /* parse request reference to be used in immediate assign */ if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) return -EINVAL; rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; /* parse access delay and use as TA */ if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) return -EINVAL; rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; /* Determine channel request cause code */ chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n", msg->lchan->ts->trx->bts->nr, get_value_string(gsm_chreq_descs, chreq_reason), rqd_ref->ra, bts->network->neci, chreq_reason); /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */ if (chreq_reason == GSM_CHREQ_REASON_PDCH) return rsl_rx_pchan_rqd(msg, bts); /* determine channel type (SDCCH/TCH_F/TCH_H) based on * request reference RA */ lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]); /* check availability / allocate channel * * - First try to allocate SDCCH. * - If SDCCH is not available, try whatever MS requested, if not SDCCH. * - If there is still no channel available, reject channel request. * * lchan_alloc() possibly tries to allocate larger lchans. * * Note: If the MS requests not TCH/H, we don't know if the phone * supports TCH/H, so we must assign TCH/F or SDCCH. */ lchan = lchan_alloc(bts, GSM_LCHAN_SDCCH, 0); if (!lchan && lctype != GSM_LCHAN_SDCCH) { LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s " "0x%x, retrying with %s\n", msg->lchan->ts->trx->bts->nr, gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra, gsm_lchant_name(lctype)); lchan = lchan_alloc(bts, lctype, 0); } if (!lchan) { uint8_t wait_ind; LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s 0x%x\n", msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]); if (bts->T3122) wait_ind = bts->T3122; else if (bts->network->T3122) wait_ind = bts->network->T3122 & 0xff; else wait_ind = GSM_T3122_DEFAULT; /* The BTS will gather multiple CHAN RQD and reject up to 4 MS at the same time. */ rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind); return 0; } /* * Expecting lchan state to be NONE, except for dyn TS in PDCH mode. * Those are expected to be ACTIVE: the PDCH release will be sent from * rsl_chan_activate_lchan() below. */ if (lchan->state != LCHAN_S_NONE && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH && lchan->state == LCHAN_S_ACTIVE)) LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " "in state %s\n", gsm_lchan_name(lchan), gsm_lchans_name(lchan->state)); /* save the RACH data as we need it after the CHAN ACT ACK */ lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); if (!lchan->rqd_ref) { LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); lchan_free(lchan); return -ENOMEM; } memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); lchan->rqd_ta = rqd_ta; arfcn = lchan->ts->trx->arfcn; subch = lchan->nr; lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; lchan->tch_mode = GSM48_CMODE_SIGN; /* Start another timer or assume the BTS sends a ACK/NACK? */ osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); osmo_timer_schedule(&lchan->act_timer, 4, 0); DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), rqd_ref->ra, rqd_ta); rsl_chan_activate_lchan(lchan, RSL_ACT_INTRA_IMM_ASS, 0); return 0; } static int rsl_send_imm_assignment(struct gsm_lchan *lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; uint8_t buf[GSM_MACBLOCK_LEN]; struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; /* create IMMEDIATE ASSIGN 04.08 messge */ memset(ia, 0, sizeof(*ia)); /* we set ia->l2_plen once we know the length of the MA below */ ia->proto_discr = GSM48_PDISC_RR; ia->msg_type = GSM48_MT_RR_IMM_ASS; ia->page_mode = GSM48_PM_SAME; gsm48_lchan2chan_desc(&ia->chan_desc, lchan); /* use request reference extracted from CHAN_RQD */ memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); ia->timing_advance = lchan->rqd_ta; if (!lchan->ts->hopping.enabled) { ia->mob_alloc_len = 0; } else { ia->mob_alloc_len = lchan->ts->hopping.ma_len; memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); } /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ osmo_timer_setup(&lchan->T3101, t3101_expired, lchan); osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); } /* current load on the CCCH */ static int rsl_rx_ccch_load(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); struct ccch_signal_data sd; sd.bts = sign_link->trx->bts; sd.rach_slot_count = -1; sd.rach_busy_count = -1; sd.rach_access_count = -1; switch (rslh->data[0]) { case RSL_IE_PAGING_LOAD: sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { /* paging load below configured threshold, use 50 as default */ sd.pg_buf_space = 50; } paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); break; case RSL_IE_RACH_LOAD: if (msg->data_len >= 7) { sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); } break; default: break; } return 0; } /* Ericsson specific: Immediate Assign Sent */ static int rsl_rx_ericsson_imm_assign_sent(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); uint32_t tlli; LOGP(DRSL, LOGL_INFO, "IMM.ass sent\n"); msgb_pull(msg, sizeof(*dh)); /* FIXME: Move to TLV once we support defining TV types with V having len != 1 byte */ if(msg->len < 5) LOGP(DRSL, LOGL_ERROR, "short IMM.ass sent message!\n"); else if(msg->data[0] != RSL_IE_ERIC_MOBILE_ID) LOGP(DRSL, LOGL_ERROR, "unsupported IMM.ass message format! (please fix)\n"); else { msgb_pull(msg, 1); /* drop previous data to use msg_pull_u32 */ tlli = msgb_pull_u32(msg); pcu_tx_imm_ass_sent(sign_link->trx->bts, tlli); } return 0; } static int abis_rsl_rx_cchan(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); int rc = 0; msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, "Abis RSL rx CCHAN: "); switch (rslh->c.msg_type) { case RSL_MT_CHAN_RQD: /* MS has requested a channel on the RACH */ rc = rsl_rx_chan_rqd(msg); break; case RSL_MT_CCCH_LOAD_IND: /* current load on the CCCH */ rc = rsl_rx_ccch_load(msg); break; case RSL_MT_DELETE_IND: /* CCCH overloaded, IMM_ASSIGN was dropped */ case RSL_MT_CBCH_LOAD_IND: /* current load on the CBCH */ LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " "type %s\n", rsl_msg_name(rslh->c.msg_type)); break; case RSL_MT_ERICSSON_IMM_ASS_SENT: rc = rsl_rx_ericsson_imm_assign_sent(msg); break; default: LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " "0x%02x\n", rslh->c.msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); return -EINVAL; } return rc; } static int rsl_rx_rll_err_ind(struct msgb *msg) { struct tlv_parsed tp; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); uint8_t rlm_cause; rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION without mandantory cause.\n", gsm_lchan_name(msg->lchan)); return -1; } rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s in state=%s\n", gsm_lchan_name(msg->lchan), rsl_rlm_cause_name(rlm_cause), gsm_lchans_name(msg->lchan->state)); rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { rate_ctr_inc(&msg->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RLL_ERR]); return rsl_rf_chan_release_err(msg->lchan); } return 0; } static void rsl_handle_release(struct gsm_lchan *lchan) { int sapi; struct gsm_bts *bts; /* * Maybe only one link/SAPI was releasd or the error handling * was activated. Just return now and let the other code handle * it. */ if (lchan->state != LCHAN_S_REL_REQ) return; for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) continue; LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", gsm_lchan_name(lchan), sapi); return; } /* Stop T3109 and wait for T3111 before re-using the channel */ osmo_timer_del(&lchan->T3109); osmo_timer_setup(&lchan->T3111, t3111_expired, lchan); bts = lchan->ts->trx->bts; osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); } /* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST 0x02, 0x06, 0x01, 0x20, 0x02, 0x00, 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ static int abis_rsl_rx_rll(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); int rc = 0; char *ts_name; uint8_t sapi = rllh->link_id & 7; msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, "Abis RSL rx RLL: "); ts_name = gsm_lchan_name(msg->lchan); DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); switch (rllh->c.msg_type) { case RSL_MT_DATA_IND: DEBUGPC(DRLL, "DATA INDICATION\n"); if (msgb_l2len(msg) > sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && rllh->data[0] == RSL_IE_L3_INFO) { msg->l3h = &rllh->data[3]; return gsm0408_rcvmsg(msg, rllh->link_id); } break; case RSL_MT_EST_IND: DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); /* lchan is established, stop T3101 */ /* Note: By definition the first Establish Indication must * happen first on SAPI 0, once the connection on SAPI 0 is * made, parallel connections on other SAPIs are permitted */ if (sapi != 0 && msg->lchan->sapis[0] != LCHAN_SAPI_MS) { LOGP(DRLL, LOGL_NOTICE, "MS attempted to establish DCCH on SAPI=%d (expected SAPI=0)\n", rllh->link_id & 0x7); /* Note: We do not need to close the channel, * since we might still get a proper Establish Ind. * If not, T3101 will close the channel on timeout. */ break; } /* Note: Check for MF SACCH on SAPI=0 (not permitted). By * definition we establish a link in multiframe (MF) mode. * (see also 3GPP TS 48.058, chapter 3.1. However, on SAPI=0 * SACCH is only allowed in UL mode, not in MF mode. * (see also 3GPP TS 44.005, figure 5) So we have to drop such * Establish Indications */ if (sapi == 0 && (rllh->link_id >> 6 & 0x03) == 1) { LOGP(DRLL, LOGL_NOTICE, "MS attempted to establish an SACCH in MF mode on SAPI=0 (not permitted)\n"); /* Note: We do not need to close the channel, * since we might still get a proper Establish Ind. * If not, T3101 will close the channel on timeout. */ break; } msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; osmo_timer_del(&msg->lchan->T3101); if (msgb_l2len(msg) > sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && rllh->data[0] == RSL_IE_L3_INFO) { msg->l3h = &rllh->data[3]; return gsm0408_rcvmsg(msg, rllh->link_id); } break; case RSL_MT_EST_CONF: DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_EST_CONF); break; case RSL_MT_REL_IND: /* BTS informs us of having received DISC from MS */ DEBUGPC(DRLL, "RELEASE INDICATION\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_REL_IND); rsl_handle_release(msg->lchan); /* if it was the main signalling link, let the subscr_conn_fsm know */ if (msg->lchan->conn && sapi == 0 && (rllh->link_id >> 6) == 0) osmo_fsm_inst_dispatch(msg->lchan->conn->fi, GSCON_EV_RLL_REL_IND, msg); break; case RSL_MT_REL_CONF: /* BTS informs us of having received UA from MS, * in response to DISC that we've sent earlier */ DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; rsl_handle_release(msg->lchan); break; case RSL_MT_ERROR_IND: DEBUGPC(DRLL, "ERROR INDICATION\n"); rc = rsl_rx_rll_err_ind(msg); break; case RSL_MT_UNIT_DATA_IND: DEBUGPC(DRLL, "UNIT DATA INDICATION\n"); LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " "type 0x%02x\n", rllh->c.msg_type); break; default: DEBUGPC(DRLL, "UNKNOWN\n"); LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " "type 0x%02x\n", rllh->c.msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); } return rc; } static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x00; case GSM_LCHAN_TCH_H: return 0x03; default: break; } break; case GSM48_CMODE_SPEECH_EFR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x01; /* there's no half-rate EFR */ default: break; } break; case GSM48_CMODE_SPEECH_AMR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return 0x02; case GSM_LCHAN_TCH_H: return 0x05; default: break; } break; default: break; } LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " "tch_mode == 0x%02x\n", lchan->tch_mode); return 0; } static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) { switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: switch (lchan->type) { case GSM_LCHAN_TCH_F: return RTP_PT_GSM_FULL; case GSM_LCHAN_TCH_H: return RTP_PT_GSM_HALF; default: break; } break; case GSM48_CMODE_SPEECH_EFR: switch (lchan->type) { case GSM_LCHAN_TCH_F: return RTP_PT_GSM_EFR; /* there's no half-rate EFR */ default: break; } break; case GSM48_CMODE_SPEECH_AMR: switch (lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: return RTP_PT_AMR; default: break; } break; default: break; } LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " "tch_mode == 0x%02x & lchan_type == %d\n", lchan->tch_mode, lchan->type); return 0; } /* ip.access specific RSL extensions */ static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) { struct in_addr ip; uint16_t port, conn_id; if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_LOCAL_IP); DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); lchan->abis_ip.bound_ip = ntohl(ip.s_addr); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { port = tlvp_val16_unal(tv, RSL_IE_IPAC_LOCAL_PORT); port = ntohs(port); DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); lchan->abis_ip.bound_port = port; } if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { conn_id = tlvp_val16_unal(tv, RSL_IE_IPAC_CONN_ID); conn_id = ntohs(conn_id); DEBUGPC(DRSL, "CON_ID=%u ", conn_id); lchan->abis_ip.conn_id = conn_id; } if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { lchan->abis_ip.rtp_payload2 = *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", lchan->abis_ip.rtp_payload2); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { lchan->abis_ip.speech_mode = *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); DEBUGPC(DRSL, "speech_mode=0x%02x ", lchan->abis_ip.speech_mode); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_REMOTE_IP); DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); lchan->abis_ip.connect_ip = ntohl(ip.s_addr); } if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { port = tlvp_val16_unal(tv, RSL_IE_IPAC_REMOTE_PORT); port = ntohs(port); DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); lchan->abis_ip.connect_port = port; } DEBUGPC(DRSL, "\n"); } /*! \brief Issue IPA RSL CRCX to configure RTP on BTS side * \param[in] lchan Logical Channel for which we issue CRCX */ int rsl_ipacc_crcx(struct gsm_lchan *lchan) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; dh->chan_nr = gsm_lchan2chan_nr(lchan); /* 0x1- == receive-only, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, lchan->abis_ip.rtp_payload); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } /*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP * \param[in] lchan Logical Channel for which we issue MDCX * \param[in] ip Remote (MGW) IP address for RTP * \param[in] port Remote (MGW) UDP port number for RTP * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE */ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, uint8_t rtp_payload2) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint32_t *att_ip; struct in_addr ia; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; dh->chan_nr = gsm_lchan2chan_nr(lchan); /* we need to store these now as MDCX_ACK does not return them :( */ lchan->abis_ip.rtp_payload2 = rtp_payload2; lchan->abis_ip.connect_port = port; lchan->abis_ip.connect_ip = ip; /* 0x0- == both directions, 0x-1 == EFR codec */ lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); ia.s_addr = htonl(ip); DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); *att_ip = ia.s_addr; msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); if (rtp_payload2) msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); msg->dst = lchan->ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } static bool check_gprs_enabled(struct gsm_bts_trx_ts *ts) { if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) { LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none': not activating PDCH.\n", gsm_ts_and_pchan_name(ts)); return false; } return true; } int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) { struct msgb *msg = rsl_msgb_alloc(); struct abis_rsl_dchan_hdr *dh; uint8_t msg_type; if (ts->flags & TS_F_PDCH_PENDING_MASK) { LOGP(DRSL, LOGL_ERROR, "%s PDCH %s requested, but a PDCH%s%s is still pending\n", gsm_ts_name(ts), act ? "ACT" : "DEACT", ts->flags & TS_F_PDCH_ACT_PENDING? " ACT" : "", ts->flags & TS_F_PDCH_DEACT_PENDING? " DEACT" : ""); return -EINVAL; } if (act){ if (!check_gprs_enabled(ts)) return -ENOTSUP; msg_type = RSL_MT_IPAC_PDCH_ACT; ts->flags |= TS_F_PDCH_ACT_PENDING; } else { msg_type = RSL_MT_IPAC_PDCH_DEACT; ts->flags |= TS_F_PDCH_DEACT_PENDING; } /* TODO add timeout to cancel PDCH DE/ACT */ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); init_dchan_hdr(dh, msg_type); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_TCH_F, ts->nr, 0); DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts), act ? "" : "DE"); msg->dst = ts->trx->rsl_link; return abis_rsl_sendmsg(msg); } static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; struct gsm_lchan *lchan = msg->lchan; /* the BTS has acknowledged a local bind, it now tells us the IP * address and port number to which it has bound the given logical * channel */ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); return -EINVAL; } ipac_parse_rtp(lchan, &tv); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); return 0; } static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; struct gsm_lchan *lchan = msg->lchan; /* the BTS has acknowledged a remote connect request and * it now tells us the IP address and port number to which it has * connected the given logical channel */ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); ipac_parse_rtp(lchan, &tv); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); return 0; } static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); struct tlv_parsed tv; rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), TLVP_LEN(&tv, RSL_IE_CAUSE)); osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); return 0; } static int abis_rsl_rx_ipacc(struct msgb *msg) { struct e1inp_sign_link *sign_link = msg->dst; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); char *ts_name; int rc = 0; msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, "Abis RSL rx IPACC: "); ts_name = gsm_lchan_name(msg->lchan); switch (rllh->c.msg_type) { case RSL_MT_IPAC_CRCX_ACK: DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_crcx_ack(msg); break; case RSL_MT_IPAC_CRCX_NACK: /* somehow the BTS was unable to bind the lchan to its local * port?!? */ LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]); break; case RSL_MT_IPAC_MDCX_ACK: /* the BTS tells us that a connect operation was successful */ DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); rc = abis_rsl_rx_ipacc_mdcx_ack(msg); break; case RSL_MT_IPAC_MDCX_NACK: /* somehow the BTS was unable to connect the lchan to a remote * port */ LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]); break; case RSL_MT_IPAC_DLCX_IND: DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); rc = abis_rsl_rx_ipacc_dlcx_ind(msg); break; default: LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", rllh->c.msg_type); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); break; } return rc; } int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config to_pchan) { int ss; int rc = -EIO; OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); if (ts->dyn.pchan_is != ts->dyn.pchan_want) { LOGP(DRSL, LOGL_ERROR, "%s: Attempt to switch dynamic channel to %s," " but is already in switchover.\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan)); return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN; } if (ts->dyn.pchan_is == to_pchan) { LOGP(DRSL, LOGL_INFO, "%s %s Already is in %s mode, cannot switchover.\n", gsm_ts_name(ts), gsm_pchan_name(ts->pchan), gsm_pchan_name(to_pchan)); return -EINVAL; } /* Paranoia: let's make sure all is indeed released. */ for (ss = 0; ss < ts_subslots(ts); ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->state != LCHAN_S_NONE) { LOGP(DRSL, LOGL_ERROR, "%s Attempt to switch dynamic channel to %s," " but is not fully released.\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan)); return -EAGAIN; } } if (to_pchan == GSM_PCHAN_PDCH && !check_gprs_enabled(ts)) return -ENOTSUP; DEBUGP(DRSL, "%s starting switchover to %s\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan)); /* Record that we're busy switching. */ ts->dyn.pchan_want = to_pchan; /* * To switch from PDCH, we need to initiate the release from the BSC * side. dyn_ts_switchover_continue() will be called from * rsl_rx_rf_chan_rel_ack(). PDCH is always on lchan[0]. */ if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) { rsl_lchan_set_state(ts->lchan, LCHAN_S_REL_REQ); rc = rsl_rf_chan_release(ts->lchan, 0, SACCH_NONE); if (rc) { LOGP(DRSL, LOGL_ERROR, "%s RSL RF Chan Release failed\n", gsm_ts_and_pchan_name(ts)); return dyn_ts_switchover_failed(ts, rc); } return 0; } /* * To switch from TCH/F and TCH/H pchans, this has been called from * rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and * activate as new type. This will always be PDCH. */ return dyn_ts_switchover_continue(ts); } static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts) { int rc; uint8_t act_type; uint8_t ho_ref; int ss; struct gsm_lchan *lchan; OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); DEBUGP(DRSL, "%s switchover: release complete," " activating new pchan type\n", gsm_ts_and_pchan_name(ts)); if (ts->dyn.pchan_is == ts->dyn.pchan_want) { LOGP(DRSL, LOGL_ERROR, "%s Requested to switchover dynamic channel to the" " same type it is already in.\n", gsm_ts_and_pchan_name(ts)); return 0; } for (ss = 0; ss < ts_subslots(ts); ss++) { lchan = &ts->lchan[ss]; if (lchan->rqd_ref) { LOGP(DRSL, LOGL_ERROR, "%s During dyn TS switchover, expecting no" " Request Reference to be pending. Discarding!\n", gsm_lchan_name(lchan)); talloc_free(lchan->rqd_ref); lchan->rqd_ref = NULL; } } /* * When switching pchan modes, all lchans are unused. So always * activate whatever wants to be activated on the first lchan. (We * wouldn't remember to use lchan[1] across e.g. a PDCH deact anyway) */ lchan = ts->lchan; /* * For TCH/x, the lchan->type has been set in lchan_alloc(), but it may * have been lost during channel release due to dynamic switchover. * * For PDCH, the lchan->type will actually remain NONE. * TODO: set GSM_LCHAN_PDTCH? */ switch (ts->dyn.pchan_want) { case GSM_PCHAN_TCH_F: lchan->type = GSM_LCHAN_TCH_F; break; case GSM_PCHAN_TCH_H: lchan->type = GSM_LCHAN_TCH_H; break; case GSM_PCHAN_PDCH: lchan->type = GSM_LCHAN_NONE; break; default: LOGP(DRSL, LOGL_ERROR, "%s Invalid target pchan for dynamic TS\n", gsm_ts_and_pchan_name(ts)); } act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) ? RSL_ACT_OSMO_PDCH : lchan->dyn.act_type; ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) ? 0 : lchan->dyn.ho_ref; /* Fetch the rqd_ref back from before switchover started. */ lchan->rqd_ref = lchan->dyn.rqd_ref; lchan->rqd_ta = lchan->dyn.rqd_ta; lchan->dyn.rqd_ref = NULL; lchan->dyn.rqd_ta = 0; /* During switchover, we have received a release ack, which means that * the act_timer has been stopped. Start the timer again so we mark * this channel broken if the activation ack comes too late. */ osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); osmo_timer_schedule(&lchan->act_timer, 4, 0); rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref); if (rc) { LOGP(DRSL, LOGL_ERROR, "%s RSL Chan Activate failed\n", gsm_ts_and_pchan_name(ts)); return dyn_ts_switchover_failed(ts, rc); } return 0; } static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc) { ts->dyn.pchan_want = ts->dyn.pchan_is; LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover." " Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts), rc); return rc; } static void dyn_ts_switchover_complete(struct gsm_lchan *lchan) { enum gsm_phys_chan_config pchan_act; enum gsm_phys_chan_config pchan_was; struct gsm_bts_trx_ts *ts = lchan->ts; OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); pchan_act = pchan_for_lchant(lchan->type); /* * Paranoia: do the types match? * In case of errors: we've received an act ack already, so what to do * about it? Logging the error should suffice for now. */ if (pchan_act != ts->dyn.pchan_want) LOGP(DRSL, LOGL_ERROR, "%s Requested transition does not match lchan type %s\n", gsm_ts_and_pchan_name(ts), gsm_lchant_name(lchan->type)); pchan_was = ts->dyn.pchan_is; ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act; if (pchan_was != ts->dyn.pchan_is) LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was)); } /* Entry-point where L2 RSL from BTS enters */ int abis_rsl_rcvmsg(struct msgb *msg) { struct e1inp_sign_link *sign_link; struct abis_rsl_common_hdr *rslh; int rc = 0; if (!msg) { DEBUGP(DRSL, "Empty RSL msg?..\n"); return -1; } if (msgb_l2len(msg) < sizeof(*rslh)) { DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); msgb_free(msg); return -1; } sign_link = msg->dst; rslh = msgb_l2(msg); switch (rslh->msg_discr & 0xfe) { case ABIS_RSL_MDISC_RLL: rc = abis_rsl_rx_rll(msg); break; case ABIS_RSL_MDISC_DED_CHAN: rc = abis_rsl_rx_dchan(msg); break; case ABIS_RSL_MDISC_COM_CHAN: rc = abis_rsl_rx_cchan(msg); break; case ABIS_RSL_MDISC_TRX: rc = abis_rsl_rx_trx(msg); break; case ABIS_RSL_MDISC_LOC: LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", rslh->msg_discr); break; case ABIS_RSL_MDISC_IPACCESS: rc = abis_rsl_rx_ipacc(msg); break; default: LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " "0x%02x\n", rslh->msg_discr); rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_UNKNOWN]); rc = -EINVAL; } msgb_free(msg); return rc; } int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, struct rsl_ie_cb_cmd_type cb_command, const uint8_t *data, int len) { struct abis_rsl_dchan_hdr *dh; struct msgb *cb_cmd; cb_cmd = rsl_msgb_alloc(); if (!cb_cmd) return -1; dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh)); init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; dh->chan_nr = chan_number; /* TODO: check the chan config */ msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command); msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); cb_cmd->dst = bts->c0->rsl_link; return abis_rsl_sendmsg(cb_cmd); } int rsl_nokia_si_begin(struct gsm_bts_trx *trx) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = 0x40; /* Nokia SI Begin */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_nokia_si_end(struct gsm_bts_trx *trx) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_TRX; ch->msg_type = 0x41; /* Nokia SI End */ msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) { struct abis_rsl_common_hdr *ch; struct msgb *msg = rsl_msgb_alloc(); ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; ch->msg_type = RSL_MT_BS_POWER_CONTROL; msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ msg->dst = trx->rsl_link; return abis_rsl_sendmsg(msg); } /** * Release all allocated SAPIs starting from @param start and * release them with the given release mode. Once the release * confirmation arrives it will be attempted to release the * the RF channel. */ int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, enum rsl_rel_mode release_mode) { int no_sapi = 1; int sapi; for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { uint8_t link_id; if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) continue; link_id = sapi; if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) link_id |= 0x40; rsl_release_request(lchan, link_id, release_mode); no_sapi = 0; } return no_sapi; } int rsl_start_t3109(struct gsm_lchan *lchan) { struct gsm_bts *bts = lchan->ts->trx->bts; osmo_timer_setup(&lchan->T3109, t3109_expired, lchan); osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); return 0; } /** * \brief directly RF Channel Release the lchan * * When no SAPI was allocated, directly release the logical channel. This * should only be called from chan_alloc.c on channel release handling. In * case no SAPI was established the RF Channel can be directly released, */ int rsl_direct_rf_release(struct gsm_lchan *lchan) { int i; for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", gsm_lchan_name(lchan), i); return -1; } } /* Now release it */ return rsl_rf_chan_release(lchan, 0, SACCH_NONE); } /* Initial timeslot actions when a timeslot first comes into operation. */ bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { dyn_ts_init(ts); return true; } osmo-bsc-1.3.0/src/osmo-bsc/acc_ramp.c000066400000000000000000000307601332665256100174760ustar00rootroot00000000000000/* (C) 2018 by sysmocom s.f.m.c. GmbH * * Author: Stefan Sperling * * 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 /* * Check if an ACC has been permanently barred for a BTS, * e.g. with the 'rach access-control-class' VTY command. */ static bool acc_is_permanently_barred(struct gsm_bts *bts, unsigned int acc) { OSMO_ASSERT(acc >= 0 && acc <= 9); if (acc == 8 || acc == 9) return (bts->si_common.rach_control.t2 & (1 << (acc - 8))); return (bts->si_common.rach_control.t3 & (1 << (acc))); } static void allow_one_acc(struct acc_ramp *acc_ramp, unsigned int acc) { OSMO_ASSERT(acc >= 0 && acc <= 9); if (acc_ramp->barred_accs & (1 << acc)) LOGP(DRSL, LOGL_NOTICE, "(bts=%d) ACC RAMP: allowing Access Control Class %u\n", acc_ramp->bts->nr, acc); acc_ramp->barred_accs &= ~(1 << acc); } static void barr_one_acc(struct acc_ramp *acc_ramp, unsigned int acc) { OSMO_ASSERT(acc >= 0 && acc <= 9); if ((acc_ramp->barred_accs & (1 << acc)) == 0) LOGP(DRSL, LOGL_NOTICE, "(bts=%d) ACC RAMP: barring Access Control Class %u\n", acc_ramp->bts->nr, acc); acc_ramp->barred_accs |= (1 << acc); } static void barr_all_accs(struct acc_ramp *acc_ramp) { unsigned int acc; for (acc = 0; acc < 10; acc++) { if (!acc_is_permanently_barred(acc_ramp->bts, acc)) barr_one_acc(acc_ramp, acc); } } static void allow_all_accs(struct acc_ramp *acc_ramp) { unsigned int acc; for (acc = 0; acc < 10; acc++) { if (!acc_is_permanently_barred(acc_ramp->bts, acc)) allow_one_acc(acc_ramp, acc); } } static unsigned int get_next_step_interval(struct acc_ramp *acc_ramp) { struct gsm_bts *bts = acc_ramp->bts; uint64_t load; if (acc_ramp->step_interval_is_fixed) return acc_ramp->step_interval_sec; /* Scale the step interval to current channel load average. */ load = (bts->chan_load_avg << 8); /* convert to fixed-point */ acc_ramp->step_interval_sec = ((load * ACC_RAMP_STEP_INTERVAL_MAX) / 100) >> 8; if (acc_ramp->step_interval_sec < ACC_RAMP_STEP_SIZE_MIN) acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN; else if (acc_ramp->step_interval_sec > ACC_RAMP_STEP_INTERVAL_MAX) acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MAX; LOGP(DRSL, LOGL_DEBUG, "(bts=%d) ACC RAMP: step interval set to %u seconds based on %u%% channel load average\n", bts->nr, acc_ramp->step_interval_sec, bts->chan_load_avg); return acc_ramp->step_interval_sec; } static void do_acc_ramping_step(void *data) { struct acc_ramp *acc_ramp = data; int i; /* Shortcut in case we only do one ramping step. */ if (acc_ramp->step_size == ACC_RAMP_STEP_SIZE_MAX) { allow_all_accs(acc_ramp); gsm_bts_set_system_infos(acc_ramp->bts); return; } /* Allow 'step_size' ACCs, starting from ACC0. ACC9 will be allowed last. */ for (i = 0; i < acc_ramp->step_size; i++) { int idx = ffs(acc_ramp_get_barred_t3(acc_ramp)); if (idx > 0) { /* One of ACC0-ACC7 is still bared. */ unsigned int acc = idx - 1; if (!acc_is_permanently_barred(acc_ramp->bts, acc)) allow_one_acc(acc_ramp, acc); } else { idx = ffs(acc_ramp_get_barred_t2(acc_ramp)); if (idx == 1 || idx == 2) { /* ACC8 or ACC9 is still barred. */ unsigned int acc = idx - 1 + 8; if (!acc_is_permanently_barred(acc_ramp->bts, acc)) allow_one_acc(acc_ramp, acc); } else { /* All ACCs are now allowed. */ break; } } } gsm_bts_set_system_infos(acc_ramp->bts); /* If we have not allowed all ACCs yet, schedule another ramping step. */ if (acc_ramp_get_barred_t2(acc_ramp) != 0x00 || acc_ramp_get_barred_t3(acc_ramp) != 0x00) osmo_timer_schedule(&acc_ramp->step_timer, get_next_step_interval(acc_ramp), 0); } /* Implements osmo_signal_cbfn() -- trigger or abort ACC ramping upon changes RF lock state. */ static int acc_ramp_nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct nm_statechg_signal_data *nsd = signal_data; struct acc_ramp *acc_ramp = handler_data; struct gsm_bts_trx *trx = NULL; bool trigger_ramping = false, abort_ramping = false; /* Handled signals map to an Administrative State Change ACK, or a State Changed Event Report. */ if (signal != S_NM_STATECHG_ADM && signal != S_NM_STATECHG_OPER) return 0; if (nsd->obj_class != NM_OC_RADIO_CARRIER) return 0; trx = nsd->obj; LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: administrative state %s -> %s\n", acc_ramp->bts->nr, trx->nr, get_value_string(abis_nm_adm_state_names, nsd->old_state->administrative), get_value_string(abis_nm_adm_state_names, nsd->new_state->administrative)); LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: operational state %s -> %s\n", acc_ramp->bts->nr, trx->nr, abis_nm_opstate_name(nsd->old_state->operational), abis_nm_opstate_name(nsd->new_state->operational)); /* We only care about state changes of the first TRX. */ if (trx->nr != 0) return 0; /* RSL must already be up. We cannot send RACH system information to the BTS otherwise. */ if (trx->rsl_link == NULL) { LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: ignoring state change because RSL link is down\n", acc_ramp->bts->nr, trx->nr); return 0; } /* Trigger or abort ACC ramping based on the new state of this TRX. */ if (nsd->old_state->administrative != nsd->new_state->administrative) { switch (nsd->new_state->administrative) { case NM_STATE_UNLOCKED: if (nsd->old_state->operational != nsd->new_state->operational) { /* * Administrative and operational state have both changed. * Trigger ramping only if TRX 0 will be both enabled and unlocked. */ if (nsd->new_state->operational == NM_OPSTATE_ENABLED) trigger_ramping = true; else LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: ignoring state change " "because TRX is transitioning into operational state '%s'\n", acc_ramp->bts->nr, trx->nr, abis_nm_opstate_name(nsd->new_state->operational)); } else { /* * Operational state has not changed. * Trigger ramping only if TRX 0 is already usable. */ if (trx_is_usable(trx)) trigger_ramping = true; else LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: ignoring state change " "because TRX is not usable\n", acc_ramp->bts->nr, trx->nr); } break; case NM_STATE_LOCKED: case NM_STATE_SHUTDOWN: abort_ramping = true; break; case NM_STATE_NULL: default: LOGP(DRSL, LOGL_ERROR, "(bts=%d) ACC RAMP: unrecognized administrative state '0x%x' " "reported for TRX 0\n", acc_ramp->bts->nr, nsd->new_state->administrative); break; } } if (nsd->old_state->operational != nsd->new_state->operational) { switch (nsd->new_state->operational) { case NM_OPSTATE_ENABLED: if (nsd->old_state->administrative != nsd->new_state->administrative) { /* * Administrative and operational state have both changed. * Trigger ramping only if TRX 0 will be both enabled and unlocked. */ if (nsd->new_state->administrative == NM_STATE_UNLOCKED) trigger_ramping = true; else LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: ignoring state change " "because TRX is transitioning into administrative state '%s'\n", acc_ramp->bts->nr, trx->nr, get_value_string(abis_nm_adm_state_names, nsd->new_state->administrative)); } else { /* * Administrative state has not changed. * Trigger ramping only if TRX 0 is already unlocked. */ if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) trigger_ramping = true; else LOGP(DRSL, LOGL_DEBUG, "(bts=%d,trx=%d) ACC RAMP: ignoring state change " "because TRX is in administrative state '%s'\n", acc_ramp->bts->nr, trx->nr, get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative)); } break; case NM_OPSTATE_DISABLED: abort_ramping = true; break; case NM_OPSTATE_NULL: default: LOGP(DRSL, LOGL_ERROR, "(bts=%d) ACC RAMP: unrecognized operational state '0x%x' " "reported for TRX 0\n", acc_ramp->bts->nr, nsd->new_state->administrative); break; } } if (trigger_ramping) acc_ramp_trigger(acc_ramp); else if (abort_ramping) acc_ramp_abort(acc_ramp); return 0; } /*! * Initialize an acc_ramp data structure. * Storage for this structure must be provided by the caller. * * By default, ACC ramping is disabled and all ACCs are allowed. * * \param[in] acc_ramp Pointer to acc_ramp structure to be initialized. * \param[in] bts BTS which uses this ACC ramp data structure. */ void acc_ramp_init(struct acc_ramp *acc_ramp, struct gsm_bts *bts) { acc_ramp->bts = bts; acc_ramp_set_enabled(acc_ramp, false); acc_ramp->step_size = ACC_RAMP_STEP_SIZE_DEFAULT; acc_ramp->step_interval_sec = ACC_RAMP_STEP_INTERVAL_MIN; acc_ramp->step_interval_is_fixed = false; allow_all_accs(acc_ramp); osmo_timer_setup(&acc_ramp->step_timer, do_acc_ramping_step, acc_ramp); osmo_signal_register_handler(SS_NM, acc_ramp_nm_sig_cb, acc_ramp); } /*! * Change the ramping step size which controls how many ACCs will be allowed per ramping step. * Returns negative on error (step_size out of range), else zero. * \param[in] acc_ramp Pointer to acc_ramp structure. * \param[in] step_size The new step size value. */ int acc_ramp_set_step_size(struct acc_ramp *acc_ramp, unsigned int step_size) { if (step_size < ACC_RAMP_STEP_SIZE_MIN || step_size > ACC_RAMP_STEP_SIZE_MAX) return -ERANGE; acc_ramp->step_size = step_size; LOGP(DRSL, LOGL_DEBUG, "(bts=%d) ACC RAMP: ramping step size set to %u\n", acc_ramp->bts->nr, step_size); return 0; } /*! * Change the ramping step interval to a fixed value. Unless this function is called, * the interval is automatically scaled to the BTS channel load average. * \param[in] acc_ramp Pointer to acc_ramp structure. * \param[in] step_interval The new fixed step interval in seconds. */ int acc_ramp_set_step_interval(struct acc_ramp *acc_ramp, unsigned int step_interval) { if (step_interval < ACC_RAMP_STEP_INTERVAL_MIN || step_interval > ACC_RAMP_STEP_INTERVAL_MAX) return -ERANGE; acc_ramp->step_interval_sec = step_interval; acc_ramp->step_interval_is_fixed = true; LOGP(DRSL, LOGL_DEBUG, "(bts=%d) ACC RAMP: ramping step interval set to %u seconds\n", acc_ramp->bts->nr, step_interval); return 0; } /*! * Clear a previously set fixed ramping step interval, so that the interval * is again automatically scaled to the BTS channel load average. * \param[in] acc_ramp Pointer to acc_ramp structure. */ void acc_ramp_set_step_interval_dynamic(struct acc_ramp *acc_ramp) { acc_ramp->step_interval_is_fixed = false; LOGP(DRSL, LOGL_DEBUG, "(bts=%d) ACC RAMP: ramping step interval set to 'dynamic'\n", acc_ramp->bts->nr); } /*! * Determine if ACC ramping should be started according to configuration, and * begin the ramping process if the necessary conditions are present. * Perform at least one ramping step to allow 'step_size' ACCs. * If 'step_size' is ACC_RAMP_STEP_SIZE_MAX, or if ACC ramping is disabled, * all ACCs will be allowed immediately. * \param[in] acc_ramp Pointer to acc_ramp structure. */ void acc_ramp_trigger(struct acc_ramp *acc_ramp) { /* Abort any previously running ramping process and allow all available ACCs. */ acc_ramp_abort(acc_ramp); if (acc_ramp_is_enabled(acc_ramp)) { /* Set all available ACCs to barred and start ramping up. */ barr_all_accs(acc_ramp); do_acc_ramping_step(acc_ramp); } } /*! * Abort the ramping process and allow all available ACCs immediately. * \param[in] acc_ramp Pointer to acc_ramp structure. */ void acc_ramp_abort(struct acc_ramp *acc_ramp) { if (osmo_timer_pending(&acc_ramp->step_timer)) osmo_timer_del(&acc_ramp->step_timer); allow_all_accs(acc_ramp); } osmo-bsc-1.3.0/src/osmo-bsc/arfcn_range_encode.c000066400000000000000000000205341332665256100215110ustar00rootroot00000000000000/* gsm 04.08 system information (si) encoding and decoding * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ /* * (C) 2012 Holger Hans Peter Freyther * (C) 2012 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 #include #include #include #include static inline int greatest_power_of_2_lesser_or_equal_to(int index) { int power_of_2 = 1; do { power_of_2 *= 2; } while (power_of_2 <= index); /* now go back one step */ return power_of_2 / 2; } static inline int mod(int data, int range) { int res = data % range; while (res < 0) res += range; return res; } /** * Determine at which index to split the ARFCNs to create an * equally size partition for the given range. Return -1 if * no such partition exists. */ int range_enc_find_index(enum gsm48_range range, const int *freqs, const int size) { int i, j, n; const int RANGE_DELTA = (range - 1) / 2; for (i = 0; i < size; ++i) { n = 0; for (j = 0; j < size; ++j) { if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) n += 1; } if (n - 1 == (size - 1) / 2) return i; } return -1; } /* Worker for range_enc_arfcns(), do not call directly. */ int _range_enc_arfcns(enum gsm48_range range, const int *arfcns, int size, int *out, const int index) { int split_at; int i; /* * The below is a GNU extension and we can remove it when * we move to a quicksort like in-situ swap with the pivot. */ int arfcns_left[size / 2]; int arfcns_right[size / 2]; int l_size; int r_size; int l_origin; int r_origin; /* Now do the processing */ split_at = range_enc_find_index(range, arfcns, size); if (split_at < 0) return -EINVAL; /* we now know where to split */ out[index] = 1 + arfcns[split_at]; /* calculate the work that needs to be done for the leafs */ l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); r_origin = mod(arfcns[split_at] + 1, range); for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { if (mod(arfcns[i] - l_origin, range) < range / 2) arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); if (mod(arfcns[i] - r_origin, range) < range / 2) arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); } /* * Now recurse and we need to make this iterative... but as the * tree is balanced the stack will not be too deep. */ if (l_size) range_enc_arfcns(range / 2, arfcns_left, l_size, out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); if (r_size) range_enc_arfcns((range - 1) / 2, arfcns_right, r_size, out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); return 0; } /** * Range encode the ARFCN list. * \param range The range to use. * \param arfcns The list of ARFCNs * \param size The size of the list of ARFCNs * \param out Place to store the W(i) output. */ int range_enc_arfcns(enum gsm48_range range, const int *arfcns, int size, int *out, const int index) { if (size <= 0) return 0; if (size == 1) { out[index] = 1 + arfcns[0]; return 0; } return _range_enc_arfcns(range, arfcns, size, out, index); } /* * The easiest is to use f0 == arfcns[0]. This means that under certain * circumstances we can encode less ARFCNs than possible with an optimal f0. * * TODO: Solve the optimisation problem and pick f0 so that the max distance * is the smallest. Taking into account the modulo operation. I think picking * size/2 will be the optimal arfcn. */ /** * This implements the range determination as described in GSM 04.08 J4. The * result will be a base frequency f0 and the range to use. Note that for range * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of * the arfcns list. * * \param[in] arfcns The input frequencies, they must be sorted, lowest number first * \param[in] size The length of the array * \param[out] f0 The selected F0 base frequency. It might not be inside the list */ int range_enc_determine_range(const int *arfcns, const int size, int *f0) { int max = 0; /* * Go for the easiest. And pick arfcns[0] == f0. */ max = arfcns[size - 1] - arfcns[0]; *f0 = arfcns[0]; if (max < 128 && size <= 29) return ARFCN_RANGE_128; if (max < 256 && size <= 22) return ARFCN_RANGE_256; if (max < 512 && size <= 18) return ARFCN_RANGE_512; if (max < 1024 && size <= 17) { *f0 = 0; return ARFCN_RANGE_1024; } return ARFCN_RANGE_INVALID; } static void write_orig_arfcn(uint8_t *chan_list, int f0) { chan_list[0] |= (f0 >> 9) & 1; chan_list[1] = (f0 >> 1); chan_list[2] = (f0 & 1) << 7; } static void write_all_wn(uint8_t *chan_list, int bit_offs, int *w, int w_size, int w1_len) { int octet_offs = 0; /* offset into chan_list */ int wk_len = w1_len; /* encoding size in bits of w[k] */ int k; /* 1 based */ int level = 0; /* tree level, top level = 0 */ int lvl_left = 1; /* nodes per tree level */ /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */ for (k = 1; k <= w_size; k++) { int wk_left = wk_len; DEBUGP(DRR, "k=%d, wk_len=%d, offs=%d:%d, level=%d, " "lvl_left=%d\n", k, wk_len, octet_offs, bit_offs, level, lvl_left); while (wk_left > 0) { int cur_bits = 8 - bit_offs; int cur_mask; int wk_slice; if (cur_bits > wk_left) cur_bits = wk_left; cur_mask = ((1 << cur_bits) - 1); DEBUGP(DRR, " wk_left=%d, cur_bits=%d, offs=%d:%d\n", wk_left, cur_bits, octet_offs, bit_offs); /* advance */ wk_left -= cur_bits; bit_offs += cur_bits; /* right aligned wk data for current out octet */ wk_slice = (w[k-1] >> wk_left) & cur_mask; /* cur_bits now contains the number of bits * that are to be copied from wk to the chan_list. * wk_left is set to the number of bits that must * not yet be copied. * bit_offs points after the bit area that is going to * be overwritten: * * wk_left * | * v * wk: WWWWWWWWWWW * |||||<-- wk_slice, cur_bits=5 * --WWWWW- * ^ * | * bit_offs */ DEBUGP(DRR, " wk=%02x, slice=%02x/%02x, cl=%02x\n", w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs)); chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs)); chan_list[octet_offs] |= wk_slice << (8 - bit_offs); /* adjust output */ if (bit_offs == 8) { bit_offs = 0; octet_offs += 1; } } /* adjust bit sizes */ lvl_left -= 1; if (!lvl_left) { /* completed tree level, advance to next */ level += 1; lvl_left = 1 << level; wk_len -= 1; } } } int range_enc_range128(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x8C; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 28, 7); return 0; } int range_enc_range256(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x8A; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 21, 8); return 0; } int range_enc_range512(uint8_t *chan_list, int f0, int *w) { chan_list[0] = 0x88; write_orig_arfcn(chan_list, f0); write_all_wn(&chan_list[2], 1, w, 17, 9); return 0; } int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) { chan_list[0] = 0x80 | (f0_included << 2); write_all_wn(&chan_list[0], 6, w, 16, 10); return 0; } int range_enc_filter_arfcns(int *arfcns, const int size, const int f0, int *f0_included) { int i, j = 0; *f0_included = 0; for (i = 0; i < size; ++i) { /* * Appendix J.4 says the following: * All frequencies except F(0), minus F(0) + 1. * I assume we need to exclude it here. */ if (arfcns[i] == f0) { *f0_included = 1; continue; } arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); } return j; } osmo-bsc-1.3.0/src/osmo-bsc/bsc_api.c000066400000000000000000000524221332665256100173300ustar00rootroot00000000000000/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ /* (C) 2010-2011 by Holger Hans Peter Freyther * (C) 2010-2011 by On-Waves * (C) 2009,2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define GSM0808_T10_VALUE 6, 0 #define HO_DTAP_CACHE_MSGB_CB_LINK_ID 0 #define HO_DTAP_CACHE_MSGB_CB_ALLOW_SACCH 1 static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); static void handle_release(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan); static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan); static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan); /* * Start a new assignment and make sure that it is completed within T10 either * positively, negatively or by the timeout. * * 1.) allocate a new lchan * 2.) copy the encryption key and other data from the * old to the new channel. * 3.) RSL Channel Activate this channel and wait * * -> Signal handler for the LCHAN * 4.) Send GSM 04.08 assignment command to the MS * * -> Assignment Complete/Assignment Failure * 5.) Release the SDCCH, continue signalling on the new link */ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) { struct gsm_lchan *new_lchan; enum gsm_chan_t chan_type; chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; new_lchan = lchan_alloc(conn_get_bts(conn), chan_type, 0); if (!new_lchan) { LOGP(DMSC, LOGL_NOTICE, "%s No free channel for %s\n", bsc_subscr_name(conn->bsub), gsm_lchant_name(chan_type)); return -1; } /* check if we are on TCH/F and requested TCH/H, but got TCH/F */ if (conn->lchan->type == new_lchan->type && chan_type != new_lchan->type) { LOGPLCHAN(conn->lchan, DHO, LOGL_NOTICE, "-> %s Will not re-assign to identical channel type, %s was requested\n", gsm_lchan_name(new_lchan), gsm_lchant_name(chan_type)); lchan_free(new_lchan); return -1; } /* copy old data to the new channel */ memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); new_lchan->ms_power = conn->lchan->ms_power; new_lchan->bs_power = conn->lchan->bs_power; new_lchan->rqd_ta = conn->lchan->rqd_ta; /* copy new data to it */ new_lchan->tch_mode = chan_mode; new_lchan->rsl_cmode = (chan_mode == GSM48_CMODE_SIGN) ? RSL_CMOD_SPD_SIGN : RSL_CMOD_SPD_SPEECH; /* handle AMR correctly */ if (chan_mode == GSM48_CMODE_SPEECH_AMR) bsc_mr_config(conn, new_lchan, full_rate); if (rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTRA_NORM_ASS, 0) < 0) { LOGPLCHAN(new_lchan, DHO, LOGL_ERROR, "could not activate channel\n"); lchan_free(new_lchan); return -1; } /* remember that we have the channel */ conn->secondary_lchan = new_lchan; new_lchan->conn = conn; return 0; } static void ho_dtap_cache_add(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, bool allow_sacch) { if (conn->ho_dtap_cache_len >= 23) { LOGP(DHO, LOGL_ERROR, "%s: Cannot cache more DTAP messages," " already reached sane maximum of %u cached messages\n", bsc_subscr_name(conn->bsub), conn->ho_dtap_cache_len); msgb_free(msg); return; } conn->ho_dtap_cache_len ++; LOGP(DHO, LOGL_DEBUG, "%s: Caching DTAP message during ho/ass (%u)\n", bsc_subscr_name(conn->bsub), conn->ho_dtap_cache_len); msg->cb[HO_DTAP_CACHE_MSGB_CB_LINK_ID] = (unsigned long)link_id; msg->cb[HO_DTAP_CACHE_MSGB_CB_ALLOW_SACCH] = allow_sacch ? 1 : 0; msgb_enqueue(&conn->ho_dtap_cache, msg); } void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send) { struct msgb *msg; unsigned int flushed_count = 0; if (conn->secondary_lchan || conn->ho) { LOGP(DHO, LOGL_ERROR, "%s: Cannot send cached DTAP messages, handover/assignment is still ongoing\n", bsc_subscr_name(conn->bsub)); send = 0; } while ((msg = msgb_dequeue(&conn->ho_dtap_cache))) { conn->ho_dtap_cache_len --; flushed_count ++; if (send) { int link_id = (int)msg->cb[HO_DTAP_CACHE_MSGB_CB_LINK_ID]; bool allow_sacch = !!msg->cb[HO_DTAP_CACHE_MSGB_CB_ALLOW_SACCH]; LOGP(DHO, LOGL_DEBUG, "%s: Sending cached DTAP message after handover/assignment (%u/%u)\n", bsc_subscr_name(conn->bsub), flushed_count, conn->ho_dtap_cache_len); gsm0808_submit_dtap(conn, msg, link_id, allow_sacch); } else msgb_free(msg); } } /*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch) { uint8_t sapi; if (!conn->lchan) { LOGP(DMSC, LOGL_ERROR, "%s Called submit dtap without an lchan.\n", bsc_subscr_name(conn->bsub)); msgb_free(msg); return -1; } /* buffer message during assignment / handover */ if (conn->secondary_lchan || conn->ho) { ho_dtap_cache_add(conn, msg, link_id, !! allow_sacch); return 0; } sapi = link_id & 0x7; msg->lchan = conn->lchan; msg->dst = msg->lchan->ts->trx->rsl_link; /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ if (allow_sacch && sapi != 0) { if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) link_id |= 0x40; } msg->l3h = msg->data; /* is requested SAPI already up? */ if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { /* Establish L2 for additional SAPI */ OBSC_LINKID_CB(msg) = link_id; if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { msgb_free(msg); bsc_sapi_n_reject(conn, link_id); return -1; } return 0; } else { /* Directly forward via RLL/RSL to BTS */ return rsl_data_request(msg, link_id); } } /* * \brief Check if the given channel is compatible with the mode/fullrate */ static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) { switch (chan_mode) { case GSM48_CMODE_SIGN: switch (lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: case GSM_LCHAN_SDCCH: return 1; default: return 0; } case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_DATA_3k6: case GSM48_CMODE_DATA_6k0: /* these services can all run on TCH/H, but we may have * an explicit override by the 'full_rate' argument */ switch (lchan->type) { case GSM_LCHAN_TCH_F: return full_rate ? 1 : 0; case GSM_LCHAN_TCH_H: return full_rate ? 0 : 1; default: return 0; } case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_SPEECH_EFR: /* these services all explicitly require a TCH/F */ return (lchan->type == GSM_LCHAN_TCH_F) ? 1 : 0; default: return 0; } } /*! Send a GSM08.08 Assignment Request. Right now this does not contain the * audio codec type or the allowed rates for the config. In case the current * channel does not allow the selected mode a new one will be allocated. * \param[out] conn related subscriber connection * \param[in] chan_mode mode of the channel (see enum gsm48_chan_mode) * \param[in] full_rate select full rate or half rate channel * \returns 0 on success, 1 when no operation is neccessary, -1 on failure */ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) { /* TODO: Add multirate configuration, make it work for more than audio. */ if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { if (handle_new_assignment(conn, chan_mode, full_rate) != 0) goto error; } else { /* Check if the channel is already in the requested mode, if * yes, we skip unnecessary channel mode modify operations. */ if (conn->lchan->tch_mode == chan_mode) return 1; if (chan_mode == GSM48_CMODE_SPEECH_AMR) bsc_mr_config(conn, conn->lchan, full_rate); LOGPLCHAN(conn->lchan, DMSC, LOGL_NOTICE, "Sending ChanModify for speech: %s\n", get_value_string(gsm48_chan_mode_names, chan_mode)); gsm48_lchan_modify(conn->lchan, chan_mode); } /* we expect the caller will manage T10 */ return 0; error: bsc_assign_fail(conn, 0, NULL); return -1; } int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, uint8_t *mi, int chan_type) { return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type, false); } static void handle_ass_compl(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); enum gsm48_rr_cause cause; /* Expecting gsm48_hdr + cause value */ if (msgb_l3len(msg) != sizeof(*gh) + 1) { LOGPLCHAN(msg->lchan, DRR, LOGL_ERROR, "RR Assignment Complete: length invalid: %u, expected %zu\n", msgb_l3len(msg), sizeof(*gh) + 1); return; } cause = gh->data[0]; LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "ASSIGNMENT COMPLETE cause = %s\n", rr_cause_name(cause)); if (conn->ho) { struct lchan_signal_data sig = { .lchan = msg->lchan, }; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_ASSIGNMENT_COMPL, &sig); /* FIXME: release old channel */ /* send pending messages, if any */ ho_dtap_cache_flush(conn, 1); return; } if (conn->secondary_lchan != msg->lchan) { LOGPLCHAN(msg->lchan, DRR, LOGL_ERROR, "RR Assignment Complete does not match conn's secondary lchan.\n"); return; } lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); conn->lchan = conn->secondary_lchan; conn->secondary_lchan = NULL; /* send pending messages, if any */ ho_dtap_cache_flush(conn, 1); if (is_ipaccess_bts(conn_get_bts(conn)) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) rsl_ipacc_crcx(conn->lchan); bsc_assign_compl(conn, cause); } static void handle_ass_fail(struct gsm_subscriber_connection *conn, struct msgb *msg) { uint8_t *rr_failure; struct gsm48_hdr *gh; if (conn->ho) { struct lchan_signal_data sig; struct gsm48_hdr *gh = msgb_l3(msg); LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "ASSIGNMENT FAILED cause = %s\n", rr_cause_name(gh->data[0])); sig.lchan = msg->lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_ASSIGNMENT_FAIL, &sig); /* FIXME: release allocated new channel */ /* send pending messages, if any */ ho_dtap_cache_flush(conn, 1); return; } if (conn->lchan != msg->lchan) { LOGPLCHAN(msg->lchan, DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); return; } /* stop the timer and release it */ if (conn->secondary_lchan) { lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); conn->secondary_lchan = NULL; } /* send pending messages, if any */ ho_dtap_cache_flush(conn, 1); gh = msgb_l3(msg); if (msgb_l3len(msg) - sizeof(*gh) != 1) { LOGPLCHAN(conn->lchan, DMSC, LOGL_ERROR, "assignment failure unhandled: %zu\n", msgb_l3len(msg) - sizeof(*gh)); rr_failure = NULL; } else { rr_failure = &gh->data[0]; } bsc_assign_fail(conn, GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, rr_failure); } static void handle_classmark_chg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); uint8_t cm2_len, cm3_len = 0; uint8_t *cm2, *cm3 = NULL; LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "CLASSMARK CHANGE "); /* classmark 2 */ cm2_len = gh->data[0]; cm2 = &gh->data[1]; DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); if (payload_len > cm2_len + 1) { /* we must have a classmark3 */ if (gh->data[cm2_len+1] != 0x20) { DEBUGPC(DRR, "ERR CM3 TAG\n"); return; } if (cm2_len > 3) { DEBUGPC(DRR, "CM2 too long!\n"); return; } cm3_len = gh->data[cm2_len+2]; cm3 = &gh->data[cm2_len+3]; if (cm3_len > 14) { DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); return; } DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); } bsc_cm_update(conn, cm2, cm2_len, cm3, cm3_len); } /* Chapter 9.1.16 Handover complete */ static void handle_rr_ho_compl(struct msgb *msg) { struct lchan_signal_data sig; struct gsm48_hdr *gh = msgb_l3(msg); LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "HANDOVER COMPLETE cause = %s\n", rr_cause_name(gh->data[0])); sig.lchan = msg->lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); /* FIXME: release old channel */ /* send pending messages, if any */ ho_dtap_cache_flush(msg->lchan->conn, 1); } /* Chapter 9.1.17 Handover Failure */ static void handle_rr_ho_fail(struct msgb *msg) { struct lchan_signal_data sig; struct gsm48_hdr *gh = msgb_l3(msg); /* Log on both RR and HO categories: it is an RR message, but is still quite important when * filtering on HO. */ LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "HANDOVER FAILED cause = %s\n", rr_cause_name(gh->data[0])); LOGPLCHAN(msg->lchan, DHO, LOGL_DEBUG, "HANDOVER FAILED cause = %s\n", rr_cause_name(gh->data[0])); sig.lchan = msg->lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); /* FIXME: release allocated new channel */ /* send pending messages, if any */ ho_dtap_cache_flush(msg->lchan->conn, 1); } static void dispatch_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { struct gsm48_hdr *gh; uint8_t pdisc; uint8_t msg_type; int rc; if (msgb_l3len(msg) < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "(%s) Message too short for a GSM48 header.\n", bsc_subscr_name(conn->bsub)); return; } gh = msgb_l3(msg); pdisc = gsm48_hdr_pdisc(gh); msg_type = gsm48_hdr_msg_type(gh); /* the idea is to handle all RR messages here, and only hand * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING * RESPONSE or CM SERVICE REQUEST will not be covered here, as * they are only possible in the first L3 message of each L2 * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() * will call api->compl_l3() for it */ switch (pdisc) { case GSM48_PDISC_RR: switch (msg_type) { case GSM48_MT_RR_GPRS_SUSP_REQ: LOGPLCHAN(msg->lchan, DRR, LOGL_DEBUG, "%s\n", gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ)); break; case GSM48_MT_RR_STATUS: LOGPLCHAN(msg->lchan, DRR, LOGL_NOTICE, "%s (cause: %s)\n", gsm48_rr_msg_name(GSM48_MT_RR_STATUS), rr_cause_name(gh->data[0])); break; case GSM48_MT_RR_MEAS_REP: /* This shouldn't actually end up here, as RSL treats * L3 Info of 08.58 MEASUREMENT REPORT different by calling * directly into gsm48_parse_meas_rep */ LOGPLCHAN(msg->lchan, DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!?\n"); gsm48_tx_rr_status(conn, GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT); break; case GSM48_MT_RR_HANDO_COMPL: handle_rr_ho_compl(msg); break; case GSM48_MT_RR_HANDO_FAIL: handle_rr_ho_fail(msg); break; case GSM48_MT_RR_CIPH_M_COMPL: bsc_cipher_mode_compl(conn, msg, conn->lchan->encr.alg_id); break; case GSM48_MT_RR_ASS_COMPL: handle_ass_compl(conn, msg); break; case GSM48_MT_RR_ASS_FAIL: handle_ass_fail(conn, msg); break; case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: rc = gsm48_rx_rr_modif_ack(msg); if (rc < 0) bsc_assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); else bsc_assign_compl(conn, 0); break; case GSM48_MT_RR_CLSM_CHG: handle_classmark_chg(conn, msg); break; case GSM48_MT_RR_APP_INFO: /* Passing RR APP INFO to MSC, not quite * according to spec */ bsc_dtap(conn, link_id, msg); break; default: /* Drop unknown RR message */ LOGPLCHAN(msg->lchan, DRR, LOGL_NOTICE, "Dropping %s 04.08 RR message\n", gsm48_rr_msg_name(msg_type)); gsm48_tx_rr_status(conn, GSM48_RR_CAUSE_MSG_TYPE_N); break; } break; default: bsc_dtap(conn, link_id, msg); break; } } /*! \brief RSL has received a DATA INDICATION with L3 from MS */ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) { int rc; struct gsm_lchan *lchan; lchan = msg->lchan; if (lchan->state != LCHAN_S_ACTIVE) { LOGPLCHAN(msg->lchan, DRSL, LOGL_INFO, "Got data in non active state, discarding.\n"); return -1; } if (lchan->conn) { /* if we already have a connection, forward via DTAP to * MSC */ dispatch_dtap(lchan->conn, link_id, msg); } else { /* allocate a new connection */ rc = BSC_API_CONN_POL_REJECT; lchan->conn = bsc_subscr_con_allocate(msg->lchan->ts->trx->bts->network); if (!lchan->conn) { lchan_release(lchan, 1, RSL_REL_NORMAL); return -1; } lchan->conn->lchan = lchan; /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ rc = bsc_compl_l3(lchan->conn, msg, 0); if (rc != BSC_API_CONN_POL_ACCEPT) { //osmo_fsm_inst_dispatch(lchan->conn->fi, FIXME, NULL); } } return 0; } /*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, const uint8_t *key, int len, int include_imeisv) { if (cipher > 0 && key == NULL) { LOGP(DRSL, LOGL_ERROR, "%s: Need to have an encryption key.\n", bsc_subscr_name(conn->bsub)); return -1; } if (len > MAX_A5_KEY_LEN) { LOGP(DRSL, LOGL_ERROR, "%s: The key is too long: %d\n", bsc_subscr_name(conn->bsub), len); return -1; } LOGP(DRSL, LOGL_DEBUG, "(subscr %s) Cipher Mode: cipher=%d key=%s include_imeisv=%d\n", bsc_subscr_name(conn->bsub), cipher, osmo_hexdump_nospc(key, len), include_imeisv); conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); if (key) { conn->lchan->encr.key_len = len; memcpy(conn->lchan->encr.key, key, len); } return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); } /* * Release all occupied RF Channels but stay around for more. */ int gsm0808_clear(struct gsm_subscriber_connection *conn) { if (conn->ho) bsc_clear_handover(conn, 1); if (conn->secondary_lchan) lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); if (conn->lchan) lchan_release(conn->lchan, 1, RSL_REL_NORMAL); conn->lchan = NULL; conn->secondary_lchan = NULL; return 0; } static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) { struct msgb *msg = _data; /* * There seems to be a small window that the RLL timer can * fire after a lchan_release call and before the S_CHALLOC_FREED * is called. Check if a conn is set before proceeding. */ if (!lchan->conn) return; switch (rllr_ind) { case BSC_RLLR_IND_EST_CONF: rsl_data_request(msg, OBSC_LINKID_CB(msg)); break; case BSC_RLLR_IND_REL_IND: case BSC_RLLR_IND_ERR_IND: case BSC_RLLR_IND_TIMEOUT: bsc_sapi_n_reject(lchan->conn, OBSC_LINKID_CB(msg)); msgb_free(msg); break; } } static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_lchan *lchan; struct lchan_signal_data *lchan_data; if (subsys != SS_LCHAN) return 0; lchan_data = signal_data; if (!lchan_data->lchan || !lchan_data->lchan->conn) return 0; lchan = lchan_data->lchan; switch (signal) { case S_LCHAN_UNEXPECTED_RELEASE: LOGPLCHAN(lchan, DMSC, LOGL_NOTICE, "S_LCHAN_UNEXPECTED_RELEASE\n"); handle_release(lchan->conn, lchan); break; case S_LCHAN_ACTIVATE_ACK: handle_chan_ack(lchan->conn, lchan); break; case S_LCHAN_ACTIVATE_NACK: handle_chan_nack(lchan->conn, lchan); break; } return 0; } static void handle_release(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan) { if (conn->secondary_lchan == lchan) { LOGPLCHAN(lchan, DMSC, LOGL_NOTICE, "lchan release on new lchan, Assignment failed\n"); conn->secondary_lchan = NULL; bsc_assign_fail(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, NULL); } /* clear the connection now */ bsc_clear_request(conn, 0); /* now give up all channels */ if (conn->lchan == lchan) conn->lchan = NULL; if (conn->ho && conn->ho->new_lchan == lchan) bsc_clear_handover(conn, 0); lchan->conn = NULL; } static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan) { if (conn->secondary_lchan != lchan) return; LOGPLCHAN(lchan, DMSC, LOGL_NOTICE, "Sending RR Assignment\n"); gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); } static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan) { if (conn->secondary_lchan != lchan) return; LOGPLCHAN(lchan, DMSC, LOGL_ERROR, "Channel activation failed.\n"); conn->secondary_lchan->conn = NULL; conn->secondary_lchan = NULL; bsc_assign_fail(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, NULL); } static __attribute__((constructor)) void on_dso_load_bsc(void) { osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); } osmo-bsc-1.3.0/src/osmo-bsc/bsc_ctrl_commands.c000066400000000000000000000310351332665256100214010ustar00rootroot00000000000000/* * (C) 2013-2015 by Holger Hans Peter Freyther * (C) 2013-2015 by sysmocom s.f.m.c. GmbH * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 CTRL_CMD_DEFINE(net_mcc, "mcc"); static int get_net_mcc(struct ctrl_cmd *cmd, void *_data) { struct gsm_network *net = cmd->node; cmd->reply = talloc_asprintf(cmd, "%s", osmo_mcc_name(net->plmn.mcc)); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_net_mcc(struct ctrl_cmd *cmd, void *_data) { struct gsm_network *net = cmd->node; uint16_t mcc; if (osmo_mcc_from_str(cmd->value, &mcc)) return -1; net->plmn.mcc = mcc; return get_net_mcc(cmd, _data); } static int verify_net_mcc(struct ctrl_cmd *cmd, const char *value, void *_data) { if (osmo_mcc_from_str(value, NULL)) return -1; return 0; } CTRL_CMD_DEFINE(net_mnc, "mnc"); static int get_net_mnc(struct ctrl_cmd *cmd, void *_data) { struct gsm_network *net = cmd->node; cmd->reply = talloc_asprintf(cmd, "%s", osmo_mnc_name(net->plmn.mnc, net->plmn.mnc_3_digits)); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_net_mnc(struct ctrl_cmd *cmd, void *_data) { struct gsm_network *net = cmd->node; struct osmo_plmn_id plmn = net->plmn; if (osmo_mnc_from_str(cmd->value, &plmn.mnc, &plmn.mnc_3_digits)) { cmd->reply = "Error while decoding MNC"; return CTRL_CMD_ERROR; } net->plmn = plmn; return get_net_mnc(cmd, _data); } static int verify_net_mnc(struct ctrl_cmd *cmd, const char *value, void *_data) { if (osmo_mnc_from_str(value, NULL, NULL)) return -1; return 0; } static int set_net_apply_config(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { if (!is_ipaccess_bts(bts)) continue; /* * The ip.access nanoBTS seems to be unrelaible on BSSGP * so let's us just reboot it. For the sysmoBTS we can just * restart the process as all state is gone. */ if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) { struct gsm_bts_trx *trx; llist_for_each_entry_reverse(trx, &bts->trx_list, list) abis_nm_ipaccess_restart(trx); } else ipaccess_drop_oml(bts); } cmd->reply = "Tried to drop the BTS"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration"); static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d) { char *tmp, *saveptr, *mcc, *mnc; int rc = 0; tmp = talloc_strdup(cmd, value); if (!tmp) return 1; mcc = strtok_r(tmp, ",", &saveptr); mnc = strtok_r(NULL, ",", &saveptr); if (osmo_mcc_from_str(mcc, NULL) || osmo_mnc_from_str(mnc, NULL, NULL)) rc = -1; talloc_free(tmp); return rc; } static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; char *tmp, *saveptr, *mcc_str, *mnc_str; struct osmo_plmn_id plmn; tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; mcc_str = strtok_r(tmp, ",", &saveptr); mnc_str = strtok_r(NULL, ",", &saveptr); if (osmo_mcc_from_str(mcc_str, &plmn.mcc)) { cmd->reply = "Error while decoding MCC"; talloc_free(tmp); return CTRL_CMD_ERROR; } if (osmo_mnc_from_str(mnc_str, &plmn.mnc, &plmn.mnc_3_digits)) { cmd->reply = "Error while decoding MNC"; talloc_free(tmp); return CTRL_CMD_ERROR; } talloc_free(tmp); if (!osmo_plmn_cmp(&net->plmn, &plmn)) { cmd->reply = "Nothing changed"; return CTRL_CMD_REPLY; } net->plmn = plmn; return set_net_apply_config(cmd, data); oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply"); /* BTS related commands below */ CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535); CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535); static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; if (!is_ipaccess_bts(bts)) { cmd->reply = "BTS is not IP based"; return CTRL_CMD_ERROR; } ipaccess_drop_oml(bts); cmd->reply = "Tried to drop the BTS"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration"); static int set_bts_si(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; int rc; rc = gsm_bts_set_system_infos(bts); if (rc != 0) { cmd->reply = "Failed to generate SI"; return CTRL_CMD_ERROR; } cmd->reply = "Generated new System Information"; return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations"); static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data) { int i; struct pchan_load pl; struct gsm_bts *bts; const char *space = ""; bts = cmd->node; memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); cmd->reply = talloc_strdup(cmd, ""); for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) { const struct load_counter *lc = &pl.pchan[i]; /* These can never have user load */ if (i == GSM_PCHAN_NONE) continue; if (i == GSM_PCHAN_CCCH) continue; if (i == GSM_PCHAN_PDCH) continue; if (i == GSM_PCHAN_UNKNOWN) continue; cmd->reply = talloc_asprintf_append(cmd->reply, "%s%s,%u,%u", space, gsm_pchan_name(i), lc->used, lc->total); if (!cmd->reply) goto error; space = " "; } return CTRL_CMD_REPLY; error: cmd->reply = "Memory allocation failure"; return CTRL_CMD_ERROR; } CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load"); static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data) { const struct gsm_bts *bts = cmd->node; cmd->reply = get_model_oml_status(bts); return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state"); static int get_bts_oml_up(struct ctrl_cmd *cmd, void *data) { const struct gsm_bts *bts = cmd->node; cmd->reply = talloc_asprintf(cmd, "%llu", bts_uptime(bts)); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(bts_oml_up, "oml-uptime"); static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data) { int valid; enum bts_gprs_mode mode; struct gsm_bts *bts = cmd->node; mode = bts_gprs_mode_parse(value, &valid); if (!valid) { cmd->reply = "Mode is not known"; return 1; } if (!bts_gprs_mode_is_compat(bts, mode)) { cmd->reply = "bts does not support this mode"; return 1; } return 0; } static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode)); return CTRL_CMD_REPLY; } static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) { struct gsm_bts *bts = cmd->node; bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL); return get_bts_gprs_mode(cmd, data); } CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode"); static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data) { const char *oper, *admin, *policy; struct gsm_bts *bts = cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy); if (!cmd->reply) { cmd->reply = "OOM."; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state"); static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; struct gsm_bts *bts; const char *policy_name; policy_name = osmo_bsc_rf_get_policy_name(net->bsc_data->rf_ctrl->policy); llist_for_each_entry(bts, &net->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) continue; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability == NM_AVSTATE_OK && trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { cmd->reply = talloc_asprintf(cmd, "state=on,policy=%s,bts=%u,trx=%u", policy_name, bts->nr, trx->nr); return CTRL_CMD_REPLY; } } } cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s", policy_name); return CTRL_CMD_REPLY; } #define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z" static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) { int locked = atoi(cmd->value); struct gsm_network *net = cmd->node; time_t now = time(NULL); char now_buf[64]; struct osmo_bsc_rf *rf; if (!net) { cmd->reply = "net not found."; return CTRL_CMD_ERROR; } rf = net->bsc_data->rf_ctrl; if (!rf) { cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; return CTRL_CMD_ERROR; } talloc_free(rf->last_rf_lock_ctrl_command); strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now)); rf->last_rf_lock_ctrl_command = talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf); osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1'); cmd->reply = talloc_asprintf(cmd, "%u", locked); if (!cmd->reply) { cmd->reply = "OOM."; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data) { int locked = atoi(cmd->value); if ((locked != 0) && (locked != 1)) return 1; return 0; } CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); static int get_net_bts_num(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = cmd->node; cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts); return CTRL_CMD_REPLY; } CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts"); /* TRX related commands below here */ CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red); static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data) { int tmp = atoi(value); if (tmp < 0 || tmp > 22) { cmd->reply = "Value must be between 0 and 22"; return -1; } if (tmp & 1) { cmd->reply = "Value must be even"; return -1; } return 0; } CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023); static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data) { struct gsm_bts_trx *trx = cmd->node; int old_power; /* remember the old value, set the new one */ old_power = trx->max_power_red; trx->max_power_red = atoi(cmd->value); /* Maybe update the value */ if (old_power != trx->max_power_red) { LOGP(DCTRL, LOGL_NOTICE, "%s updating max_pwr_red(%d)\n", gsm_trx_name(trx), trx->max_power_red); abis_nm_update_max_power_red(trx); } return get_trx_max_power(cmd, _data); } CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction"); int bsc_base_ctrl_cmds_install(void) { int rc = 0; rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock); rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_up); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode); rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power); rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn); return rc; } osmo-bsc-1.3.0/src/osmo-bsc/bsc_ctrl_lookup.c000066400000000000000000000070601332665256100211120ustar00rootroot00000000000000/* SNMP-like status interface. Look-up of BTS/TRX/MSC * * (C) 2010-2011 by Daniel Willmann * (C) 2010-2011 by On-Waves * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include extern vector ctrl_node_vec; /*! \brief control interface lookup function for bsc/bts/msc gsm_data * \param[in] data Private data passed to controlif_setup() * \param[in] vline Vector of the line holding the command string * \param[out] node_type type (CTRL_NODE_) that was determined * \param[out] node_data private dta of node that was determined * \param i Current index into vline, up to which it is parsed */ static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i) { struct gsm_network *net = data; struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; struct bsc_msc_data *msc = NULL; char *token = vector_slot(vline, *i); long num; /* TODO: We need to make sure that the following chars are digits * and/or use strtol to check if number conversion was successful * Right now something like net.bts_stats will not work */ if (!strcmp(token, "bts")) { if (*node_type != CTRL_NODE_ROOT || !net) goto err_missing; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; bts = gsm_bts_num(net, num); if (!bts) goto err_missing; *node_data = bts; *node_type = CTRL_NODE_BTS; } else if (!strcmp(token, "trx")) { if (*node_type != CTRL_NODE_BTS || !*node_data) goto err_missing; bts = *node_data; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; trx = gsm_bts_trx_num(bts, num); if (!trx) goto err_missing; *node_data = trx; *node_type = CTRL_NODE_TRX; } else if (!strcmp(token, "ts")) { if (*node_type != CTRL_NODE_TRX || !*node_data) goto err_missing; trx = *node_data; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; if ((num >= 0) && (num < TRX_NR_TS)) ts = &trx->ts[num]; if (!ts) goto err_missing; *node_data = ts; *node_type = CTRL_NODE_TS; } else if (!strcmp(token, "msc")) { if (*node_type != CTRL_NODE_ROOT || !net) goto err_missing; (*i)++; if (!ctrl_parse_get_num(vline, *i, &num)) goto err_index; msc = osmo_msc_data_find(net, num); if (!msc) goto err_missing; *node_data = msc; *node_type = CTRL_NODE_MSC; } else return 0; return 1; err_missing: return -ENODEV; err_index: return -ERANGE; } struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, const char *bind_addr, uint16_t port) { return ctrl_interface_setup_dynip2(net, bind_addr, port, bsc_ctrl_node_lookup, _LAST_CTRL_NODE_BSC); } osmo-bsc-1.3.0/src/osmo-bsc/bsc_dyn_ts.c000066400000000000000000000033631332665256100200570ustar00rootroot00000000000000/* Dynamic PDCH initialisation implementation shared across NM and RSL */ /* (C) 2016 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 static void tchf_pdch_ts_init(struct gsm_bts_trx_ts *ts) { int rc; rc = rsl_ipacc_pdch_activate(ts, 1); if (rc != 0 && rc != -ENOTSUP) LOGP(DRSL, LOGL_ERROR, "%s %s: PDCH ACT failed\n", gsm_ts_name(ts), gsm_pchan_name(ts->pchan)); } static void tchf_tchh_pdch_ts_init(struct gsm_bts_trx_ts *ts) { dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); } void dyn_ts_init(struct gsm_bts_trx_ts *ts) { /* Clear all TCH/F_PDCH flags */ ts->flags &= ~(TS_F_PDCH_PENDING_MASK | TS_F_PDCH_ACTIVE); /* Clear TCH/F_TCH/H_PDCH state */ ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE; ts->dyn.pending_chan_activ = NULL; switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: tchf_pdch_ts_init(ts); break; case GSM_PCHAN_TCH_F_TCH_H_PDCH: tchf_tchh_pdch_ts_init(ts); break; default: break; } } osmo-bsc-1.3.0/src/osmo-bsc/bsc_init.c000066400000000000000000000175401332665256100175240ustar00rootroot00000000000000/* A hackish minimal BSC (+MSC +HLR) implementation */ /* (C) 2008-2018 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int bsc_shutdown_net(struct gsm_network *net) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); } return 0; } unsigned long long bts_uptime(const struct gsm_bts *bts) { struct timespec tp; if (!bts->uptime || !bts->oml_link) { LOGP(DNM, LOGL_ERROR, "BTS %u OML link uptime unavailable\n", bts->nr); return 0; } if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) { LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno)); return 0; } /* monotonic clock helps to ensure that the conversion is valid */ return difftime(tp.tv_sec, bts->uptime); } static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) { struct gsm_bts *bts = trx->bts; int rc, j; if (si_len) { DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); } else DEBUGP(DRR, "SI%s: OFF\n", get_value_string(osmo_sitype_strs, i)); switch (i) { case SYSINFO_TYPE_5: case SYSINFO_TYPE_5bis: case SYSINFO_TYPE_5ter: case SYSINFO_TYPE_6: rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), si_len ? GSM_BTS_SI(bts, i) : NULL, si_len); break; case SYSINFO_TYPE_2quater: if (si_len == 0) { rc = rsl_bcch_info(trx, i, NULL, 0); break; } rc = 0; for (j = 0; j <= bts->si2q_count; j++) rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN); break; default: rc = rsl_bcch_info(trx, i, si_len ? GSM_BTS_SI(bts, i) : NULL, si_len); break; } return rc; } /* set all system information types for a TRX */ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) { int i, rc; struct gsm_bts *bts = trx->bts; uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; int si_len[_MAX_SYSINFO_TYPE]; bts->si_common.cell_sel_par.ms_txpwr_max_ccch = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); bts->si_common.cell_sel_par.neci = bts->network->neci; /* Zero/forget the state of the dynamically computed SIs, leeping the static ones */ bts->si_valid = bts->si_mode_static; /* First, we determine which of the SI messages we actually need */ if (trx == bts->c0) { /* 1...4 are always present on a C0 TRX */ gen_si[n_si++] = SYSINFO_TYPE_1; gen_si[n_si++] = SYSINFO_TYPE_2; gen_si[n_si++] = SYSINFO_TYPE_2bis; gen_si[n_si++] = SYSINFO_TYPE_2ter; gen_si[n_si++] = SYSINFO_TYPE_2quater; gen_si[n_si++] = SYSINFO_TYPE_3; gen_si[n_si++] = SYSINFO_TYPE_4; /* 13 is always present on a C0 TRX of a GPRS BTS */ if (bts->gprs.mode != BTS_GPRS_NONE) gen_si[n_si++] = SYSINFO_TYPE_13; } /* 5 and 6 are always present on every TRX */ gen_si[n_si++] = SYSINFO_TYPE_5; gen_si[n_si++] = SYSINFO_TYPE_5bis; gen_si[n_si++] = SYSINFO_TYPE_5ter; gen_si[n_si++] = SYSINFO_TYPE_6; /* Second, we generate the selected SI via RSL */ for (n = 0; n < n_si; n++) { i = gen_si[n]; /* Only generate SI if this SI is not in "static" (user-defined) mode */ if (!(bts->si_mode_static & (1 << i))) { /* Set SI as being valid. gsm_generate_si() might unset * it, if SI is not required. */ bts->si_valid |= (1 << i); rc = gsm_generate_si(bts, i); if (rc < 0) goto err_out; si_len[i] = rc; } else { if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis || i == SYSINFO_TYPE_5ter) si_len[i] = 18; else if (i == SYSINFO_TYPE_6) si_len[i] = 11; else si_len[i] = 23; } } /* Third, we send the selected SI via RSL */ for (n = 0; n < n_si; n++) { i = gen_si[n]; /* if we don't currently have this SI, we send a zero-length * RSL BCCH FILLING / SACCH FILLING * in order to deactivate * the SI, in case it might have previously been active */ if (!GSM_BTS_HAS_SI(bts, i)) rc = rsl_si(trx, i, 0); else rc = rsl_si(trx, i, si_len[i]); if (rc < 0) return rc; } /* Make sure the PCU is aware (in case anything GPRS related has * changed in SI */ pcu_info_update(bts); return 0; err_out: LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, " "most likely a problem with neighbor cell list generation\n", get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc)); return rc; } /* set all system information types for a BTS */ int gsm_bts_set_system_infos(struct gsm_bts *bts) { struct gsm_bts_trx *trx; /* Generate a new ID */ bts->bcch_change_mark += 1; bts->bcch_change_mark %= 0x7; llist_for_each_entry(trx, &bts->trx_list, list) { int rc; rc = gsm_bts_trx_set_system_infos(trx); if (rc != 0) return rc; } return 0; } /* XXX hard-coded for now */ #define T3122_CHAN_LOAD_SAMPLE_INTERVAL 1 /* in seconds */ static void update_t3122_chan_load_timer(void *data) { struct gsm_network *net = data; struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) bts_update_t3122_chan_load(bts); /* Keep this timer ticking. */ osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0); } static struct gsm_network *bsc_network_init(void *ctx) { struct gsm_network *net = gsm_network_init(ctx); net->bsc_data = talloc_zero(net, struct osmo_bsc_data); if (!net->bsc_data) { talloc_free(net); return NULL; } /* Init back pointer */ net->bsc_data->auto_off_timeout = -1; net->bsc_data->network = net; INIT_LLIST_HEAD(&net->bsc_data->mscs); net->ho = ho_cfg_init(net, NULL); net->hodec2.congestion_check_interval_s = HO_CFG_CONGESTION_CHECK_DEFAULT; /* init statistics */ net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0); if (!net->bsc_ctrs) { talloc_free(net); return NULL; } gsm_net_update_ctype(net); /* * At present all BTS in the network share one channel load timeout. * If this becomes a problem for networks with a lot of BTS, this * code could be refactored to run the timeout individually per BTS. */ osmo_timer_setup(&net->t3122_chan_load_timer, update_t3122_chan_load_timer, net); osmo_timer_schedule(&net->t3122_chan_load_timer, T3122_CHAN_LOAD_SAMPLE_INTERVAL, 0); return net; } int bsc_network_alloc(void) { /* initialize our data structures */ bsc_gsmnet = bsc_network_init(tall_bsc_ctx); if (!bsc_gsmnet) return -ENOMEM; return 0; } struct gsm_bts *bsc_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic) { struct gsm_bts *bts = gsm_bts_alloc_register(net, type, bsic); bts->ho = ho_cfg_init(bts, net->ho); return bts; } osmo-bsc-1.3.0/src/osmo-bsc/bsc_rf_ctrl.c000066400000000000000000000312051332665256100202060ustar00rootroot00000000000000/* RF Ctl handling socket */ /* (C) 2010 by Harald Welte * (C) 2010-2014 by Holger Hans Peter Freyther * (C) 2010-2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #define RF_CMD_QUERY '?' #define RF_CMD_OFF '0' #define RF_CMD_ON '1' #define RF_CMD_D_OFF 'd' #define RF_CMD_ON_G 'g' static const struct value_string opstate_names[] = { { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, "inoperational" }, { OSMO_BSC_RF_OPSTATE_OPERATIONAL, "operational" }, { 0, NULL } }; static const struct value_string adminstate_names[] = { { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, "unlocked" }, { OSMO_BSC_RF_ADMINSTATE_LOCKED, "locked" }, { 0, NULL } }; static const struct value_string policy_names[] = { { OSMO_BSC_RF_POLICY_OFF, "off" }, { OSMO_BSC_RF_POLICY_ON, "on" }, { OSMO_BSC_RF_POLICY_GRACE, "grace" }, { OSMO_BSC_RF_POLICY_UNKNOWN, "unknown" }, { 0, NULL } }; const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate) { return get_value_string(opstate_names, opstate); } const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate) { return get_value_string(adminstate_names, adminstate); } const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy) { return get_value_string(policy_names, policy); } enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED) return OSMO_BSC_RF_OPSTATE_OPERATIONAL; } /* No trx were active, so this bts is disabled */ return OSMO_BSC_RF_OPSTATE_INOPERATIONAL; } enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) return OSMO_BSC_RF_ADMINSTATE_UNLOCKED; } /* All trx administrative states were locked */ return OSMO_BSC_RF_ADMINSTATE_LOCKED; } enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts) { struct osmo_bsc_data *bsc_data = bts->network->bsc_data; if (!bsc_data) return OSMO_BSC_RF_POLICY_UNKNOWN; switch (bsc_data->rf_ctrl->policy) { case S_RF_ON: return OSMO_BSC_RF_POLICY_ON; case S_RF_OFF: return OSMO_BSC_RF_POLICY_OFF; case S_RF_GRACE: return OSMO_BSC_RF_POLICY_GRACE; default: return OSMO_BSC_RF_POLICY_UNKNOWN; } } static int lock_each_trx(struct gsm_network *net, bool lock) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from trx lock.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { gsm_trx_lock_rf(trx, lock, "ctrl"); } } return 0; } static void send_resp(struct osmo_bsc_rf_conn *conn, char send) { struct msgb *msg; msg = msgb_alloc(10, "RF Query"); if (!msg) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); return; } msg->l2h = msgb_put(msg, 1); msg->l2h[0] = send; if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); msgb_free(msg); return; } return; } /* * Send a * 'g' when we are in grace mode * '1' when one TRX is online, * '0' otherwise */ static void handle_query(struct osmo_bsc_rf_conn *conn) { struct gsm_bts *bts; char send = RF_CMD_OFF; if (conn->rf->policy == S_RF_GRACE) return send_resp(conn, RF_CMD_ON_G); llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { struct gsm_bts_trx *trx; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from query.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability == NM_AVSTATE_OK && trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { send = RF_CMD_ON; break; } } } send_resp(conn, send); } static void rf_check_cb(void *_data) { struct gsm_bts *bts; struct osmo_bsc_rf *rf = _data; llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { struct gsm_bts_trx *trx; /* don't bother to check a booting or missing BTS */ if (!bts->oml_link || !is_ipaccess_bts(bts)) continue; /* Exclude the BTS from the global lock */ if (bts->excl_from_rf_lock) { LOGP(DLINP, LOGL_DEBUG, "Excluding BTS(%d) from query.\n", bts->nr); continue; } llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->mo.nm_state.availability != NM_AVSTATE_OK || trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); ipaccess_drop_oml(bts); break; } } } } static void send_signal(struct osmo_bsc_rf *rf, int val) { struct rf_signal_data sig; sig.net = rf->gsm_network; rf->policy = val; osmo_signal_dispatch(SS_RF, val, &sig); } static int switch_rf_off(struct osmo_bsc_rf *rf) { lock_each_trx(rf->gsm_network, true); send_signal(rf, S_RF_OFF); return 0; } static void grace_timeout(void *_data) { struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); switch_rf_off(rf); } static int enter_grace(struct osmo_bsc_rf *rf) { if (osmo_timer_pending(&rf->grace_timeout)) { LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); return 0; } osmo_timer_setup(&rf->grace_timeout, grace_timeout, rf); osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", rf->gsm_network->bsc_data->mid_call_timeout); send_signal(rf, S_RF_GRACE); return 0; } static void rf_delay_cmd_cb(void *data) { struct osmo_bsc_rf *rf = data; switch (rf->last_request) { case RF_CMD_D_OFF: rf->last_state_command = "RF Direct Off"; osmo_timer_del(&rf->rf_check); osmo_timer_del(&rf->grace_timeout); switch_rf_off(rf); break; case RF_CMD_ON: rf->last_state_command = "RF Direct On"; osmo_timer_del(&rf->grace_timeout); lock_each_trx(rf->gsm_network, false); send_signal(rf, S_RF_ON); osmo_timer_schedule(&rf->rf_check, 3, 0); break; case RF_CMD_OFF: rf->last_state_command = "RF Scheduled Off"; osmo_timer_del(&rf->rf_check); enter_grace(rf); break; } } static int rf_read_cmd(struct osmo_fd *fd) { struct osmo_bsc_rf_conn *conn = fd->data; char buf[1]; int rc; rc = read(fd->fd, buf, sizeof(buf)); if (rc != sizeof(buf)) { LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); osmo_fd_unregister(fd); close(fd->fd); osmo_wqueue_clear(&conn->queue); talloc_free(conn); return -1; } switch (buf[0]) { case RF_CMD_QUERY: handle_query(conn); break; case RF_CMD_D_OFF: case RF_CMD_ON: case RF_CMD_OFF: osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); break; default: conn->rf->last_state_command = "Unknown command"; LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); break; } return 0; } static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) { int rc; rc = write(fd->fd, msg->data, msg->len); if (rc != msg->len) { LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); return -1; } return 0; } static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) { struct osmo_bsc_rf_conn *conn; struct osmo_bsc_rf *rf = bfd->data; struct sockaddr_un addr; socklen_t len = sizeof(addr); int fd; fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); if (fd < 0) { LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", errno, strerror(errno)); return -1; } conn = talloc_zero(rf, struct osmo_bsc_rf_conn); if (!conn) { LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); close(fd); return -1; } osmo_wqueue_init(&conn->queue, 10); conn->queue.bfd.data = conn; conn->queue.bfd.fd = fd; conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; conn->queue.read_cb = rf_read_cmd; conn->queue.write_cb = rf_write_cmd; conn->rf = rf; if (osmo_fd_register(&conn->queue.bfd) != 0) { close(fd); talloc_free(conn); return -1; } return 0; } static void rf_auto_off_cb(void *_timer) { struct osmo_bsc_rf *rf = _timer; LOGP(DLINP, LOGL_NOTICE, "Going to switch off RF due lack of a MSC connection.\n"); osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); } static int msc_signal_handler(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_network *net; struct msc_signal_data *msc; struct osmo_bsc_rf *rf; /* check if we want to handle this signal */ if (subsys != SS_MSC) return 0; net = handler_data; msc = signal_data; /* check if we have the needed information */ if (!net->bsc_data) return 0; if (msc->data->type != MSC_CON_TYPE_NORMAL) return 0; rf = net->bsc_data->rf_ctrl; switch (signal) { case S_MSC_LOST: if (net->bsc_data->auto_off_timeout < 0) return 0; if (osmo_timer_pending(&rf->auto_off_timer)) return 0; osmo_timer_schedule(&rf->auto_off_timer, net->bsc_data->auto_off_timeout, 0); break; case S_MSC_CONNECTED: osmo_timer_del(&rf->auto_off_timer); break; } return 0; } static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path) { unsigned int namelen; struct sockaddr_un local; struct osmo_fd *bfd; int rc; bfd = &rf->listen; bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (bfd->fd < 0) { LOGP(DLINP, LOGL_ERROR, "Can not create socket. %d/%s\n", errno, strerror(errno)); return -1; } local.sun_family = AF_UNIX; osmo_strlcpy(local.sun_path, path, sizeof(local.sun_path)); unlink(local.sun_path); /* we use the same magic that X11 uses in Xtranssock.c for * calculating the proper length of the sockaddr */ #if defined(BSD44SOCKETS) || defined(__UNIXWARE__) local.sun_len = strlen(local.sun_path); #endif #if defined(BSD44SOCKETS) || defined(SUN_LEN) namelen = SUN_LEN(&local); #else namelen = strlen(local.sun_path) + offsetof(struct sockaddr_un, sun_path); #endif rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); if (rc != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", local.sun_path, errno, strerror(errno)); close(bfd->fd); return -1; } if (listen(bfd->fd, 0) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); close(bfd->fd); return -1; } bfd->when = BSC_FD_READ; bfd->cb = rf_ctrl_accept; bfd->data = rf; if (osmo_fd_register(bfd) != 0) { LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n"); close(bfd->fd); return -1; } return 0; } struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) { struct osmo_bsc_rf *rf; rf = talloc_zero(NULL, struct osmo_bsc_rf); if (!rf) { LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); return NULL; } if (path && rf_create_socket(rf, path) != 0) { talloc_free(rf); return NULL; } rf->gsm_network = net; rf->policy = S_RF_ON; rf->last_state_command = ""; rf->last_rf_lock_ctrl_command = talloc_strdup(rf, ""); /* check the rf state */ osmo_timer_setup(&rf->rf_check, rf_check_cb, rf); /* delay cmd handling */ osmo_timer_setup(&rf->delay_cmd, rf_delay_cmd_cb, rf); osmo_timer_setup(&rf->auto_off_timer, rf_auto_off_cb, rf); /* listen to RF signals */ osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); return rf; } void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) { rf->last_request = cmd; if (!osmo_timer_pending(&rf->delay_cmd)) osmo_timer_schedule(&rf->delay_cmd, 1, 0); } osmo-bsc-1.3.0/src/osmo-bsc/bsc_rll.c000066400000000000000000000073751332665256100173570ustar00rootroot00000000000000/* GSM BSC Radio Link Layer API * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ /* (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 #include #include #include #include #include #include #include #include struct bsc_rll_req { struct llist_head list; struct osmo_timer_list timer; struct gsm_lchan *lchan; uint8_t link_id; void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, void *data, enum bsc_rllr_ind); void *data; }; /* we only compare C1, C2 and SAPI */ #define LINKID_MASK 0xC7 static LLIST_HEAD(bsc_rll_reqs); static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) { llist_del(&rllr->list); rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); talloc_free(rllr); } static void timer_cb(void *_rllr) { struct bsc_rll_req *rllr = _rllr; complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); } /* establish a RLL connection with given SAPI / priority */ int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, void (*cb)(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind), void *data) { struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); uint8_t link_id; if (!rllr) return -ENOMEM; link_id = sapi; /* If we are a TCH and not in signalling mode, we need to * indicate that the new RLL connection is to be made on the SACCH */ if ((lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) link_id |= 0x40; rllr->lchan = lchan; rllr->link_id = link_id; rllr->cb = cb; rllr->data = data; llist_add(&rllr->list, &bsc_rll_reqs); osmo_timer_setup(&rllr->timer, timer_cb, rllr); osmo_timer_schedule(&rllr->timer, 7, 0); /* send the RSL RLL ESTablish REQuest */ return rsl_establish_request(rllr->lchan, rllr->link_id); } /* Called from RSL code in case we have received an indication regarding * any RLL link */ void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type) { struct bsc_rll_req *rllr, *rllr2; llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { if (rllr->lchan == lchan && (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) { osmo_timer_del(&rllr->timer); complete_rllr(rllr, type); return; } } } static int rll_lchan_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct challoc_signal_data *challoc; struct bsc_rll_req *rllr, *rllr2; if (subsys != SS_CHALLOC || signal != S_CHALLOC_FREED) return 0; challoc = (struct challoc_signal_data *) signal_data; llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { if (rllr->lchan == challoc->lchan) { osmo_timer_del(&rllr->timer); complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); } } return 0; } static __attribute__((constructor)) void on_dso_load_rll(void) { osmo_signal_register_handler(SS_CHALLOC, rll_lchan_signal, NULL); } osmo-bsc-1.3.0/src/osmo-bsc/bsc_subscr_conn_fsm.c000066400000000000000000001115511332665256100217410ustar00rootroot00000000000000/* (C) 2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define S(x) (1 << (x)) #define MGCP_MGW_TIMEOUT 4 /* in seconds */ #define MGCP_MGW_TIMEOUT_TIMER_NR 1 #define MGCP_MGW_HO_TIMEOUT 4 /* in seconds */ #define MGCP_MGW_HO_TIMEOUT_TIMER_NR 2 #define ENDPOINT_ID "rtpbridge/*@mgw" enum gscon_fsm_states { ST_INIT, /* waiting for CC from MSC */ ST_WAIT_CC, /* active connection */ ST_ACTIVE, /* during assignment; waiting for ASS_CMPL */ ST_WAIT_ASS_CMPL, /* BSSMAP CLEAR has been received */ ST_CLEARING, /* MGW handling */ /* during assignment; waiting for MGW response to CRCX for BTS */ ST_WAIT_CRCX_BTS, /* during assignment; waiting for MGW response to MDCX for BTS */ ST_WAIT_MDCX_BTS, /* during assignment; waiting for MGW response to CRCX for MSC */ ST_WAIT_CRCX_MSC, /* MT (inbound) handover */ /* Wait for Handover Access from MS/BTS */ ST_WAIT_MT_HO_ACC, /* Wait for RR Handover Complete from MS/BTS */ ST_WAIT_MT_HO_COMPL, /* MO (outbound) handover */ /* Wait for Handover Command / Handover Required Reject from MSC */ ST_WAIT_MO_HO_CMD, /* Wait for Clear Command from MSC */ ST_MO_HO_PROCEEDING, /* Internal HO handling */ /* Wait for the handover logic to complete the handover */ ST_WAIT_HO_COMPL, /* during handover; waiting for MGW response to MDCX for BTS */ ST_WAIT_MDCX_BTS_HO, }; static const struct value_string gscon_fsm_event_names[] = { {GSCON_EV_A_CONN_IND, "MT-CONNECT.ind"}, {GSCON_EV_A_CONN_REQ, "MO-CONNECT.req"}, {GSCON_EV_A_CONN_CFM, "MO-CONNECT.cfm"}, {GSCON_EV_A_ASSIGNMENT_CMD, "ASSIGNMENT_CMD"}, {GSCON_EV_A_CLEAR_CMD, "CLEAR_CMD"}, {GSCON_EV_A_DISC_IND, "DISCONNET.ind"}, {GSCON_EV_A_HO_REQ, "HANDOVER_REQUEST"}, {GSCON_EV_RR_ASS_COMPL, "RR_ASSIGN_COMPL"}, {GSCON_EV_RR_ASS_FAIL, "RR_ASSIGN_FAIL"}, {GSCON_EV_RLL_REL_IND, "RLL_RELEASE.ind"}, {GSCON_EV_RSL_CONN_FAIL, "RSL_CONN_FAIL.ind"}, {GSCON_EV_RSL_CLEAR_COMPL, "RSL_CLEAR_COMPLETE"}, {GSCON_EV_MO_DTAP, "MO-DTAP"}, {GSCON_EV_MT_DTAP, "MT-DTAP"}, {GSCON_EV_TX_SCCP, "TX_SCCP"}, {GSCON_EV_MGW_FAIL_BTS, "MGW_FAILURE_BTS"}, {GSCON_EV_MGW_FAIL_MSC, "MGW_FAILURE_MSC"}, {GSCON_EV_MGW_CRCX_RESP_BTS, "MGW_CRCX_RESPONSE_BTS"}, {GSCON_EV_MGW_MDCX_RESP_BTS, "MGW_MDCX_RESPONSE_BTS"}, {GSCON_EV_MGW_CRCX_RESP_MSC, "MGW_CRCX_RESPONSE_MSC"}, {GSCON_EV_MGW_MDCX_RESP_MSC, "MGW_MDCX_RESPONSE_MSC"}, {GSCON_EV_HO_START, "HO_START"}, {GSCON_EV_HO_TIMEOUT, "HO_TIMEOUT"}, {GSCON_EV_HO_FAIL, "HO_FAIL"}, {GSCON_EV_HO_COMPL, "HO_COMPL"}, {GSCON_EV_LCLS_FAIL, "LCLS_FAIL"}, {0, NULL} }; /* Depending on the channel mode and rate, set the codec type that is signalled * towards the MGW. */ void bsc_subscr_pick_codec(struct mgcp_conn_peer *conn_peer, struct gsm_subscriber_connection *conn) { switch (conn->user_plane.chan_mode) { case GSM48_CMODE_SPEECH_V1: if (conn->user_plane.full_rate) conn_peer->codecs[0] = CODEC_GSM_8000_1; else conn_peer->codecs[0] = CODEC_GSMHR_8000_1; conn_peer->codecs_len = 1; break; case GSM48_CMODE_SPEECH_EFR: conn_peer->codecs[0] = CODEC_GSMEFR_8000_1; conn_peer->codecs_len = 1; break; case GSM48_CMODE_SPEECH_AMR: conn_peer->codecs[0] = CODEC_AMR_8000_1; conn_peer->codecs_len = 1; break; default: conn_peer->codecs_len = 0; } } /* Send data SCCP message through SCCP connection. All sigtran messages * that are send from this FSM must use this function. Never use * osmo_bsc_sigtran_send() directly since this would defeat the checks * provided by this function. */ static void sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi) { int rc; /* Make sure that we only attempt to send SCCP messages if we have * a life SCCP connection. Otherwise drop the message. */ if (fi->state == ST_INIT || fi->state == ST_WAIT_CC) { LOGPFSML(fi, LOGL_ERROR, "No active SCCP connection, dropping message!\n"); msgb_free(msg); return; } rc = osmo_bsc_sigtran_send(conn, msg); if (rc < 0) LOGPFSML(fi, LOGL_ERROR, "Unable to deliver SCCP message!\n"); } /* Add the LCLS BSS Status IE to a BSSMAP message. We assume this is * called on a msgb that was returned by gsm0808_create_ass_compl() */ static void bssmap_add_lcls_status(struct msgb *msg, enum gsm0808_lcls_status status) { OSMO_ASSERT(msg->l3h[0] == BSSAP_MSG_BSS_MANAGEMENT); OSMO_ASSERT(msg->l3h[2] == BSS_MAP_MSG_ASSIGMENT_COMPLETE || msg->l3h[2] == BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE || msg->l3h[2] == BSS_MAP_MSG_HANDOVER_COMPLETE || msg->l3h[2] == BSS_MAP_MSG_HANDOVER_PERFORMED); OSMO_ASSERT(msgb_tailroom(msg) >= 2); /* append IE to end of message */ msgb_tv_put(msg, GSM0808_IE_LCLS_BSS_STATUS, status); /* increment the "length" byte in the BSSAP header */ msg->l3h[1] += 2; } /* Add (append) the LCLS BSS Status IE to a BSSMAP message, if there is any LCLS * active on the given \a conn */ static void bssmap_add_lcls_status_if_needed(struct gsm_subscriber_connection *conn, struct msgb *msg) { enum gsm0808_lcls_status status = lcls_get_status(conn); if (status != 0xff) { LOGPFSM(conn->fi, "Adding LCLS BSS-Status (%s) to %s\n", gsm0808_lcls_status_name(status), gsm0808_bssmap_name(msg->l3h[2])); bssmap_add_lcls_status(msg, status); } } /* Generate and send assignment complete message */ static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi, bool voice) { struct msgb *resp; struct gsm0808_speech_codec sc; struct gsm0808_speech_codec *sc_ptr = NULL; struct gsm_subscriber_connection *conn; struct sockaddr_storage *addr_local = NULL; int perm_spch = 0; uint8_t chosen_channel; conn = lchan->conn; OSMO_ASSERT(conn); /* apply LCLS configuration (if any) */ lcls_apply_config(conn); LOGPFSML(fi, LOGL_DEBUG, "Sending assignment complete message... (id=%i)\n", conn->sccp.conn_id); /* Generate voice related fields */ if (voice) { perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); switch (conn->sccp.msc->a.asp_proto) { case OSMO_SS7_ASP_PROT_IPA: /* don't add any AoIP specific fields. CIC allocated by MSC */ break; default: OSMO_ASSERT(lchan->abis_ip.ass_compl.valid); addr_local = &conn->user_plane.aoip_rtp_addr_local; /* Extrapolate speech codec from speech mode */ gsm0808_speech_codec_from_chan_type(&sc, perm_spch); sc_ptr = ≻ break; } /* FIXME: AMR codec configuration must be derived from lchan1! */ } chosen_channel = gsm0808_chosen_channel(lchan->tch_mode, lchan->type); if (!chosen_channel) LOGP(DMSC, LOGL_ERROR, "Unknown lchan type or TCH mode: %s\n", gsm_lchan_name(lchan)); /* Generate message */ resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause, chosen_channel, lchan->encr.alg_id, perm_spch, addr_local, sc_ptr, NULL); if (!resp) { LOGPFSML(fi, LOGL_ERROR, "Failed to generate assignment completed message! (id=%i)\n", conn->sccp.conn_id); } /* Add LCLS BSS-Status IE in case there is any LCLS status for this connection */ bssmap_add_lcls_status_if_needed(conn, resp); sigtran_send(conn, resp, fi); } /* forward MT DTAP from BSSAP side to RSL side */ static void submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi) { int rc; struct msgb *resp = NULL; OSMO_ASSERT(fi); OSMO_ASSERT(msg); OSMO_ASSERT(conn); rc = gsm0808_submit_dtap(conn, msg, OBSC_LINKID_CB(msg), 1); if (rc != 0) { LOGPFSML(fi, LOGL_ERROR, "Tx BSSMAP CLEAR REQUEST to MSC\n"); resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE); sigtran_send(conn, resp, fi); osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); return; } } /* forward MO DTAP from RSL side to BSSAP side */ static void forward_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi) { struct msgb *resp = NULL; OSMO_ASSERT(msg); OSMO_ASSERT(conn); resp = gsm0808_create_dtap(msg, OBSC_LINKID_CB(msg)); sigtran_send(conn, resp, fi); } /* In case there are open MGCP connections, toss * those connections */ static void toss_mgcp_conn(struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi) { LOGPFSML(fi, LOGL_ERROR, "tossing all MGCP connections...\n"); if (conn->user_plane.fi_bts) { mgcp_conn_delete(conn->user_plane.fi_bts); conn->user_plane.fi_bts = NULL; } if (conn->user_plane.fi_msc) { mgcp_conn_delete(conn->user_plane.fi_msc); conn->user_plane.fi_msc = NULL; } if (conn->user_plane.mgw_endpoint) { talloc_free(conn->user_plane.mgw_endpoint); conn->user_plane.mgw_endpoint = NULL; } } static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct osmo_scu_prim *scu_prim = NULL; struct msgb *msg = NULL; int rc; switch (event) { case GSCON_EV_A_CONN_REQ: /* RLL ESTABLISH IND with initial L3 Message */ msg = data; /* FIXME: Extract Mobile ID and update FSM using osmo_fsm_inst_set_id() * i.e. we will probably extract the mobile identity earlier, where the * imsi filter code is. Then we could just use it here. * related: OS#2969 */ rc = osmo_bsc_sigtran_open_conn(conn, msg); if (rc < 0) { osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); } else { /* SCCP T(conn est) is 1-2 minutes, way too long. The MS will timeout * using T3210 (20s), T3220 (5s) or T3230 (10s) */ osmo_fsm_inst_state_chg(fi, ST_WAIT_CC, 20, 993210); } break; case GSCON_EV_A_CONN_IND: scu_prim = data; if (!conn->sccp.msc) { LOGPFSML(fi, LOGL_NOTICE, "N-CONNECT.ind from unknown MSC %s\n", osmo_sccp_addr_dump(&scu_prim->u.connect.calling_addr)); osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); } /* FIXME: Extract optional IMSI and update FSM using osmo_fsm_inst_set_id() * related: OS2969 (same as above) */ LOGPFSML(fi, LOGL_NOTICE, "No support for MSC-originated SCCP Connections yet\n"); osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); break; default: OSMO_ASSERT(false); break; } } /* We've sent the CONNECTION.req to the SCCP provider and are waiting for CC from MSC */ static void gscon_fsm_wait_cc(struct osmo_fsm_inst *fi, uint32_t event, void *data) { switch (event) { case GSCON_EV_A_CONN_CFM: /* MSC has confirmed the connection, we now change into the * active state and wait there for further operations */ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); /* if there's user payload, forward it just like EV_MT_DTAP */ /* FIXME: Question: if there's user payload attached to the CC, forward it like EV_MT_DTAP? */ break; default: OSMO_ASSERT(false); break; } } static const char *get_mgw_ep_name(struct gsm_subscriber_connection *conn) { static char ep_name[256]; struct bsc_msc_data *msc = conn->sccp.msc; switch (conn->sccp.msc->a.asp_proto) { case OSMO_SS7_ASP_PROT_IPA: /* derive endpoint name from CIC on A interface side */ snprintf(ep_name, sizeof(ep_name), "%x@mgw", mgcp_port_to_cic(conn->user_plane.rtp_port, msc->rtp_base)); break; default: /* use dynamic RTPBRIDGE endpoint allocation in MGW */ osmo_strlcpy(ep_name, ENDPOINT_ID, sizeof(ep_name)); break; } return ep_name; } #define assignment_failed(fi, cause) \ _assignment_failed(fi, cause, __FILE__, __LINE__) static void _assignment_failed(struct osmo_fsm_inst *fi, enum gsm0808_cause cause, const char *file, int line) { struct gsm_subscriber_connection *conn = fi->priv; struct msgb *resp = NULL; LOGPFSMLSRC(fi, LOGL_ERROR, file, line, "Assignment failed: %s\n", gsm0808_cause_name(cause)); resp = gsm0808_create_assignment_failure(cause, NULL); sigtran_send(conn, resp, fi); if (fi->state != ST_ACTIVE) osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); } /* We're on an active subscriber connection, passing DTAP back and forth */ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct msgb *resp = NULL; struct mgcp_conn_peer conn_peer; int rc; switch (event) { case GSCON_EV_A_ASSIGNMENT_CMD: /* MSC requests us to perform assignment, this code section is * triggered via signal GSCON_EV_A_ASSIGNMENT_CMD from * bssmap_handle_assignm_req() in osmo_bsc_bssap.c, which does * the parsing of incoming assignment requests. */ LOGPFSML(fi, LOGL_NOTICE, "Channel assignment: chan_mode=%s, full_rate=%i\n", get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode), conn->user_plane.full_rate); /* FIXME: We need to check if current channel is sufficient. If * yes, do MODIFY. If not, do assignment (see commented lines below) */ switch (conn->user_plane.chan_mode) { case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_AMR: /* A voice channel is requested, so we run down the * mgcp-ass-mgcp state-chain (see FIXME above) */ memset(&conn_peer, 0, sizeof(conn_peer)); bsc_subscr_pick_codec(&conn_peer, conn); conn_peer.call_id = conn->sccp.conn_id; conn_peer.ptime = 20; osmo_strlcpy(conn_peer.endpoint, get_mgw_ep_name(conn), sizeof(conn_peer.endpoint)); /* (Pre)Change state and create the connection */ osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); conn->user_plane.fi_bts = mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_BTS, GSCON_EV_MGW_CRCX_RESP_BTS, &conn_peer); if (!conn->user_plane.fi_bts) { assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); return; } break; case GSM48_CMODE_SIGN: /* A signalling channel is requested, so we perform the * channel assignment directly without performing any * MGCP actions. ST_WAIT_ASS_CMPL will see by the * conn->user_plane.chan_mode parameter that this * assignment is for a signalling channel and will then * change back to ST_ACTIVE (here) immediately. */ rc = gsm0808_assign_req(conn, conn->user_plane.chan_mode, conn->user_plane.full_rate); if (rc == 1) { send_ass_compl(conn->lchan, fi, false); return; } else if (rc != 0) { assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); return; } osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, conn->network->T10, 10); break; default: /* An unsupported channel is requested, so we have to * reject this request by sending an assignment failure * message immediately */ LOGPFSML(fi, LOGL_ERROR, "Requested channel mode is not supported! chan_mode=%s full_rate=%d\n", get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode), conn->user_plane.full_rate); /* The requested channel mode is not supported */ assignment_failed(fi, GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP); break; } break; case GSCON_EV_HO_START: rc = bsc_handover_start_gscon(conn); if (rc) { resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE); sigtran_send(conn, resp, fi); osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0); return; } /* Note: No timeout is set here, T3103 in handover_logic.c * will generate a GSCON_EV_HO_TIMEOUT event should the * handover time out, so we do not need another timeout * here (maybe its worth to think about giving GSCON * more power over the actual handover process). */ osmo_fsm_inst_state_chg(fi, ST_WAIT_HO_COMPL, 0, 0); break; case GSCON_EV_A_HO_REQ: /* FIXME: reject any handover requests with HO FAIL until implemented */ break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } /* Before we may start the channel assignment we need to get an IP/Port for the * RTP connection from the MGW */ static void gscon_fsm_wait_crcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct mgcp_conn_peer *conn_peer = NULL; int rc; switch (event) { case GSCON_EV_MGW_CRCX_RESP_BTS: conn_peer = data; /* Check if the MGW has assigned an enpoint to us, otherwise we * can not proceed. */ if (strlen(conn_peer->endpoint) <= 0) { assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); return; } /* Memorize the endpoint name we got assigned from the MGW. * When the BTS sided connection is done, we need to create * a second connection on that same endpoint, so we need * to know its ID */ if (!conn->user_plane.mgw_endpoint) conn->user_plane.mgw_endpoint = talloc_zero_size(conn, MGCP_ENDPOINT_MAXLEN); OSMO_ASSERT(conn->user_plane.mgw_endpoint); osmo_strlcpy(conn->user_plane.mgw_endpoint, conn_peer->endpoint, MGCP_ENDPOINT_MAXLEN); /* Store the IP-Address and the port the MGW assigned to us, * then start the channel assignment. */ conn->user_plane.rtp_port = conn_peer->port; conn->user_plane.rtp_ip = osmo_ntohl(inet_addr(conn_peer->addr)); rc = gsm0808_assign_req(conn, conn->user_plane.chan_mode, conn->user_plane.full_rate); if (rc != 0) { assignment_failed(fi, GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE); return; } osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, conn->network->T10, 10); break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } /* We're waiting for an ASSIGNMENT COMPLETE from MS */ static void gscon_fsm_wait_ass_cmpl(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct gsm_lchan *lchan = conn->lchan; struct mgcp_conn_peer conn_peer; struct in_addr addr; int rc; switch (event) { case GSCON_EV_RR_ASS_COMPL: switch (conn->user_plane.chan_mode) { case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_AMR: /* FIXME: What if we are using SCCP-Lite? */ /* We are dealing with a voice channel, so we can not * confirm the assignment directly. We must first do * some final steps on the MGCP side. */ /* Prepare parameters with the information we got during the assignment */ memset(&conn_peer, 0, sizeof(conn_peer)); bsc_subscr_pick_codec(&conn_peer, conn); addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip); osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr)); conn_peer.port = lchan->abis_ip.bound_port; conn_peer.ptime = 20; /* (Pre)Change state and modify the connection */ osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer); if (rc != 0) { assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); return; } break; case GSM48_CMODE_SIGN: /* Confirm the successful assignment on BSSMAP and * change back into active state */ send_ass_compl(lchan, fi, false); osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; default: /* Unsupported modes should have been already filtered * by gscon_fsm_active(). If we reach the default * section here anyway than some unsupported mode must * have made it into the FSM, this would be a bug, so * we fire an assertion here */ OSMO_ASSERT(false); break; } break; case GSCON_EV_RR_ASS_FAIL: { enum gsm0808_cause cause = GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE; if (data) cause = *((enum gsm0808_cause*)data); assignment_failed(fi, cause); } break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } /* We are waiting for the MGW response to the MDCX */ static void gscon_fsm_wait_mdcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct mgcp_conn_peer conn_peer; struct sockaddr_in *sin = NULL; switch (event) { case GSCON_EV_MGW_MDCX_RESP_BTS: /* Prepare parameters with the connection information we got * with the assignment command */ memset(&conn_peer, 0, sizeof(conn_peer)); bsc_subscr_pick_codec(&conn_peer, conn); conn_peer.call_id = conn->sccp.conn_id; sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote; conn_peer.port = osmo_ntohs(sin->sin_port); osmo_strlcpy(conn_peer.addr, inet_ntoa(sin->sin_addr), sizeof(conn_peer.addr)); conn_peer.ptime = 20; /* Make sure we use the same endpoint where we created the * BTS connection. */ osmo_strlcpy(conn_peer.endpoint, conn->user_plane.mgw_endpoint, sizeof(conn_peer.endpoint)); switch (conn->sccp.msc->a.asp_proto) { case OSMO_SS7_ASP_PROT_IPA: /* Send assignment complete message to the MSC */ send_ass_compl(conn->lchan, fi, true); osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; default: /* (Pre)Change state and create the connection */ osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_MSC, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); conn->user_plane.fi_msc = mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_MSC, GSCON_EV_MGW_CRCX_RESP_MSC, &conn_peer); if (!conn->user_plane.fi_msc) { assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); return; } break; } break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } static void gscon_fsm_wait_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct mgcp_conn_peer *conn_peer = NULL; struct gsm_lchan *lchan = conn->lchan; struct sockaddr_in *sin = NULL; switch (event) { case GSCON_EV_MGW_CRCX_RESP_MSC: conn_peer = data; /* Store address information we got in response from the CRCX command. */ sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_local; sin->sin_family = AF_INET; sin->sin_addr.s_addr = inet_addr(conn_peer->addr); sin->sin_port = osmo_ntohs(conn_peer->port); /* Send assignment complete message to the MSC */ send_ass_compl(lchan, fi, true); osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } static void gscon_fsm_clearing(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct msgb *resp; switch (event) { case GSCON_EV_RSL_CLEAR_COMPL: resp = gsm0808_create_clear_complete(); sigtran_send(conn, resp, fi); /* we cannot terminate the FSM here, as that would send N-DISCCONNET.req * and 3GPP TS 48.006 Section 9.2 clearly states that SCCP connections must * always be released from the MSC side*/ break; default: OSMO_ASSERT(false); break; } } /* Wait for the handover logic to tell us whether the handover completed, * failed or has timed out */ static void gscon_fsm_wait_ho_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct mgcp_conn_peer conn_peer; struct gsm_lchan *lchan = conn->lchan; struct in_addr addr; struct msgb *resp; int rc; switch (event) { case GSCON_EV_HO_COMPL: /* The handover logic informs us that the handover has been * completet. Now we have to tell the MGW the IP/Port on the * new BTS so that the uplink RTP traffic can be redirected * there. */ /* Prepare parameters with the information we got during the * handover procedure (via IPACC) */ memset(&conn_peer, 0, sizeof(conn_peer)); bsc_subscr_pick_codec(&conn_peer, conn); addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip); osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr)); conn_peer.port = lchan->abis_ip.bound_port; conn_peer.ptime = 20; /* (Pre)Change state and modify the connection */ osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS_HO, MGCP_MGW_TIMEOUT, MGCP_MGW_HO_TIMEOUT_TIMER_NR); rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer); if (rc != 0) { resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE); sigtran_send(conn, resp, fi); osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0); return; } break; case GSCON_EV_HO_TIMEOUT: case GSCON_EV_HO_FAIL: /* The handover logic informs us that the handover failed for * some reason. This means the phone stays on the TS/BTS on * which it currently is. We will change back to the active * state again as there are no further operations needed */ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; default: OSMO_ASSERT(false); break; } } /* Wait for the MGW to confirm handover related modification of the connection * parameters */ static void gscon_fsm_wait_mdcx_bts_ho(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; switch (event) { case GSCON_EV_MGW_MDCX_RESP_BTS: /* The MGW has confirmed the handover MDCX, and the handover * is now also done on the RTP side. We may now change back * to the active state. */ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; case GSCON_EV_MO_DTAP: forward_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_MT_DTAP: submit_dtap(conn, (struct msgb *)data, fi); break; case GSCON_EV_TX_SCCP: sigtran_send(conn, (struct msgb *)data, fi); break; default: OSMO_ASSERT(false); break; } } #define EV_TRANSPARENT_SCCP S(GSCON_EV_TX_SCCP) | S(GSCON_EV_MO_DTAP) | S(GSCON_EV_MT_DTAP) static const struct osmo_fsm_state gscon_fsm_states[] = { [ST_INIT] = { .name = "INIT", .in_event_mask = S(GSCON_EV_A_CONN_REQ) | S(GSCON_EV_A_CONN_IND), .out_state_mask = S(ST_WAIT_CC), .action = gscon_fsm_init, }, [ST_WAIT_CC] = { .name = "WAIT_CC", .in_event_mask = S(GSCON_EV_A_CONN_CFM), .out_state_mask = S(ST_ACTIVE), .action = gscon_fsm_wait_cc, }, [ST_ACTIVE] = { .name = "ACTIVE", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_A_ASSIGNMENT_CMD) | S(GSCON_EV_A_HO_REQ) | S(GSCON_EV_HO_START), .out_state_mask = S(ST_CLEARING) | S(ST_WAIT_CRCX_BTS) | S(ST_WAIT_ASS_CMPL) | S(ST_WAIT_MO_HO_CMD) | S(ST_WAIT_HO_COMPL), .action = gscon_fsm_active, }, [ST_WAIT_CRCX_BTS] = { .name = "WAIT_CRCX_BTS", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_BTS), .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_ASS_CMPL), .action = gscon_fsm_wait_crcx_bts, }, [ST_WAIT_ASS_CMPL] = { .name = "WAIT_ASS_CMPL", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_RR_ASS_COMPL) | S(GSCON_EV_RR_ASS_FAIL), .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS), .action = gscon_fsm_wait_ass_cmpl, }, [ST_WAIT_MDCX_BTS] = { .name = "WAIT_MDCX_BTS", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS), .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CRCX_MSC), .action = gscon_fsm_wait_mdcx_bts, }, [ST_WAIT_CRCX_MSC] = { .name = "WAIT_CRCX_MSC", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_MSC), .out_state_mask = S(ST_ACTIVE), .action = gscon_fsm_wait_crcx_msc, }, [ST_CLEARING] = { .name = "CLEARING", .in_event_mask = S(GSCON_EV_RSL_CLEAR_COMPL), .action = gscon_fsm_clearing, }, /* TODO: external handover, probably it makes sense to break up the * program flow in handover_logic.c a bit and handle some of the logic * here? */ [ST_WAIT_MT_HO_ACC] = { .name = "WAIT_MT_HO_ACC", }, [ST_WAIT_MT_HO_COMPL] = { .name = "WAIT_MT_HO_COMPL", }, [ST_WAIT_MO_HO_CMD] = { .name = "WAIT_MO_HO_CMD", }, [ST_MO_HO_PROCEEDING] = { .name = "MO_HO_PROCEEDING", }, /* Internal handover */ [ST_WAIT_HO_COMPL] = { .name = "WAIT_HO_COMPL", .in_event_mask = S(GSCON_EV_HO_COMPL) | S(GSCON_EV_HO_FAIL) | S(GSCON_EV_HO_TIMEOUT), .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS_HO) | S(ST_CLEARING), .action = gscon_fsm_wait_ho_compl, }, [ST_WAIT_MDCX_BTS_HO] = { .name = "WAIT_MDCX_BTS_HO", .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS), .action = gscon_fsm_wait_mdcx_bts_ho, .out_state_mask = S(ST_ACTIVE), }, }; static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; struct msgb *resp = NULL; /* When a connection on the MGW fails, make sure that the reference * in our book-keeping is erased. */ switch (event) { case GSCON_EV_MGW_FAIL_BTS: conn->user_plane.fi_bts = NULL; break; case GSCON_EV_MGW_FAIL_MSC: conn->user_plane.fi_msc = NULL; break; } /* Regular allstate event processing */ switch (event) { case GSCON_EV_MGW_FAIL_BTS: case GSCON_EV_MGW_FAIL_MSC: /* Note: An MGW connection die per definition at any time. * However, if it dies during the assignment we must return * with an assignment failure */ OSMO_ASSERT(fi->state != ST_INIT && fi->state != ST_WAIT_CC); if (fi->state == ST_WAIT_CRCX_BTS || fi->state == ST_WAIT_ASS_CMPL || fi->state == ST_WAIT_MDCX_BTS || fi->state == ST_WAIT_CRCX_MSC) assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); break; case GSCON_EV_A_CLEAR_CMD: /* MSC tells us to cleanly shut down */ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0); gsm0808_clear(conn); /* FIXME: Release all terestrial resources in ST_CLEARING */ /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel * release to be completed or for the guard timer to expire before returning the * CLEAR COMPLETE message" */ /* Close MGCP connections */ toss_mgcp_conn(conn, fi); /* FIXME: Question: Is this a hack to force a clear complete from internel? * nobody seems to send the event from outside? */ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RSL_CLEAR_COMPL, NULL); break; case GSCON_EV_A_DISC_IND: /* MSC or SIGTRAN network has hard-released SCCP connection, * terminate the FSM now. */ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data); break; case GSCON_EV_RLL_REL_IND: /* BTS reports that one of the LAPDm data links was released */ /* send proper clear request to MSC */ LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n"); resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE); sigtran_send(conn, resp, fi); break; case GSCON_EV_RSL_CONN_FAIL: LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n"); resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); sigtran_send(conn, resp, fi); break; case GSCON_EV_MGW_MDCX_RESP_MSC: LOGPFSML(fi, LOGL_DEBUG, "Rx MDCX of MSC side (LCLS?)\n"); break; case GSCON_EV_LCLS_FAIL: break; default: OSMO_ASSERT(false); break; } } void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send); static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) { struct gsm_subscriber_connection *conn = fi->priv; if (conn->ho) { LOGPFSML(fi, LOGL_DEBUG, "Releasing handover state\n"); bsc_clear_handover(conn, 1); conn->ho = NULL; } if (conn->secondary_lchan) { LOGPFSML(fi, LOGL_DEBUG, "Releasing secondary_lchan\n"); lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); conn->secondary_lchan = NULL; } if (conn->lchan) { LOGPFSML(fi, LOGL_DEBUG, "Releasing lchan\n"); lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); conn->lchan = NULL; } if (conn->sccp.state != SUBSCR_SCCP_ST_NONE) { LOGPFSML(fi, LOGL_DEBUG, "Disconnecting SCCP\n"); struct bsc_msc_data *msc = conn->sccp.msc; /* FIXME: include a proper cause value / error message? */ osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn_id, &msc->a.bsc_addr, 0); conn->sccp.state = SUBSCR_SCCP_ST_NONE; } /* drop pending messages */ ho_dtap_cache_flush(conn, 0); penalty_timers_free(&conn->hodec2.penalty_timers); if (conn->bsub) { LOGPFSML(fi, LOGL_DEBUG, "Putting bsc_subscr\n"); bsc_subscr_put(conn->bsub); conn->bsub = NULL; } llist_del(&conn->entry); talloc_free(conn); fi->priv = NULL; } static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) { struct gsm_subscriber_connection *conn = fi->priv; /* Make sure all possibly still open MGCP connections get closed */ toss_mgcp_conn(conn, fi); if (conn->lcls.fi) { /* request termination of LCLS FSM */ osmo_fsm_inst_term(conn->lcls.fi, cause, NULL); conn->lcls.fi = NULL; } } static int gscon_timer_cb(struct osmo_fsm_inst *fi) { struct gsm_subscriber_connection *conn = fi->priv; switch (fi->T) { case 993210: /* MSC has not responded/confirmed connection with CC, this * could indicate a bad SCCP connection. We now inform the the * FSM that controls the BSSMAP reset about the event. Maybe * a BSSMAP reset is necessary. */ a_reset_conn_fail(conn->sccp.msc->a.reset_fsm); /* Since we could not reach the MSC, we give up and terminate * the FSM instance now (N-DISCONNET.req is sent in * gscon_cleanup() above) */ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); break; case 10: /* Assignment Failed */ assignment_failed(fi, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); break; case MGCP_MGW_TIMEOUT_TIMER_NR: /* Assignment failed (no response from MGW) */ assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE); break; case MGCP_MGW_HO_TIMEOUT_TIMER_NR: /* Handover failed (no response from MGW) */ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0); break; default: OSMO_ASSERT(false); } return 0; } static struct osmo_fsm gscon_fsm = { .name = "SUBSCR_CONN", .states = gscon_fsm_states, .num_states = ARRAY_SIZE(gscon_fsm_states), .allstate_event_mask = S(GSCON_EV_A_DISC_IND) | S(GSCON_EV_A_CLEAR_CMD) | S(GSCON_EV_RSL_CONN_FAIL) | S(GSCON_EV_RLL_REL_IND) | S(GSCON_EV_MGW_FAIL_BTS) | S(GSCON_EV_MGW_FAIL_MSC) | S(GSCON_EV_MGW_MDCX_RESP_MSC) | S(GSCON_EV_LCLS_FAIL), .allstate_action = gscon_fsm_allstate, .cleanup = gscon_cleanup, .pre_term = gscon_pre_term, .timer_cb = gscon_timer_cb, .log_subsys = DMSC, .event_names = gscon_fsm_event_names, }; /* Allocate a subscriber connection and its associated FSM */ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { struct gsm_subscriber_connection *conn; static bool g_initialized = false; if (!g_initialized) { osmo_fsm_register(&gscon_fsm); osmo_fsm_register(&lcls_fsm); g_initialized = true; } conn = talloc_zero(net, struct gsm_subscriber_connection); if (!conn) return NULL; conn->network = net; INIT_LLIST_HEAD(&conn->ho_dtap_cache); /* BTW, penalty timers will be initialized on-demand. */ conn->sccp.conn_id = -1; /* don't allocate from 'conn' context, as gscon_cleanup() will call talloc_free(conn) before * libosmocore will call talloc_free(conn->fi), i.e. avoid use-after-free during cleanup */ conn->fi = osmo_fsm_inst_alloc(&gscon_fsm, net, conn, LOGL_NOTICE, NULL); if (!conn->fi) { talloc_free(conn); return NULL; } /* initialize to some magic values that indicate "IE not [yet] received" */ conn->lcls.config = 0xff; conn->lcls.control = 0xff; conn->lcls.fi = osmo_fsm_inst_alloc_child(&lcls_fsm, conn->fi, GSCON_EV_LCLS_FAIL); if (!conn->lcls.fi) { osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_ERROR, NULL); return NULL; } conn->lcls.fi->priv = conn; llist_add_tail(&conn->entry, &net->subscr_conns); return conn; } osmo-bsc-1.3.0/src/osmo-bsc/bsc_subscriber.c000066400000000000000000000077311332665256100207250ustar00rootroot00000000000000/* GSM subscriber details for use in BSC land */ /* * (C) 2016 by sysmocom s.f.m.c. GmbH * * Author: Neels Hofmeyr * * 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 static struct bsc_subscr *bsc_subscr_alloc(struct llist_head *list) { struct bsc_subscr *bsub; bsub = talloc_zero(list, struct bsc_subscr); if (!bsub) return NULL; llist_add_tail(&bsub->entry, list); return bsub; } struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list, const char *imsi) { struct bsc_subscr *bsub; if (!imsi || !*imsi) return NULL; llist_for_each_entry(bsub, list, entry) { if (!strcmp(bsub->imsi, imsi)) return bsc_subscr_get(bsub); } return NULL; } struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list, uint32_t tmsi) { struct bsc_subscr *bsub; if (tmsi == GSM_RESERVED_TMSI) return NULL; llist_for_each_entry(bsub, list, entry) { if (bsub->tmsi == tmsi) return bsc_subscr_get(bsub); } return NULL; } void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi) { if (!bsub) return; osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi)); } struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list, const char *imsi) { struct bsc_subscr *bsub; bsub = bsc_subscr_find_by_imsi(list, imsi); if (bsub) return bsub; bsub = bsc_subscr_alloc(list); bsc_subscr_set_imsi(bsub, imsi); return bsc_subscr_get(bsub); } struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list, uint32_t tmsi) { struct bsc_subscr *bsub; bsub = bsc_subscr_find_by_tmsi(list, tmsi); if (bsub) return bsub; bsub = bsc_subscr_alloc(list); bsub->tmsi = tmsi; return bsc_subscr_get(bsub); } const char *bsc_subscr_name(struct bsc_subscr *bsub) { static char buf[32]; if (!bsub) return "unknown"; if (bsub->imsi[0]) snprintf(buf, sizeof(buf), "IMSI:%s", bsub->imsi); else snprintf(buf, sizeof(buf), "TMSI:0x%08x", bsub->tmsi); return buf; } static void bsc_subscr_free(struct bsc_subscr *bsub) { llist_del(&bsub->entry); talloc_free(bsub); } struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub, const char *file, int line) { OSMO_ASSERT(bsub->use_count < INT_MAX); bsub->use_count++; LOGPSRC(DREF, LOGL_DEBUG, file, line, "BSC subscr %s usage increases to: %d\n", bsc_subscr_name(bsub), bsub->use_count); return bsub; } struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub, const char *file, int line) { bsub->use_count--; LOGPSRC(DREF, bsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR, file, line, "BSC subscr %s usage decreases to: %d\n", bsc_subscr_name(bsub), bsub->use_count); if (bsub->use_count <= 0) bsc_subscr_free(bsub); return NULL; } void log_set_filter_bsc_subscr(struct log_target *target, struct bsc_subscr *bsc_subscr) { struct bsc_subscr **fsub = (void*)&target->filter_data[LOG_FLT_BSC_SUBSCR]; /* free the old data */ if (*fsub) { bsc_subscr_put(*fsub); *fsub = NULL; } if (bsc_subscr) { target->filter_map |= (1 << LOG_FLT_BSC_SUBSCR); *fsub = bsc_subscr_get(bsc_subscr); } else target->filter_map &= ~(1 << LOG_FLT_BSC_SUBSCR); } osmo-bsc-1.3.0/src/osmo-bsc/bsc_vty.c000066400000000000000000004347351332665256100174140ustar00rootroot00000000000000/* OpenBSC interface to quagga VTY */ /* (C) 2009-2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define BTS_NR_STR "BTS Number\n" #define TRX_NR_STR "TRX Number\n" #define TS_NR_STR "Timeslot Number\n" #define SS_NR_STR "Sub-slot Number\n" #define LCHAN_NR_STR "Logical Channel Number\n" #define BTS_TRX_STR BTS_NR_STR TRX_NR_STR #define BTS_TRX_TS_STR BTS_TRX_STR TS_NR_STR #define BTS_TRX_TS_LCHAN_STR BTS_TRX_TS_STR LCHAN_NR_STR #define BTS_NR_TRX_TS_STR2 \ "BTS for manual command\n" BTS_NR_STR \ "TRX for manual command\n" TRX_NR_STR \ "Timeslot for manual command\n" TS_NR_STR #define BTS_NR_TRX_TS_SS_STR2 \ BTS_NR_TRX_TS_STR2 \ "Sub-slot for manual command\n" SS_NR_STR /* FIXME: this should go to some common file */ static const struct value_string gprs_ns_timer_strs[] = { { 0, "tns-block" }, { 1, "tns-block-retries" }, { 2, "tns-reset" }, { 3, "tns-reset-retries" }, { 4, "tns-test" }, { 5, "tns-alive" }, { 6, "tns-alive-retries" }, { 0, NULL } }; static const struct value_string gprs_bssgp_cfg_strs[] = { { 0, "blocking-timer" }, { 1, "blocking-retries" }, { 2, "unblocking-retries" }, { 3, "reset-timer" }, { 4, "reset-retries" }, { 5, "suspend-timer" }, { 6, "suspend-retries" }, { 7, "resume-timer" }, { 8, "resume-retries" }, { 9, "capability-update-timer" }, { 10, "capability-update-retries" }, { 0, NULL } }; static const struct value_string bts_neigh_mode_strs[] = { { NL_MODE_AUTOMATIC, "automatic" }, { NL_MODE_MANUAL, "manual" }, { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, { 0, NULL } }; const struct value_string bts_loc_fix_names[] = { { BTS_LOC_FIX_INVALID, "invalid" }, { BTS_LOC_FIX_2D, "fix2d" }, { BTS_LOC_FIX_3D, "fix3d" }, { 0, NULL } }; struct cmd_node net_node = { GSMNET_NODE, "%s(config-net)# ", 1, }; struct cmd_node bts_node = { BTS_NODE, "%s(config-net-bts)# ", 1, }; struct cmd_node trx_node = { TRX_NODE, "%s(config-net-bts-trx)# ", 1, }; struct cmd_node ts_node = { TS_NODE, "%s(config-net-bts-trx-ts)# ", 1, }; static struct gsm_network *vty_global_gsm_network = NULL; struct gsm_network *gsmnet_from_vty(struct vty *v) { /* It can't hurt to force callers to continue to pass the vty instance * to this function, in case we'd like to retrieve the global * gsm_network instance from the vty at some point in the future. But * until then, just return the global pointer, which should have been * initialized by common_cs_vty_init(). */ OSMO_ASSERT(vty_global_gsm_network); return vty_global_gsm_network; } static int dummy_config_write(struct vty *v) { return CMD_SUCCESS; } static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) { vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", abis_nm_opstate_name(nms->operational), get_value_string(abis_nm_adm_state_names, nms->administrative), abis_nm_avail_name(nms->availability), VTY_NEWLINE); } static void dump_pchan_load_vty(struct vty *vty, char *prefix, const struct pchan_load *pl) { int i; int dumped = 0; for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { const struct load_counter *lc = &pl->pchan[i]; unsigned int percent; if (lc->total == 0) continue; percent = (lc->used * 100) / lc->total; vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, gsm_pchan_name(i), percent, lc->used, lc->total, VTY_NEWLINE); dumped ++; } if (!dumped) vty_out(vty, "%s(none)%s", prefix, VTY_NEWLINE); } static void net_dump_vty(struct vty *vty, struct gsm_network *net) { struct pchan_load pl; int i; vty_out(vty, "BSC is on MCC-MNC %s and has %u BTS%s", osmo_plmn_name(&net->plmn), net->num_bts, VTY_NEWLINE); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " Encryption:"); for (i = 0; i < 8; i++) { if (net->a5_encryption_mask & (1 << i)) vty_out(vty, " A5/%u", i); } vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " NECI (TCH/H): %u%s", net->neci, VTY_NEWLINE); vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, VTY_NEWLINE); { struct gsm_bts *bts; unsigned int ho_active_count = 0; unsigned int ho_inactive_count = 0; llist_for_each_entry(bts, &net->bts_list, list) { if (ho_get_ho_active(bts->ho)) ho_active_count ++; else ho_inactive_count ++; } if (ho_active_count && ho_inactive_count) vty_out(vty, " Handover: On at %u BTS, Off at %u BTS%s", ho_active_count, ho_inactive_count, VTY_NEWLINE); else vty_out(vty, " Handover: %s%s", ho_active_count ? "On" : "Off", VTY_NEWLINE); } network_chan_load(&pl, net); vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); dump_pchan_load_vty(vty, " ", &pl); /* show rf */ if (net->bsc_data) vty_out(vty, " Last RF Command: %s%s", net->bsc_data->rf_ctrl->last_state_command, VTY_NEWLINE); if (net->bsc_data) vty_out(vty, " Last RF Lock Command: %s%s", net->bsc_data->rf_ctrl->last_rf_lock_ctrl_command, VTY_NEWLINE); } DEFUN(bsc_show_net, bsc_show_net_cmd, "show network", SHOW_STR "Display information about a GSM NETWORK\n") { struct gsm_network *net = gsmnet_from_vty(vty); net_dump_vty(vty, net); return CMD_SUCCESS; } static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) { struct e1inp_line *line; if (!e1l) { vty_out(vty, " None%s", VTY_NEWLINE); return; } line = e1l->ts->line; vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", line->num, line->driver->name, e1l->ts->num, e1inp_signtype_name(e1l->type), VTY_NEWLINE); vty_out(vty, " E1 TEI %u, SAPI %u%s", e1l->tei, e1l->sapi, VTY_NEWLINE); } static void vty_out_neigh_list(struct vty *vty, struct bitvec *bv) { int count = 0; int i; for (i = 0; i < 1024; i++) { if (!bitvec_get_bit_pos(bv, i)) continue; vty_out(vty, " %u", i); count ++; } if (!count) vty_out(vty, " (none)"); else vty_out(vty, " (%d)", count); } static void bts_dump_vty_features(struct vty *vty, struct gsm_bts *bts) { unsigned int i; bool no_features = true; vty_out(vty, " Features:%s", VTY_NEWLINE); for (i = 0; i < _NUM_BTS_FEAT; i++) { if (osmo_bts_has_feature(&bts->features, i)) { vty_out(vty, " %03u ", i); vty_out(vty, "%-40s%s", osmo_bts_feature_name(i), VTY_NEWLINE); no_features = false; } } if (no_features) vty_out(vty, " (not available)%s", VTY_NEWLINE); } static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) { struct pchan_load pl; unsigned long long sec; struct gsm_bts_trx *trx; int ts_hopping_total; int ts_non_hopping_total; vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s", bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), bts->cell_identity, bts->location_area_code, bts->bsic, bts->bsic >> 3, bts->bsic & 7, bts->num_trx, VTY_NEWLINE); vty_out(vty, " Description: %s%s", bts->description ? bts->description : "(null)", VTY_NEWLINE); vty_out(vty, " ARFCNs:"); ts_hopping_total = 0; ts_non_hopping_total = 0; llist_for_each_entry(trx, &bts->trx_list, list) { int ts_nr; int ts_hopping = 0; int ts_non_hopping = 0; for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; if (ts->hopping.enabled) ts_hopping++; else ts_non_hopping++; } if (ts_non_hopping) vty_out(vty, " %u", trx->arfcn); ts_hopping_total += ts_hopping; ts_non_hopping_total += ts_non_hopping; } if (ts_hopping_total) { if (ts_non_hopping_total) vty_out(vty, " / Hopping on %d of %d timeslots", ts_hopping_total, ts_hopping_total + ts_non_hopping_total); else vty_out(vty, " Hopping on all %d timeslots", ts_hopping_total); } vty_out(vty, "%s", VTY_NEWLINE); if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH)) vty_out(vty, " PCU version %s connected%s", bts->pcu_version, VTY_NEWLINE); vty_out(vty, " MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); vty_out(vty, " Minimum Rx Level for Access: %i dBm%s", rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), VTY_NEWLINE); vty_out(vty, " Cell Reselection Hysteresis: %u dBm%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); vty_out(vty, " Access Control Class ramping: %senabled%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "not ", VTY_NEWLINE); if (acc_ramp_is_enabled(&bts->acc_ramp)) { if (!acc_ramp_step_interval_is_dynamic(&bts->acc_ramp)) vty_out(vty, " Access Control Class ramping step interval: %u seconds%s", acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE); else vty_out(vty, " Access Control Class ramping step interval: dynamic%s", VTY_NEWLINE); vty_out(vty, " enabling %u Access Control Class%s per ramping step%s", acc_ramp_get_step_size(&bts->acc_ramp), acc_ramp_get_step_size(&bts->acc_ramp) > 1 ? "es" : "", VTY_NEWLINE); } vty_out(vty, " RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, VTY_NEWLINE); vty_out(vty, " RACH Max transmissions: %u%s", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) vty_out(vty, " Uplink DTX: %s%s", (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "enabled" : "forced", VTY_NEWLINE); else vty_out(vty, " Uplink DTX: not enabled%s", VTY_NEWLINE); vty_out(vty, " Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ", VTY_NEWLINE); vty_out(vty, " Channel Description Attachment: %s%s", (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); vty_out(vty, " Channel Description BS-PA-MFRMS: %u%s", bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); vty_out(vty, " Channel Description BS-AG_BLKS-RES: %u%s", bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); vty_out(vty, " System Information present: 0x%08x, static: 0x%08x%s", bts->si_valid, bts->si_mode_static, VTY_NEWLINE); vty_out(vty, " Early Classmark Sending: 2G %s, 3G %s%s%s", bts->early_classmark_allowed ? "allowed" : "forbidden", bts->early_classmark_allowed_3g ? "allowed" : "forbidden", bts->early_classmark_allowed_3g && !bts->early_classmark_allowed ? " (forbidden by 2G bit)" : "", VTY_NEWLINE); if (bts->pcu_sock_path) vty_out(vty, " PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE); if (is_ipaccess_bts(bts)) vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", bts->ip_access.site_id, bts->ip_access.bts_id, bts->oml_tei, VTY_NEWLINE); else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) vty_out(vty, " Skip Reset: %d%s", bts->nokia.skip_reset, VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &bts->mo.nm_state); vty_out(vty, " Site Mgr NM State: "); net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); vty_out(vty, " GPRS NSE: "); net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); vty_out(vty, " GPRS CELL: "); net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); vty_out(vty, " GPRS NSVC0: "); net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); vty_out(vty, " GPRS NSVC1: "); net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); vty_out(vty, " Paging: %u pending requests, %u free slots%s", paging_pending_requests_nr(bts), bts->paging.available_slots, VTY_NEWLINE); if (is_ipaccess_bts(bts)) { vty_out(vty, " OML Link state: %s", get_model_oml_status(bts)); sec = bts_uptime(bts); if (sec) vty_out(vty, " %llu days %llu hours %llu min. %llu sec.", OSMO_SEC2DAY(sec), OSMO_SEC2HRS(sec), OSMO_SEC2MIN(sec), sec % 60); vty_out(vty, "%s", VTY_NEWLINE); } else { vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, bts->oml_link); } vty_out(vty, " Neighbor Cells: "); switch (bts->neigh_list_manual_mode) { default: case NL_MODE_AUTOMATIC: vty_out(vty, "Automatic"); /* generate_bcch_chan_list() should populate si_common.neigh_list */ break; case NL_MODE_MANUAL: vty_out(vty, "Manual"); break; case NL_MODE_MANUAL_SI5SEP: vty_out(vty, "Manual/separate SI5"); break; } vty_out(vty, ", ARFCNs:"); vty_out_neigh_list(vty, &bts->si_common.neigh_list); if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) { vty_out(vty, " SI5:"); vty_out_neigh_list(vty, &bts->si_common.si5_neigh_list); } vty_out(vty, "%s", VTY_NEWLINE); /* FIXME: chan_desc */ memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); dump_pchan_load_vty(vty, " ", &pl); vty_out(vty, " Channel Requests : %"PRIu64" total, %"PRIu64" no channel%s", bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL].current, bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL].current, VTY_NEWLINE); vty_out(vty, " Channel Failures : %"PRIu64" rf_failures, %"PRIu64" rll failures%s", bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL].current, bts->bts_ctrs->ctr[BTS_CTR_CHAN_RLL_ERR].current, VTY_NEWLINE); vty_out(vty, " BTS failures : %"PRIu64" OML, %"PRIu64" RSL%s", bts->bts_ctrs->ctr[BTS_CTR_BTS_OML_FAIL].current, bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL].current, VTY_NEWLINE); bts_dump_vty_features(vty, bts); } DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", SHOW_STR "Display information about a BTS\n" "BTS number") { struct gsm_network *net = gsmnet_from_vty(vty); int bts_nr; if (argc != 0) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); return CMD_SUCCESS; } /* print all BTS's */ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); return CMD_SUCCESS; } /* utility functions */ static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, const char *ts, const char *ss) { e1_link->e1_nr = atoi(line); e1_link->e1_ts = atoi(ts); if (!strcmp(ss, "full")) e1_link->e1_ts_ss = 255; else e1_link->e1_ts_ss = atoi(ss); } static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, const char *prefix) { if (!e1_link->e1_ts) return; if (e1_link->e1_ts_ss == 255) vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); else vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", prefix, e1_link->e1_nr, e1_link->e1_ts, e1_link->e1_ts_ss, VTY_NEWLINE); } static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); if (ts->tsc != -1) vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); if (ts->pchan != GSM_PCHAN_NONE) vty_out(vty, " phys_chan_config %s%s", gsm_pchan_name(ts->pchan), VTY_NEWLINE); vty_out(vty, " hopping enabled %u%s", ts->hopping.enabled, VTY_NEWLINE); if (ts->hopping.enabled) { unsigned int i; vty_out(vty, " hopping sequence-number %u%s", ts->hopping.hsn, VTY_NEWLINE); vty_out(vty, " hopping maio %u%s", ts->hopping.maio, VTY_NEWLINE); for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) continue; vty_out(vty, " hopping arfcn add %u%s", i, VTY_NEWLINE); } } config_write_e1_link(vty, &ts->e1_link, " "); if (ts->trx->bts->model->config_write_ts) ts->trx->bts->model->config_write_ts(vty, ts); } static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) { int i; vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); if (trx->description) vty_out(vty, " description %s%s", trx->description, VTY_NEWLINE); vty_out(vty, " rf_locked %u%s", trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, VTY_NEWLINE); vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); if (trx->bts->model->config_write_trx) trx->bts->model->config_write_trx(vty, trx); for (i = 0; i < TRX_NR_TS; i++) config_write_ts_single(vty, &trx->ts[i]); } static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) { unsigned int i; vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), VTY_NEWLINE); if (bts->gprs.mode == BTS_GPRS_NONE) return; vty_out(vty, " gprs 11bit_rach_support_for_egprs %u%s", bts->gprs.supports_egprs_11bit_rach, VTY_NEWLINE); vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, VTY_NEWLINE); vty_out(vty, " gprs network-control-order nc%u%s", bts->gprs.net_ctrl_ord, VTY_NEWLINE); if (!bts->gprs.ctrl_ack_type_use_block) vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE); vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) vty_out(vty, " gprs cell timer %s %u%s", get_value_string(gprs_bssgp_cfg_strs, i), bts->gprs.cell.timer[i], VTY_NEWLINE); vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) vty_out(vty, " gprs ns timer %s %u%s", get_value_string(gprs_ns_timer_strs, i), bts->gprs.nse.timer[i], VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { struct gsm_bts_gprs_nsvc *nsvc = &bts->gprs.nsvc[i]; struct in_addr ia; ia.s_addr = htonl(nsvc->remote_ip); vty_out(vty, " gprs nsvc %u nsvci %u%s", i, nsvc->nsvci, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u local udp port %u%s", i, nsvc->local_port, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, nsvc->remote_port, VTY_NEWLINE); vty_out(vty, " gprs nsvc %u remote ip %s%s", i, inet_ntoa(ia), VTY_NEWLINE); } } /* Write the model data if there is one */ static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts) { struct gsm_bts_trx *trx; if (!bts->model) return; if (bts->model->config_write_bts) bts->model->config_write_bts(vty, bts); llist_for_each_entry(trx, &bts->trx_list, list) config_write_trx_single(vty, trx); } static void write_amr_modes(struct vty *vty, const char *prefix, const char *name, struct amr_mode *modes, int num) { int i; vty_out(vty, " %s threshold %s", prefix, name); for (i = 0; i < num - 1; i++) vty_out(vty, " %d", modes[i].threshold); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " %s hysteresis %s", prefix, name); for (i = 0; i < num - 1; i++) vty_out(vty, " %d", modes[i].hysteresis); vty_out(vty, "%s", VTY_NEWLINE); } static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts, struct amr_multirate_conf *mr, int full) { struct gsm48_multi_rate_conf *mr_conf; const char *prefix = (full) ? "amr tch-f" : "amr tch-h"; int i, num; if (!(mr->gsm48_ie[1])) return; mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; num = 0; vty_out(vty, " %s modes", prefix); for (i = 0; i < ((full) ? 8 : 6); i++) { if ((mr->gsm48_ie[1] & (1 << i))) { vty_out(vty, " %d", i); num++; } } vty_out(vty, "%s", VTY_NEWLINE); if (num > 4) num = 4; if (num > 1) { write_amr_modes(vty, prefix, "ms", mr->ms_mode, num); write_amr_modes(vty, prefix, "bts", mr->bts_mode, num); } vty_out(vty, " %s start-mode ", prefix); if (mr_conf->icmi) { num = 0; for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) { if ((mr->gsm48_ie[1] & (1 << i))) num++; if (mr_conf->smod == num - 1) { vty_out(vty, "%d%s", num, VTY_NEWLINE); break; } } } else vty_out(vty, "auto%s", VTY_NEWLINE); } static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) { int i; uint8_t tmp; vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); if (bts->description) vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); vty_out(vty, " location_area_code %u%s", bts->location_area_code, VTY_NEWLINE); if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) vty_out(vty, " dtx uplink%s%s", (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force", VTY_NEWLINE); if (bts->dtxd) vty_out(vty, " dtx downlink%s", VTY_NEWLINE); vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); vty_out(vty, " cell reselection hysteresis %u%s", bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); vty_out(vty, " rxlev access min %u%s", bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); if (bts->si_common.cell_ro_sel_par.present) { struct gsm48_si_selection_params *sp; sp = &bts->si_common.cell_ro_sel_par; if (sp->cbq) vty_out(vty, " cell bar qualify %u%s", sp->cbq, VTY_NEWLINE); if (sp->cell_resel_off) vty_out(vty, " cell reselection offset %u%s", sp->cell_resel_off*2, VTY_NEWLINE); if (sp->temp_offs == 7) vty_out(vty, " temporary offset infinite%s", VTY_NEWLINE); else if (sp->temp_offs) vty_out(vty, " temporary offset %u%s", sp->temp_offs*10, VTY_NEWLINE); if (sp->penalty_time == 31) vty_out(vty, " penalty time reserved%s", VTY_NEWLINE); else if (sp->penalty_time) vty_out(vty, " penalty time %u%s", (sp->penalty_time*20)+20, VTY_NEWLINE); } if (gsm_bts_get_radio_link_timeout(bts) < 0) vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE); else vty_out(vty, " radio-link-timeout %d%s", gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE); vty_out(vty, " channel allocator %s%s", bts->chan_alloc_reverse ? "descending" : "ascending", VTY_NEWLINE); vty_out(vty, " rach tx integer %u%s", bts->si_common.rach_control.tx_integer, VTY_NEWLINE); vty_out(vty, " rach max transmission %u%s", rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), VTY_NEWLINE); vty_out(vty, " channel-descrption attach %u%s", bts->si_common.chan_desc.att, VTY_NEWLINE); vty_out(vty, " channel-descrption bs-pa-mfrms %u%s", bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); vty_out(vty, " channel-descrption bs-ag-blks-res %u%s", bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); if (bts->rach_b_thresh != -1) vty_out(vty, " rach nm busy threshold %u%s", bts->rach_b_thresh, VTY_NEWLINE); if (bts->rach_ldavg_slots != -1) vty_out(vty, " rach nm load average %u%s", bts->rach_ldavg_slots, VTY_NEWLINE); if (bts->si_common.rach_control.cell_bar) vty_out(vty, " cell barred 1%s", VTY_NEWLINE); if ((bts->si_common.rach_control.t2 & 0x4) == 0) vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE); if ((bts->si_common.rach_control.t3) != 0) for (i = 0; i < 8; i++) if (bts->si_common.rach_control.t3 & (0x1 << i)) vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE); if ((bts->si_common.rach_control.t2 & 0xfb) != 0) for (i = 0; i < 8; i++) if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i))) vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE); vty_out(vty, " %saccess-control-class-ramping%s", acc_ramp_is_enabled(&bts->acc_ramp) ? "" : "no ", VTY_NEWLINE); if (!acc_ramp_step_interval_is_dynamic(&bts->acc_ramp)) { vty_out(vty, " access-control-class-ramping-step-interval %u%s", acc_ramp_get_step_interval(&bts->acc_ramp), VTY_NEWLINE); } else { vty_out(vty, " access-control-class-ramping-step-interval dynamic%s", VTY_NEWLINE); } vty_out(vty, " access-control-class-ramping-step-size %u%s", acc_ramp_get_step_size(&bts->acc_ramp), VTY_NEWLINE); for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { if (bts->si_mode_static & (1 << i)) { vty_out(vty, " system-information %s mode static%s", get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); vty_out(vty, " system-information %s static %s%s", get_value_string(osmo_sitype_strs, i), osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN), VTY_NEWLINE); } } vty_out(vty, " early-classmark-sending %s%s", bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE); vty_out(vty, " early-classmark-sending-3g %s%s", bts->early_classmark_allowed_3g ? "allowed" : "forbidden", VTY_NEWLINE); switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: vty_out(vty, " ip.access unit_id %u %u%s", bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); if (bts->ip_access.rsl_ip) { struct in_addr ia; ia.s_addr = htonl(bts->ip_access.rsl_ip); vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), VTY_NEWLINE); } vty_out(vty, " oml ip.access stream_id %u line %u%s", bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); break; case GSM_BTS_TYPE_NOKIA_SITE: vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); vty_out(vty, " nokia_site no-local-rel-conf %d%s", bts->nokia.no_loc_rel_cnf, VTY_NEWLINE); vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE); /* fall through: Nokia requires "oml e1" parameters also */ default: config_write_e1_link(vty, &bts->oml_e1_link, " oml "); vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); break; } /* if we have a limit, write it */ if (bts->paging.free_chans_need >= 0) vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE); vty_out(vty, " neighbor-list mode %s%s", get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE); if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) { for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i)) vty_out(vty, " neighbor-list add arfcn %u%s", i, VTY_NEWLINE); } } if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) { for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i)) vty_out(vty, " si5 neighbor-list add arfcn %u%s", i, VTY_NEWLINE); } } for (i = 0; i < MAX_EARFCN_LIST; i++) { struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; if (e->arfcn[i] != OSMO_EARFCN_INVALID) { vty_out(vty, " si2quater neighbor-list add earfcn %u " "thresh-hi %u", e->arfcn[i], e->thresh_hi); vty_out(vty, " thresh-lo %u", e->thresh_lo_valid ? e->thresh_lo : 32); vty_out(vty, " prio %u", e->prio_valid ? e->prio : 8); vty_out(vty, " qrxlv %u", e->qrxlm_valid ? e->qrxlm : 32); tmp = e->meas_bw[i]; vty_out(vty, " meas %u", (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8); vty_out(vty, "%s", VTY_NEWLINE); } } for (i = 0; i < bts->si_common.uarfcn_length; i++) { vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s", bts->si_common.data.uarfcn_list[i], bts->si_common.data.scramble_list[i] & ~(1 << 9), (bts->si_common.data.scramble_list[i] >> 9) & 1, VTY_NEWLINE); } vty_out(vty, " codec-support fr"); if (bts->codec.hr) vty_out(vty, " hr"); if (bts->codec.efr) vty_out(vty, " efr"); if (bts->codec.amr) vty_out(vty, " amr"); vty_out(vty, "%s", VTY_NEWLINE); config_write_bts_amr(vty, bts, &bts->mr_full, 1); config_write_bts_amr(vty, bts, &bts->mr_half, 0); config_write_bts_gprs(vty, bts); if (bts->excl_from_rf_lock) vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); vty_out(vty, " %sforce-combined-si%s", bts->force_combined_si ? "" : "no ", VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) { int j; if (bts->depends_on[i] == 0) continue; for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) { int bts_nr; if ((bts->depends_on[i] & (1<depends_on[i]) * 8) + j; vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE); } } if (bts->pcu_sock_path) vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE); ho_vty_write_bts(vty, bts); config_write_bts_model(vty, bts); } static int config_write_bts(struct vty *v) { struct gsm_network *gsmnet = gsmnet_from_vty(v); struct gsm_bts *bts; llist_for_each_entry(bts, &gsmnet->bts_list, list) config_write_bts_single(v, bts); return CMD_SUCCESS; } /* small helper macro for conditional dumping of timer */ #define VTY_OUT_TIMER(number) \ if (gsmnet->T##number != GSM_T##number##_DEFAULT) \ vty_out(vty, " timer t"#number" %u%s", gsmnet->T##number, VTY_NEWLINE) static int config_write_net(struct vty *vty) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); int i; vty_out(vty, "network%s", VTY_NEWLINE); vty_out(vty, " network country code %s%s", osmo_mcc_name(gsmnet->plmn.mcc), VTY_NEWLINE); vty_out(vty, " mobile network code %s%s", osmo_mnc_name(gsmnet->plmn.mnc, gsmnet->plmn.mnc_3_digits), VTY_NEWLINE); vty_out(vty, " encryption a5"); for (i = 0; i < 8; i++) { if (gsmnet->a5_encryption_mask & (1 << i)) vty_out(vty, " %u", i); } vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); ho_vty_write_net(vty, gsmnet); VTY_OUT_TIMER(3101); VTY_OUT_TIMER(3103); VTY_OUT_TIMER(3105); VTY_OUT_TIMER(3107); VTY_OUT_TIMER(3109); VTY_OUT_TIMER(3111); VTY_OUT_TIMER(3113); VTY_OUT_TIMER(3115); VTY_OUT_TIMER(3117); VTY_OUT_TIMER(3119); VTY_OUT_TIMER(3122); VTY_OUT_TIMER(3141); VTY_OUT_TIMER(10); VTY_OUT_TIMER(7); VTY_OUT_TIMER(8); VTY_OUT_TIMER(101); if (!gsmnet->dyn_ts_allow_tch_f) vty_out(vty, " dyn_ts_allow_tch_f 0%s", VTY_NEWLINE); if (gsmnet->tz.override != 0) { if (gsmnet->tz.dst) vty_out(vty, " timezone %d %d %d%s", gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst, VTY_NEWLINE); else vty_out(vty, " timezone %d %d%s", gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE); } if (gsmnet->t3212 == 0) vty_out(vty, " no periodic location update%s", VTY_NEWLINE); else vty_out(vty, " periodic location update %u%s", gsmnet->t3212 * 6, VTY_NEWLINE); { uint16_t meas_port; char *meas_host; const char *meas_scenario; meas_feed_cfg_get(&meas_host, &meas_port); meas_scenario = meas_feed_scenario_get(); if (meas_port) vty_out(vty, " meas-feed destination %s %u%s", meas_host, meas_port, VTY_NEWLINE); if (strlen(meas_scenario) > 0) vty_out(vty, " meas-feed scenario %s%s", meas_scenario, VTY_NEWLINE); } return CMD_SUCCESS; } static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) { vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); vty_out(vty, "Description: %s%s", trx->description ? trx->description : "(null)", VTY_NEWLINE); vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " "resulting BS power: %d dBm%s", trx->nominal_power, trx->max_power_red, trx->nominal_power - trx->max_power_red, VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &trx->mo.nm_state); vty_out(vty, " RSL State: %s%s", trx->rsl_link? "connected" : "disconnected", VTY_NEWLINE); vty_out(vty, " Baseband Transceiver NM State: "); net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state); if (is_ipaccess_bts(trx->bts)) { vty_out(vty, " ip.access stream ID: 0x%02x%s", trx->rsl_tei, VTY_NEWLINE); } else { vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, trx->rsl_link); } } static inline void print_all_trx(struct vty *vty, const struct gsm_bts *bts) { uint8_t trx_nr; for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) trx_dump_vty(vty, gsm_bts_trx_num(bts, trx_nr)); } DEFUN(show_trx, show_trx_cmd, "show trx [<0-255>] [<0-255>]", SHOW_STR "Display information about a TRX\n" BTS_TRX_STR) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts = NULL; int bts_nr, trx_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX '%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx_dump_vty(vty, gsm_bts_trx_num(bts, trx_nr)); return CMD_SUCCESS; } if (bts) { /* print all TRX in this BTS */ print_all_trx(vty, bts); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) print_all_trx(vty, gsm_bts_num(net, bts_nr)); return CMD_SUCCESS; } static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s, TSC %u", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts)); if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { vty_out(vty, " (%s mode)", ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F"); } else if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { vty_out(vty, " (%s mode)", gsm_pchan_name(ts->dyn.pchan_is)); } vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " NM State: "); net_dump_nmstate(vty, &ts->mo.nm_state); if (!is_ipaccess_bts(ts->trx->bts)) vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", ts->e1_link.e1_nr, ts->e1_link.e1_ts, ts->e1_link.e1_ts_ss, VTY_NEWLINE); } DEFUN(show_ts, show_ts_cmd, "show timeslot [<0-255>] [<0-255>] [<0-7>]", SHOW_STR "Display information about a TS\n" BTS_TRX_TS_STR) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; int bts_nr, trx_nr, ts_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX '%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); } if (argc >= 3) { ts_nr = atoi(argv[2]); if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% can't find TS '%s'%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } /* Fully Specified: print and exit */ ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); return CMD_SUCCESS; } if (bts && trx) { /* Iterate over all TS in this TRX */ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } else if (bts) { /* Iterate over all TRX in this BTS, TS in each TRX */ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } } else { /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { trx = gsm_bts_trx_num(bts, trx_nr); for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { ts = &trx->ts[ts_nr]; ts_dump_vty(vty, ts); } } } } return CMD_SUCCESS; } static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub) { if (strlen(bsub->imsi)) vty_out(vty, " IMSI: %s%s", bsub->imsi, VTY_NEWLINE); if (bsub->tmsi != GSM_RESERVED_TMSI) vty_out(vty, " TMSI: 0x%08x%s", bsub->tmsi, VTY_NEWLINE); vty_out(vty, " Use count: %d%s", bsub->use_count, VTY_NEWLINE); } static void meas_rep_dump_uni_vty(struct vty *vty, struct gsm_meas_rep_unidir *mru, const char *prefix, const char *dir) { vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", prefix, dir, rxlev2dbm(mru->full.rx_lev), dir, rxlev2dbm(mru->sub.rx_lev)); vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", dir, mru->full.rx_qual, dir, mru->sub.rx_qual, VTY_NEWLINE); } static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, const char *prefix) { vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", mr->flags & MEAS_REP_F_FPC ? "FPC " : "", mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_TO) vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_L1) vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_DL_VALID) meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); } /* FIXME: move this to libosmogsm */ static const struct value_string gsm48_cmode_names[] = { { GSM48_CMODE_SIGN, "signalling" }, { GSM48_CMODE_SPEECH_V1, "FR or HR" }, { GSM48_CMODE_SPEECH_EFR, "EFR" }, { GSM48_CMODE_SPEECH_AMR, "AMR" }, { GSM48_CMODE_DATA_14k5, "CSD(14k5)" }, { GSM48_CMODE_DATA_12k0, "CSD(12k0)" }, { GSM48_CMODE_DATA_6k0, "CSD(6k0)" }, { GSM48_CMODE_DATA_3k6, "CSD(3k6)" }, { 0, NULL } }; /* call vty_out() to print a string like " as TCH/H" for dynamic timeslots. * Don't do anything if the ts is not dynamic. */ static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts) { switch (ts->pchan) { case GSM_PCHAN_TCH_F_TCH_H_PDCH: if (ts->dyn.pchan_is == ts->dyn.pchan_want) vty_out(vty, " as %s", gsm_pchan_name(ts->dyn.pchan_is)); else vty_out(vty, " switching %s -> %s", gsm_pchan_name(ts->dyn.pchan_is), gsm_pchan_name(ts->dyn.pchan_want)); break; case GSM_PCHAN_TCH_F_PDCH: if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) vty_out(vty, " as %s", (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" : "TCH/F"); else vty_out(vty, " switching %s -> %s", (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" : "TCH/F", (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" : "TCH/F"); break; default: /* no dyn ts */ break; } } static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) { int idx; vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); /* show dyn TS details, if applicable */ switch (lchan->ts->pchan) { case GSM_PCHAN_TCH_F_TCH_H_PDCH: vty_out(vty, " Osmocom Dyn TS:"); vty_out_dyn_ts_status(vty, lchan->ts); vty_out(vty, VTY_NEWLINE); break; case GSM_PCHAN_TCH_F_PDCH: vty_out(vty, " IPACC Dyn PDCH TS:"); vty_out_dyn_ts_status(vty, lchan->ts); vty_out(vty, VTY_NEWLINE); break; default: /* no dyn ts */ break; } vty_out(vty, " Connection: %u, State: %s%s%s%s", lchan->conn ? 1: 0, gsm_lchans_name(lchan->state), lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "", lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "", VTY_NEWLINE); vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - lchan->bs_power*2, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), VTY_NEWLINE); vty_out(vty, " Channel Mode / Codec: %s%s", get_value_string(gsm48_cmode_names, lchan->tch_mode), VTY_NEWLINE); if (lchan->conn && lchan->conn->bsub) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); bsc_subscr_dump_vty(vty, lchan->conn->bsub); } else vty_out(vty, " No Subscriber%s", VTY_NEWLINE); if (is_ipaccess_bts(lchan->ts->trx->bts)) { struct in_addr ia; if (lchan->abis_ip.bound_ip) { ia.s_addr = htonl(lchan->abis_ip.bound_ip); vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", inet_ntoa(ia), lchan->abis_ip.bound_port, lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, VTY_NEWLINE); } if (lchan->abis_ip.connect_ip) { ia.s_addr = htonl(lchan->abis_ip.connect_ip); vty_out(vty, " Conn. IP: %s Port %u RTP_TYPE=%u SPEECH_MODE=0x%02x%s", inet_ntoa(ia), lchan->abis_ip.connect_port, lchan->abis_ip.rtp_payload, lchan->abis_ip.speech_mode, VTY_NEWLINE); } } /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); } static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) { struct gsm_meas_rep *mr; int idx; /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); mr = &lchan->meas_rep[idx]; vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, gsm_pchan_name(lchan->ts->pchan)); vty_out_dyn_ts_status(vty, lchan->ts); vty_out(vty, ", Lchan %u, Type %s, State %s - " "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", lchan->nr, gsm_lchant_name(lchan->type), gsm_lchans_name(lchan->state), mr->ms_l1.pwr, rxlev2dbm(mr->dl.full.rx_lev), rxlev2dbm(mr->ul.full.rx_lev), VTY_NEWLINE); } static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int lchan_nr; for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) continue; dump_cb(vty, lchan); } return CMD_SUCCESS; } static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int ts_nr; for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; dump_lchan_trx_ts(ts, vty, dump_cb); } return CMD_SUCCESS; } static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { int trx_nr; for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); dump_lchan_trx(trx, vty, dump_cb); } return CMD_SUCCESS; } static int lchan_summary(struct vty *vty, int argc, const char **argv, void (*dump_cb)(struct vty *, struct gsm_lchan *)) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts = NULL; struct gsm_bts_trx *trx = NULL; struct gsm_bts_trx_ts *ts = NULL; struct gsm_lchan *lchan; int bts_nr, trx_nr, ts_nr, lchan_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); if (argc == 1) return dump_lchan_bts(bts, vty, dump_cb); } if (argc >= 2) { trx_nr = atoi(argv[1]); if (trx_nr >= bts->num_trx) { vty_out(vty, "%% can't find TRX %s%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } trx = gsm_bts_trx_num(bts, trx_nr); if (argc == 2) return dump_lchan_trx(trx, vty, dump_cb); } if (argc >= 3) { ts_nr = atoi(argv[2]); if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% can't find TS %s%s", argv[2], VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; if (argc == 3) return dump_lchan_trx_ts(ts, vty, dump_cb); } if (argc >= 4) { lchan_nr = atoi(argv[3]); if (lchan_nr >= TS_MAX_LCHAN) { vty_out(vty, "%% can't find LCHAN %s%s", argv[3], VTY_NEWLINE); return CMD_WARNING; } lchan = &ts->lchan[lchan_nr]; dump_cb(vty, lchan); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); dump_lchan_bts(bts, vty, dump_cb); } return CMD_SUCCESS; } DEFUN(show_lchan, show_lchan_cmd, "show lchan [<0-255>] [<0-255>] [<0-7>] [<0-7>]", SHOW_STR "Display information about a logical channel\n" BTS_TRX_TS_LCHAN_STR) { return lchan_summary(vty, argc, argv, lchan_dump_full_vty); } DEFUN(show_lchan_summary, show_lchan_summary_cmd, "show lchan summary [<0-255>] [<0-255>] [<0-7>] [<0-7>]", SHOW_STR "Display information about a logical channel\n" "Short summary\n" BTS_TRX_TS_LCHAN_STR) { return lchan_summary(vty, argc, argv, lchan_dump_short_vty); } static void dump_one_subscr_conn(struct vty *vty, const struct gsm_subscriber_connection *conn) { vty_out(vty, "conn ID=%u, MSC=%u, hodec2_fail=%d, mode=%s, mgw_ep=%s%s", conn->sccp.conn_id, conn->sccp.msc->nr, conn->hodec2.failures, get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode), conn->user_plane.mgw_endpoint, VTY_NEWLINE); if (conn->lcls.global_call_ref_len) { vty_out(vty, " LCLS GCR: %s%s", osmo_hexdump_nospc(conn->lcls.global_call_ref, conn->lcls.global_call_ref_len), VTY_NEWLINE); vty_out(vty, " LCLS Config: 0x%02x, LCLS Control: 0x%02x, LCLS BSS Status: %s%s", conn->lcls.config, conn->lcls.control, osmo_fsm_inst_state_name(conn->lcls.fi), VTY_NEWLINE); } if (conn->lchan) lchan_dump_full_vty(vty, conn->lchan); if (conn->secondary_lchan) lchan_dump_full_vty(vty, conn->secondary_lchan); } DEFUN(show_subscr_conn, show_subscr_conn_cmd, "show conns", SHOW_STR "Display currently active subscriber connections\n") { struct gsm_subscriber_connection *conn; struct gsm_network *net = gsmnet_from_vty(vty); bool no_conns = true; unsigned int count = 0; vty_out(vty, "Active subscriber connections: %s", VTY_NEWLINE); llist_for_each_entry(conn, &net->subscr_conns, entry) { dump_one_subscr_conn(vty, conn); no_conns = false; count++; } if (no_conns) vty_out(vty, "None%s", VTY_NEWLINE); return CMD_SUCCESS; } static int trigger_ho_or_as(struct vty *vty, struct gsm_lchan *from_lchan, struct gsm_bts *to_bts) { int rc; if (!to_bts || from_lchan->ts->trx->bts == to_bts) { LOGP(DHO, LOGL_NOTICE, "%s Manually triggering Assignment from VTY\n", gsm_lchan_name(from_lchan)); to_bts = from_lchan->ts->trx->bts; } else LOGP(DHO, LOGL_NOTICE, "%s (ARFCN %u) --> BTS %u Manually triggering Handover from VTY\n", gsm_lchan_name(from_lchan), from_lchan->ts->trx->arfcn, to_bts->nr); rc = bsc_handover_start(HODEC_NONE, from_lchan, to_bts, from_lchan->type); if (rc) { vty_out(vty, "bsc_handover_start() returned %d=%s%s", rc, strerror(-rc), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } static int ho_or_as(struct vty *vty, const char *argv[], int argc) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_subscriber_connection *conn; struct gsm_bts *bts; struct gsm_bts *new_bts = NULL; unsigned int bts_nr = atoi(argv[0]); unsigned int trx_nr = atoi(argv[1]); unsigned int ts_nr = atoi(argv[2]); unsigned int ss_nr = atoi(argv[3]); unsigned int bts_nr_new; const char *action; if (argc > 4) { bts_nr_new = atoi(argv[4]); /* Lookup the BTS where we want to handover to */ llist_for_each_entry(bts, &net->bts_list, list) { if (bts->nr == bts_nr_new) { new_bts = bts; break; } } if (!new_bts) { vty_out(vty, "Unable to trigger handover, specified bts #%u does not exist %s", bts_nr_new, VTY_NEWLINE); return CMD_WARNING; } } action = new_bts ? "handover" : "assignment"; /* Find the connection/lchan that we want to handover */ llist_for_each_entry(conn, &net->subscr_conns, entry) { if (conn_get_bts(conn)->nr == bts_nr && conn->lchan->ts->trx->nr == trx_nr && conn->lchan->ts->nr == ts_nr && conn->lchan->nr == ss_nr) { vty_out(vty, "starting %s for lchan %s...%s", action, conn->lchan->name, VTY_NEWLINE); lchan_dump_full_vty(vty, conn->lchan); return trigger_ho_or_as(vty, conn->lchan, new_bts); } } vty_out(vty, "Unable to trigger %s, specified connection (bts=%u,trx=%u,ts=%u,ss=%u) does not exist%s", action, bts_nr, trx_nr, ts_nr, ss_nr, VTY_NEWLINE); return CMD_WARNING; } #define MANUAL_HANDOVER_STR "Manually trigger handover (for debugging)\n" #define MANUAL_ASSIGNMENT_STR "Manually trigger assignment (for debugging)\n" DEFUN(handover_subscr_conn, handover_subscr_conn_cmd, "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> handover <0-255>", BTS_NR_TRX_TS_SS_STR2 MANUAL_HANDOVER_STR "New " BTS_NR_STR) { return ho_or_as(vty, argv, argc); } DEFUN(assignment_subscr_conn, assignment_subscr_conn_cmd, "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> assignment", BTS_NR_TRX_TS_SS_STR2 MANUAL_ASSIGNMENT_STR) { return ho_or_as(vty, argv, argc); } static struct gsm_lchan *find_used_voice_lchan(struct vty *vty) { struct gsm_bts *bts; struct gsm_network *network = gsmnet_from_vty(vty); llist_for_each_entry(bts, &network->bts_list, list) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { int i; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; int j; int subslots; /* skip administratively deactivated timeslots */ if (!nm_is_running(&ts->mo.nm_state)) continue; subslots = ts_subslots(ts); for (j = 0; j < subslots; j++) { struct gsm_lchan *lchan = &ts->lchan[j]; if (lchan->state == LCHAN_S_ACTIVE && (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) { vty_out(vty, "Found voice call: %s%s", gsm_lchan_name(lchan), VTY_NEWLINE); lchan_dump_full_vty(vty, lchan); return lchan; } } } } } vty_out(vty, "Cannot find any ongoing voice calls%s", VTY_NEWLINE); return NULL; } static struct gsm_bts *find_other_bts_with_free_slots(struct vty *vty, struct gsm_bts *not_this_bts, enum gsm_phys_chan_config free_type) { struct gsm_bts *bts; struct gsm_network *network = gsmnet_from_vty(vty); llist_for_each_entry(bts, &network->bts_list, list) { struct gsm_bts_trx *trx; if (bts == not_this_bts) continue; llist_for_each_entry(trx, &bts->trx_list, list) { int i; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; int j; int subslots; /* skip administratively deactivated timeslots */ if (!nm_is_running(&ts->mo.nm_state)) continue; if (ts->pchan != free_type) continue; subslots = ts_subslots(ts); for (j = 0; j < subslots; j++) { struct gsm_lchan *lchan = &ts->lchan[j]; if (lchan->state == LCHAN_S_NONE) { vty_out(vty, "Found unused %s slot: %s%s", gsm_pchan_name(free_type), gsm_lchan_name(lchan), VTY_NEWLINE); lchan_dump_full_vty(vty, lchan); return bts; } } } } } vty_out(vty, "Cannot find any BTS (other than BTS %u) with free %s lchan%s", not_this_bts? not_this_bts->nr : 255, gsm_lchant_name(free_type), VTY_NEWLINE); return NULL; } DEFUN(handover_any, handover_any_cmd, "handover any", MANUAL_HANDOVER_STR "Pick any actively used TCH/F or TCH/H lchan and handover to any other BTS." " This is likely to fail if not all BTS are guaranteed to be reachable by the MS.\n") { struct gsm_lchan *from_lchan; struct gsm_bts *to_bts; from_lchan = find_used_voice_lchan(vty); if (!from_lchan) return CMD_WARNING; to_bts = find_other_bts_with_free_slots(vty, from_lchan->ts->trx->bts, ts_pchan(from_lchan->ts)); if (!to_bts) return CMD_WARNING; return trigger_ho_or_as(vty, from_lchan, to_bts); } DEFUN(assignment_any, assignment_any_cmd, "assignment any", MANUAL_ASSIGNMENT_STR "Pick any actively used TCH/F or TCH/H lchan and re-assign within the same BTS." " This will fail if no lchans of the same type are available besides the used one.\n") { struct gsm_lchan *from_lchan; from_lchan = find_used_voice_lchan(vty); if (!from_lchan) return CMD_WARNING; return trigger_ho_or_as(vty, from_lchan, NULL); } static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) { vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); bsc_subscr_dump_vty(vty, pag->bsub); } static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) { struct gsm_paging_request *pag; if (!bts->paging.bts) return; llist_for_each_entry(pag, &bts->paging.pending_requests, entry) paging_dump_vty(vty, pag); } DEFUN(show_paging, show_paging_cmd, "show paging [<0-255>]", SHOW_STR "Display information about paging reuqests of a BTS\n" BTS_NR_STR) { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts; int bts_nr; if (argc >= 1) { /* use the BTS number that the user has specified */ bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); bts_paging_dump_vty(vty, bts); return CMD_SUCCESS; } for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { bts = gsm_bts_num(net, bts_nr); bts_paging_dump_vty(vty, bts); } return CMD_SUCCESS; } DEFUN(show_paging_group, show_paging_group_cmd, "show paging-group <0-255> IMSI", SHOW_STR "Display the paging group\n" BTS_NR_STR "IMSI\n") { struct gsm_network *net = gsmnet_from_vty(vty); struct gsm_bts *bts; unsigned int page_group; int bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); if (!bts) { vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(argv[1])); vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s", str_to_imsi(argv[1]), bts->nr, page_group, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_net_neci, cfg_net_neci_cmd, "neci (0|1)", "New Establish Cause Indication\n" "Don't set the NECI bit\n" "Set the NECI bit\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->neci = atoi(argv[0]); gsm_net_update_ctype(gsmnet); return CMD_SUCCESS; } DEFUN(cfg_net_pag_any_tch, cfg_net_pag_any_tch_cmd, "paging any use tch (0|1)", "Assign a TCH when receiving a Paging Any request\n" "Any Channel\n" "Use\n" "TCH\n" "Do not use TCH for Paging Request Any\n" "Do use TCH for Paging Request Any\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->pag_any_tch = atoi(argv[0]); gsm_net_update_ctype(gsmnet); return CMD_SUCCESS; } #define DEFAULT_TIMER(number) GSM_T##number##_DEFAULT /* Add another expansion so that DEFAULT_TIMER() becomes its value */ #define EXPAND_AND_STRINGIFY(x) OSMO_STRINGIFY(x) #define DECLARE_TIMER(number, doc) \ DEFUN(cfg_net_T##number, \ cfg_net_T##number##_cmd, \ "timer t" #number " (default|<1-65535>)", \ "Configure GSM Timers\n" \ doc " (default: " EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " seconds)\n" \ "Set to default timer value" \ " (" EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " seconds)\n" \ "Timer Value in seconds\n") \ { \ struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ int value; \ if (strcmp(argv[0], "default") == 0) \ value = DEFAULT_TIMER(number); \ else \ value = atoi(argv[0]); \ \ gsmnet->T##number = value; \ return CMD_SUCCESS; \ } DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT") DECLARE_TIMER(3103, "Set the timeout value for HANDOVER") DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION") DECLARE_TIMER(3107, "Currently not used") DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout") DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel") DECLARE_TIMER(3113, "Set the time to try paging a subscriber") DECLARE_TIMER(3115, "Currently not used") DECLARE_TIMER(3117, "Currently not used") DECLARE_TIMER(3119, "Currently not used") DECLARE_TIMER(3122, "Default waiting time (seconds) after IMM ASS REJECT") DECLARE_TIMER(3141, "Currently not used") DECLARE_TIMER(10, "Assignment Command timeout in seconds") DECLARE_TIMER(7, "Set the outgoing inter-BSC Handover timeout, from Handover Required to Handover Command") DECLARE_TIMER(8, "Set the outgoing inter-BSC Handover timeout, from Handover Command to final Clear") DECLARE_TIMER(101, "Set the incoming inter-BSC Handover timeout, from Handover Request to Accept") DEFUN_DEPRECATED(cfg_net_dtx, cfg_net_dtx_cmd, "dtx-used (0|1)", ".HIDDEN\n""Obsolete\n""Obsolete\n") { vty_out(vty, "%% 'dtx-used' is now deprecated: use dtx * " "configuration options of BTS instead%s", VTY_NEWLINE); return CMD_SUCCESS; } /* per-BTS configuration */ DEFUN(cfg_bts, cfg_bts_cmd, "bts <0-255>", "Select a BTS to configure\n" BTS_NR_STR) { struct gsm_network *gsmnet = gsmnet_from_vty(vty); int bts_nr = atoi(argv[0]); struct gsm_bts *bts; if (bts_nr > gsmnet->num_bts) { vty_out(vty, "%% The next unused BTS number is %u%s", gsmnet->num_bts, VTY_NEWLINE); return CMD_WARNING; } else if (bts_nr == gsmnet->num_bts) { /* allocate a new one */ bts = bsc_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, HARDCODED_BSIC); /* * Initalize bts->acc_ramp here. Else we could segfault while * processing a configuration file with ACC ramping settings. */ acc_ramp_init(&bts->acc_ramp, bts); } else bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "%% Unable to allocate BTS %u%s", gsmnet->num_bts, VTY_NEWLINE); return CMD_WARNING; } vty->index = bts; vty->index_sub = &bts->description; vty->node = BTS_NODE; return CMD_SUCCESS; } DEFUN(cfg_bts_type, cfg_bts_type_cmd, "type TYPE", /* dynamically created */ "Set the BTS type\n" "Type\n") { struct gsm_bts *bts = vty->index; int rc; rc = gsm_set_bts_type(bts, str2btstype(argv[0])); if (rc < 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_bts_band, cfg_bts_band_cmd, "band BAND", "Set the frequency band of this BTS\n" "Frequency band\n") { struct gsm_bts *bts = vty->index; int band = gsm_band_parse(argv[0]); if (band < 0) { vty_out(vty, "%% BAND %d is not a valid GSM band%s", band, VTY_NEWLINE); return CMD_WARNING; } bts->band = band; return CMD_SUCCESS; } DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]", "Configure discontinuous transmission\n" "Enable Uplink DTX for this BTS\n" "MS 'shall' use DTXu instead of 'may' use (might not be supported by " "older phones).\n") { struct gsm_bts *bts = vty->index; bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED; if (!is_ipaccess_bts(bts)) vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " "neither supported nor tested!%s", VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink", NO_STR "Configure discontinuous transmission\n" "Disable Uplink DTX for this BTS\n") { struct gsm_bts *bts = vty->index; bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; return CMD_SUCCESS; } DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink", "Configure discontinuous transmission\n" "Enable Downlink DTX for this BTS\n") { struct gsm_bts *bts = vty->index; bts->dtxd = true; if (!is_ipaccess_bts(bts)) vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " "neither supported nor tested!%s", VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink", NO_STR "Configure discontinuous transmission\n" "Disable Downlink DTX for this BTS\n") { struct gsm_bts *bts = vty->index; bts->dtxd = false; return CMD_SUCCESS; } DEFUN(cfg_bts_ci, cfg_bts_ci_cmd, "cell_identity <0-65535>", "Set the Cell identity of this BTS\n" "Cell Identity\n") { struct gsm_bts *bts = vty->index; int ci = atoi(argv[0]); if (ci < 0 || ci > 0xffff) { vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", ci, VTY_NEWLINE); return CMD_WARNING; } bts->cell_identity = ci; return CMD_SUCCESS; } DEFUN(cfg_bts_lac, cfg_bts_lac_cmd, "location_area_code <0-65535>", "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") { struct gsm_bts *bts = vty->index; int lac = atoi(argv[0]); if (lac < 0 || lac > 0xffff) { vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", lac, VTY_NEWLINE); return CMD_WARNING; } if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", lac, VTY_NEWLINE); return CMD_WARNING; } bts->location_area_code = lac; return CMD_SUCCESS; } /* compatibility wrapper for old config files */ DEFUN_HIDDEN(cfg_bts_tsc, cfg_bts_tsc_cmd, "training_sequence_code <0-7>", "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") { return CMD_SUCCESS; } DEFUN(cfg_bts_bsic, cfg_bts_bsic_cmd, "base_station_id_code <0-63>", "Set the Base Station Identity Code (BSIC) of this BTS\n" "BSIC of this BTS\n") { struct gsm_bts *bts = vty->index; int bsic = atoi(argv[0]); if (bsic < 0 || bsic > 0x3f) { vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", bsic, VTY_NEWLINE); return CMD_WARNING; } bts->bsic = bsic; return CMD_SUCCESS; } DEFUN(cfg_bts_unit_id, cfg_bts_unit_id_cmd, "ip.access unit_id <0-65534> <0-255>", "Abis/IP specific options\n" "Set the IPA BTS Unit ID\n" "Unit ID (Site)\n" "Unit ID (BTS)\n") { struct gsm_bts *bts = vty->index; int site_id = atoi(argv[0]); int bts_id = atoi(argv[1]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } bts->ip_access.site_id = site_id; bts->ip_access.bts_id = bts_id; return CMD_SUCCESS; } DEFUN(cfg_bts_rsl_ip, cfg_bts_rsl_ip_cmd, "ip.access rsl-ip A.B.C.D", "Abis/IP specific options\n" "Set the IPA RSL IP Address of the BSC\n" "Destination IP address for RSL connection\n") { struct gsm_bts *bts = vty->index; struct in_addr ia; if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[0], &ia); bts->ip_access.rsl_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } #define NOKIA_STR "Nokia *Site related commands\n" DEFUN(cfg_bts_nokia_site_skip_reset, cfg_bts_nokia_site_skip_reset_cmd, "nokia_site skip-reset (0|1)", NOKIA_STR "Skip the reset step during bootstrap process of this BTS\n" "Do NOT skip the reset\n" "Skip the reset\n") { struct gsm_bts *bts = vty->index; if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.skip_reset = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf, cfg_bts_nokia_site_no_loc_rel_cnf_cmd, "nokia_site no-local-rel-conf (0|1)", NOKIA_STR "Do not wait for RELease CONFirm message when releasing channel locally\n" "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n") { struct gsm_bts *bts = vty->index; if (!is_nokia_bts(bts)) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.no_loc_rel_cnf = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf, cfg_bts_nokia_site_bts_reset_timer_cnf_cmd, "nokia_site bts-reset-timer <15-100>", NOKIA_STR "The amount of time (in sec.) between BTS_RESET is sent,\n" "and the BTS is being bootstrapped.\n") { struct gsm_bts *bts = vty->index; if (!is_nokia_bts(bts)) { vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); return CMD_WARNING; } bts->nokia.bts_reset_timer_cnf = atoi(argv[0]); return CMD_SUCCESS; } #define OML_STR "Organization & Maintenance Link\n" #define IPA_STR "A-bis/IP Specific Options\n" DEFUN(cfg_bts_stream_id, cfg_bts_stream_id_cmd, "oml ip.access stream_id <0-255> line E1_LINE", OML_STR IPA_STR "Set the ip.access Stream ID of the OML link of this BTS\n" "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n") { struct gsm_bts *bts = vty->index; int stream_id = atoi(argv[0]), linenr = atoi(argv[1]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); return CMD_WARNING; } bts->oml_tei = stream_id; /* This is used by e1inp_bind_ops callback for each BTS model. */ bts->oml_e1_link.e1_nr = linenr; return CMD_SUCCESS; } #define OML_E1_STR OML_STR "OML E1/T1 Configuration\n" DEFUN(cfg_bts_oml_e1, cfg_bts_oml_e1_cmd, "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", OML_E1_STR "E1/T1 line number to be used for OML\n" "E1/T1 line number to be used for OML\n" "E1/T1 timeslot to be used for OML\n" "E1/T1 timeslot to be used for OML\n" "E1/T1 sub-slot to be used for OML\n" "Use E1/T1 sub-slot 0\n" "Use E1/T1 sub-slot 1\n" "Use E1/T1 sub-slot 2\n" "Use E1/T1 sub-slot 3\n" "Use full E1 slot 3\n" ) { struct gsm_bts *bts = vty->index; parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } DEFUN(cfg_bts_oml_e1_tei, cfg_bts_oml_e1_tei_cmd, "oml e1 tei <0-63>", OML_E1_STR "Set the TEI to be used for OML\n" "TEI Number\n") { struct gsm_bts *bts = vty->index; bts->oml_tei = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, "channel allocator (ascending|descending)", "Channnel Allocator\n" "Channel Allocator\n" "Allocate Timeslots and Transceivers in ascending order\n" "Allocate Timeslots and Transceivers in descending order\n") { struct gsm_bts *bts = vty->index; if (!strcmp(argv[0], "ascending")) bts->chan_alloc_reverse = 0; else bts->chan_alloc_reverse = 1; return CMD_SUCCESS; } #define RACH_STR "Random Access Control Channel\n" DEFUN(cfg_bts_rach_tx_integer, cfg_bts_rach_tx_integer_cmd, "rach tx integer <0-15>", RACH_STR "Set the raw tx integer value in RACH Control parameters IE\n" "Set the raw tx integer value in RACH Control parameters IE\n" "Raw tx integer value in RACH Control parameters IE\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; return CMD_SUCCESS; } DEFUN(cfg_bts_rach_max_trans, cfg_bts_rach_max_trans_cmd, "rach max transmission (1|2|4|7)", RACH_STR "Set the maximum number of RACH burst transmissions\n" "Set the maximum number of RACH burst transmissions\n" "Maximum number of 1 RACH burst transmissions\n" "Maximum number of 2 RACH burst transmissions\n" "Maximum number of 4 RACH burst transmissions\n" "Maximum number of 7 RACH burst transmissions\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); return CMD_SUCCESS; } #define CD_STR "Channel Description\n" DEFUN(cfg_bts_chan_desc_att, cfg_bts_chan_desc_att_cmd, "channel-descrption attach (0|1)", CD_STR "Set if attachment is required\n" "Attachment is NOT required\n" "Attachment is required (standard)\n") { struct gsm_bts *bts = vty->index; bts->si_common.chan_desc.att = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_chan_desc_bs_pa_mfrms, cfg_bts_chan_desc_bs_pa_mfrms_cmd, "channel-descrption bs-pa-mfrms <2-9>", CD_STR "Set number of multiframe periods for paging groups\n" "Number of multiframe periods for paging groups\n") { struct gsm_bts *bts = vty->index; int bs_pa_mfrms = atoi(argv[0]); bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2; return CMD_SUCCESS; } DEFUN(cfg_bts_chan_desc_bs_ag_blks_res, cfg_bts_chan_desc_bs_ag_blks_res_cmd, "channel-descrption bs-ag-blks-res <0-7>", CD_STR "Set number of blocks reserved for access grant\n" "Number of blocks reserved for access grant\n") { struct gsm_bts *bts = vty->index; int bs_ag_blks_res = atoi(argv[0]); bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res; return CMD_SUCCESS; } #define NM_STR "Network Management\n" DEFUN(cfg_bts_rach_nm_b_thresh, cfg_bts_rach_nm_b_thresh_cmd, "rach nm busy threshold <0-255>", RACH_STR NM_STR "Set the NM Busy Threshold\n" "Set the NM Busy Threshold\n" "NM Busy Threshold in dB") { struct gsm_bts *bts = vty->index; bts->rach_b_thresh = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_rach_nm_ldavg, cfg_bts_rach_nm_ldavg_cmd, "rach nm load average <0-65535>", RACH_STR NM_STR "Set the NM Loadaverage Slots value\n" "Set the NM Loadaverage Slots value\n" "NM Loadaverage Slots value\n") { struct gsm_bts *bts = vty->index; bts->rach_ldavg_slots = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, "cell barred (0|1)", "Should this cell be barred from access?\n" "Should this cell be barred from access?\n" "Cell should NOT be barred\n" "Cell should be barred\n") { struct gsm_bts *bts = vty->index; bts->si_common.rach_control.cell_bar = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd, "rach emergency call allowed (0|1)", RACH_STR "Should this cell allow emergency calls?\n" "Should this cell allow emergency calls?\n" "Should this cell allow emergency calls?\n" "Do NOT allow emergency calls\n" "Allow emergency calls\n") { struct gsm_bts *bts = vty->index; if (atoi(argv[0]) == 0) bts->si_common.rach_control.t2 |= 0x4; else bts->si_common.rach_control.t2 &= ~0x4; return CMD_SUCCESS; } DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd, "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)", RACH_STR "Set access control class\n" "Access control class 0\n" "Access control class 1\n" "Access control class 2\n" "Access control class 3\n" "Access control class 4\n" "Access control class 5\n" "Access control class 6\n" "Access control class 7\n" "Access control class 8\n" "Access control class 9\n" "Access control class 11 for PLMN use\n" "Access control class 12 for security services\n" "Access control class 13 for public utilities (e.g. water/gas suppliers)\n" "Access control class 14 for emergency services\n" "Access control class 15 for PLMN staff\n" "barred to use access control class\n" "allowed to use access control class\n") { struct gsm_bts *bts = vty->index; uint8_t control_class; uint8_t allowed = 0; if (strcmp(argv[1], "allowed") == 0) allowed = 1; control_class = atoi(argv[0]); if (control_class < 8) if (allowed) bts->si_common.rach_control.t3 &= ~(0x1 << control_class); else bts->si_common.rach_control.t3 |= (0x1 << control_class); else if (allowed) bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8)); else bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8)); return CMD_SUCCESS; } DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd, "ms max power <0-40>", "MS Options\n" "Maximum transmit power of the MS\n" "Maximum transmit power of the MS\n" "Maximum transmit power of the MS in dBm") { struct gsm_bts *bts = vty->index; bts->ms_max_power = atoi(argv[0]); return CMD_SUCCESS; } #define CELL_STR "Cell Parameters\n" DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, "cell reselection hysteresis <0-14>", CELL_STR "Cell re-selection parameters\n" "Cell Re-Selection Hysteresis in dB\n" "Cell Re-Selection Hysteresis in dB") { struct gsm_bts *bts = vty->index; bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2; return CMD_SUCCESS; } DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd, "rxlev access min <0-63>", "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access\n" "Minimum RxLev needed for cell access (better than -110dBm)") { struct gsm_bts *bts = vty->index; bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd, "cell bar qualify (0|1)", CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n" "Set CBQ to 0\n" "Set CBQ to 1\n") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd, "cell reselection offset <0-126>", CELL_STR "Cell Re-Selection Parameters\n" "Cell Re-Selection Offset (CRO) in dB\n" "Cell Re-Selection Offset (CRO) in dB\n" ) { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2; return CMD_SUCCESS; } DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd, "temporary offset <0-60>", "Cell selection temporary negative offset\n" "Cell selection temporary negative offset\n" "Cell selection temporary negative offset in dB") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10; return CMD_SUCCESS; } DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd, "temporary offset infinite", "Cell selection temporary negative offset\n" "Cell selection temporary negative offset\n" "Sets cell selection temporary negative offset to infinity") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.temp_offs = 7; return CMD_SUCCESS; } DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd, "penalty time <20-620>", "Cell selection penalty time\n" "Cell selection penalty time\n" "Cell selection penalty time in seconds (by 20s increments)\n") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20; return CMD_SUCCESS; } DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, "penalty time reserved", "Cell selection penalty time\n" "Cell selection penalty time\n" "Set cell selection penalty time to reserved value 31, " "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 " "and TEMPORARY_OFFSET is ignored)") { struct gsm_bts *bts = vty->index; bts->si_common.cell_ro_sel_par.present = 1; bts->si_common.cell_ro_sel_par.penalty_time = 31; return CMD_SUCCESS; } DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd, "radio-link-timeout <4-64>", "Radio link timeout criterion (BTS side)\n" "Radio link timeout value (lost SACCH block)\n") { struct gsm_bts *bts = vty->index; gsm_bts_set_radio_link_timeout(bts, atoi(argv[0])); return CMD_SUCCESS; } DEFUN(cfg_bts_radio_link_timeout_inf, cfg_bts_radio_link_timeout_inf_cmd, "radio-link-timeout infinite", "Radio link timeout criterion (BTS side)\n" "Infinite Radio link timeout value (use only for BTS RF testing)\n") { struct gsm_bts *bts = vty->index; if (bts->type != GSM_BTS_TYPE_OSMOBTS) { vty_out(vty, "%% infinite radio link timeout not supported by this BTS%s", VTY_NEWLINE); return CMD_WARNING; } vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE); gsm_bts_set_radio_link_timeout(bts, -1); return CMD_SUCCESS; } #define GPRS_TEXT "GPRS Packet Network\n" DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, "gprs cell bvci <2-65535>", GPRS_TEXT "GPRS Cell Settings\n" "GPRS BSSGP VC Identifier\n" "GPRS BSSGP VC Identifier") { /* ETSI TS 101 343: values 0 and 1 are reserved for signalling and PTM */ struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.cell.bvci = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, "gprs nsei <0-65535>", GPRS_TEXT "GPRS NS Entity Identifier\n" "GPRS NS Entity Identifier") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nse.nsei = atoi(argv[0]); return CMD_SUCCESS; } #define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ "NSVC Logical Number\n" DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, "gprs nsvc <0-1> nsvci <0-65535>", GPRS_TEXT NSVC_TEXT "NS Virtual Connection Identifier\n" "GPRS NS VC Identifier") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, "gprs nsvc <0-1> local udp port <0-65535>", GPRS_TEXT NSVC_TEXT "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port\n" "GPRS NS Local UDP Port Number\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].local_port = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, "gprs nsvc <0-1> remote udp port <0-65535>", GPRS_TEXT NSVC_TEXT "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port\n" "GPRS NS Remote UDP Port Number\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, "gprs nsvc <0-1> remote ip A.B.C.D", GPRS_TEXT NSVC_TEXT "GPRS NS Remote IP Address\n" "GPRS NS Remote IP Address\n" "GPRS NS Remote IP Address\n") { struct gsm_bts *bts = vty->index; int idx = atoi(argv[0]); struct in_addr ia; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } inet_aton(argv[1], &ia); bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); return CMD_SUCCESS; } DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, "paging free <-1-1024>", "Paging options\n" "Only page when having a certain amount of free slots\n" "amount of required free paging slots. -1 to disable\n") { struct gsm_bts *bts = vty->index; bts->paging.free_chans_need = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, "gprs ns timer " NS_TIMERS " <0-255>", GPRS_TEXT "Network Service\n" "Network Service Timer\n" NS_TIMERS_HELP "Timer Value\n") { struct gsm_bts *bts = vty->index; int idx = get_string_value(gprs_ns_timer_strs, argv[0]); int val = atoi(argv[1]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) return CMD_WARNING; bts->gprs.nse.timer[idx] = val; return CMD_SUCCESS; } #define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" #define BSSGP_TIMERS_HELP \ "Tbvc-block timeout\n" \ "Tbvc-block retries\n" \ "Tbvc-unblock retries\n" \ "Tbvcc-reset timeout\n" \ "Tbvc-reset retries\n" \ "Tbvc-suspend timeout\n" \ "Tbvc-suspend retries\n" \ "Tbvc-resume timeout\n" \ "Tbvc-resume retries\n" \ "Tbvc-capa-update timeout\n" \ "Tbvc-capa-update retries\n" DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, "gprs cell timer " BSSGP_TIMERS " <0-255>", GPRS_TEXT "Cell / BSSGP\n" "Cell/BSSGP Timer\n" BSSGP_TIMERS_HELP "Timer Value\n") { struct gsm_bts *bts = vty->index; int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); int val = atoi(argv[1]); if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) return CMD_WARNING; bts->gprs.cell.timer[idx] = val; return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, "gprs routing area <0-255>", GPRS_TEXT "GPRS Routing Area Code\n" "GPRS Routing Area Code\n" "GPRS Routing Area Code\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.rac = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd, "gprs control-ack-type-rach", GPRS_TEXT "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " "four access bursts format instead of default RLC/MAC control block\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.ctrl_ack_type_use_block = false; return CMD_SUCCESS; } DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd, "no gprs control-ack-type-rach", NO_STR GPRS_TEXT "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " "four access bursts format instead of default RLC/MAC control block\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.ctrl_ack_type_use_block = true; return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, "gprs network-control-order (nc0|nc1|nc2)", GPRS_TEXT "GPRS Network Control Order\n" "MS controlled cell re-selection, no measurement reporting\n" "MS controlled cell re-selection, MS sends measurement reports\n" "Network controlled cell re-selection, MS sends measurement reports\n") { struct gsm_bts *bts = vty->index; if (bts->gprs.mode == BTS_GPRS_NONE) { vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); return CMD_WARNING; } bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, "gprs mode (none|gprs|egprs)", GPRS_TEXT "GPRS Mode for this BTS\n" "GPRS Disabled on this BTS\n" "GPRS Enabled on this BTS\n" "EGPRS (EDGE) Enabled on this BTS\n") { struct gsm_bts *bts = vty->index; enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL); if (!bts_gprs_mode_is_compat(bts, mode)) { vty_out(vty, "This BTS type does not support %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts->gprs.mode = mode; return CMD_SUCCESS; } DEFUN(cfg_bts_gprs_11bit_rach_support_for_egprs, cfg_bts_gprs_11bit_rach_support_for_egprs_cmd, "gprs 11bit_rach_support_for_egprs (0|1)", GPRS_TEXT "11 bit RACH options\n" "Disable 11 bit RACH for EGPRS\n" "Enable 11 bit RACH for EGPRS") { struct gsm_bts *bts = vty->index; bts->gprs.supports_egprs_11bit_rach = atoi(argv[0]); if (bts->gprs.supports_egprs_11bit_rach > 1) { vty_out(vty, "Error in RACH type%s", VTY_NEWLINE); return CMD_WARNING; } if ((bts->gprs.mode == BTS_GPRS_NONE) && (bts->gprs.supports_egprs_11bit_rach == 1)) { vty_out(vty, "Error:gprs mode is none and 11bit rach is" " enabled%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define SI_TEXT "System Information Messages\n" #define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" #define SI_TYPE_HELP "System Information Type 1\n" \ "System Information Type 2\n" \ "System Information Type 3\n" \ "System Information Type 4\n" \ "System Information Type 5\n" \ "System Information Type 6\n" \ "System Information Type 7\n" \ "System Information Type 8\n" \ "System Information Type 9\n" \ "System Information Type 10\n" \ "System Information Type 13\n" \ "System Information Type 16\n" \ "System Information Type 17\n" \ "System Information Type 18\n" \ "System Information Type 19\n" \ "System Information Type 20\n" \ "System Information Type 2bis\n" \ "System Information Type 2ter\n" \ "System Information Type 2quater\n" \ "System Information Type 5bis\n" \ "System Information Type 5ter\n" DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, "system-information " SI_TYPE_TEXT " mode (static|computed)", SI_TEXT SI_TYPE_HELP "System Information Mode\n" "Static user-specified\n" "Dynamic, BSC-computed\n") { struct gsm_bts *bts = vty->index; int type; type = get_string_value(osmo_sitype_strs, argv[0]); if (type < 0) { vty_out(vty, "Error SI Type%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[1], "static")) bts->si_mode_static |= (1 << type); else bts->si_mode_static &= ~(1 << type); return CMD_SUCCESS; } DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, "system-information " SI_TYPE_TEXT " static HEXSTRING", SI_TEXT SI_TYPE_HELP "Static System Information filling\n" "Static user-specified SI content in HEX notation\n") { struct gsm_bts *bts = vty->index; int rc, type; type = get_string_value(osmo_sitype_strs, argv[0]); if (type < 0) { vty_out(vty, "Error SI Type%s", VTY_NEWLINE); return CMD_WARNING; } if (!(bts->si_mode_static & (1 << type))) { vty_out(vty, "SI Type %s is not configured in static mode%s", get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); return CMD_WARNING; } /* Fill buffer with padding pattern */ memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN); /* Parse the user-specified SI in hex format, [partially] overwriting padding */ rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN); if (rc < 0 || rc > GSM_MACBLOCK_LEN) { vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); return CMD_WARNING; } /* Mark this SI as present */ bts->si_valid |= (1 << type); return CMD_SUCCESS; } DEFUN(cfg_bts_early_cm, cfg_bts_early_cm_cmd, "early-classmark-sending (allowed|forbidden)", "Early Classmark Sending\n" "Early Classmark Sending is allowed\n" "Early Classmark Sending is forbidden\n") { struct gsm_bts *bts = vty->index; if (!strcmp(argv[0], "allowed")) bts->early_classmark_allowed = true; else bts->early_classmark_allowed = false; return CMD_SUCCESS; } DEFUN(cfg_bts_early_cm_3g, cfg_bts_early_cm_3g_cmd, "early-classmark-sending-3g (allowed|forbidden)", "3G Early Classmark Sending\n" "3G Early Classmark Sending is allowed\n" "3G Early Classmark Sending is forbidden\n") { struct gsm_bts *bts = vty->index; if (!strcmp(argv[0], "allowed")) bts->early_classmark_allowed_3g = true; else bts->early_classmark_allowed_3g = false; return CMD_SUCCESS; } DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, "neighbor-list mode (automatic|manual|manual-si5)", "Neighbor List\n" "Mode of Neighbor List generation\n" "Automatically from all BTS in this OpenBSC\n" "Manual\n" "Manual with different lists for SI2 and SI5\n") { struct gsm_bts *bts = vty->index; int mode = get_string_value(bts_neigh_mode_strs, argv[0]); switch (mode) { case NL_MODE_MANUAL_SI5SEP: case NL_MODE_MANUAL: /* make sure we clear the current list when switching to * manual mode */ if (bts->neigh_list_manual_mode == 0) memset(&bts->si_common.data.neigh_list, 0, sizeof(bts->si_common.data.neigh_list)); break; default: break; } bts->neigh_list_manual_mode = mode; return CMD_SUCCESS; } DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, "neighbor-list (add|del) arfcn <0-1023>", "Neighbor List\n" "Add to manual neighbor list\n" "Delete from manual neighbor list\n" "ARFCN of neighbor\n" "ARFCN of neighbor\n") { struct gsm_bts *bts = vty->index; struct bitvec *bv = &bts->si_common.neigh_list; uint16_t arfcn = atoi(argv[1]); if (!bts->neigh_list_manual_mode) { vty_out(vty, "%% Cannot configure neighbor list in " "automatic mode%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "add")) bitvec_set_bit_pos(bv, arfcn, 1); else bitvec_set_bit_pos(bv, arfcn, 0); return CMD_SUCCESS; } /* help text should be kept in sync with EARFCN_*_INVALID defines */ DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd, "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> " "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>", "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n" "EARFCN of neighbor\n" "EARFCN of neighbor\n" "threshold high bits\n" "threshold high bits\n" "threshold low bits\n" "threshold low bits (32 means NA)\n" "priority\n" "priority (8 means NA)\n" "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n" "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n") { struct gsm_bts *bts = vty->index; struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; uint16_t arfcn = atoi(argv[0]); uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]), prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]); int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas); switch (r) { case 1: vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s", thresh_hi, VTY_NEWLINE); break; case EARFCN_THRESH_LOW_INVALID: vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s", thresh_lo, VTY_NEWLINE); break; case EARFCN_QRXLV_INVALID + 1: vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s", qrx, VTY_NEWLINE); break; case EARFCN_PRIO_INVALID: vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s", prio, VTY_NEWLINE); break; default: if (r < 0) { vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE); return CMD_WARNING; } } if (si2q_num(bts) <= SI2Q_MAX_NUM) return CMD_SUCCESS; vty_out(vty, "Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s", bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE); osmo_earfcn_del(e, arfcn); return CMD_WARNING; } DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd, "si2quater neighbor-list del earfcn <0-65535>", "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" "Delete from SI2quater manual neighbor list\n" "EARFCN of neighbor\n" "EARFCN\n") { struct gsm_bts *bts = vty->index; struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; uint16_t arfcn = atoi(argv[0]); int r = osmo_earfcn_del(e, arfcn); if (r < 0) { vty_out(vty, "Unable to delete arfcn %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd, "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>", "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n" "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n" "diversity bit\n") { struct gsm_bts *bts = vty->index; uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]); switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) { case -ENOMEM: vty_out(vty, "Unable to add UARFCN: max number of UARFCNs (%u) reached%s", MAX_EARFCN_LIST, VTY_NEWLINE); return CMD_WARNING; case -ENOSPC: vty_out(vty, "Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s", arfcn, scramble, VTY_NEWLINE); return CMD_WARNING; case -EADDRINUSE: vty_out(vty, "Unable to add UARFCN: (%u, %u) is already added%s", arfcn, scramble, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd, "si2quater neighbor-list del uarfcn <0-16383> <0-511>", "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" "Delete from SI2quater manual neighbor list\n" "UARFCN of neighbor\n" "UARFCN\n" "scrambling code\n") { struct gsm_bts *bts = vty->index; if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) { vty_out(vty, "Unable to delete uarfcn: pair not found%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, "si5 neighbor-list (add|del) arfcn <0-1023>", "SI5 Neighbor List\n" "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" "ARFCN of neighbor\n") { struct gsm_bts *bts = vty->index; struct bitvec *bv = &bts->si_common.si5_neigh_list; uint16_t arfcn = atoi(argv[1]); if (!bts->neigh_list_manual_mode) { vty_out(vty, "%% Cannot configure neighbor list in " "automatic mode%s", VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[0], "add")) bitvec_set_bit_pos(bv, arfcn, 1); else bitvec_set_bit_pos(bv, arfcn, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd, "pcu-socket PATH", "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n" "Path in the file system for the unix-domain PCU socket\n") { struct gsm_bts *bts = vty->index; int rc; osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]); pcu_sock_exit(bts); rc = pcu_sock_init(bts->pcu_sock_path, bts); if (rc < 0) { vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s", bts->pcu_sock_path, bts->nr, VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_bts_acc_ramping, cfg_bts_acc_ramping_cmd, "access-control-class-ramping", "Enable Access Control Class ramping\n") { struct gsm_bts *bts = vty->index; if (!acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_set_enabled(&bts->acc_ramp, true); /* * ACC ramping takes effect either when the BTS reconnects RSL, * or when RF administrative state changes to 'unlocked'. */ return CMD_SUCCESS; } DEFUN(cfg_bts_no_acc_ramping, cfg_bts_no_acc_ramping_cmd, "no access-control-class-ramping", NO_STR "Disable Access Control Class ramping\n") { struct gsm_bts *bts = vty->index; if (acc_ramp_is_enabled(&bts->acc_ramp)) { acc_ramp_abort(&bts->acc_ramp); acc_ramp_set_enabled(&bts->acc_ramp, false); gsm_bts_set_system_infos(bts); } return CMD_SUCCESS; } DEFUN(cfg_bts_acc_ramping_step_interval, cfg_bts_acc_ramping_step_interval_cmd, "access-control-class-ramping-step-interval (<" OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MIN) "-" OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_INTERVAL_MAX) ">|dynamic)", "Configure Access Control Class ramping step interval\n" "Set a fixed step interval (in seconds)\n" "Use dynamic step interval based on BTS channel load\n") { struct gsm_bts *bts = vty->index; bool dynamic = (strcmp(argv[0], "dynamic") == 0); int error; if (dynamic) { acc_ramp_set_step_interval_dynamic(&bts->acc_ramp); return CMD_SUCCESS; } error = acc_ramp_set_step_interval(&bts->acc_ramp, atoi(argv[0])); if (error != 0) { if (error == -ERANGE) vty_out(vty, "Unable to set ACC ramp step interval: value out of range%s", VTY_NEWLINE); else vty_out(vty, "Unable to set ACC ramp step interval: unknown error%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(cfg_bts_acc_ramping_step_size, cfg_bts_acc_ramping_step_size_cmd, "access-control-class-ramping-step-size (<" OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MIN) "-" OSMO_STRINGIFY_VAL(ACC_RAMP_STEP_SIZE_MAX) ">)", "Configure Access Control Class ramping step size\n" "Set the number of Access Control Classes to enable per ramping step\n") { struct gsm_bts *bts = vty->index; int error; error = acc_ramp_set_step_size(&bts->acc_ramp, atoi(argv[0])); if (error != 0) { if (error == -ERANGE) vty_out(vty, "Unable to set ACC ramp step size: value out of range%s", VTY_NEWLINE); else vty_out(vty, "Unable to set ACC ramp step size: unknown error%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" DEFUN(cfg_bts_excl_rf_lock, cfg_bts_excl_rf_lock_cmd, "rf-lock-exclude", EXCL_RFLOCK_STR) { struct gsm_bts *bts = vty->index; bts->excl_from_rf_lock = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_no_excl_rf_lock, cfg_bts_no_excl_rf_lock_cmd, "no rf-lock-exclude", NO_STR EXCL_RFLOCK_STR) { struct gsm_bts *bts = vty->index; bts->excl_from_rf_lock = 0; return CMD_SUCCESS; } #define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n" DEFUN(cfg_bts_force_comb_si, cfg_bts_force_comb_si_cmd, "force-combined-si", FORCE_COMB_SI_STR) { struct gsm_bts *bts = vty->index; bts->force_combined_si = 1; return CMD_SUCCESS; } DEFUN(cfg_bts_no_force_comb_si, cfg_bts_no_force_comb_si_cmd, "no force-combined-si", NO_STR FORCE_COMB_SI_STR) { struct gsm_bts *bts = vty->index; bts->force_combined_si = 0; return CMD_SUCCESS; } static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) { struct gsm_bts *bts = vty->index; struct bts_codec_conf *codec = &bts->codec; int i; codec->hr = 0; codec->efr = 0; codec->amr = 0; for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "hr")) codec->hr = 1; if (!strcmp(argv[i], "efr")) codec->efr = 1; if (!strcmp(argv[i], "amr")) codec->amr = 1; } } #define CODEC_PAR_STR " (hr|efr|amr)" #define CODEC_HELP_STR "Half Rate\n" \ "Enhanced Full Rate\nAdaptive Multirate\n" DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd, "codec-support fr", "Codec Support settings\nFullrate\n") { _get_codec_from_arg(vty, 0, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, "codec-support fr" CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR) { _get_codec_from_arg(vty, 1, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 2, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 3, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, "Codec Support settings\nFullrate\n" CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) { _get_codec_from_arg(vty, 4, argv); return CMD_SUCCESS; } DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd, "depends-on-bts <0-255>", "This BTS can only be started if another one is up\n" BTS_NR_STR) { struct gsm_bts *bts = vty->index; struct gsm_bts *other_bts; int dep = atoi(argv[0]); if (!is_ipaccess_bts(bts)) { vty_out(vty, "This feature is only available for IP systems.%s", VTY_NEWLINE); return CMD_WARNING; } other_bts = gsm_bts_num(bts->network, dep); if (!other_bts || !is_ipaccess_bts(other_bts)) { vty_out(vty, "This feature is only available for IP systems.%s", VTY_NEWLINE); return CMD_WARNING; } if (dep >= bts->nr) { vty_out(vty, "%%Need to depend on an already declared unit.%s", VTY_NEWLINE); return CMD_WARNING; } bts_depend_mark(bts, dep); return CMD_SUCCESS; } DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd, "depeneds-on-bts <0-255>", NO_STR "This BTS can only be started if another one is up\n" BTS_NR_STR) { struct gsm_bts *bts = vty->index; int dep = atoi(argv[0]); bts_depend_clear(bts, dep); return CMD_SUCCESS; } #define AMR_TEXT "Adaptive Multi Rate settings\n" #define AMR_MODE_TEXT "Codec modes to use with AMR codec\n" #define AMR_START_TEXT "Initial codec to use with AMR\n" \ "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n" #define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n" #define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n" static void get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct gsm48_multi_rate_conf *mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; int i; mr->gsm48_ie[1] = 0; for (i = 0; i < argc; i++) mr->gsm48_ie[1] |= 1 << atoi(argv[i]); mr_conf->icmi = 0; } static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct amr_mode *modes; int i; modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; for (i = 0; i < argc - 1; i++) modes[i].threshold = atoi(argv[i + 1]); } static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct amr_mode *modes; int i; modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; for (i = 0; i < argc - 1; i++) modes[i].hysteresis = atoi(argv[i + 1]); } static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full) { struct gsm_bts *bts = vty->index; struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; struct gsm48_multi_rate_conf *mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; int num = 0, i; for (i = 0; i < ((full) ? 8 : 6); i++) { if ((mr->gsm48_ie[1] & (1 << i))) { num++; } } if (argv[0][0] == 'a' || num == 0) mr_conf->icmi = 0; else { mr_conf->icmi = 1; if (num < atoi(argv[0])) mr_conf->smod = num - 1; else mr_conf->smod = atoi(argv[0]) - 1; } } #define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)" #define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \ "10,2k\n12,2k\n" #define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)" #define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" #define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n" #define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n" DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 1, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd, "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, AMR_TEXT "Full Rate\n" AMR_MODE_TEXT AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) { get_amr_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd, "amr tch-f start-mode (auto|1|2|3|4)", AMR_TEXT "Full Rate\n" AMR_START_TEXT) { get_amr_start_from_arg(vty, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd, "amr tch-f threshold (ms|bts) <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd, "amr tch-f threshold (ms|bts) <0-63> <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd, "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>", AMR_TEXT "Full Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd, "amr tch-f hysteresis (ms|bts) <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 2, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd, "amr tch-f hysteresis (ms|bts) <0-15> <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 3, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd, "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>", AMR_TEXT "Full Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 4, argv, 1); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 1, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd, "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, AMR_TEXT "Half Rate\n" AMR_MODE_TEXT AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) { get_amr_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd, "amr tch-h start-mode (auto|1|2|3|4)", AMR_TEXT "Half Rate\n" AMR_START_TEXT) { get_amr_start_from_arg(vty, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd, "amr tch-h threshold (ms|bts) <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd, "amr tch-h threshold (ms|bts) <0-63> <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd, "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>", AMR_TEXT "Half Rate\n" AMR_TH_TEXT AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) { get_amr_th_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd, "amr tch-h hysteresis (ms|bts) <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 2, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd, "amr tch-h hysteresis (ms|bts) <0-15> <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 3, argv, 0); return CMD_SUCCESS; } DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>", AMR_TEXT "Half Rate\n" AMR_HY_TEXT AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) { get_amr_hy_from_arg(vty, 4, argv, 0); return CMD_SUCCESS; } #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ DEFUN(cfg_trx, cfg_trx_cmd, "trx <0-255>", TRX_TEXT "Select a TRX to configure") { int trx_nr = atoi(argv[0]); struct gsm_bts *bts = vty->index; struct gsm_bts_trx *trx; if (trx_nr > bts->num_trx) { vty_out(vty, "%% The next unused TRX number in this BTS is %u%s", bts->num_trx, VTY_NEWLINE); return CMD_WARNING; } else if (trx_nr == bts->num_trx) { /* we need to allocate a new one */ trx = gsm_bts_trx_alloc(bts); } else trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) return CMD_WARNING; vty->index = trx; vty->index_sub = &trx->description; vty->node = TRX_NODE; return CMD_SUCCESS; } DEFUN(cfg_trx_arfcn, cfg_trx_arfcn_cmd, "arfcn <0-1023>", "Set the ARFCN for this TRX\n" "Absolute Radio Frequency Channel Number\n") { int arfcn = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; /* FIXME: check if this ARFCN is supported by this TRX */ trx->arfcn = arfcn; /* FIXME: patch ARFCN into SYSTEM INFORMATION */ /* FIXME: use OML layer to update the ARFCN */ /* FIXME: use RSL layer to update SYSTEM INFORMATION */ return CMD_SUCCESS; } DEFUN(cfg_trx_nominal_power, cfg_trx_nominal_power_cmd, "nominal power <0-100>", "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n") { struct gsm_bts_trx *trx = vty->index; trx->nominal_power = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_trx_max_power_red, cfg_trx_max_power_red_cmd, "max_power_red <0-100>", "Reduction of maximum BS RF Power (relative to nominal power)\n" "Reduction of maximum BS RF Power in dB\n") { int maxpwr_r = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; int upper_limit = 24; /* default 12.21 max power red. */ /* FIXME: check if our BTS type supports more than 12 */ if (maxpwr_r < 0 || maxpwr_r > upper_limit) { vty_out(vty, "%% Power %d dB is not in the valid range%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } if (maxpwr_r & 1) { vty_out(vty, "%% Power %d dB is not an even value%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } trx->max_power_red = maxpwr_r; /* FIXME: make sure we update this using OML */ return CMD_SUCCESS; } DEFUN(cfg_trx_rsl_e1, cfg_trx_rsl_e1_cmd, "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "RSL Parameters\n" "E1/T1 interface to be used for RSL\n" "E1/T1 interface to be used for RSL\n" "E1/T1 Line Number to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Sub-slot to be used for RSL\n" "E1/T1 Sub-slot 0 is to be used for RSL\n" "E1/T1 Sub-slot 1 is to be used for RSL\n" "E1/T1 Sub-slot 2 is to be used for RSL\n" "E1/T1 Sub-slot 3 is to be used for RSL\n" "E1/T1 full timeslot is to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } DEFUN(cfg_trx_rsl_e1_tei, cfg_trx_rsl_e1_tei_cmd, "rsl e1 tei <0-63>", "RSL Parameters\n" "Set the TEI to be used for RSL\n" "Set the TEI to be used for RSL\n" "TEI to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; trx->rsl_tei = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_trx_rf_locked, cfg_trx_rf_locked_cmd, "rf_locked (0|1)", "Set or unset the RF Locking (Turn off RF of the TRX)\n" "TRX is NOT RF locked (active)\n" "TRX is RF locked (turned off)\n") { int locked = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; gsm_trx_lock_rf(trx, locked, "vty"); return CMD_SUCCESS; } /* per TS configuration */ DEFUN(cfg_ts, cfg_ts_cmd, "timeslot <0-7>", "Select a Timeslot to configure\n" "Timeslot number\n") { int ts_nr = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; struct gsm_bts_trx_ts *ts; if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", TRX_NR_TS, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; vty->index = ts; vty->node = TS_NODE; return CMD_SUCCESS; } DEFUN(cfg_ts_pchan, cfg_ts_pchan_cmd, "phys_chan_config PCHAN", /* dynamically generated! */ "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) return CMD_WARNING; ts->pchan = pchanc; return CMD_SUCCESS; } /* used for backwards compatibility with old config files that still * have uppercase pchan type names */ DEFUN_HIDDEN(cfg_ts_pchan_compat, cfg_ts_pchan_compat_cmd, "phys_chan_config PCHAN", "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) { vty_out(vty, "Unknown physical channel name '%s'%s", argv[0], VTY_NEWLINE); return CMD_ERR_NO_MATCH; } ts->pchan = pchanc; return CMD_SUCCESS; } DEFUN(cfg_ts_tsc, cfg_ts_tsc_cmd, "training_sequence_code <0-7>", "Training Sequence Code of the Timeslot\n" "TSC\n") { struct gsm_bts_trx_ts *ts = vty->index; if (!osmo_bts_has_feature(&ts->trx->bts->model->features, BTS_FEAT_MULTI_TSC)) { vty_out(vty, "%% This BTS does not support a TSC != BCC, " "falling back to BCC%s", VTY_NEWLINE); ts->tsc = -1; return CMD_WARNING; } ts->tsc = atoi(argv[0]); return CMD_SUCCESS; } #define HOPPING_STR "Configure frequency hopping\n" DEFUN(cfg_ts_hopping, cfg_ts_hopping_cmd, "hopping enabled (0|1)", HOPPING_STR "Enable or disable frequency hopping\n" "Disable frequency hopping\n" "Enable frequency hopping\n") { struct gsm_bts_trx_ts *ts = vty->index; int enabled = atoi(argv[0]); if (enabled && !osmo_bts_has_feature(&ts->trx->bts->model->features, BTS_FEAT_HOPPING)) { vty_out(vty, "BTS model does not support hopping%s", VTY_NEWLINE); return CMD_WARNING; } ts->hopping.enabled = enabled; return CMD_SUCCESS; } DEFUN(cfg_ts_hsn, cfg_ts_hsn_cmd, "hopping sequence-number <0-63>", HOPPING_STR "Which hopping sequence to use for this channel\n" "Hopping Sequence Number (HSN)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.hsn = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_ts_maio, cfg_ts_maio_cmd, "hopping maio <0-63>", HOPPING_STR "Which hopping MAIO to use for this channel\n" "Mobile Allocation Index Offset (MAIO)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.maio = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_ts_arfcn_add, cfg_ts_arfcn_add_cmd, "hopping arfcn add <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Add an entry to the hopping ARFCN list\n" "ARFCN\n") { struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); return CMD_SUCCESS; } DEFUN(cfg_ts_arfcn_del, cfg_ts_arfcn_del_cmd, "hopping arfcn del <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") { struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); return CMD_SUCCESS; } DEFUN(cfg_ts_e1_subslot, cfg_ts_e1_subslot_cmd, "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 line connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 sub-slot connected to this on-air timeslot\n" "E1/T1 sub-slot 0 connected to this on-air timeslot\n" "E1/T1 sub-slot 1 connected to this on-air timeslot\n" "E1/T1 sub-slot 2 connected to this on-air timeslot\n" "E1/T1 sub-slot 3 connected to this on-air timeslot\n" "Full E1/T1 timeslot connected to this on-air timeslot\n") { struct gsm_bts_trx_ts *ts = vty->index; parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } int print_counter(struct rate_ctr_group *bsc_ctrs, struct rate_ctr *ctr, const struct rate_ctr_desc *desc, void *data) { struct vty *vty = data; vty_out(vty, "%25s: %10"PRIu64" %s%s", desc->name, ctr->current, desc->description, VTY_NEWLINE); return 0; } void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) { rate_ctr_for_each_counter(net->bsc_ctrs, print_counter, vty); } DEFUN(drop_bts, drop_bts_cmd, "drop bts connection <0-65535> (oml|rsl)", "Debug/Simulation command to drop Abis/IP BTS\n" "Debug/Simulation command to drop Abis/IP BTS\n" "Debug/Simulation command to drop Abis/IP BTS\n" "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n") { struct gsm_network *gsmnet; struct gsm_bts_trx *trx; struct gsm_bts *bts; unsigned int bts_nr; gsmnet = gsmnet_from_vty(vty); bts_nr = atoi(argv[0]); if (bts_nr >= gsmnet->num_bts) { vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", gsmnet->num_bts, bts_nr, VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (!is_ipaccess_bts(bts)) { vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE); return CMD_WARNING; } /* close all connections */ if (strcmp(argv[1], "oml") == 0) { ipaccess_drop_oml(bts); } else if (strcmp(argv[1], "rsl") == 0) { /* close all rsl connections */ llist_for_each_entry(trx, &bts->trx_list, list) { ipaccess_drop_rsl(trx); } } else { vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(restart_bts, restart_bts_cmd, "restart-bts <0-65535>", "Restart ip.access nanoBTS through OML\n" BTS_NR_STR) { struct gsm_network *gsmnet; struct gsm_bts_trx *trx; struct gsm_bts *bts; unsigned int bts_nr; gsmnet = gsmnet_from_vty(vty); bts_nr = atoi(argv[0]); if (bts_nr >= gsmnet->num_bts) { vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", gsmnet->num_bts, bts_nr, VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) { vty_out(vty, "This command only works for ipaccess nanoBTS.%s", VTY_NEWLINE); return CMD_WARNING; } /* go from last TRX to c0 */ llist_for_each_entry_reverse(trx, &bts->trx_list, list) abis_nm_ipaccess_restart(trx); return CMD_SUCCESS; } DEFUN(bts_resend, bts_resend_cmd, "bts <0-255> resend-system-information", "BTS Specific Commands\n" BTS_NR_STR "Re-generate + re-send BCCH SYSTEM INFORMATION\n") { struct gsm_network *gsmnet; struct gsm_bts_trx *trx; struct gsm_bts *bts; unsigned int bts_nr; gsmnet = gsmnet_from_vty(vty); bts_nr = atoi(argv[0]); if (bts_nr >= gsmnet->num_bts) { vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", gsmnet->num_bts, bts_nr, VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(gsmnet, bts_nr); if (!bts) { vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } llist_for_each_entry_reverse(trx, &bts->trx_list, list) gsm_bts_trx_set_system_infos(trx); return CMD_SUCCESS; } DEFUN(smscb_cmd, smscb_cmd_cmd, "bts <0-255> smscb-command <1-4> HEXSTRING", "BTS related commands\n" BTS_NR_STR "SMS Cell Broadcast\n" "Last Valid Block\n" "Hex Encoded SMSCB message (up to 88 octets)\n") { struct gsm_bts *bts; int bts_nr = atoi(argv[0]); int last_block = atoi(argv[1]); struct rsl_ie_cb_cmd_type cb_cmd; uint8_t buf[88]; int rc; bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return CMD_WARNING; } rc = osmo_hexparse(argv[2], buf, sizeof(buf)); if (rc < 0 || rc > sizeof(buf)) { vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); return CMD_WARNING; } cb_cmd.spare = 0; cb_cmd.def_bcast = 0; cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL; switch (last_block) { case 1: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1; break; case 2: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2; break; case 3: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3; break; case 4: cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4; break; default: vty_out(vty, "Error parsing LASTBLOCK%s", VTY_NEWLINE); return CMD_WARNING; } rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc); return CMD_SUCCESS; } /* resolve a gsm_bts_trx_ts basd on the given numeric identifiers */ static struct gsm_bts_trx_ts *vty_get_ts(struct vty *vty, const char *bts_str, const char *trx_str, const char *ts_str) { int bts_nr = atoi(bts_str); int trx_nr = atoi(trx_str); int ts_nr = atoi(ts_str); struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); if (!bts) { vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); return NULL; } trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) { vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); return NULL; } ts = &trx->ts[ts_nr]; return ts; } DEFUN(pdch_act, pdch_act_cmd, "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", BTS_NR_TRX_TS_STR2 "Packet Data Channel\n" "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") { struct gsm_bts_trx_ts *ts; int activate; ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); if (!ts) return CMD_WARNING; if (!is_ipaccess_bts(ts->trx->bts)) { vty_out(vty, "%% This command only works for ipaccess BTS%s", VTY_NEWLINE); return CMD_WARNING; } if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " "mode%s", ts->nr, VTY_NEWLINE); return CMD_WARNING; } if (!strcmp(argv[3], "activate")) activate = 1; else activate = 0; rsl_ipacc_pdch_activate(ts, activate); return CMD_SUCCESS; } /* determine the logical channel type based on the physical channel type */ static int lchan_type_by_pchan(enum gsm_phys_chan_config pchan) { switch (pchan) { case GSM_PCHAN_TCH_F: return GSM_LCHAN_TCH_F; case GSM_PCHAN_TCH_H: return GSM_LCHAN_TCH_H; case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4_CBCH: return GSM_LCHAN_SDCCH; default: return -1; } } /* configure the lchan for a single AMR mode (as specified) */ static int lchan_set_single_amr_mode(struct gsm_lchan *lchan, uint8_t amr_mode) { struct amr_multirate_conf mr; struct gsm48_multi_rate_conf *mr_conf; mr_conf = (struct gsm48_multi_rate_conf *) &mr.gsm48_ie; if (amr_mode > 7) return -1; memset(&mr, 0, sizeof(mr)); mr_conf->ver = 1; /* bit-mask of supported modes, only one bit is set. Reflects * Figure 10.5.2.47a where there are no thershold and only a * single mode */ mr.gsm48_ie[1] = 1 << amr_mode; mr.ms_mode[0].mode = amr_mode; mr.bts_mode[0].mode = amr_mode; /* encode this configuration into the lchan for both uplink and * downlink direction */ gsm48_multirate_config(lchan->mr_ms_lv, &mr, mr.ms_mode); gsm48_multirate_config(lchan->mr_bts_lv, &mr, mr.bts_mode); return 0; } /* Debug/Measurement command to activate a given logical channel * manually in a given mode/codec. This is useful for receiver * performance testing (FER/RBER/...) */ DEFUN(lchan_act, lchan_act_cmd, "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> (activate|deactivate) (hr|fr|efr|amr) [<0-7>]", BTS_NR_TRX_TS_SS_STR2 "Manual Channel Activation (e.g. for BER test)\n" "Manual Channel Deactivation (e.g. for BER test)\n" "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "AMR Mode\n") { struct gsm_bts_trx_ts *ts; struct gsm_lchan *lchan; int ss_nr = atoi(argv[3]); const char *act_str = argv[4]; const char *codec_str = argv[5]; int activate; ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); if (!ts) return CMD_WARNING; lchan = &ts->lchan[ss_nr]; if (!strcmp(act_str, "activate")) activate = 1; else activate = 0; if (ss_nr >= ts_subslots(ts)) { vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); return CMD_WARNING; } if (activate) { int lchan_t; if (lchan->state != LCHAN_S_NONE) { vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE); return CMD_WARNING; } lchan_t = lchan_type_by_pchan(ts->pchan); if (lchan_t < 0) return CMD_WARNING; /* configure the lchan */ lchan->type = lchan_t; lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) lchan->tch_mode = GSM48_CMODE_SPEECH_V1; else if (!strcmp(codec_str, "efr")) lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; else if (!strcmp(codec_str, "amr")) { int amr_mode; if (argc < 7) { vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE); return CMD_WARNING; } amr_mode = atoi(argv[6]); lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; lchan_set_single_amr_mode(lchan, amr_mode); } vty_out(vty, "%% activating lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE); rsl_chan_activate_lchan(lchan, RSL_ACT_TYPE_INITIAL, 0); rsl_ipacc_crcx(lchan); } else { rsl_direct_rf_release(lchan); } return CMD_SUCCESS; } DEFUN(lchan_mdcx, lchan_mdcx_cmd, "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> mdcx A.B.C.D <0-65535>", BTS_NR_TRX_TS_SS_STR2 "Modify RTP Connection\n" "MGW IP Address\n" "MGW UDP Port\n") { struct gsm_bts_trx_ts *ts; struct gsm_lchan *lchan; int ss_nr = atoi(argv[3]); int port = atoi(argv[5]); struct in_addr ia; inet_aton(argv[4], &ia); ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); if (!ts) return CMD_WARNING; lchan = &ts->lchan[ss_nr]; if (ss_nr >= ts_subslots(ts)) { vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); return CMD_WARNING; } vty_out(vty, "%% connecting RTP of %s to %s:%u%s", gsm_lchan_name(lchan), inet_ntoa(ia), port, VTY_NEWLINE); rsl_ipacc_mdcx(lchan, ntohl(ia.s_addr), port, 0); return CMD_SUCCESS; } DEFUN(ctrl_trap, ctrl_trap_cmd, "ctrl-interface generate-trap TRAP VALUE", "Commands related to the CTRL Interface\n" "Generate a TRAP for test purpose\n" "Identity/Name of the TRAP variable\n" "Value of the TRAP variable\n") { struct gsm_network *net = gsmnet_from_vty(vty); ctrl_cmd_send_trap(net->ctrl, argv[0], (char *) argv[1]); return CMD_SUCCESS; } #define NETWORK_STR "Configure the GSM network\n" #define CODE_CMD_STR "Code commands\n" #define NAME_CMD_STR "Name Commands\n" #define NAME_STR "Name to use\n" DEFUN(cfg_net, cfg_net_cmd, "network", NETWORK_STR) { vty->index = gsmnet_from_vty(vty); vty->node = GSMNET_NODE; return CMD_SUCCESS; } DEFUN(cfg_net_ncc, cfg_net_ncc_cmd, "network country code <1-999>", "Set the GSM network country code\n" "Country commands\n" CODE_CMD_STR "Network Country Code to use\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); uint16_t mcc; if (osmo_mcc_from_str(argv[0], &mcc)) { vty_out(vty, "%% Error decoding MCC: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } gsmnet->plmn.mcc = mcc; return CMD_SUCCESS; } DEFUN(cfg_net_mnc, cfg_net_mnc_cmd, "mobile network code <0-999>", "Set the GSM mobile network code\n" "Network Commands\n" CODE_CMD_STR "Mobile Network Code to use\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); uint16_t mnc; bool mnc_3_digits; if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) { vty_out(vty, "%% Error decoding MNC: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } gsmnet->plmn.mnc = mnc; gsmnet->plmn.mnc_3_digits = mnc_3_digits; return CMD_SUCCESS; } DEFUN(cfg_net_encryption, cfg_net_encryption_cmd, "encryption a5 <0-3> [<0-3>] [<0-3>] [<0-3>]", "Encryption options\n" "GSM A5 Air Interface Encryption\n" "A5/n Algorithm Number\n" "A5/n Algorithm Number\n" "A5/n Algorithm Number\n" "A5/n Algorithm Number\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); unsigned int i; gsmnet->a5_encryption_mask = 0; for (i = 0; i < argc; i++) gsmnet->a5_encryption_mask |= (1 << atoi(argv[i])); return CMD_SUCCESS; } DEFUN_DEPRECATED(cfg_net_dyn_ts_allow_tch_f, cfg_net_dyn_ts_allow_tch_f_cmd, "dyn_ts_allow_tch_f (0|1)", "Allow or disallow allocating TCH/F on TCH_F_TCH_H_PDCH timeslots\n" "Disallow TCH/F on TCH_F_TCH_H_PDCH (default)\n" "Allow TCH/F on TCH_F_TCH_H_PDCH\n") { struct gsm_network *gsmnet = gsmnet_from_vty(vty); gsmnet->dyn_ts_allow_tch_f = atoi(argv[0]) ? true : false; vty_out(vty, "%% dyn_ts_allow_tch_f is deprecated, rather use msc/codec-list to pick codecs%s", VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_net_timezone, cfg_net_timezone_cmd, "timezone <-19-19> (0|15|30|45)", "Set the Timezone Offset of the network\n" "Timezone offset (hours)\n" "Timezone offset (00 minutes)\n" "Timezone offset (15 minutes)\n" "Timezone offset (30 minutes)\n" "Timezone offset (45 minutes)\n" ) { struct gsm_network *net = vty->index; int tzhr = atoi(argv[0]); int tzmn = atoi(argv[1]); net->tz.hr = tzhr; net->tz.mn = tzmn; net->tz.dst = 0; net->tz.override = 1; return CMD_SUCCESS; } DEFUN(cfg_net_timezone_dst, cfg_net_timezone_dst_cmd, "timezone <-19-19> (0|15|30|45) <0-2>", "Set the Timezone Offset of the network\n" "Timezone offset (hours)\n" "Timezone offset (00 minutes)\n" "Timezone offset (15 minutes)\n" "Timezone offset (30 minutes)\n" "Timezone offset (45 minutes)\n" "DST offset (hours)\n" ) { struct gsm_network *net = vty->index; int tzhr = atoi(argv[0]); int tzmn = atoi(argv[1]); int tzdst = atoi(argv[2]); net->tz.hr = tzhr; net->tz.mn = tzmn; net->tz.dst = tzdst; net->tz.override = 1; return CMD_SUCCESS; } DEFUN(cfg_net_no_timezone, cfg_net_no_timezone_cmd, "no timezone", NO_STR "Disable network timezone override, use system tz\n") { struct gsm_network *net = vty->index; net->tz.override = 0; return CMD_SUCCESS; } DEFUN(cfg_net_per_loc_upd, cfg_net_per_loc_upd_cmd, "periodic location update <6-1530>", "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval in Minutes\n") { struct gsm_network *net = vty->index; net->t3212 = atoi(argv[0]) / 6; return CMD_SUCCESS; } DEFUN(cfg_net_no_per_loc_upd, cfg_net_no_per_loc_upd_cmd, "no periodic location update", NO_STR "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n" "Periodic Location Updating Interval\n") { struct gsm_network *net = vty->index; net->t3212 = 0; return CMD_SUCCESS; } #define MEAS_FEED_STR "Measurement Report export\n" DEFUN(cfg_net_meas_feed_dest, cfg_net_meas_feed_dest_cmd, "meas-feed destination ADDR <0-65535>", MEAS_FEED_STR "Where to forward Measurement Report feeds\n" "address or hostname\n" "port number\n") { int rc; const char *host = argv[0]; uint16_t port = atoi(argv[1]); rc = meas_feed_cfg_set(host, port); if (rc < 0) return CMD_WARNING; return CMD_SUCCESS; } DEFUN(cfg_net_meas_feed_scenario, cfg_net_meas_feed_scenario_cmd, "meas-feed scenario NAME", MEAS_FEED_STR "Set a name to include in the Measurement Report feeds\n" "Name string, up to 31 characters\n") { meas_feed_scenario_set(argv[0]); return CMD_SUCCESS; } extern int bsc_vty_init_extra(void); int bsc_vty_init(struct gsm_network *network) { cfg_ts_pchan_cmd.string = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_names, "phys_chan_config (", "|", ")", VTY_DO_LOWER); cfg_ts_pchan_cmd.doc = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_descs, "Physical Channel Combination\n", "\n", "", 0); cfg_bts_type_cmd.string = vty_cmd_string_from_valstr(tall_bsc_ctx, bts_type_names, "type (", "|", ")", VTY_DO_LOWER); cfg_bts_type_cmd.doc = vty_cmd_string_from_valstr(tall_bsc_ctx, bts_type_descs, "BTS Vendor/Type\n", "\n", "", 0); OSMO_ASSERT(vty_global_gsm_network == NULL); vty_global_gsm_network = network; osmo_stats_vty_add_cmds(); install_element(CONFIG_NODE, &cfg_net_cmd); install_node(&net_node, config_write_net); install_element(GSMNET_NODE, &cfg_net_ncc_cmd); install_element(GSMNET_NODE, &cfg_net_mnc_cmd); install_element(GSMNET_NODE, &cfg_net_encryption_cmd); install_element(GSMNET_NODE, &cfg_net_timezone_cmd); install_element(GSMNET_NODE, &cfg_net_timezone_dst_cmd); install_element(GSMNET_NODE, &cfg_net_no_timezone_cmd); install_element(GSMNET_NODE, &cfg_net_per_loc_upd_cmd); install_element(GSMNET_NODE, &cfg_net_no_per_loc_upd_cmd); install_element(GSMNET_NODE, &cfg_net_dyn_ts_allow_tch_f_cmd); install_element(GSMNET_NODE, &cfg_net_meas_feed_dest_cmd); install_element(GSMNET_NODE, &cfg_net_meas_feed_scenario_cmd); install_element_ve(&bsc_show_net_cmd); install_element_ve(&show_bts_cmd); install_element_ve(&show_trx_cmd); install_element_ve(&show_ts_cmd); install_element_ve(&show_lchan_cmd); install_element_ve(&show_lchan_summary_cmd); install_element_ve(&show_subscr_conn_cmd); install_element_ve(&handover_any_cmd); install_element_ve(&assignment_any_cmd); install_element_ve(&show_paging_cmd); install_element_ve(&show_paging_group_cmd); logging_vty_add_cmds(NULL); osmo_talloc_vty_add_cmds(); install_element(GSMNET_NODE, &cfg_net_neci_cmd); install_element(GSMNET_NODE, &cfg_net_T3101_cmd); install_element(GSMNET_NODE, &cfg_net_T3103_cmd); install_element(GSMNET_NODE, &cfg_net_T3105_cmd); install_element(GSMNET_NODE, &cfg_net_T3107_cmd); install_element(GSMNET_NODE, &cfg_net_T3109_cmd); install_element(GSMNET_NODE, &cfg_net_T3111_cmd); install_element(GSMNET_NODE, &cfg_net_T3113_cmd); install_element(GSMNET_NODE, &cfg_net_T3115_cmd); install_element(GSMNET_NODE, &cfg_net_T3117_cmd); install_element(GSMNET_NODE, &cfg_net_T3119_cmd); install_element(GSMNET_NODE, &cfg_net_T3122_cmd); install_element(GSMNET_NODE, &cfg_net_T3141_cmd); install_element(GSMNET_NODE, &cfg_net_T10_cmd); install_element(GSMNET_NODE, &cfg_net_T7_cmd); install_element(GSMNET_NODE, &cfg_net_T8_cmd); install_element(GSMNET_NODE, &cfg_net_T101_cmd); install_element(GSMNET_NODE, &cfg_net_dtx_cmd); install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); /* See also handover commands added on net level from handover_vty.c */ install_element(GSMNET_NODE, &cfg_bts_cmd); install_node(&bts_node, config_write_bts); install_element(BTS_NODE, &cfg_bts_type_cmd); install_element(BTS_NODE, &cfg_description_cmd); install_element(BTS_NODE, &cfg_no_description_cmd); install_element(BTS_NODE, &cfg_bts_band_cmd); install_element(BTS_NODE, &cfg_bts_ci_cmd); install_element(BTS_NODE, &cfg_bts_dtxu_cmd); install_element(BTS_NODE, &cfg_bts_dtxd_cmd); install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd); install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd); install_element(BTS_NODE, &cfg_bts_lac_cmd); install_element(BTS_NODE, &cfg_bts_tsc_cmd); install_element(BTS_NODE, &cfg_bts_bsic_cmd); install_element(BTS_NODE, &cfg_bts_unit_id_cmd); install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd); install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd); install_element(BTS_NODE, &cfg_bts_stream_id_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); install_element(BTS_NODE, &cfg_bts_challoc_cmd); install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd); install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd); install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd); install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd); install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd); install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd); install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd); install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd); install_element(BTS_NODE, &cfg_bts_penalty_time_cmd); install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd); install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd); install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd); install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd); install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd); install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd); install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd); install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); install_element(BTS_NODE, &cfg_bts_pag_free_cmd); install_element(BTS_NODE, &cfg_bts_si_mode_cmd); install_element(BTS_NODE, &cfg_bts_si_static_cmd); install_element(BTS_NODE, &cfg_bts_early_cm_cmd); install_element(BTS_NODE, &cfg_bts_early_cm_3g_cmd); install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); install_element(BTS_NODE, &cfg_bts_neigh_cmd); install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd); install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd); install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd); install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd); install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd); install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd); install_element(BTS_NODE, &cfg_bts_codec0_cmd); install_element(BTS_NODE, &cfg_bts_codec1_cmd); install_element(BTS_NODE, &cfg_bts_codec2_cmd); install_element(BTS_NODE, &cfg_bts_codec3_cmd); install_element(BTS_NODE, &cfg_bts_codec4_cmd); install_element(BTS_NODE, &cfg_bts_depends_on_cmd); install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd); install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd); install_element(BTS_NODE, &cfg_bts_acc_ramping_cmd); install_element(BTS_NODE, &cfg_bts_no_acc_ramping_cmd); install_element(BTS_NODE, &cfg_bts_acc_ramping_step_interval_cmd); install_element(BTS_NODE, &cfg_bts_acc_ramping_step_size_cmd); /* See also handover commands added on bts level from handover_vty.c */ install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); install_element(TRX_NODE, &cfg_trx_arfcn_cmd); install_element(TRX_NODE, &cfg_description_cmd); install_element(TRX_NODE, &cfg_no_description_cmd); install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); install_element(TRX_NODE, &cfg_ts_cmd); install_node(&ts_node, dummy_config_write); install_element(TS_NODE, &cfg_ts_pchan_cmd); install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); install_element(TS_NODE, &cfg_ts_tsc_cmd); install_element(TS_NODE, &cfg_ts_hopping_cmd); install_element(TS_NODE, &cfg_ts_hsn_cmd); install_element(TS_NODE, &cfg_ts_maio_cmd); install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); install_element(ENABLE_NODE, &drop_bts_cmd); install_element(ENABLE_NODE, &restart_bts_cmd); install_element(ENABLE_NODE, &bts_resend_cmd); install_element(ENABLE_NODE, &pdch_act_cmd); install_element(ENABLE_NODE, &lchan_act_cmd); install_element(ENABLE_NODE, &lchan_mdcx_cmd); install_element(ENABLE_NODE, &handover_subscr_conn_cmd); install_element(ENABLE_NODE, &assignment_subscr_conn_cmd); install_element(ENABLE_NODE, &smscb_cmd_cmd); install_element(ENABLE_NODE, &ctrl_trap_cmd); abis_nm_vty_init(); abis_om2k_vty_init(); e1inp_vty_init(); osmo_fsm_vty_add_cmds(); ho_vty_init(); bsc_vty_init_extra(); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/bts_ericsson_rbs2000.c000066400000000000000000000135301332665256100215720ustar00rootroot00000000000000/* Ericsson RBS-2xxx specific code */ /* (C) 2011 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 static void bootstrap_om_bts(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); /* FIXME: this is global init, not bootstrapping */ abis_om2k_bts_init(bts); abis_om2k_trx_init(bts->c0); /* TODO: Should we wait for a Failure report? */ om2k_bts_fsm_start(bts); } static void bootstrap_om_trx(struct gsm_bts_trx *trx) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", trx->bts->nr, trx->nr); /* FIXME */ } static int shutdown_om(struct gsm_bts *bts) { gsm_bts_mark_all_ts_uninitialized(bts); /* FIXME */ return 0; } /* Tell LAPD to start start the SAP (send SABM requests) for all signalling * timeslots in this line */ static void start_sabm_in_line(struct e1inp_line *line, int start) { struct e1inp_sign_link *link; int i; for (i = 0; i < ARRAY_SIZE(line->ts); i++) { struct e1inp_ts *ts = &line->ts[i]; if (ts->type != E1INP_TS_TYPE_SIGN) continue; llist_for_each_entry(link, &ts->sign.sign_links, list) { if (!ts->lapd) continue; lapd_instance_set_profile(ts->lapd, &lapd_profile_abis_ericsson); if (start) lapd_sap_start(ts->lapd, link->tei, link->sapi); else lapd_sap_stop(ts->lapd, link->tei, link->sapi); } } } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_RBS2000) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; struct e1inp_ts *e1i_ts; if (subsys != SS_L_INPUT) return 0; LOGP(DNM, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, get_value_string(e1inp_signal_names, signal)); switch (signal) { case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) break; if (isd->tei == isd->trx->bts->oml_tei) bootstrap_om_bts(isd->trx->bts); else bootstrap_om_trx(isd->trx); break; } break; case S_L_INP_TEI_DN: if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) break; LOGP(DNM, LOGL_NOTICE, "Line-%u TS-%u TEI-%u SAPI-%u: Link " "Lost for Ericsson RBS2000. Re-starting DL Establishment\n", isd->line->num, isd->ts_nr, isd->tei, isd->sapi); /* Some datalink for a given TEI/SAPI went down, try to re-start it */ e1i_ts = &isd->line->ts[isd->ts_nr-1]; OSMO_ASSERT(e1i_ts->type == E1INP_TS_TYPE_SIGN); lapd_sap_start(e1i_ts->lapd, isd->tei, isd->sapi); break; case S_L_INP_LINE_INIT: case S_L_INP_LINE_NOALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD") && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) break; start_sabm_in_line(isd->line, 1); break; case S_L_INP_LINE_ALARM: if (strcasecmp(isd->line->driver->name, "DAHDI") && strcasecmp(isd->line->driver->name, "MISDN_LAPD") && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) break; start_sabm_in_line(isd->line, 0); break; } return 0; } static void config_write_bts(struct vty *vty, struct gsm_bts *bts) { abis_om2k_config_write_bts(vty, bts); } static int bts_model_rbs2k_start(struct gsm_network *net); static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static bool bts_model_rbs2k_is_ts_ready(const struct gsm_bts_trx_ts *ts) { return ts && ts->mo.nm_state.operational == NM_OPSTATE_ENABLED; } static struct gsm_bts_model model_rbs2k = { .type = GSM_BTS_TYPE_RBS2000, .name = "rbs2000", .start = bts_model_rbs2k_start, .oml_rcvmsg = &abis_om2k_rcvmsg, .oml_is_ts_ready = bts_model_rbs2k_is_ts_ready, .config_write_bts = &config_write_bts, .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, }; static int bts_model_rbs2k_start(struct gsm_network *net) { model_rbs2k.features.data = &model_rbs2k._features_data[0]; model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_GPRS); osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_EGPRS); osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_HOPPING); osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_HSCSD); osmo_bts_set_feature(&model_rbs2k.features, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); return 0; } int bts_model_rbs2k_init(void) { return gsm_bts_model_register(&model_rbs2k); } osmo-bsc-1.3.0/src/osmo-bsc/bts_init.c000066400000000000000000000017221332665256100175400ustar00rootroot00000000000000/* (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 int bts_init(void) { bts_model_bs11_init(); bts_model_rbs2k_init(); bts_model_nanobts_init(); bts_model_nokia_site_init(); bts_model_sysmobts_init(); /* Your new BTS here. */ return 0; } osmo-bsc-1.3.0/src/osmo-bsc/bts_ipaccess_nanobts.c000066400000000000000000000417471332665256100221260ustar00rootroot00000000000000/* ip.access nanoBTS specific code */ /* (C) 2009-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int bts_model_nanobts_start(struct gsm_network *net); static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); static char *get_oml_status(const struct gsm_bts *bts) { if (bts->oml_link) return all_trx_rsl_connected_unlocked(bts) ? "connected" : "degraded"; return "disconnected"; } static bool oml_is_ts_ready(const struct gsm_bts_trx_ts *ts) { return ts && ts->mo.nm_state.operational == NM_OPSTATE_ENABLED; } struct gsm_bts_model bts_model_nanobts = { .type = GSM_BTS_TYPE_NANOBTS, .name = "nanobts", .start = bts_model_nanobts_start, .oml_rcvmsg = &abis_nm_rcvmsg, .oml_status = &get_oml_status, .oml_is_ts_ready = oml_is_ts_ready, .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, .nm_att_tlvdef = { .def = { /* ip.access specifics */ [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, }, }, }; /* Callback function to be called whenever we get a GSM 12.21 state change event */ static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) { uint8_t obj_class = nsd->obj_class; void *obj = nsd->obj; struct gsm_nm_state *new_state = nsd->new_state; struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; struct gsm_bts_gprs_nsvc *nsvc; struct msgb *msgb; if (!is_ipaccess_bts(nsd->bts)) return 0; /* This event-driven BTS setup is currently only required on nanoBTS */ /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create * endless loop */ if (evt != S_NM_STATECHG_OPER) return 0; switch (obj_class) { case NM_OC_SITE_MANAGER: bts = container_of(obj, struct gsm_bts, site_mgr); if ((new_state->operational == NM_OPSTATE_ENABLED && new_state->availability == NM_AVSTATE_OK) || (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_OFF_LINE)) abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); break; case NM_OC_BTS: bts = obj; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { msgb = nanobts_attr_bts_get(bts); abis_nm_set_bts_attr(bts, msgb->data, msgb->len); msgb_free(msgb); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, 0xff, 0xff, NM_STATE_UNLOCKED); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0xff, 0xff); } break; case NM_OC_CHANNEL: ts = obj; trx = ts->trx; if (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_DEPENDENCY) { enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) { ipaccess_drop_oml(trx->bts); return -1; } abis_nm_chg_adm_state(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, ts->nr, NM_STATE_UNLOCKED); abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, ts->nr); } break; case NM_OC_RADIO_CARRIER: trx = obj; if (new_state->operational == NM_OPSTATE_DISABLED && new_state->availability == NM_AVSTATE_OK) abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, trx->nr, 0xff); break; case NM_OC_GPRS_NSE: bts = container_of(obj, struct gsm_bts, gprs.nse); if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { msgb = nanobts_attr_nse_get(bts); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, 0xff, 0xff, msgb->data, msgb->len); msgb_free(msgb); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0xff, 0xff); } break; case NM_OC_GPRS_CELL: bts = container_of(obj, struct gsm_bts, gprs.cell); if (bts->gprs.mode == BTS_GPRS_NONE) break; if (new_state->availability == NM_AVSTATE_DEPENDENCY) { msgb = nanobts_attr_cell_get(bts); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, 0, 0xff, msgb->data, msgb->len); msgb_free(msgb); abis_nm_opstart(bts, obj_class, bts->bts_nr, 0, 0xff); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, 0, 0xff, NM_STATE_UNLOCKED); abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, 0xff, 0xff, NM_STATE_UNLOCKED); } break; case NM_OC_GPRS_NSVC: nsvc = obj; bts = nsvc->bts; if (bts->gprs.mode == BTS_GPRS_NONE) break; /* We skip NSVC1 since we only use NSVC0 */ if (nsvc->id == 1) break; if ((new_state->availability == NM_AVSTATE_OFF_LINE) || (new_state->availability == NM_AVSTATE_DEPENDENCY)) { msgb = nanobts_attr_nscv_get(bts); abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, nsvc->id, 0xff, msgb->data, msgb->len); msgb_free(msgb); abis_nm_opstart(bts, obj_class, bts->bts_nr, nsvc->id, 0xff); abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, nsvc->id, 0xff, NM_STATE_UNLOCKED); } default: break; } return 0; } /* Callback function to be called every time we receive a 12.21 SW activated report */ static int sw_activ_rep(struct msgb *mb) { struct abis_om_fom_hdr *foh = msgb_l3(mb); struct e1inp_sign_link *sign_link = mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); if (!trx) return -EINVAL; if (!is_ipaccess_bts(trx->bts)) return 0; switch (foh->obj_class) { case NM_OC_BASEB_TRANSC: abis_nm_chg_adm_state(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff, NM_STATE_UNLOCKED); abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff); /* TRX software is active, tell it to initiate RSL Link */ abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip, 3003, trx->rsl_tei); break; case NM_OC_RADIO_CARRIER: { /* * Locking the radio carrier will make it go * offline again and we would come here. The * framework should determine that there was * no change and avoid recursion. * * This code is here to make sure that on start * a TRX remains locked. */ int rc_state = trx->mo.nm_state.administrative; /* Patch ARFCN into radio attribute */ struct msgb *msgb = nanobts_attr_radio_get(trx->bts, trx); abis_nm_set_radio_attr(trx, msgb->data, msgb->len); msgb_free(msgb); abis_nm_chg_adm_state(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff, rc_state); abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, trx->nr, 0xff); break; } } return 0; } static void nm_rx_opstart_ack_chan(struct msgb *oml_msg) { struct gsm_bts_trx_ts *ts; ts = abis_nm_get_ts(oml_msg); if (!ts) /* error already logged in abis_nm_get_ts() */ return; gsm_ts_check_init(ts); } static void nm_rx_opstart_ack(struct msgb *oml_msg) { struct abis_om_fom_hdr *foh = msgb_l3(oml_msg); switch (foh->obj_class) { case NM_OC_CHANNEL: nm_rx_opstart_ack_chan(oml_msg); break; default: break; } } /* Callback function to be called every time we receive a signal from NM */ static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_NM) return 0; switch (signal) { case S_NM_SW_ACTIV_REP: return sw_activ_rep(signal_data); case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: return nm_statechg_event(signal, signal_data); case S_NM_OPSTART_ACK: nm_rx_opstart_ack(signal_data); return 0; default: break; } return 0; } static int bts_model_nanobts_start(struct gsm_network *net) { osmo_signal_unregister_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); osmo_signal_register_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); return 0; } int bts_model_nanobts_init(void) { bts_model_nanobts.features.data = &bts_model_nanobts._features_data[0]; bts_model_nanobts.features.data_len = sizeof(bts_model_nanobts._features_data); osmo_bts_set_feature(&bts_model_nanobts.features, BTS_FEAT_GPRS); osmo_bts_set_feature(&bts_model_nanobts.features, BTS_FEAT_EGPRS); osmo_bts_set_feature(&bts_model_nanobts.features, BTS_FEAT_MULTI_TSC); return gsm_bts_model_register(&bts_model_nanobts); } #define OML_UP 0x0001 #define RSL_UP 0x0002 static struct gsm_bts * find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { if (!is_ipaccess_bts(bts)) continue; if (bts->ip_access.site_id == site_id && bts->ip_access.bts_id == bts_id) return bts; } return NULL; } /* These are exported because they are used by the VTY interface. */ void ipaccess_drop_rsl(struct gsm_bts_trx *trx) { if (!trx->rsl_link) return; LOGP(DLINP, LOGL_NOTICE, "(bts=%d,trx=%d) Dropping RSL link.\n", trx->bts->nr, trx->nr); e1inp_sign_link_destroy(trx->rsl_link); trx->rsl_link = NULL; if (trx->bts->c0 == trx) paging_flush_bts(trx->bts, NULL); } void ipaccess_drop_oml(struct gsm_bts *bts) { struct gsm_bts *rdep_bts; struct gsm_bts_trx *trx; if (!bts->oml_link) return; LOGP(DLINP, LOGL_NOTICE, "(bts=%d) Dropping OML link.\n", bts->nr); e1inp_sign_link_destroy(bts->oml_link); bts->oml_link = NULL; bts->uptime = 0; /* we have issues reconnecting RSL, drop everything. */ llist_for_each_entry(trx, &bts->trx_list, list) ipaccess_drop_rsl(trx); gsm_bts_mark_all_ts_uninitialized(bts); bts->ip_access.flags = 0; /* * Go through the list and see if we are the depndency of a BTS * and then drop the BTS. This can lead to some recursion but it * should be fine in userspace. * The oml_link is serving as recursion anchor for us and * it is set to NULL some lines above. */ llist_for_each_entry(rdep_bts, &bts->network->bts_list, list) { if (!bts_depend_is_depedency(rdep_bts, bts)) continue; LOGP(DLINP, LOGL_NOTICE, "Dropping BTS(%u) due BTS(%u).\n", rdep_bts->nr, bts->nr); ipaccess_drop_oml(rdep_bts); } } /* This function is called once the OML/RSL link becomes up. */ static struct e1inp_sign_link * ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, enum e1inp_sign_type type) { struct gsm_bts *bts; struct ipaccess_unit *dev = unit_data; struct e1inp_sign_link *sign_link = NULL; struct timespec tp; int rc; bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id); if (!bts) { LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for " " %u/%u/%u, disconnecting\n", dev->site_id, dev->bts_id, dev->trx_id); rate_ctr_inc(&bsc_gsmnet->bsc_ctrs->ctr[BSC_CTR_UNKNOWN_UNIT_ID]); return NULL; } DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", dev->site_id, dev->bts_id, dev->trx_id); switch(type) { case E1INP_SIGN_OML: /* remove old OML signal link for this BTS. */ ipaccess_drop_oml(bts); if (!bts_depend_check(bts)) { LOGP(DLINP, LOGL_NOTICE, "Dependency not full-filled for %u/%u/%u\n", dev->site_id, dev->bts_id, dev->trx_id); return NULL; } /* create new OML link. */ sign_link = bts->oml_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], E1INP_SIGN_OML, bts->c0, bts->oml_tei, 0); rc = clock_gettime(CLOCK_MONOTONIC, &tp); bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */ if (!(sign_link->trx->bts->ip_access.flags & OML_UP)) { e1inp_event(sign_link->ts, S_L_INP_TEI_UP, sign_link->tei, sign_link->sapi); sign_link->trx->bts->ip_access.flags |= OML_UP; } break; case E1INP_SIGN_RSL: { struct e1inp_ts *ts; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id); /* no OML link set yet? give up. */ if (!bts->oml_link || !trx) return NULL; /* remove old RSL link for this TRX. */ ipaccess_drop_rsl(trx); /* set new RSL link for this TRX. */ line = bts->oml_link->ts->line; ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1]; e1inp_ts_config_sign(ts, line); sign_link = trx->rsl_link = e1inp_sign_link_create(ts, E1INP_SIGN_RSL, trx, trx->rsl_tei, 0); trx->rsl_link->ts->sign.delay = 0; if (!(sign_link->trx->bts->ip_access.flags & (RSL_UP << sign_link->trx->nr))) { e1inp_event(sign_link->ts, S_L_INP_TEI_UP, sign_link->tei, sign_link->sapi); sign_link->trx->bts->ip_access.flags |= (RSL_UP << sign_link->trx->nr); } break; } default: break; } return sign_link; } static void ipaccess_sign_link_down(struct e1inp_line *line) { /* No matter what link went down, we close both signal links. */ struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1]; struct gsm_bts *bts = NULL; struct e1inp_sign_link *link; llist_for_each_entry(link, &ts->sign.sign_links, list) { /* Get bts pointer from the first element of the list. */ if (bts == NULL) bts = link->trx->bts; /* Cancel RSL connection timeout in case are still waiting for an RSL connection. */ if (link->trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) osmo_timer_del(&link->trx->rsl_connect_timeout); } if (bts != NULL) ipaccess_drop_oml(bts); } /* This function is called if we receive one OML/RSL message. */ static int ipaccess_sign_link(struct msgb *msg) { int ret = 0; struct e1inp_sign_link *link = msg->dst; switch (link->type) { case E1INP_SIGN_RSL: ret = abis_rsl_rcvmsg(msg); break; case E1INP_SIGN_OML: ret = abis_nm_rcvmsg(msg); break; default: LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n", link->type); msgb_free(msg); break; } return ret; } /* not static, ipaccess-config needs it. */ struct e1inp_line_ops ipaccess_e1inp_line_ops = { .cfg = { .ipa = { .addr = "0.0.0.0", .role = E1INP_LINE_R_BSC, }, }, .sign_link_up = ipaccess_sign_link_up, .sign_link_down = ipaccess_sign_link_down, .sign_link = ipaccess_sign_link, }; static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops); } osmo-bsc-1.3.0/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c000066400000000000000000000153571332665256100236660ustar00rootroot00000000000000/* ip.access nanoBTS specific code, OML attribute table generator */ /* (C) 2016 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 void patch_16(uint8_t *data, const uint16_t val) { memcpy(data, &val, sizeof(val)); } static void patch_32(uint8_t *data, const uint32_t val) { memcpy(data, &val, sizeof(val)); } struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts) { struct msgb *msgb; uint8_t buf[256]; int rlt; msgb = msgb_alloc(1024, "nanobts_attr_bts"); memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6); msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf); /* interference avg. period in numbers of SACCH multifr */ msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, 0x06); rlt = gsm_bts_get_radio_link_timeout(bts); if (rlt == -1) { /* Osmocom extension: Use infinite radio link timeout */ buf[0] = 0xFF; buf[1] = 0x00; } else { /* conn fail based on SACCH error rate */ buf[0] = 0x01; buf[1] = rlt; } msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf); memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7); msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf); msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f); /* seconds */ memcpy(buf, "\x00\x01\x0a", 3); msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf); /* percent */ msgb_tv_put(msgb, NM_ATT_CCCH_L_T, 10); /* seconds */ msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, 1); /* busy threshold in - dBm */ buf[0] = 90; /* -90 dBm as default "busy" threshold */ if (bts->rach_b_thresh != -1) buf[0] = bts->rach_b_thresh & 0xff; msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]); /* rach load averaging 1000 slots */ buf[0] = 0x03; buf[1] = 0xe8; if (bts->rach_ldavg_slots != -1) { buf[0] = (bts->rach_ldavg_slots >> 8) & 0x0f; buf[1] = bts->rach_ldavg_slots & 0xff; } msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf); /* 10 milliseconds */ msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, bts->network->T3105 > 0? bts->network->T3105 : 13); /* 10 retransmissions of physical config */ msgb_tv_put(msgb, NM_ATT_NY1, 10); buf[0] = (bts->c0->arfcn >> 8) & 0x0f; buf[1] = bts->c0->arfcn & 0xff; msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf); msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic); abis_nm_ipaccess_cgi(buf, bts); msgb_tl16v_put(msgb, NM_ATT_IPACC_CGI, 7, buf); return msgb; } struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, "nanobts_attr_bts"); /* NSEI 925 */ buf[0] = bts->gprs.nse.nsei >> 8; buf[1] = bts->gprs.nse.nsei & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf); /* all timers in seconds */ OSMO_ASSERT(ARRAY_SIZE(bts->gprs.nse.timer) < sizeof(buf)); memcpy(buf, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer)); msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf); /* all timers in seconds */ buf[0] = 3; /* blockimg timer (T1) */ buf[1] = 3; /* blocking retries */ buf[2] = 3; /* unblocking retries */ buf[3] = 3; /* reset timer (T2) */ buf[4] = 3; /* reset retries */ buf[5] = 10; /* suspend timer (T3) in 100ms */ buf[6] = 3; /* suspend retries */ buf[7] = 10; /* resume timer (T4) in 100ms */ buf[8] = 3; /* resume retries */ buf[9] = 10; /* capability update timer (T5) */ buf[10] = 3; /* capability update retries */ OSMO_ASSERT(ARRAY_SIZE(bts->gprs.cell.timer) < sizeof(buf)); memcpy(buf, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer)); msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, 11, buf); return msgb; } struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, "nanobts_attr_bts"); /* routing area code */ buf[0] = bts->gprs.rac; msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf); buf[0] = 5; /* repeat time (50ms) */ buf[1] = 3; /* repeat count */ msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf); /* BVCI 925 */ buf[0] = bts->gprs.cell.bvci >> 8; buf[1] = bts->gprs.cell.bvci & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf); /* all timers in seconds, unless otherwise stated */ buf[0] = 20; /* T3142 */ buf[1] = 5; /* T3169 */ buf[2] = 5; /* T3191 */ buf[3] = 160; /* T3193 (units of 10ms) */ buf[4] = 5; /* T3195 */ buf[5] = 10; /* N3101 */ buf[6] = 4; /* N3103 */ buf[7] = 8; /* N3105 */ buf[8] = 15; /* RLC CV countdown */ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, 9, buf); if (bts->gprs.mode == BTS_GPRS_EGPRS) { buf[0] = 0x8f; buf[1] = 0xff; } else { buf[0] = 0x0f; buf[1] = 0x00; } msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf); buf[0] = 0; /* T downlink TBF extension (0..500, high byte) */ buf[1] = 250; /* T downlink TBF extension (0..500, low byte) */ buf[2] = 0; /* T uplink TBF extension (0..500, high byte) */ buf[3] = 250; /* T uplink TBF extension (0..500, low byte) */ buf[4] = 2; /* CS2 */ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, 5, buf); #if 0 /* EDGE model only, breaks older models. * Should inquire the BTS capabilities */ buf[0] = 2; /* MCS2 */ msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, 1, buf); #endif return msgb; } struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, "nanobts_attr_bts"); /* 925 */ buf[0] = bts->gprs.nsvc[0].nsvci >> 8; buf[1] = bts->gprs.nsvc[0].nsvci & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf); /* remote udp port */ patch_16(&buf[0], htons(bts->gprs.nsvc[0].remote_port)); /* remote ip address */ patch_32(&buf[2], htonl(bts->gprs.nsvc[0].remote_ip)); /* local udp port */ patch_16(&buf[6], htons(bts->gprs.nsvc[0].local_port)); msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf); return msgb; } struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts, struct gsm_bts_trx *trx) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, "nanobts_attr_bts"); /* number of -2dB reduction steps / Pn */ msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2); buf[0] = trx->arfcn >> 8; buf[1] = trx->arfcn & 0xff; msgb_tl16v_put(msgb, NM_ATT_ARFCN_LIST, 2, buf); return msgb; } osmo-bsc-1.3.0/src/osmo-bsc/bts_nokia_site.c000066400000000000000000001240371332665256100207270ustar00rootroot00000000000000/* Nokia XXXsite family specific code */ /* (C) 2011 by Dieter Spaar * * 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 . * */ /* TODO: Attention: There are some static variables used for states during configuration. Those variables have to be moved to a BTS specific context, otherwise there will most certainly be problems if more than one Nokia BTS is used. */ #include #include #include #include #include #include #include #include #include /* TODO: put in a separate file ? */ extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); /* was static in system_information.c */ extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts); static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts); static void reset_timer_cb(void *_bts); static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref); static int dump_elements(uint8_t * data, int len) __attribute__((unused)); static void bootstrap_om_bts(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); gsm_bts_mark_all_ts_uninitialized(bts); if (!bts->nokia.skip_reset) { if (!bts->nokia.did_reset) abis_nm_reset(bts, 1); } else bts->nokia.did_reset = 1; } static void bootstrap_om_trx(struct gsm_bts_trx *trx) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", trx->bts->nr, trx->nr); gsm_trx_mark_all_ts_uninitialized(trx); } static int shutdown_om(struct gsm_bts *bts) { /* TODO !? */ return 0; } #define SAPI_OML 62 #define SAPI_RSL 0 /* Tell LAPD to start start the SAP (send SABM requests) for all signalling timeslots in this line Attention: this has to be adapted for mISDN */ static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi) { struct e1inp_sign_link *link; int i; for (i = 0; i < ARRAY_SIZE(line->ts); i++) { struct e1inp_ts *ts = &line->ts[i]; if (ts->type != E1INP_TS_TYPE_SIGN) continue; llist_for_each_entry(link, &ts->sign.sign_links, list) { if (sapi != -1 && link->sapi != sapi) continue; #if 0 /* debugging */ printf("sap start/stop (%d): %d tei=%d sapi=%d\n", start, i + 1, link->tei, link->sapi); #endif if (start) { ts->lapd->profile.t200_sec = 1; ts->lapd->profile.t200_usec = 0; lapd_sap_start(ts->lapd, link->tei, link->sapi); } else lapd_sap_stop(ts->lapd, link->tei, link->sapi); } } } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; if (subsys != SS_L_INPUT) return 0; switch (signal) { case S_L_INP_LINE_INIT: start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */ break; case S_L_INP_TEI_DN: break; case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE) break; if (isd->tei == isd->trx->bts->oml_tei) bootstrap_om_bts(isd->trx->bts); else bootstrap_om_trx(isd->trx); break; } break; case S_L_INP_TEI_UNKNOWN: /* We are receiving LAPD frames with one TEI that we do not * seem to know, likely that we (the BSC) stopped working * and lost our local states. However, the BTS is already * configured, we try to take over the RSL links. */ start_sabm_in_line(isd->line, 1, SAPI_RSL); break; } return 0; } static void nm_statechg_evt(unsigned int signal, struct nm_statechg_signal_data *nsd) { if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE) return; } static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { if (subsys != SS_NM) return 0; switch (signal) { case S_NM_STATECHG_OPER: case S_NM_STATECHG_ADM: nm_statechg_evt(signal, signal_data); break; default: break; } return 0; } /* TODO: put in a separate file ? */ static const struct value_string nokia_msgt_name[] = { { 0x80, "NOKIA_BTS_CONF_DATA" }, { 0x81, "NOKIA_BTS_ACK" }, { 0x82, "NOKIA_BTS_OMU_STARTED" }, { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" }, { 0x84, "NOKIA_BTS_MF_REQ" }, { 0x85, "NOKIA_BTS_AF_REQ" }, { 0x86, "NOKIA_BTS_RESET_REQ" }, { 0x87, "NOKIA_reserved" }, { 0x88, "NOKIA_BTS_CONF_REQ" }, { 0x89, "NOKIA_BTS_TEST_REQ" }, { 0x8A, "NOKIA_BTS_TEST_REPORT" }, { 0x8B, "NOKIA_reserved" }, { 0x8C, "NOKIA_reserved" }, { 0x8D, "NOKIA_reserved" }, { 0x8E, "NOKIA_BTS_CONF_COMPL" }, { 0x8F, "NOKIA_reserved" }, { 0x90, "NOKIA_BTS_STM_TEST_REQ" }, { 0x91, "NOKIA_BTS_STM_TEST_REPORT" }, { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" }, { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" }, { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" }, { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" }, { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" }, { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" }, { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" }, { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" }, { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" }, { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" }, { 0x9C, "NOKIA_BTS_HW_REQ" }, { 0x9D, "NOKIA_BTS_HW_REPORT" }, { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" }, { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" }, { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" }, { 0xA1, "NOKIA_BTS_CLOCK_REQ" }, { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" }, { 0xA3, "NOKIA_AC_INTERRUPTED" }, { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" }, { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" }, { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" }, { 0xA7, "NOKIA_AC_CIRCUIT_REQ" }, { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" }, { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" }, { 0xAA, "NOKIA_BTS_GSM_TIME" }, { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" }, { 0xAC, "NOKIA_BTS_STATE_CHANGED" }, { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" }, { 0xAE, "NOKIA_BTS_ALARM" }, { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" }, { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" }, { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" }, { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" }, { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" }, { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" }, { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" }, { 0xB6, "NOKIA_BTS_LCS_COMMAND" }, { 0xB7, "NOKIA_BTS_LCS_ANSWER" }, { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" }, { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" }, { 0, NULL } }; static const char *get_msg_type_name_string(uint8_t msg_type) { return get_value_string(nokia_msgt_name, msg_type); } static const struct value_string nokia_element_name[] = { { 0x01, "Ny1" }, { 0x02, "T3105_F" }, { 0x03, "Interference band limits" }, { 0x04, "Interference report timer in secs" }, { 0x05, "Channel configuration per TS" }, { 0x06, "BSIC" }, { 0x07, "RACH report timer in secs" }, { 0x08, "Hardware database status" }, { 0x09, "BTS RX level" }, { 0x0A, "ARFN" }, { 0x0B, "STM antenna attenuation" }, { 0x0C, "Cell allocation bitmap" }, { 0x0D, "Radio definition per TS" }, { 0x0E, "Frame number" }, { 0x0F, "Antenna diversity" }, { 0x10, "T3105_D" }, { 0x11, "File format" }, { 0x12, "Last File" }, { 0x13, "BTS type" }, { 0x14, "Erasure mode" }, { 0x15, "Hopping mode" }, { 0x16, "Floating TRX" }, { 0x17, "Power supplies" }, { 0x18, "Reset type" }, { 0x19, "Averaging period" }, { 0x1A, "RBER2" }, { 0x1B, "LAC" }, { 0x1C, "CI" }, { 0x1D, "Failure parameters" }, { 0x1E, "(RF max power reduction)" }, { 0x1F, "Measured RX_SENS" }, { 0x20, "Extended cell radius" }, { 0x21, "reserved" }, { 0x22, "Success-Failure" }, { 0x23, "Ack-Nack" }, { 0x24, "OMU test results" }, { 0x25, "File identity" }, { 0x26, "Generation and version code" }, { 0x27, "SW description" }, { 0x28, "BCCH LEV" }, { 0x29, "Test type" }, { 0x2A, "Subscriber number" }, { 0x2B, "reserved" }, { 0x2C, "HSN" }, { 0x2D, "reserved" }, { 0x2E, "MS RXLEV" }, { 0x2F, "MS TXLEV" }, { 0x30, "RXQUAL" }, { 0x31, "RX SENS" }, { 0x32, "Alarm block" }, { 0x33, "Neighbouring BCCH levels" }, { 0x34, "STM report type" }, { 0x35, "MA" }, { 0x36, "MAIO" }, { 0x37, "H_FLAG" }, { 0x38, "TCH_ARFN" }, { 0x39, "Clock output" }, { 0x3A, "Transmitted power" }, { 0x3B, "Clock sync" }, { 0x3C, "TMS protocol discriminator" }, { 0x3D, "TMS protocol data" }, { 0x3E, "FER" }, { 0x3F, "SWR result" }, { 0x40, "Object identity" }, { 0x41, "STM RX Antenna Test" }, { 0x42, "reserved" }, { 0x43, "reserved" }, { 0x44, "Object current state" }, { 0x45, "reserved" }, { 0x46, "FU channel configuration" }, { 0x47, "reserved" }, { 0x48, "ARFN of a CU" }, { 0x49, "FU radio definition" }, { 0x4A, "reserved" }, { 0x4B, "Severity" }, { 0x4C, "Diversity selection" }, { 0x4D, "RX antenna test" }, { 0x4E, "RX antenna supervision period" }, { 0x4F, "RX antenna state" }, { 0x50, "Sector configuration" }, { 0x51, "Additional info" }, { 0x52, "SWR parameters" }, { 0x53, "HW inquiry mode" }, { 0x54, "reserved" }, { 0x55, "Availability status" }, { 0x56, "reserved" }, { 0x57, "EAC inputs" }, { 0x58, "EAC outputs" }, { 0x59, "reserved" }, { 0x5A, "Position" }, { 0x5B, "HW unit identity" }, { 0x5C, "RF test signal attenuation" }, { 0x5D, "Operational state" }, { 0x5E, "Logical object identity" }, { 0x5F, "reserved" }, { 0x60, "BS_TXPWR_OM" }, { 0x61, "Loop_Duration" }, { 0x62, "LNA_Path_Selection" }, { 0x63, "Serial number" }, { 0x64, "HW version" }, { 0x65, "Obj. identity and obj. state" }, { 0x66, "reserved" }, { 0x67, "EAC input definition" }, { 0x68, "EAC id and text" }, { 0x69, "HW unit status" }, { 0x6A, "SW release version" }, { 0x6B, "FW version" }, { 0x6C, "Bit_Error_Ratio" }, { 0x6D, "RXLEV_with_Attenuation" }, { 0x6E, "RXLEV_without_Attenuation" }, { 0x6F, "reserved" }, { 0x70, "CU_Results" }, { 0x71, "reserved" }, { 0x72, "LNA_Path_Results" }, { 0x73, "RTE Results" }, { 0x74, "Real Time" }, { 0x75, "RX diversity selection" }, { 0x76, "EAC input config" }, { 0x77, "Feature support" }, { 0x78, "File version" }, { 0x79, "Outputs" }, { 0x7A, "FU parameters" }, { 0x7B, "Diagnostic info" }, { 0x7C, "FU BSIC" }, { 0x7D, "TRX Configuration" }, { 0x7E, "Download status" }, { 0x7F, "RX difference limit" }, { 0x80, "TRX HW capability" }, { 0x81, "Common HW config" }, { 0x82, "Autoconfiguration pool size" }, { 0x83, "TRE diagnostic info" }, { 0x84, "TRE object identity" }, { 0x85, "New TRE Info" }, { 0x86, "Acknowledgement period" }, { 0x87, "Synchronization mode" }, { 0x88, "reserved" }, { 0x89, "Block Control Data" }, { 0x8A, "SW load mode" }, { 0x8B, "Recommended recovery action" }, { 0x8C, "BSC BCF id" }, { 0x8D, "Q1 baud rate" }, { 0x8E, "Allocation status" }, { 0x8F, "Functional entity number" }, { 0x90, "Transmission delay" }, { 0x91, "Loop Duration ms" }, { 0x92, "Logical channel" }, { 0x93, "Q1 address" }, { 0x94, "Alarm detail" }, { 0x95, "Cabinet type" }, { 0x96, "HW unit existence" }, { 0x97, "RF power parameters" }, { 0x98, "Message scenario" }, { 0x99, "HW unit max amount" }, { 0x9A, "Master TRX" }, { 0x9B, "Transparent data" }, { 0x9C, "BSC topology info" }, { 0x9D, "Air i/f modulation" }, { 0x9E, "LCS Q1 command data" }, { 0x9F, "Frame number offset" }, { 0xA0, "Abis TSL" }, { 0xA1, "Dynamic pool info" }, { 0xA2, "LCS LLP data" }, { 0xA3, "LCS Q1 answer data" }, { 0xA4, "DFCA FU Radio Definition" }, { 0xA5, "Antenna hopping" }, { 0xA6, "Field record sequence number" }, { 0xA7, "Timeslot offslot" }, { 0xA8, "EPCR capability" }, { 0xA9, "Connectsite optional element" }, { 0xAA, "TSC" }, { 0xAB, "Special TX Power Setting" }, { 0xAC, "Optional sync settings" }, { 0xFA, "Abis If parameters" }, { 0, NULL } }; static const char *get_element_name_string(uint16_t element) { return get_value_string(nokia_element_name, element); } static const struct value_string nokia_bts_types[] = { { 0x0a, "MetroSite GSM 900" }, { 0x0b, "MetroSite GSM 1800" }, { 0x0c, "MetroSite GSM 1900 (PCS)" }, { 0x0d, "MetroSite GSM 900 & 1800" }, { 0x0e, "InSite GSM 900" }, { 0x0f, "InSite GSM 1800" }, { 0x10, "InSite GSM 1900" }, { 0x11, "UltraSite GSM 900" }, { 0x12, "UltraSite GSM 1800" }, { 0x13, "UltraSite GSM/US-TDMA 1900" }, { 0x14, "UltraSite GSM 900 & 1800" }, { 0x16, "UltraSite GSM/US-TDMA 850" }, { 0x18, "MetroSite GSM/US-TDMA 850" }, { 0x19, "UltraSite GSM 800/1900" }, { 0, NULL } }; static const char *get_bts_type_string(uint8_t type) { return get_value_string(nokia_bts_types, type); } static const struct value_string nokia_severity[] = { { 0, "indeterminate" }, { 1, "critical" }, { 2, "major" }, { 3, "minor" }, { 4, "warning" }, { 0, NULL } }; static const char *get_severity_string(uint8_t severity) { return get_value_string(nokia_severity, severity); } /* TODO: put in a separate file ? */ /* some message IDs */ #define NOKIA_MSG_CONF_DATA 128 #define NOKIA_MSG_ACK 129 #define NOKIA_MSG_OMU_STARTED 130 #define NOKIA_MSG_START_DOWNLOAD_REQ 131 #define NOKIA_MSG_MF_REQ 132 #define NOKIA_MSG_RESET_REQ 134 #define NOKIA_MSG_CONF_REQ 136 #define NOKIA_MSG_CONF_COMPLETE 142 #define NOKIA_MSG_BLOCK_CTRL_REQ 168 #define NOKIA_MSG_STATE_CHANGED 172 #define NOKIA_MSG_ALARM 174 /* some element IDs */ #define NOKIA_EI_BTS_TYPE 0x13 #define NOKIA_EI_ACK 0x23 #define NOKIA_EI_ADD_INFO 0x51 #define NOKIA_EI_SEVERITY 0x4B #define NOKIA_EI_ALARM_DETAIL 0x94 #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 static uint8_t fu_config_template[] = { 0x7F, 0x7A, 0x39, /* ID = 0x7A (FU parameters) ## constructed ## */ /* length = 57 */ /* [3] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [6] */ 0x00, 0x07, 0x01, 0xFF, 0x41, 0x02, /* ID = 0x01 (Ny1) */ /* length = 2 */ /* [12] */ 0x00, 0x05, 0x42, 0x02, /* ID = 0x02 (T3105_F) */ /* length = 2 */ /* [16] */ 0x00, 0x28, /* FIXME: use net->T3105 */ 0x50, 0x02, /* ID = 0x10 (T3105_D) */ /* length = 2 */ /* [20] */ 0x00, 0x28, /* FIXME: use net->T3105 */ 0x43, 0x05, /* ID = 0x03 (Interference band limits) */ /* length = 5 */ /* [24] */ 0x0F, 0x1B, 0x27, 0x33, 0x3F, 0x44, 0x02, /* ID = 0x04 (Interference report timer in secs) */ /* length = 2 */ /* [31] */ 0x00, 0x10, 0x47, 0x01, /* ID = 0x07 (RACH report timer in secs) */ /* length = 1 */ /* [35] */ 0x1E, 0x4C, 0x10, /* ID = 0x0C (Cell allocation bitmap) ####### */ /* length = 16 */ /* [38] */ 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x01, /* ID = 0x19 (Averaging period) */ /* length = 1 */ /* [56] */ 0x01, 0x5E, 0x01, /* ID = 0x1E ((RF max power reduction)) */ /* length = 1 */ /* [59] */ 0x00, 0x7F, 0x46, 0x11, /* ID = 0x46 (FU channel configuration) ## constructed ## */ /* length = 17 */ /* [63] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [66] */ 0x00, 0x07, 0x01, 0xFF, 0x45, 0x08, /* ID = 0x05 (Channel configuration per TS) */ /* length = 8 */ /* [72] */ 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x7F, 0x65, 0x0B, /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */ /* length = 11 */ /* [83] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [86] */ 0x00, 0x04, 0x01, 0xFF, 0x5F, 0x44, 0x01, /* ID = 0x44 (Object current state) */ /* length = 1 */ /* [93] */ 0x03, 0x7F, 0x7C, 0x0A, /* ID = 0x7C (FU BSIC) ## constructed ## */ /* length = 10 */ /* [97] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [100] */ 0x00, 0x07, 0x01, 0xFF, 0x46, 0x01, /* ID = 0x06 (BSIC) */ /* length = 1 */ /* [106] */ 0x00, 0x7F, 0x48, 0x0B, /* ID = 0x48 (ARFN of a CU) ## constructed ## */ /* length = 11 */ /* [110] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [113] */ 0x00, 0x08, 0x01, 0xFF, 0x4A, 0x02, /* ID = 0x0A (ARFN) ####### */ /* length = 2 */ /* [119] */ 0x03, 0x62, 0x7F, 0x49, 0x59, /* ID = 0x49 (FU radio definition) ## constructed ## */ /* length = 89 */ /* [124] */ 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [127] */ 0x00, 0x07, 0x01, 0xFF, 0x4D, 0x50, /* ID = 0x0D (Radio definition per TS) ####### */ /* length = 80 */ /* [133] */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */ 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x62, }; /* TODO: put in a separate file ? */ /* build the configuration for each TRX */ static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, uint8_t * fu_config, int *hopping) { int i; *hopping = 0; memcpy(fu_config, fu_config_template, sizeof(fu_config_template)); /* set ID */ fu_config[6 + 2] = id; fu_config[66 + 2] = id; fu_config[86 + 2] = id; fu_config[100 + 2] = id; fu_config[113 + 2] = id; fu_config[127 + 2] = id; /* set ARFCN */ uint16_t arfcn = trx->arfcn; fu_config[119] = arfcn >> 8; fu_config[119 + 1] = arfcn & 0xFF; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; if (ts->hopping.enabled) { /* reverse order */ int j; for (j = 0; j < ts->hopping.ma_len; j++) fu_config[133 + (i * 10) + (7 - j)] = ts->hopping.ma_data[j]; fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn; fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio; *hopping = 1; } else { fu_config[133 + 8 + (i * 10)] = arfcn >> 8; fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF; } } /* set BSIC */ /* Attention: all TRX except the first one seem to get the TSC from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION, GSM 04.08 CHANNEL DESCRIPTION). There was a bug in rsl_chan_activate_lchan() setting this parameter. */ uint8_t bsic = trx->bts->bsic; fu_config[106] = bsic; /* set CA */ if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) { fprintf(stderr, "generate_cell_chan_list failed\n"); return 0; } /* set channel configuration */ for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; uint8_t chan_config; /* 0 = FCCH + SCH + BCCH + CCCH 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4 2 = BCCH + CCCH (This combination is not used in any BTS) 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH 4 = SDCCH/8 + SACCH/8 5 = SDCCH/8 with SDCCH2 used as CBCH 6 = TCH/F + FACCH/F + SACCH/F 7 = E-RACH (Talk family) 9 = Dual rate (capability for TCH/F and TCH/H) 10 = reserved for BTS internal use 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2). 0xFF = spare TS */ if (ts->pchan == GSM_PCHAN_NONE) chan_config = 0xFF; else if (ts->pchan == GSM_PCHAN_CCCH) chan_config = 0; else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4) chan_config = 1; else if (ts->pchan == GSM_PCHAN_TCH_F) chan_config = 6; /* 9 should work too */ else if (ts->pchan == GSM_PCHAN_TCH_H) chan_config = 9; else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C) chan_config = 4; else if (ts->pchan == GSM_PCHAN_PDCH) chan_config = 11; else { fprintf(stderr, "unsupported channel config %d for timeslot %d\n", ts->pchan, i); return 0; } fu_config[72 + i] = chan_config; } return sizeof(fu_config_template); } /* TODO: put in a separate file ? */ static uint8_t bts_config_1[] = { 0x4E, 0x02, /* ID = 0x0E (Frame number) */ /* length = 2 */ /* [2] */ 0xFF, 0xFF, 0x5F, 0x4E, 0x02, /* ID = 0x4E (RX antenna supervision period) */ /* length = 2 */ /* [7] */ 0xFF, 0xFF, 0x5F, 0x50, 0x02, /* ID = 0x50 (Sector configuration) */ /* length = 2 */ /* [12] */ 0x01, 0x01, }; static uint8_t bts_config_2[] = { 0x55, 0x02, /* ID = 0x15 (Hopping mode) */ /* length = 2 */ /* [2] */ 0x01, 0x00, 0x5F, 0x75, 0x02, /* ID = 0x75 (RX diversity selection) */ /* length = 2 */ /* [7] */ 0x01, 0x01, }; static uint8_t bts_config_3[] = { 0x5F, 0x20, 0x02, /* ID = 0x20 (Extended cell radius) */ /* length = 2 */ /* [3] */ 0x01, 0x00, }; static uint8_t bts_config_4[] = { 0x5F, 0x74, 0x09, /* ID = 0x74 (Real Time) */ /* length = 9 */ /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [15] */ 0x01, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [21] */ 0x02, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [27] */ 0x03, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [33] */ 0x04, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [39] */ 0x05, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [45] */ 0x06, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [51] */ 0x07, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [57] */ 0x08, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [63] */ 0x09, 0x01, 0x00, 0x5F, 0x76, 0x03, /* ID = 0x76 (EAC input config) */ /* length = 3 */ /* [69] */ 0x0A, 0x01, 0x00, }; static uint8_t bts_config_insite[] = { 0x4E, 0x02, /* ID = 0x0E (Frame number) */ /* length = 2 */ /* [2] */ 0xFF, 0xFF, 0x5F, 0x4E, 0x02, /* ID = 0x4E (RX antenna supervision period) */ /* length = 2 */ /* [7] */ 0xFF, 0xFF, 0x5F, 0x50, 0x02, /* ID = 0x50 (Sector configuration) */ /* length = 2 */ /* [12] */ 0x01, 0x01, 0x55, 0x02, /* ID = 0x15 (Hopping mode) */ /* length = 2 */ /* [16] */ 0x01, 0x00, 0x5F, 0x20, 0x02, /* ID = 0x20 (Extended cell radius) */ /* length = 2 */ /* [21] */ 0x01, 0x00, 0x5F, 0x74, 0x09, /* ID = 0x74 (Real Time) */ /* length = 9 */ /* [26] */ 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, 0x00, }; void set_real_time(uint8_t * real_time) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ real_time[0] = (1900 + tm->tm_year) >> 8; real_time[1] = (1900 + tm->tm_year) & 0xFF; real_time[2] = tm->tm_mon + 1; real_time[3] = tm->tm_mday; real_time[4] = tm->tm_hour; real_time[5] = tm->tm_min; real_time[6] = tm->tm_sec; real_time[7] = 0; real_time[8] = 0; } /* TODO: put in a separate file ? */ /* build the configuration data */ static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config, int need_hopping) { /* is it an InSite BTS ? */ if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */ if (n_trx != 1) { fprintf(stderr, "InSite has only one TRX\n"); return 0; } if (need_hopping != 0) { fprintf(stderr, "InSite does not support hopping\n"); return 0; } memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite)); set_real_time(&fu_config[26]); return sizeof(bts_config_insite); } int len = 0; int i; memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1)); /* set sector configuration */ fu_config[len + 12 - 1] = 1 + n_trx; /* len */ for (i = 0; i < n_trx; i++) fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF); len += (sizeof(bts_config_1) + (n_trx - 1)); memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2)); /* set hopping mode (Baseband and RF hopping work for the MetroSite) */ if (need_hopping) fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */ len += sizeof(bts_config_2); /* set extended cell radius for each TRX */ for (i = 0; i < n_trx; i++) { memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3)); fu_config[len + 3] = ((i + 1) & 0xFF); len += sizeof(bts_config_3); } memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4)); set_real_time(&fu_config[len + 3]); len += sizeof(bts_config_4); return len; } /* TODO: put in a separate file ? */ static struct msgb *nm_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); } /* TODO: put in a separate file ? */ struct abis_om_nokia_hdr { uint8_t msg_type; uint8_t spare; uint16_t reference; uint8_t data[0]; } __attribute__ ((packed)); #define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr)) static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t * data, int len_data) { struct abis_om_hdr *oh; struct abis_om_nokia_hdr *noh; struct msgb *msg = nm_msgb_alloc(); oh = (struct abis_om_hdr *)msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_data); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_ONLY; oh->sequence = 0; oh->length = sizeof(struct abis_om_nokia_hdr) + len_data; noh = (struct abis_om_nokia_hdr *)oh->data; noh->msg_type = msg_type; noh->spare = 0; noh->reference = htons(ref); memcpy(noh->data, data, len_data); DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type)); return abis_nm_sendmsg(bts, msg); } /* TODO: put in a separate file ? */ static uint8_t download_req[] = { 0x5F, 0x25, 0x0B, /* ID = 0x25 (File identity) */ /* length = 11 */ /* [3] */ 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x5F, 0x78, 0x03, /* ID = 0x78 (File version) */ /* length = 3 */ /* [17] */ 0x2A, 0x2A, 0x2A, 0x5F, 0x81, 0x0A, 0x01, /* ID = 0x8A (SW load mode) */ /* length = 1 */ /* [24] */ 0x01, 0x5F, 0x81, 0x06, 0x01, /* ID = 0x86 (Acknowledgement period) */ /* length = 1 */ /* [29] */ 0x01, }; static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = download_req; int len_data = sizeof(download_req); return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, len_data); } /* TODO: put in a separate file ? */ static uint8_t ack[] = { 0x5F, 0x23, 0x01, /* ID = 0x23 (Ack-Nack) */ /* length = 1 */ /* [3] */ 0x01, }; static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = ack; int len_data = sizeof(ack); return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data); } /* TODO: put in a separate file ? */ static uint8_t reset[] = { 0x5F, 0x40, 0x04, /* ID = 0x40 (Object identity) */ /* length = 4 */ /* [3] */ 0x00, 0x01, 0xFF, 0xFF, }; static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref) { uint8_t *data = reset; int len_data = sizeof(reset); LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf); return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); } /* TODO: put in a separate file ? */ static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, uint8_t * data, int len) { int len_remain, len_to_send, max_send; int seq = 0; int ret; len_remain = len; while (len_remain) { struct abis_om_hdr *oh; struct abis_om_nokia_hdr *noh; struct msgb *msg = nm_msgb_alloc(); if (seq == 0) max_send = 256 - sizeof(struct abis_om_nokia_hdr); else max_send = 256; if (len_remain > max_send) { len_to_send = max_send; if (seq == 0) { /* first segment */ oh = (struct abis_om_hdr *)msgb_put(msg, ABIS_OM_NOKIA_HDR_SIZE + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ oh->sequence = seq; oh->length = 0; /* 256 bytes */ noh = (struct abis_om_nokia_hdr *)oh->data; noh->msg_type = msg_type; noh->spare = 0; noh->reference = htons(ref); memcpy(noh->data, data, len_to_send); } else { /* segment in between */ oh = (struct abis_om_hdr *)msgb_put(msg, sizeof (struct abis_om_hdr) + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ oh->sequence = seq; oh->length = 0; /* 256 bytes */ memcpy(oh->data, data, len_to_send); } } else { len_to_send = len_remain; /* check if message fits in a single segment */ if (seq == 0) return abis_nm_send(bts, msg_type, ref, data, len_to_send); /* last segment */ oh = (struct abis_om_hdr *)msgb_put(msg, sizeof(struct abis_om_hdr) + len_to_send); oh->mdisc = ABIS_OM_MDISC_FOM; oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ oh->sequence = seq; oh->length = len_to_send; memcpy(oh->data, data, len_to_send); } DEBUGPC(DNM, "Sending multi-segment %d\n", seq); ret = abis_nm_sendmsg(bts, msg); if (ret < 0) return ret; nokia_abis_nm_queue_send_next(bts); /* next segment */ len_remain -= len_to_send; data += len_to_send; seq++; } return 0; } /* TODO: put in a separate file ? */ static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) { struct gsm_bts_trx *trx; uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ int len = 0; int idx = 0; int ret; int hopping = 0; int need_hopping = 0; memset(config, 0, sizeof(config)); llist_for_each_entry(trx, &bts->trx_list, list) { #if 0 /* debugging */ printf("TRX\n"); printf(" arfcn: %d\n", trx->arfcn); printf(" bsic: %d\n", trx->bts->bsic); uint8_t ca[20]; memset(ca, 0xFF, sizeof(ca)); ret = generate_cell_chan_list(ca, trx->bts); printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); int i; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; printf(" pchan %d: %d\n", i, ts->pchan); } #endif ret = make_fu_config(trx, idx + 1, config + len, &hopping); need_hopping |= hopping; len += ret; idx++; } ret = make_bts_config(bts_type, idx, config + len, need_hopping); len += ret; #if 0 /* debugging */ dump_elements(config, len); #endif return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, len); } #define GET_NEXT_BYTE if(idx >= len) return 0; \ ub = data[idx++]; static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, int max_value) { uint8_t ub; int idx = 0; int found = 0; int constructed __attribute__((unused)); uint16_t id_value; for (;;) { GET_NEXT_BYTE; /* encoding bit, construced means that other elements are contained */ constructed = ((ub & 0x20) ? 1 : 0); if ((ub & 0x1F) == 0x1F) { /* fixed pattern, ID follows */ GET_NEXT_BYTE; /* ID */ id_value = ub & 0x7F; if (ub & 0x80) { /* extension bit */ GET_NEXT_BYTE; /* ID low part */ id_value = (id_value << 7) | (ub & 0x7F); } if (id_value == id) found = 1; } else { id_value = (ub & 0x3F); if (id_value == id) found = 1; } GET_NEXT_BYTE; /* length */ if (found) { /* get data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; if (max_value <= 0) return -1; /* buffer too small */ *value = ub; value++; max_value--; } return n; /* length */ } else { /* skip data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; } } } return 0; /* not found */ } static int dump_elements(uint8_t * data, int len) { uint8_t ub; int idx = 0; int constructed; uint16_t id_value; static char indent[100] = ""; /* TODO: move static to BTS context */ for (;;) { GET_NEXT_BYTE; /* encoding bit, construced means that other elements are contained */ constructed = ((ub & 0x20) ? 1 : 0); if ((ub & 0x1F) == 0x1F) { /* fixed pattern, ID follows */ GET_NEXT_BYTE; /* ID */ id_value = ub & 0x7F; if (ub & 0x80) { /* extension bit */ GET_NEXT_BYTE; /* ID low part */ id_value = (id_value << 7) | (ub & 0x7F); } } else { id_value = (ub & 0x3F); } GET_NEXT_BYTE; /* length */ printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, get_element_name_string(id_value), constructed ? "** constructed **" : ""); printf("%s length = %d\n", indent, ub); printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); if (constructed) { int indent_len = strlen(indent); strcat(indent, " "); dump_elements(data + idx, ub); indent[indent_len] = 0; } /* skip data */ uint8_t n = ub; uint8_t i; for (i = 0; i < n; i++) { GET_NEXT_BYTE; } } return 0; } /* TODO: put in a separate file ? */ /* taken from abis_nm.c */ static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) { int wait = 0; struct msgb *msg; /* the queue is empty */ while (!llist_empty(&bts->abis_queue)) { msg = msgb_dequeue(&bts->abis_queue); wait = OBSC_NM_W_ACK_CB(msg); abis_sendmsg(msg); if (wait) break; } bts->abis_nm_pend = wait; } /* TODO: put in a separate file ? */ /* timer for restarting OML after BTS reset */ static void reset_timer_cb(void *_bts) { struct gsm_bts *bts = _bts; struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_line *line; bts->nokia.wait_reset = 0; /* OML link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return; } start_sabm_in_line(line, 0, -1); /* stop all first */ start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ } /* TODO: put in a separate file ? */ /* This is how the configuration is done: - start OML link - reset BTS - receive ACK, wait some time and restart OML link - receive OMU STARTED message, send START DOWNLOAD REQ - receive CNF REQ message, send CONF DATA - receive ACK, start RSL link(s) ACK some other messages received from the BTS. Probably its also possible to configure the BTS without a reset, this has not been tested yet. */ static int abis_nm_rcvmsg_fom(struct msgb *mb) { struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; struct gsm_bts *bts = sign_link->trx->bts; struct abis_om_hdr *oh = msgb_l2(mb); struct abis_om_nokia_hdr *noh = msgb_l3(mb); uint8_t mt = noh->msg_type; int ret = 0; uint16_t ref = ntohs(noh->reference); uint8_t info[256]; uint8_t ack = 0xFF; uint8_t severity = 0xFF; int str_len; int len_data; if (bts->nokia.wait_reset) { LOGP(DNM, LOGL_INFO, "Ignore message while waiting for reset\n"); return ret; } if (oh->length < sizeof(struct abis_om_nokia_hdr)) { LOGP(DNM, LOGL_ERROR, "Message too short\n"); return -EINVAL; } len_data = oh->length - sizeof(struct abis_om_nokia_hdr); LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); #if 0 /* debugging */ dump_elements(noh->data, len_data); #endif switch (mt) { case NOKIA_MSG_OMU_STARTED: if (find_element(noh->data, len_data, NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, sizeof(uint8_t)) == sizeof(uint8_t)) LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", bts->nokia.bts_type, get_bts_type_string(bts->nokia.bts_type)); else LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); /* send START_DOWNLOAD_REQ */ abis_nm_download_req(bts, ref); break; case NOKIA_MSG_MF_REQ: break; case NOKIA_MSG_CONF_REQ: /* send ACK */ abis_nm_ack(bts, ref); nokia_abis_nm_queue_send_next(bts); /* send CONF_DATA */ abis_nm_send_config(bts, bts->nokia.bts_type); bts->nokia.configured = 1; break; case NOKIA_MSG_ACK: if (find_element (noh->data, len_data, NOKIA_EI_ACK, &ack, sizeof(uint8_t)) == sizeof(uint8_t)) { LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); if (ack != 1) { LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", ack); /* TODO: properly handle failures (NACK) */ } } else LOGP(DNM, LOGL_ERROR, "ACK not found\n"); /* TODO: the assumption for the following is that no NACK was received */ /* ACK for reset message ? */ if (!bts->nokia.did_reset) { bts->nokia.did_reset = 1; /* TODO: For the InSite processing the received data is blocked in the driver during reset. Otherwise the LAPD module might assert because the InSite sends garbage on the E1 line during reset. This is done by looking at "wait_reset" in the driver (function handle_ts1_read()) and ignoring the received data. It seems to be necessary for the MetroSite too. */ bts->nokia.wait_reset = 1; osmo_timer_setup(&bts->nokia.reset_timer, reset_timer_cb, bts); osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_line *line; /* OML link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return -ENOMEM; } start_sabm_in_line(line, 0, -1); /* stop all first */ } /* ACK for CONF DATA message ? */ if (bts->nokia.configured != 0) { /* start TRX (RSL link) */ struct gsm_e1_subslot *e1_link = &sign_link->trx->rsl_e1_link; struct e1inp_line *line; bts->nokia.configured = 0; /* RSL Link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " "to non-existing E1 line %u\n", sign_link->trx->bts->nr, sign_link->trx->nr, e1_link->e1_nr); return -ENOMEM; } /* start TRX */ start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */ } break; case NOKIA_MSG_STATE_CHANGED: /* send ACK */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_CONF_COMPLETE: /* send ACK */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */ /* send ACK (do we have to send an ACK ?) */ abis_nm_ack(bts, ref); break; case NOKIA_MSG_ALARM: find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, sizeof(severity)); /* TODO: there might be alarms with both elements set */ str_len = find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, sizeof(info)); if (str_len > 0) { info[str_len] = 0; LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", get_severity_string(severity), severity, info); } else { /* nothing found, try details */ str_len = find_element(noh->data, len_data, NOKIA_EI_ALARM_DETAIL, info, sizeof(info)); if (str_len > 0) { uint16_t code; info[str_len] = 0; code = (info[0] << 8) + info[1]; LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d), code 0x%X : %s\n", get_severity_string(severity), severity, code, info + 2); } } /* send ACK */ abis_nm_ack(bts, ref); break; } nokia_abis_nm_queue_send_next(bts); return ret; } /* TODO: put in a separate file ? */ int abis_nokia_rcvmsg(struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int rc = 0; /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) return -EINVAL; } if (oh->sequence != 0) { LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); return -EINVAL; } msg->l3h = (unsigned char *)oh + sizeof(*oh); switch (oh->mdisc) { case ABIS_OM_MDISC_FOM: LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n"); rc = abis_nm_rcvmsg_fom(msg); break; case ABIS_OM_MDISC_MANUF: LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n"); break; case ABIS_OM_MDISC_MMI: case ABIS_OM_MDISC_TRAU: LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", oh->mdisc); break; default: LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", oh->mdisc); return -EINVAL; } msgb_free(msg); return rc; } static int bts_model_nokia_site_start(struct gsm_network *net); static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static struct gsm_bts_model model_nokia_site = { .type = GSM_BTS_TYPE_NOKIA_SITE, .name = "nokia_site", .start = bts_model_nokia_site_start, .oml_rcvmsg = &abis_nokia_rcvmsg, .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops, }; static struct gsm_network *my_net; static int bts_model_nokia_site_start(struct gsm_network *net) { model_nokia_site.features.data = &model_nokia_site._features_data[0]; model_nokia_site.features.data_len = sizeof(model_nokia_site._features_data); osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HOPPING); osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_HSCSD); osmo_bts_set_feature(&model_nokia_site.features, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); my_net = net; return 0; } int bts_model_nokia_site_init(void) { return gsm_bts_model_register(&model_nokia_site); } osmo-bsc-1.3.0/src/osmo-bsc/bts_siemens_bs11.c000066400000000000000000000415361332665256100210750ustar00rootroot00000000000000/* Siemens BS-11 specific code */ /* (C) 2009-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 static int bts_model_bs11_start(struct gsm_network *net); static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line) { e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); } static struct gsm_bts_model model_bs11 = { .type = GSM_BTS_TYPE_BS11, .name = "bs11", .start = bts_model_bs11_start, .oml_rcvmsg = &abis_nm_rcvmsg, .e1line_bind_ops = bts_model_bs11_e1line_bind_ops, .nm_att_tlvdef = { .def = { [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, /* BS11 specifics */ [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, [0xd5] = { TLV_TYPE_TLV }, [0xa8] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, [0x95] = { TLV_TYPE_FIXED, 2 }, }, }, }; /* The following definitions are for OM and NM packets that we cannot yet * generate by code but we just pass on */ // BTS Site Manager, SET ATTRIBUTES /* Object Class: BTS Site Manager Instance 1: FF Instance 2: FF Instance 3: FF SET ATTRIBUTES sAbisExternalTime: 2007/09/08 14:36:11 omLAPDRelTimer: 30sec shortLAPDIntTimer: 5sec emergencyTimer1: 10 minutes emergencyTimer2: 0 minutes */ unsigned char msg_1[] = { NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, NM_ATT_BS11_ABIS_EXT_TIME, 0x07, 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, 0x02, 0x00, 0x1E, NM_ATT_BS11_SH_LAPD_INT_TIMER, 0x01, 0x05, 0x42, 0x02, 0x00, 0x0A, 0x44, 0x02, 0x00, 0x00 }; // BTS, SET BTS ATTRIBUTES /* Object Class: BTS BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET BTS ATTRIBUTES bsIdentityCode / BSIC: PLMN_colour_code: 7h BS_colour_code: 7h BTS Air Timer T3105: 4 ,unit 10 ms btsIsHopping: FALSE periodCCCHLoadIndication: 1sec thresholdCCCHLoadIndication: 0% cellAllocationNumber: 00h = GSM 900 enableInterferenceClass: 00h = Disabled fACCHQual: 6 (FACCH stealing flags minus 1) intaveParameter: 31 SACCH multiframes interferenceLevelBoundaries: Interference Boundary 1: 0Ah Interference Boundary 2: 0Fh Interference Boundary 3: 14h Interference Boundary 4: 19h Interference Boundary 5: 1Eh mSTxPwrMax: 11 GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm 30=33dBm, 31=32dBm ny1: Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 powerOutputThresholds: Out Power Fault Threshold: -10 dB Red Out Power Threshold: - 6 dB Excessive Out Power Threshold: 5 dB rACHBusyThreshold: -127 dBm rACHLoadAveragingSlots: 250 ,number of RACH burst periods rfResourceIndicationPeriod: 125 SACCH multiframes T200: SDCCH: 044 in 5 ms FACCH/Full rate: 031 in 5 ms FACCH/Half rate: 041 in 5 ms SACCH with TCH SAPI0: 090 in 10 ms SACCH with SDCCH: 090 in 10 ms SDCCH with SAPI3: 090 in 5 ms SACCH with TCH SAPI3: 135 in 10 ms tSync: 9000 units of 10 msec tTrau: 9000 units of 10 msec enableUmLoopTest: 00h = disabled enableExcessiveDistance: 00h = Disabled excessiveDistance: 64km hoppingMode: 00h = baseband hopping cellType: 00h = Standard Cell BCCH ARFCN / bCCHFrequency: 1 */ static unsigned char bs11_attr_bts[] = { NM_ATT_BSIC, HARDCODED_BSIC, NM_ATT_BTS_AIR_TIMER, 0x04, NM_ATT_BS11_BTSLS_HOPPING, 0x00, NM_ATT_CCCH_L_I_P, 0x01, NM_ATT_CCCH_L_T, 0x00, NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, NM_ATT_BS11_FACCH_QUAL, 0x06, /* interference avg. period in numbers of SACCH multifr */ NM_ATT_INTAVE_PARAM, 0x1F, NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, NM_ATT_CCCH_L_T, 0x23, NM_ATT_GSM_TIME, 0x28, 0x00, NM_ATT_ADM_STATE, 0x03, NM_ATT_RACH_B_THRESH, 0x7F, NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, NM_ATT_BS11_RF_RES_IND_PER, 0x7D, NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, NM_ATT_BS11_TSYNC, 0x23, 0x28, NM_ATT_BS11_TTRAU, 0x23, 0x28, NM_ATT_TEST_DUR, 0x01, 0x00, NM_ATT_OUTST_ALARM, 0x01, 0x00, NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, NM_ATT_BS11_PLL, 0x01, 0x00, NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, }; // Handover Recognition, SET ATTRIBUTES /* Illegal Contents GSM Formatted O&M Msg Object Class: Handover Recognition BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET ATTRIBUTES enableDelayPowerBudgetHO: 00h = Disabled enableDistanceHO: 00h = Disabled enableInternalInterCellHandover: 00h = Disabled enableInternalIntraCellHandover: 00h = Disabled enablePowerBudgetHO: 00h = Disabled enableRXLEVHO: 00h = Disabled enableRXQUALHO: 00h = Disabled hoAveragingDistance: 8 SACCH multiframes hoAveragingLev: A_LEV_HO: 8 SACCH multiframes W_LEV_HO: 1 SACCH multiframes hoAveragingPowerBudget: 16 SACCH multiframes hoAveragingQual: A_QUAL_HO: 8 SACCH multiframes W_QUAL_HO: 2 SACCH multiframes hoLowerThresholdLevDL: (10 - 110) dBm hoLowerThresholdLevUL: (5 - 110) dBm hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% hoThresholdLevDLintra : (20 - 110) dBm hoThresholdLevULintra: (20 - 110) dBm hoThresholdMsRangeMax: 20 km nCell: 06h timerHORequest: 3 ,unit 2 SACCH multiframes */ unsigned char msg_3[] = { NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, 0xD0, 0x00, /* enableDelayPowerBudgetHO */ 0x64, 0x00, /* enableDistanceHO */ 0x67, 0x00, /* enableInternalInterCellHandover */ 0x68, 0x00, /* enableInternalInterCellHandover */ 0x6A, 0x00, /* enablePowerBudgetHO */ 0x6C, 0x00, /* enableRXLEVHO */ 0x6D, 0x00, /* enableRXQUALHO */ 0x6F, 0x08, /* hoAveragingDistance */ 0x70, 0x08, 0x01, /* hoAveragingLev */ 0x71, 0x10, 0x10, 0x10, 0x72, 0x08, 0x02, /* hoAveragingQual */ 0x73, 0x0A, /* hoLowerThresholdLevDL */ 0x74, 0x05, /* hoLowerThresholdLevUL */ 0x75, 0x06, /* hoLowerThresholdQualDL */ 0x76, 0x06, /* hoLowerThresholdQualUL */ 0x78, 0x14, /* hoThresholdLevDLintra */ 0x79, 0x14, /* hoThresholdLevULintra */ 0x7A, 0x14, /* hoThresholdMsRangeMax */ 0x7D, 0x06, /* nCell */ NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, 0x20, 0x01, 0x00, 0x45, 0x01, 0x00, 0x48, 0x01, 0x00, 0x5A, 0x01, 0x00, 0x5B, 0x01, 0x05, 0x5E, 0x01, 0x1A, 0x5F, 0x01, 0x20, 0x9D, 0x01, 0x00, 0x47, 0x01, 0x00, 0x5C, 0x01, 0x64, 0x5D, 0x01, 0x1E, 0x97, 0x01, 0x20, 0xF7, 0x01, 0x3C, }; // Power Control, SET ATTRIBUTES /* Object Class: Power Control BTS relat. Number: 0 Instance 2: FF Instance 3: FF SET ATTRIBUTES enableMsPowerControl: 00h = Disabled enablePowerControlRLFW: 00h = Disabled pcAveragingLev: A_LEV_PC: 4 SACCH multiframes W_LEV_PC: 1 SACCH multiframes pcAveragingQual: A_QUAL_PC: 4 SACCH multiframes W_QUAL_PC: 2 SACCH multiframes pcLowerThresholdLevDL: 0Fh pcLowerThresholdLevUL: 0Ah pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% pcRLFThreshold: 0Ch pcUpperThresholdLevDL: 14h pcUpperThresholdLevUL: 0Fh pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% powerConfirm: 2 ,unit 2 SACCH multiframes powerControlInterval: 2 ,unit 2 SACCH multiframes powerIncrStepSize: 02h = 4 dB powerRedStepSize: 01h = 2 dB radioLinkTimeoutBs: 64 SACCH multiframes enableBSPowerControl: 00h = disabled */ unsigned char msg_4[] = { NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, 0x7E, 0x04, 0x01, /* pcAveragingLev */ 0x7F, 0x04, 0x02, /* pcAveragingQual */ 0x80, 0x0F, /* pcLowerThresholdLevDL */ 0x81, 0x0A, /* pcLowerThresholdLevUL */ 0x82, 0x05, /* pcLowerThresholdQualDL */ 0x83, 0x05, /* pcLowerThresholdQualUL */ 0x84, 0x0C, /* pcRLFThreshold */ 0x85, 0x14, /* pcUpperThresholdLevDL */ 0x86, 0x0F, /* pcUpperThresholdLevUL */ 0x87, 0x04, /* pcUpperThresholdQualDL */ 0x88, 0x04, /* pcUpperThresholdQualUL */ 0x89, 0x02, /* powerConfirm */ 0x8A, 0x02, /* powerConfirmInterval */ 0x8B, 0x02, /* powerIncrStepSize */ 0x8C, 0x01, /* powerRedStepSize */ 0x8D, 0x40, /* radioLinkTimeoutBs */ 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl }; // Transceiver, SET TRX ATTRIBUTES (TRX 0) /* Object Class: Transceiver BTS relat. Number: 0 Tranceiver number: 0 Instance 3: FF SET TRX ATTRIBUTES aRFCNList (HEX): 0001 txPwrMaxReduction: 00h = 30dB radioMeasGran: 254 SACCH multiframes radioMeasRep: 01h = enabled memberOfEmergencyConfig: 01h = TRUE trxArea: 00h = TRX doesn't belong to a concentric cell */ static unsigned char bs11_attr_radio[] = { NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, NM_ATT_RF_MAXPOWR_R, 0x00, NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, NM_ATT_BS11_TRX_AREA, 0x01, 0x00, }; /* * Patch the various SYSTEM INFORMATION tables to update * the LAI */ static void patch_nm_tables(struct gsm_bts *bts) { uint8_t arfcn_low = bts->c0->arfcn & 0xff; uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; /* T3105 attribute in units of 10ms */ bs11_attr_bts[2] = bts->network->T3105 / 10; /* patch ARFCN into BTS Attributes */ bs11_attr_bts[69] &= 0xf0; bs11_attr_bts[69] |= arfcn_high; bs11_attr_bts[70] = arfcn_low; /* patch ARFCN into TRX Attributes */ bs11_attr_radio[2] &= 0xf0; bs11_attr_radio[2] |= arfcn_high; bs11_attr_radio[3] = arfcn_low; /* patch the RACH attributes */ if (bts->rach_b_thresh != -1) bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; if (bts->rach_ldavg_slots != -1) { uint8_t avg_high = bts->rach_ldavg_slots & 0xff; uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; bs11_attr_bts[35] = avg_high; bs11_attr_bts[36] = avg_low; } /* patch BSIC */ bs11_attr_bts[1] = bts->bsic; /* patch the power reduction */ bs11_attr_radio[5] = bts->c0->max_power_red / 2; } static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) { enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); struct gsm_e1_subslot *e1l = &ts->e1_link; abis_nm_set_channel_attr(ts, ccomb); if (is_ipaccess_bts(ts->trx->bts)) return; if (ts_is_tch(ts)) abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss); } static void nm_reconfig_trx(struct gsm_bts_trx *trx) { struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; int i; patch_nm_tables(trx->bts); switch (trx->bts->type) { case GSM_BTS_TYPE_BS11: /* FIXME: discover this by fetching an attribute */ #if 0 trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ #else trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ #endif abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss); abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); /* Set Radio Attributes */ if (trx == trx->bts->c0) abis_nm_set_radio_attr(trx, bs11_attr_radio, sizeof(bs11_attr_radio)); else { uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; uint8_t arfcn_low = trx->arfcn & 0xff; uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; memcpy(trx1_attr_radio, bs11_attr_radio, sizeof(trx1_attr_radio)); /* patch ARFCN into TRX Attributes */ trx1_attr_radio[2] &= 0xf0; trx1_attr_radio[2] |= arfcn_high; trx1_attr_radio[3] = arfcn_low; abis_nm_set_radio_attr(trx, trx1_attr_radio, sizeof(trx1_attr_radio)); } break; case GSM_BTS_TYPE_NANOBTS: switch (trx->bts->band) { case GSM_BAND_850: case GSM_BAND_900: trx->nominal_power = 20; break; case GSM_BAND_1800: case GSM_BAND_1900: trx->nominal_power = 23; break; default: LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", gsm_band_name(trx->bts->band)); break; } break; default: break; } for (i = 0; i < TRX_NR_TS; i++) nm_reconfig_ts(&trx->ts[i]); } static void nm_reconfig_bts(struct gsm_bts *bts) { struct gsm_bts_trx *trx; switch (bts->type) { case GSM_BTS_TYPE_BS11: patch_nm_tables(bts); abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ break; default: break; } llist_for_each_entry(trx, &bts->trx_list, list) nm_reconfig_trx(trx); } static void bootstrap_om_bs11(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); /* stop sending event reports */ abis_nm_event_reports(bts, 0); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* reconfigure BTS with all TRX and all TS */ nm_reconfig_bts(bts); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); /* restart sending event reports */ abis_nm_event_reports(bts, 1); } static int shutdown_om(struct gsm_bts *bts) { /* stop sending event reports */ abis_nm_event_reports(bts, 0); /* begin DB transmission */ abis_nm_bs11_db_transmission(bts, 1); /* end DB transmission */ abis_nm_bs11_db_transmission(bts, 0); /* Reset BTS Site manager resource */ abis_nm_bs11_reset_resource(bts); gsm_bts_mark_all_ts_uninitialized(bts); return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int gbl_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_bts *bts; if (subsys != SS_L_GLOBAL) return 0; switch (signal) { case S_GLOBAL_BTS_CLOSE_OM: bts = signal_data; if (bts->type == GSM_BTS_TYPE_BS11) shutdown_om(signal_data); break; } return 0; } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; if (subsys != SS_L_INPUT) return 0; switch (signal) { case S_L_INP_TEI_UP: switch (isd->link_type) { case E1INP_SIGN_OML: if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) bootstrap_om_bs11(isd->trx->bts); break; } } return 0; } static int bts_model_bs11_start(struct gsm_network *net) { model_bs11.features.data = &model_bs11._features_data[0]; model_bs11.features.data_len = sizeof(model_bs11._features_data); osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HOPPING); osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_HSCSD); osmo_bts_set_feature(&model_bs11.features, BTS_FEAT_MULTI_TSC); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); return 0; } int bts_model_bs11_init(void) { return gsm_bts_model_register(&model_bs11); } osmo-bsc-1.3.0/src/osmo-bsc/bts_sysmobts.c000066400000000000000000000037041332665256100204620ustar00rootroot00000000000000/* sysmocom sysmoBTS specific code */ /* (C) 2010-2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include extern struct gsm_bts_model bts_model_nanobts; static struct gsm_bts_model model_sysmobts; int bts_model_sysmobts_init(void) { model_sysmobts = bts_model_nanobts; model_sysmobts.name = "sysmobts"; model_sysmobts.type = GSM_BTS_TYPE_OSMOBTS; model_sysmobts.features.data = &model_sysmobts._features_data[0]; model_sysmobts.features.data_len = sizeof(model_sysmobts._features_data); memset(model_sysmobts.features.data, 0, sizeof(model_sysmobts.features.data_len)); osmo_bts_set_feature(&model_sysmobts.features, BTS_FEAT_GPRS); osmo_bts_set_feature(&model_sysmobts.features, BTS_FEAT_EGPRS); return gsm_bts_model_register(&model_sysmobts); } osmo-bsc-1.3.0/src/osmo-bsc/bts_unknown.c000066400000000000000000000022571332665256100203000ustar00rootroot00000000000000/* Generic BTS - VTY code tries to allocate this BTS before type is known */ /* (C) 2010 by Daniel Willmann * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 static struct gsm_bts_model model_unknown = { .type = GSM_BTS_TYPE_UNKNOWN, .name = "unknown", .oml_rcvmsg = &abis_nm_rcvmsg, .nm_att_tlvdef = { .def = { }, }, }; int bts_model_unknown_init(void) { return gsm_bts_model_register(&model_unknown); } osmo-bsc-1.3.0/src/osmo-bsc/chan_alloc.c000066400000000000000000000475751332665256100200300ustar00rootroot00000000000000/* GSM Channel allocation routines * * (C) 2008 by Harald Welte * (C) 2008, 2009 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 bool ts_is_usable(const struct gsm_bts_trx_ts *ts) { if (!trx_is_usable(ts->trx)) { LOGP(DRLL, LOGL_DEBUG, "%s not usable\n", gsm_trx_name(ts->trx)); return false; } /* If a TCH/F_PDCH TS is busy changing, it is already taken or not * yet available. */ if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { if (ts->flags & TS_F_PDCH_PENDING_MASK) { LOGP(DRLL, LOGL_DEBUG, "%s in switchover, not available\n", gsm_ts_and_pchan_name(ts)); return false; } } /* If a dynamic channel is busy changing, it is already taken or not * yet available. */ if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { if (ts->dyn.pchan_is != ts->dyn.pchan_want) { LOGP(DRLL, LOGL_DEBUG, "%s in switchover, not available\n", gsm_ts_and_pchan_name(ts)); return false; } } return true; } static int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) { struct gsm_bts_trx_ts *ts; int j, ss; int count = 0; if (!trx_is_usable(trx)) return 0; for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { enum gsm_phys_chan_config ts_pchan_is; ts = &trx->ts[j]; if (!ts_is_usable(ts)) continue; ts_pchan_is = ts_pchan(ts); if (ts_pchan_is == GSM_PCHAN_PDCH) { /* Dynamic timeslots in PDCH mode will become TCH if needed. */ switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: if (pchan == GSM_PCHAN_TCH_F) count++; continue; case GSM_PCHAN_TCH_F_TCH_H_PDCH: if (pchan == GSM_PCHAN_TCH_F) count++; else if (pchan == GSM_PCHAN_TCH_H) count += 2; continue; default: /* Not dynamic, not applicable. */ continue; } } if (ts_pchan_is != pchan) continue; /* check if all sub-slots are allocated yet */ for (ss = 0; ss < ts_subslots(ts); ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->type == GSM_LCHAN_NONE && lc->state == LCHAN_S_NONE) count++; } } return count; } /* Count number of free TS of given pchan type */ int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { struct gsm_bts_trx *trx; int count = 0; llist_for_each_entry(trx, &bts->trx_list, list) count += trx_count_free_ts(trx, pchan); return count; } static bool ts_usable_as_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config as_pchan) { switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: if (ts->flags & TS_F_PDCH_PENDING_MASK) { /* currently being switched over. Not usable. */ return false; } switch (as_pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_PDCH: /* continue to check below. */ break; default: return false; } break; case GSM_PCHAN_TCH_F_TCH_H_PDCH: if (ts->dyn.pchan_is != ts->dyn.pchan_want) { /* currently being switched over. Not usable. */ return false; } switch (as_pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: case GSM_PCHAN_PDCH: /* continue to check below. */ break; default: return false; } break; default: /* static timeslots never switch. */ return ts->pchan == as_pchan; } /* Dynamic timeslots -- Checks depending on the current actual pchan mode: */ switch (ts_pchan(ts)) { case GSM_PCHAN_NONE: /* Not initialized, possibly because GPRS was disabled. We may switch. */ return true; case GSM_PCHAN_PDCH: /* This slot is in PDCH mode and available to switch pchan mode. But check for * error states: */ if (ts->lchan->state != LCHAN_S_NONE && ts->lchan->state != LCHAN_S_ACTIVE) return false; return true; case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: /* No need to switch at all? */ if (ts_pchan(ts) == as_pchan) return true; /* If any lchan is in use, we can't change the pchan kind */ { int ss; int subslots = ts_subslots(ts); for (ss = 0; ss < subslots; ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->type != GSM_LCHAN_NONE || lc->state != LCHAN_S_NONE) return false; } } return true; default: /* Not implemented. */ return false; } } static struct gsm_lchan * _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan, enum gsm_phys_chan_config as_pchan) { struct gsm_bts_trx_ts *ts; int j, start, stop, dir, ss; int check_subslots; #define LOGPLCHANALLOC(fmt, args...) \ LOGP(DRLL, LOGL_DEBUG, "looking for lchan %s as %s: " fmt, \ gsm_pchan_name(pchan), gsm_pchan_name(as_pchan), ## args) if (!trx_is_usable(trx)) { LOGPLCHANALLOC("%s trx not usable\n", gsm_trx_name(trx)); return NULL; } if (trx->bts->chan_alloc_reverse) { /* check TS 7..0 */ start = 7; stop = -1; dir = -1; } else { /* check TS 0..7 */ start = 0; stop = 8; dir = 1; } for (j = start; j != stop; j += dir) { ts = &trx->ts[j]; if (!ts_is_usable(ts)) continue; /* The caller first selects what kind of TS to search in, e.g. looking for exact * GSM_PCHAN_TCH_F, or maybe among dynamic GSM_PCHAN_TCH_F_TCH_H_PDCH... */ if (ts->pchan != pchan) { LOGPLCHANALLOC("%s is != %s\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan)); continue; } /* Next, is this timeslot in or can it be switched to the pchan we want to use it for? */ if (!ts_usable_as_pchan(ts, as_pchan)) { LOGPLCHANALLOC("%s is not usable as %s\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(as_pchan)); continue; } /* If we need to switch it, after above check we are also allowed to switch it, and we * will always use the first lchan after the switch. Return that lchan and rely on the * caller to perform the pchan switchover. */ if (ts_pchan(ts) != as_pchan) { LOGPLCHANALLOC("%s is a match, will switch to %s\n", gsm_ts_and_pchan_name(ts), gsm_pchan_name(as_pchan)); return ts->lchan; } /* TS is in desired pchan mode. Go ahead and check for an available lchan. */ check_subslots = ts_subslots(ts); for (ss = 0; ss < check_subslots; ss++) { struct gsm_lchan *lc = &ts->lchan[ss]; if (lc->type == GSM_LCHAN_NONE && lc->state == LCHAN_S_NONE) { LOGPLCHANALLOC("%s ss=%d is available\n", gsm_ts_and_pchan_name(ts), lc->nr); return lc; } LOGPLCHANALLOC("%s ss=%d in type=%s,state=%s not suitable\n", gsm_ts_and_pchan_name(ts), lc->nr, gsm_lchant_name(lc->type), gsm_lchans_name(lc->state)); } } return NULL; #undef LOGPLCHANALLOC } static struct gsm_lchan * _lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan, enum gsm_phys_chan_config dyn_as_pchan) { struct gsm_bts_trx *trx; struct gsm_lchan *lc; if (bts->chan_alloc_reverse) { llist_for_each_entry_reverse(trx, &bts->trx_list, list) { lc = _lc_find_trx(trx, pchan, dyn_as_pchan); if (lc) return lc; } } else { llist_for_each_entry(trx, &bts->trx_list, list) { lc = _lc_find_trx(trx, pchan, dyn_as_pchan); if (lc) return lc; } } return NULL; } static struct gsm_lchan * _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) { return _lc_dyn_find_bts(bts, pchan, pchan); } /* Allocate a logical channel. * * Dynamic channel types: we always prefer a dedicated TS, and only pick + * switch a dynamic TS if no pure TS of the requested PCHAN is available. * * TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH * will be disabled in rsl_chan_activate_lchan(); there is no need to check * whether PDCH mode is currently active, here. */ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger) { struct gsm_lchan *lchan = NULL; enum gsm_phys_chan_config first, first_cbch, second, second_cbch; LOGP(DRLL, LOGL_DEBUG, "(bts=%d) lchan_alloc(%s)\n", bts->nr, gsm_lchant_name(type)); switch (type) { case GSM_LCHAN_SDCCH: if (bts->chan_alloc_reverse) { first = GSM_PCHAN_SDCCH8_SACCH8C; first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; second = GSM_PCHAN_CCCH_SDCCH4; second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; } else { first = GSM_PCHAN_CCCH_SDCCH4; first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; second = GSM_PCHAN_SDCCH8_SACCH8C; second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; } lchan = _lc_find_bts(bts, first); if (lchan == NULL) lchan = _lc_find_bts(bts, first_cbch); if (lchan == NULL) lchan = _lc_find_bts(bts, second); if (lchan == NULL) lchan = _lc_find_bts(bts, second_cbch); /* allow to assign bigger channels */ if (allow_bigger) { if (lchan == NULL) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); if (lchan) type = GSM_LCHAN_TCH_H; } if (lchan == NULL) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); if (lchan) type = GSM_LCHAN_TCH_F; } /* try dynamic TCH/F_PDCH */ if (lchan == NULL) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, GSM_PCHAN_TCH_F); /* TCH/F_PDCH will be used as TCH/F */ if (lchan) type = GSM_LCHAN_TCH_F; } /* try fully dynamic TCH/F_TCH/H_PDCH */ if (lchan == NULL) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, GSM_PCHAN_TCH_H); if (lchan) type = GSM_LCHAN_TCH_H; } /* * No need to check fully dynamic channels for TCH/F: * if no TCH/H was available, neither will be TCH/F. */ } break; case GSM_LCHAN_TCH_F: lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); /* If we don't have TCH/F available, fall-back to TCH/H */ if (!lchan) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); if (lchan) type = GSM_LCHAN_TCH_H; } /* If we don't have TCH/H either, try dynamic TCH/F_PDCH */ if (!lchan) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, GSM_PCHAN_TCH_F); /* TCH/F_PDCH used as TCH/F -- here, type is already * set to GSM_LCHAN_TCH_F, but for clarity's sake... */ if (lchan) type = GSM_LCHAN_TCH_F; } /* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */ if (!lchan && bts->network->dyn_ts_allow_tch_f) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, GSM_PCHAN_TCH_F); if (lchan) type = GSM_LCHAN_TCH_F; } /* ...and as TCH/H. */ if (!lchan) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, GSM_PCHAN_TCH_H); if (lchan) type = GSM_LCHAN_TCH_H; } break; case GSM_LCHAN_TCH_H: lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); /* If we don't have TCH/H available, fall-back to TCH/F */ if (!lchan) { lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); if (lchan) type = GSM_LCHAN_TCH_F; } /* No dedicated TCH/x available -- try fully dynamic * TCH/F_TCH/H_PDCH */ if (!lchan) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, GSM_PCHAN_TCH_H); if (lchan) type = GSM_LCHAN_TCH_H; } /* * No need to check TCH/F_TCH/H_PDCH channels for TCH/F: * if no TCH/H was available, neither will be TCH/F. */ /* If we don't have TCH/F either, try dynamic TCH/F_PDCH */ if (!lchan) { lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_PDCH, GSM_PCHAN_TCH_F); if (lchan) type = GSM_LCHAN_TCH_F; } break; default: LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); } if (lchan) { lchan->type = type; LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n", gsm_ts_and_pchan_name(lchan->ts), lchan->nr, gsm_lchant_name(lchan->type)); /* reset measurement report counter and index */ lchan->meas_rep_count = 0; lchan->meas_rep_idx = 0; lchan->meas_rep_last_seen_nr = 255; /* clear sapis */ memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); /* clear multi rate config */ memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); lchan->broken_reason = ""; } else { struct challoc_signal_data sig; LOGP(DRLL, LOGL_ERROR, "(bts=%d) Failed to allocate %s channel\n", bts->nr, gsm_lchant_name(type)); sig.bts = bts; sig.type = type; osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); } return lchan; } /* Free a logical channel */ void lchan_free(struct gsm_lchan *lchan) { struct challoc_signal_data sig; int i; sig.type = lchan->type; lchan->type = GSM_LCHAN_NONE; if (lchan->conn && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && lchan->ts->dyn.pchan_is != lchan->ts->dyn.pchan_want)) { struct lchan_signal_data sig; /* We might kill an active channel... */ sig.lchan = lchan; sig.mr = NULL; osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); } /* stop the timer */ osmo_timer_del(&lchan->T3101); /* clear cached measuement reports */ lchan->meas_rep_idx = 0; for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { lchan->meas_rep[i].flags = 0; lchan->meas_rep[i].nr = 0; } for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) lchan->neigh_meas[i].arfcn = 0; if (lchan->rqd_ref) { talloc_free(lchan->rqd_ref); lchan->rqd_ref = NULL; lchan->rqd_ta = 0; } sig.lchan = lchan; sig.bts = lchan->ts->trx->bts; osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); if (lchan->conn && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && lchan->ts->dyn.pchan_is != lchan->ts->dyn.pchan_want)) { LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); lchan->conn = NULL; } /* FIXME: ts_free() the timeslot, if we're the last logical * channel using it */ /* reset RTP voice connection related data */ memset(&lchan->abis_ip, 0, sizeof(lchan->abis_ip)); } /* * There was an error with the TRX and we need to forget * any state so that a lchan can be allocated again after * the trx is fully usable. * * This should be called after lchan_free to force a channel * be available for allocation again. This means that this * method will stop the "delay after error"-timer and set the * state to LCHAN_S_NONE. */ void lchan_reset(struct gsm_lchan *lchan) { osmo_timer_del(&lchan->T3101); osmo_timer_del(&lchan->T3109); osmo_timer_del(&lchan->T3111); osmo_timer_del(&lchan->error_timer); lchan->type = GSM_LCHAN_NONE; rsl_lchan_set_state(lchan, LCHAN_S_NONE); } /* Drive the release process of the lchan */ static void _lchan_handle_release(struct gsm_lchan *lchan, int sacch_deact, int mode) { /* Release all SAPIs on the local end and continue */ rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); /* * Shall we send a RR Release, start T3109 and wait for the * release indication from the BTS or just take it down (e.g. * on assignment requests) */ if (sacch_deact) { gsm48_send_rr_release(lchan); /* Deactivate the SACCH on the BTS side */ rsl_deact_sacch(lchan); rsl_start_t3109(lchan); } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { rsl_direct_rf_release(lchan); } else { rsl_release_request(lchan, 0, mode); } } /* Consider releasing the channel now */ int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) { DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); lchan->conn = NULL; _lchan_handle_release(lchan, sacch_deact, mode); return 1; } void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { int i; /* skip administratively deactivated tranxsceivers */ if (!nm_is_running(&trx->mo.nm_state) || !nm_is_running(&trx->bb_transc.mo.nm_state)) continue; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; struct load_counter *pl = &cl->pchan[ts->pchan]; int j; int subslots; /* skip administratively deactivated timeslots */ if (!nm_is_running(&ts->mo.nm_state)) continue; subslots = ts_subslots(ts); for (j = 0; j < subslots; j++) { struct gsm_lchan *lchan = &ts->lchan[j]; pl->total++; switch (lchan->state) { case LCHAN_S_NONE: break; default: pl->used++; break; } } } } } void network_chan_load(struct pchan_load *pl, struct gsm_network *net) { struct gsm_bts *bts; memset(pl, 0, sizeof(*pl)); llist_for_each_entry(bts, &net->bts_list, list) bts_chan_load(pl, bts); } /* Update T3122 wait indicator based on samples of BTS channel load. */ void bts_update_t3122_chan_load(struct gsm_bts *bts) { struct pchan_load pl; uint64_t used = 0; uint32_t total = 0; uint64_t load; uint64_t wait_ind; static const uint8_t min_wait_ind = GSM_T3122_DEFAULT; static const uint8_t max_wait_ind = 128; /* max wait ~2 minutes */ int i; /* Ignore BTS that are not in operation, in order to not flood the log with "bogus channel load" * messages */ if (!trx_is_usable(bts->c0)) return; /* Sum up current load across all channels. */ memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); for (i = 0; i < ARRAY_SIZE(pl.pchan); i++) { struct load_counter *lc = &pl.pchan[i]; /* Ignore samples too large for fixed-point calculations (shouldn't happen). */ if (lc->used > UINT16_MAX || lc->total > UINT16_MAX) { LOGP(DRLL, LOGL_NOTICE, "(bts=%d) numbers in channel load sample " "too large (used=%u / total=%u)\n", bts->nr, lc->used, lc->total); continue; } used += lc->used; total += lc->total; } /* Check for invalid samples (shouldn't happen). */ if (total == 0 || used > total) { LOGP(DRLL, LOGL_NOTICE, "(bts=%d) bogus channel load sample (used=%"PRIu64" / total=%"PRIu32")\n", bts->nr, used, total); bts->T3122 = 0; /* disable override of network-wide default value */ bts->chan_load_samples_idx = 0; /* invalidate other samples collected so far */ return; } /* If we haven't got enough samples yet, store measurement for later use. */ if (bts->chan_load_samples_idx < ARRAY_SIZE(bts->chan_load_samples)) { struct load_counter *sample = &bts->chan_load_samples[bts->chan_load_samples_idx++]; sample->total = (unsigned int)total; sample->used = (unsigned int)used; return; } /* We have enough samples and will overwrite our current samples later. */ bts->chan_load_samples_idx = 0; /* Add all previous samples to the current sample. */ for (i = 0; i < ARRAY_SIZE(bts->chan_load_samples); i++) { struct load_counter *sample = &bts->chan_load_samples[i]; total += sample->total; used += sample->used; } used <<= 8; /* convert to fixed-point */ /* Log channel load average. */ load = ((used / total) * 100); LOGP(DRLL, LOGL_DEBUG, "(bts=%d) channel load average is %"PRIu64".%.2"PRIu64"%%\n", bts->nr, (load & 0xffffff00) >> 8, (load & 0xff) / 10); bts->chan_load_avg = ((load & 0xffffff00) >> 8); OSMO_ASSERT(bts->chan_load_avg <= 100); osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_CHAN_LOAD_AVERAGE], bts->chan_load_avg); /* Calculate new T3122 wait indicator. */ wait_ind = ((used / total) * max_wait_ind); wait_ind >>= 8; /* convert from fixed-point to integer */ if (wait_ind < min_wait_ind) wait_ind = min_wait_ind; else if (wait_ind > max_wait_ind) wait_ind = max_wait_ind; LOGP(DRLL, LOGL_DEBUG, "(bts=%d) T3122 wait indicator set to %"PRIu64" seconds\n", bts->nr, wait_ind); bts->T3122 = (uint8_t)wait_ind; osmo_stat_item_set(bts->bts_statg->items[BTS_STAT_T3122], wait_ind); } osmo-bsc-1.3.0/src/osmo-bsc/codec_pref.c000066400000000000000000000143001332665256100200120ustar00rootroot00000000000000/* * (C) 2017-2018 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 /* Helper function for match_codec_pref(), looks up a matching chan mode for * a given permitted speech value */ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) { switch (speech) { case GSM0808_PERM_HR1: case GSM0808_PERM_FR1: return GSM48_CMODE_SPEECH_V1; break; case GSM0808_PERM_HR2: case GSM0808_PERM_FR2: return GSM48_CMODE_SPEECH_EFR; break; case GSM0808_PERM_HR3: case GSM0808_PERM_FR3: return GSM48_CMODE_SPEECH_AMR; break; default: LOGP(DMSC, LOGL_FATAL, "Unsupported permitted speech selected, assuming AMR as channel mode...\n"); return GSM48_CMODE_SPEECH_AMR; } } /* Helper function for match_codec_pref(), looks up a matching permitted speech * value for a given msc audio codec pref */ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) { if (audio->hr) { switch (audio->ver) { case 1: return GSM0808_PERM_HR1; break; case 2: return GSM0808_PERM_HR2; break; case 3: return GSM0808_PERM_HR3; break; default: LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: hr%d, using hr1 instead\n", audio->ver); return GSM0808_PERM_HR1; } } else { switch (audio->ver) { case 1: return GSM0808_PERM_FR1; break; case 2: return GSM0808_PERM_FR2; break; case 3: return GSM0808_PERM_FR3; break; default: LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: fr%d, using fr1 instead\n", audio->ver); return GSM0808_PERM_FR1; } } } /* Helper function for match_codec_pref(), tests if a given audio support * matches one of the permitted speech settings of the channel type element. * The matched permitted speech value is then also compared against the * speech codec list. (optional, only relevant for AoIP) */ static bool test_codec_pref(const struct gsm0808_channel_type *ct, const struct gsm0808_speech_codec_list *scl, uint8_t perm_spch) { unsigned int i; bool match = false; struct gsm0808_speech_codec sc; int rc; /* Try to find the given permitted speech value in the * codec list of the channel type element */ for (i = 0; i < ct->perm_spch_len; i++) { if (ct->perm_spch[i] == perm_spch) { match = true; break; } } /* If we do not have a speech codec list to test against, * we just exit early (will be always the case in non-AoIP networks) */ if (!scl) return match; /* If we failed to match until here, there is no * point in testing further */ if (match == false) return false; /* Extrapolate speech codec data */ rc = gsm0808_speech_codec_from_chan_type(&sc, perm_spch); if (rc < 0) return false; /* Try to find extrapolated speech codec data in * the speech codec list */ for (i = 0; i < scl->len; i++) { if (sc.type == scl->codec[i].type) return true; } return false; } /* Helper function to check if the given permitted speech value is supported * by the BTS. (vty option bts->codec-support). */ static bool test_codec_support_bts(struct gsm_bts *bts, uint8_t perm_spch) { switch (perm_spch) { case GSM0808_PERM_FR1: /* GSM-FR is always supported by all BTSs. There is also no way to * selectively disable GSM-RF per BTS via VTY. */ return true; case GSM0808_PERM_FR2: if (bts->codec.efr) return true; case GSM0808_PERM_FR3: if (bts->codec.amr) return true; case GSM0808_PERM_HR1: if (bts->codec.hr) return true; case GSM0808_PERM_HR3: if (bts->codec.amr) return true; default: return false; } return false; } /*! Helper function for bssmap_handle_assignm_req(), matches the codec * preferences from the MSC with the codec preferences * \param[out] full_rate '1' if full-rate, '0' if half-rate, '-1' if no match * \param[out] chan_mode GSM 04.08 channel mode * \param[in] ct GSM 08.08 channel type * \param[in] scl GSM 08.08 speech codec list * \param[in] msc MSC data [for configuration] * \param[in] bts BTS data [for configuration] * \returns 0 on success, -1 in case no match was found */ int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode, const struct gsm0808_channel_type *ct, const struct gsm0808_speech_codec_list *scl, const struct bsc_msc_data *msc, struct gsm_bts *bts) { unsigned int i; uint8_t perm_spch; bool match = false; for (i = 0; i < msc->audio_length; i++) { /* Pick a permitted speech value from the global codec configuration list */ perm_spch = audio_support_to_gsm88(msc->audio_support[i]); /* Check this permitted speech value against the BTS specific parameters. * if the BTS does not support the codec, try the next one */ if (test_codec_support_bts(bts, perm_spch) == false) continue; /* Match the permitted speech value against the codec lists that were * advertised by the MS and the MSC */ if (test_codec_pref(ct, scl, perm_spch)) { match = true; break; } } /* Exit without result, in case no match can be deteched */ if (!match) { *full_rate = -1; *chan_mode = GSM48_CMODE_SIGN; return -1; } /* Check if the result is a half or full rate codec */ if (perm_spch == GSM0808_PERM_HR1 || perm_spch == GSM0808_PERM_HR2 || perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_HR4 || perm_spch == GSM0808_PERM_HR6) *full_rate = 0; else *full_rate = 1; /* Lookup a channel mode for the selected codec */ *chan_mode = gsm88_to_chan_mode(perm_spch); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/e1_config.c000066400000000000000000000214261332665256100175620ustar00rootroot00000000000000/* OpenBSC E1 Input code */ /* (C) 2008-2010 by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #define SAPI_L2ML 0 #define SAPI_OML 62 #define SAPI_RSL 0 /* 63 ? */ /* The e1_reconfig_*() functions below take the configuration present in the * bts/trx/ts data structures and ensure the E1 configuration reflects the * timeslot/subslot/TEI configuration */ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) { struct gsm_e1_subslot *e1_link = &ts->e1_link; struct e1inp_line *line; DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", ts->nr, ts->trx->nr, ts->trx->bts->nr); return 0; } line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " "non-existing E1 line %u\n", ts->nr, ts->trx->nr, ts->trx->bts->nr, e1_link->e1_nr); return -ENOMEM; } return 0; } int e1_reconfig_trx(struct gsm_bts_trx *trx) { struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; struct e1inp_ts *sign_ts; struct e1inp_line *line; struct e1inp_sign_link *rsl_link; int i; if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " "timeslot?\n", trx->bts->nr, trx->nr); return -EINVAL; } /* RSL Link */ line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " "to non-existing E1 line %u\n", trx->bts->nr, trx->nr, e1_link->e1_nr); return -ENOMEM; } sign_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config_sign(sign_ts, line); /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { struct e1inp_sign_link *oml_link; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, trx->rsl_tei, SAPI_OML); if (!oml_link) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " "failed\n", trx->bts->nr, trx->nr); return -ENOMEM; } if (trx->oml_link) e1inp_sign_link_destroy(trx->oml_link); trx->oml_link = oml_link; } rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, trx->rsl_tei, SAPI_RSL); if (!rsl_link) { LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " "failed\n", trx->bts->nr, trx->nr); return -ENOMEM; } if (trx->rsl_link) e1inp_sign_link_destroy(trx->rsl_link); trx->rsl_link = rsl_link; for (i = 0; i < TRX_NR_TS; i++) e1_reconfig_ts(&trx->ts[i]); return 0; } /* this is the generic callback for all ISDN-based BTS. */ static int bts_isdn_sign_link(struct msgb *msg) { int ret = -EINVAL; struct e1inp_sign_link *link = msg->dst; struct gsm_bts *bts; switch (link->type) { case E1INP_SIGN_OML: bts = link->trx->bts; ret = bts->model->oml_rcvmsg(msg); break; case E1INP_SIGN_RSL: if (link->trx->mo.nm_state.administrative == NM_STATE_LOCKED) { LOGP(DLMI, LOGL_ERROR, "(bts=%d/trx=%d) discarding RSL message received " "in locked administrative state\n", link->trx->bts->nr, link->trx->nr); msgb_free(msg); break; } ret = abis_rsl_rcvmsg(msg); break; default: LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); msgb_free(msg); break; } return ret; } struct e1inp_line_ops bts_isdn_e1inp_line_ops = { .sign_link = bts_isdn_sign_link, }; int e1_reconfig_bts(struct gsm_bts *bts) { struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; struct e1inp_ts *sign_ts; struct e1inp_line *line; struct e1inp_sign_link *oml_link; struct gsm_bts_trx *trx; struct timespec tp; int rc; DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); line = e1inp_line_find(e1_link->e1_nr); if (!line) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); return -ENOMEM; } if (!bts->model->e1line_bind_ops) { LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); return -EINVAL; } if (!line->ops) bts->model->e1line_bind_ops(line); /* skip signal link initialization, this is done later for these BTS. */ if (bts->type == GSM_BTS_TYPE_NANOBTS || bts->type == GSM_BTS_TYPE_OSMOBTS) return e1inp_line_update(line); /* OML link */ if (!e1_link->e1_ts) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", bts->nr); return -EINVAL; } sign_ts = &line->ts[e1_link->e1_ts-1]; e1inp_ts_config_sign(sign_ts, line); oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, bts->c0, bts->oml_tei, SAPI_OML); if (!oml_link) { LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", bts->nr); return -ENOMEM; } if (bts->oml_link) e1inp_sign_link_destroy(bts->oml_link); bts->oml_link = oml_link; rc = clock_gettime(CLOCK_MONOTONIC, &tp); bts->uptime = (rc < 0) ? 0 : tp.tv_sec; /* we don't need sub-second precision for uptime */ llist_for_each_entry(trx, &bts->trx_list, list) e1_reconfig_trx(trx); /* notify E1 input something has changed */ return e1inp_line_update(line); } #if 0 /* do some compiled-in configuration for our BTS/E1 setup */ int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) { struct e1inp_line *line; struct e1inp_ts *sign_ts; struct e1inp_sign_link *oml_link, *rsl_link; struct gsm_bts_trx *trx = bts->c0; int base_ts; switch (bts->nr) { case 0: /* First BTS uses E1 TS 01,02,03,04,05 */ base_ts = HARDCODED_BTS0_TS - 1; break; case 1: /* Second BTS uses E1 TS 06,07,08,09,10 */ base_ts = HARDCODED_BTS1_TS - 1; break; case 2: /* Third BTS uses E1 TS 11,12,13,14,15 */ base_ts = HARDCODED_BTS2_TS - 1; default: return -EINVAL; } line = talloc_zero(tall_bsc_ctx, struct e1inp_line); if (!line) return -ENOMEM; /* create E1 timeslots for signalling and TRAU frames */ e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); /* create signalling links for TS1 */ sign_ts = &line->ts[base_ts+1-1]; oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, TEI_OML, SAPI_OML); rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, TEI_RSL, SAPI_RSL); /* create back-links from bts/trx */ bts->oml_link = oml_link; trx->rsl_link = rsl_link; /* enable subchannel demuxer on TS2 */ subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); /* enable subchannel demuxer on TS3 */ subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); trx = gsm_bts_trx_num(bts, 1); if (trx) { /* create E1 timeslots for TRAU frames of TRX1 */ e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); /* create RSL signalling link for TRX1 */ sign_ts = &line->ts[base_ts+1-1]; rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, trx, TEI_RSL+1, SAPI_RSL); /* create back-links from trx */ trx->rsl_link = rsl_link; /* enable subchannel demuxer on TS2 */ subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); /* enable subchannel demuxer on TS3 */ subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); } return mi_setup(cardnr, line, release_l2); } #endif osmo-bsc-1.3.0/src/osmo-bsc/gsm_04_08_utils.c000066400000000000000000000517371332665256100205600ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 * utility functions */ /* (C) 2008-2009 by Harald Welte * (C) 2008, 2009 by Holger Hans Peter Freyther * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 /* should ip.access BTS use direct RTP streams between each other (1), * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ int ipacc_rtp_direct = 1; static int gsm48_sendmsg(struct msgb *msg) { if (msg->lchan) msg->dst = msg->lchan->ts->trx->rsl_link; msg->l3h = msg->data; return rsl_data_request(msg, 0); } /* Section 9.1.8 / Table 9.9 */ struct chreq { uint8_t val; uint8_t mask; enum chreq_type type; }; /* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ static const struct chreq chreq_type_neci1[] = { { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, { 0xe0, 0xe0, CHREQ_T_TCH_F }, { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, { 0x10, 0xf0, CHREQ_T_SDCCH }, { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, { 0x67, 0xff, CHREQ_T_LMU }, { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, }; /* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ static const struct chreq chreq_type_neci0[] = { { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, { 0xe0, 0xe0, CHREQ_T_TCH_F }, { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, { 0x67, 0xff, CHREQ_T_LMU }, { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, }; static const enum gsm_chan_t ctype_by_chreq[] = { [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_H, [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, [CHREQ_T_PDCH_ONE_PHASE] = GSM_LCHAN_PDTCH, [CHREQ_T_PDCH_TWO_PHASE] = GSM_LCHAN_PDTCH, [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, }; static const enum gsm_chreq_reason_t reason_by_chreq[] = { [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_PDCH_ONE_PHASE] = GSM_CHREQ_REASON_PDCH, [CHREQ_T_PDCH_TWO_PHASE] = GSM_CHREQ_REASON_PDCH, [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, }; /* verify that the two tables match */ osmo_static_assert(sizeof(ctype_by_chreq) == sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); /* * Update channel types for request based on policy. E.g. in the * case of a TCH/H network/bsc use TCH/H for the emergency calls, * for early assignment assign a SDCCH and some other options. */ void gsm_net_update_ctype(struct gsm_network *network) { /* copy over the data */ memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); /* * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it * is better to iterate over the BTS/TRX and check if no TCH/F is available * and then set it to TCH/H. */ if (network->neci) network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; if (network->pag_any_tch) { if (network->neci) { network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; } else { network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; } } } enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) { int i; int length; const struct chreq *chreq; if (network->neci) { chreq = chreq_type_neci1; length = ARRAY_SIZE(chreq_type_neci1); } else { chreq = chreq_type_neci0; length = ARRAY_SIZE(chreq_type_neci0); } for (i = 0; i < length; i++) { const struct chreq *chr = &chreq[i]; if ((ra & chr->mask) == chr->val) return network->ctype_by_chreq[chr->type]; } LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); return GSM_LCHAN_SDCCH; } int get_reason_by_chreq(uint8_t ra, int neci) { int i; int length; const struct chreq *chreq; if (neci) { chreq = chreq_type_neci1; length = ARRAY_SIZE(chreq_type_neci1); } else { chreq = chreq_type_neci0; length = ARRAY_SIZE(chreq_type_neci0); } for (i = 0; i < length; i++) { const struct chreq *chr = &chreq[i]; if ((ra & chr->mask) == chr->val) return reason_by_chreq[chr->type]; } LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); return GSM_CHREQ_REASON_OTHER; } static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg) { if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], lchan->mr_ms_lv + 1); } /* 7.1.7 and 9.1.7: RR CHANnel RELease */ int gsm48_send_rr_release(struct gsm_lchan *lchan) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); uint8_t *cause; msg->lchan = lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CHAN_REL; cause = msgb_put(msg, 1); cause[0] = GSM48_RR_CAUSE_NORMAL; DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", lchan->nr, lchan->type); /* Send actual release request to MS */ return gsm48_sendmsg(msg); } int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv) { struct rsl_mrpci mrpci; if (classmark2_lv[0] < 2) return -EINVAL; mrpci.power_class = classmark2_lv[1] & 0x7; mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; return rsl_siemens_mrpci(lchan, &mrpci); } /* Chapter 9.1.9: Ciphering Mode Command */ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH"); struct gsm48_hdr *gh; uint8_t ciph_mod_set; msg->lchan = lchan; DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) ciph_mod_set = 0; else ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); return rsl_encryption_cmd(msg); } static void gsm48_cell_desc(struct gsm48_cell_desc *cd, const struct gsm_bts *bts) { cd->ncc = (bts->bsic >> 3 & 0x7); cd->bcc = (bts->bsic & 0x7); cd->arfcn_hi = bts->c0->arfcn >> 8; cd->arfcn_lo = bts->c0->arfcn & 0xff; } /*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa * \param[out] lv caller-allocated buffer of 7 bytes. First octet is IS length * \param[in] mr multi-rate configuration to encode * \param[in] modes array describing the AMR modes * \returns 0 on success */ int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes) { int num = 0, i; for (i = 0; i < 8; i++) { if (((mr->gsm48_ie[1] >> i) & 1)) num++; } if (num > 4) { LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too " "many modes in config.\n"); num = 4; } if (num < 1) { LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no " "mode in config.\n"); num = 1; } lv[0] = (num == 1) ? 2 : (num + 2); memcpy(lv + 1, mr->gsm48_ie, 2); if (num == 1) return 0; lv[3] = modes[0].threshold & 0x3f; lv[4] = modes[0].hysteresis << 4; if (num == 2) return 0; lv[4] |= (modes[1].threshold & 0x3f) >> 2; lv[5] = modes[1].threshold << 6; lv[5] |= (modes[1].hysteresis & 0x0f) << 2; if (num == 3) return 0; lv[5] |= (modes[2].threshold & 0x3f) >> 4; lv[6] = modes[2].threshold << 4; lv[6] |= modes[2].hysteresis & 0x0f; return 0; } #define GSM48_HOCMD_CCHDESC_LEN 16 /* Chapter 9.1.15: Handover Command */ int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); msg->lchan = old_lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_HANDO_CMD; /* mandatory bits */ gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); ho->ho_ref = ho_ref; ho->power_command = power_command; if (new_lchan->ts->hopping.enabled) { struct gsm_bts *bts = new_lchan->ts->trx->bts; struct gsm48_system_information_type_1 *si1; uint8_t *cur; si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); /* Copy the Cell Chan Desc (ARFCNS in this cell) */ msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); memcpy(cur, si1->cell_channel_description, GSM48_HOCMD_CCHDESC_LEN); /* Copy the Mobile Allocation */ msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, new_lchan->ts->hopping.ma_len, new_lchan->ts->hopping.ma_data); } /* FIXME: optional bits for type of synchronization? */ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode); /* in case of multi rate we need to attach a config */ if (new_lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0], new_lchan->mr_ms_lv + 1); return gsm48_sendmsg(msg); } /* Chapter 9.1.2: Assignment Command */ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_ass_cmd *ass = (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); msg->lchan = dest_lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_ASS_CMD; /* * fill the channel information element, this code * should probably be shared with rsl_rx_chan_rqd(), * gsm48_lchan_modify(). But beware that 10.5.2.5 * 10.5.2.5.a have slightly different semantic for * the chan_desc. But as long as multi-slot configurations * are not used we seem to be fine. */ gsm48_lchan2chan_desc(&ass->chan_desc, lchan); ass->power_command = power_command; /* optional: cell channel description */ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); /* mobile allocation in case of hopping */ if (lchan->ts->hopping.enabled) { msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, lchan->ts->hopping.ma_data); } /* in case of multi rate we need to attach a config */ mr_config_for_ms(lchan, msg); return gsm48_sendmsg(msg); } /* 9.1.5 Channel mode modify: Modify the mode on the MS side */ int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); struct gsm48_chan_mode_modify *cmm = (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); lchan->tch_mode = mode; msg->lchan = lchan; gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; /* fill the channel information element, this code * should probably be shared with rsl_rx_chan_rqd() */ gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); cmm->mode = mode; /* in case of multi rate we need to attach a config */ mr_config_for_ms(lchan, msg); return gsm48_sendmsg(msg); } int gsm48_rx_rr_modif_ack(struct msgb *msg) { int rc; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_chan_mode_modify *mod = (struct gsm48_chan_mode_modify *) gh->data; DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); if (mod->mode != msg->lchan->tch_mode) { LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", msg->lchan->tch_mode, mod->mode); return -1; } /* update the channel type */ switch (mod->mode) { case GSM48_CMODE_SIGN: msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; break; case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_AMR: msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; break; case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: case GSM48_CMODE_DATA_3k6: msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; break; } /* We've successfully modified the MS side of the channel, * now go on to modify the BTS side of the channel */ rc = rsl_chan_mode_modify_req(msg->lchan); /* FIXME: we not only need to do this after mode modify, but * also after channel activation */ if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) rsl_ipacc_crcx(msg->lchan); return rc; } int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t *data = gh->data; struct gsm_bts *bts = msg->lchan->ts->trx->bts; struct bitvec *nbv = &bts->si_common.neigh_list; struct gsm_meas_rep_cell *mrc; if (gh->msg_type != GSM48_MT_RR_MEAS_REP) return -EINVAL; if (data[0] & 0x80) rep->flags |= MEAS_REP_F_BA1; if (data[0] & 0x40) rep->flags |= MEAS_REP_F_UL_DTX; if ((data[1] & 0x40) == 0x00) rep->flags |= MEAS_REP_F_DL_VALID; rep->dl.full.rx_lev = data[0] & 0x3f; rep->dl.sub.rx_lev = data[1] & 0x3f; rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); if (rep->num_cell < 1 || rep->num_cell > 6) { /* There are no neighbor cell reports present. */ rep->num_cell = 0; return 0; } /* an encoding nightmare in perfection */ mrc = &rep->cell[0]; mrc->rxlev = data[3] & 0x3f; mrc->neigh_idx = data[4] >> 3; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); if (rep->num_cell < 2) return 0; mrc = &rep->cell[1]; mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); mrc->neigh_idx = (data[6] >> 2) & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); if (rep->num_cell < 3) return 0; mrc = &rep->cell[2]; mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); mrc->neigh_idx = (data[8] >> 1) & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); if (rep->num_cell < 4) return 0; mrc = &rep->cell[3]; mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); mrc->neigh_idx = data[10] & 0x1f; mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = data[11] >> 2; if (rep->num_cell < 5) return 0; mrc = &rep->cell[4]; mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = (data[13] >> 1) & 0x3f; if (rep->num_cell < 6) return 0; mrc = &rep->cell[5]; mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); mrc->bsic = data[15] & 0x3f; return 0; } /* 9.2.5 CM service accept */ int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) { struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); msg->lchan = conn->lchan; gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; DEBUGP(DMM, "-> CM SERVICE ACK\n"); return gsm0808_submit_dtap(conn, msg, 0, 0); } /* 9.2.6 CM service reject */ int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value) { struct msgb *msg; msg = gsm48_create_mm_serv_rej(value); if (!msg) { LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); return -1; } DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); return gsm0808_submit_dtap(conn, msg, 0, 0); } /* 9.1.29 RR Status */ struct msgb *gsm48_create_rr_status(uint8_t cause) { struct msgb *msg; struct gsm48_hdr *gh; msg = gsm48_msgb_alloc_name("GSM 04.08 RR STATUS"); if (!msg) return NULL; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_STATUS; gh->data[0] = cause; return msg; } /* 9.1.29 RR Status */ int gsm48_tx_rr_status(struct gsm_subscriber_connection *conn, uint8_t cause) { struct msgb *msg = gsm48_create_rr_status(cause); if (!msg) return -1; return gsm0808_submit_dtap(conn, msg, 0, 0); } struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) { struct msgb *msg; struct gsm48_hdr *gh; msg = gsm48_msgb_alloc_name("GSM 04.08 SERV REJ"); if (!msg) return NULL; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; gh->data[0] = value; return msg; } struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) { struct gsm48_hdr *gh; struct msgb *msg; msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD REJ"); if (!msg) return NULL; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; gh->data[0] = cause; return msg; } int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) { /* Check the size for the classmark */ if (length < 1 + *classmark2_lv) return -1; uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; if (length < 2 + *classmark2_lv + mi_lv[0]) return -2; *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); } int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, char *mi_string, uint8_t *mi_type) { static const uint32_t classmark_offset = offsetof(struct gsm48_pag_resp, classmark2); uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; return gsm48_extract_mi(classmark2_lv, length - classmark_offset, mi_string, mi_type); } /* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ uint64_t str_to_imsi(const char *imsi_str) { uint64_t ret; ret = strtoull(imsi_str, NULL, 10); return ret; } osmo-bsc-1.3.0/src/osmo-bsc/gsm_04_80_utils.c000066400000000000000000000024521332665256100205460ustar00rootroot00000000000000/* OpenBSC utility functions for 3GPP TS 04.80 */ /* (C) 2016 by sysmocom s.m.f.c. GmbH * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 int bsc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) { struct msgb *msg = gsm0480_create_ussd_notify(level, text); if (!msg) return -1; return gsm0808_submit_dtap(conn, msg, 0, 0); } int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) { struct msgb *msg = gsm0480_create_ussd_release_complete(); if (!msg) return -1; return gsm0808_submit_dtap(conn, msg, 0, 0); } osmo-bsc-1.3.0/src/osmo-bsc/gsm_data.c000066400000000000000000001031011332665256100174760ustar00rootroot00000000000000/* (C) 2008-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_bsc_ctx = NULL; static LLIST_HEAD(bts_models); void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, uint8_t e1_ts, uint8_t e1_ts_ss) { ts->e1_link.e1_nr = e1_nr; ts->e1_link.e1_ts = e1_ts; ts->e1_link.e1_ts_ss = e1_ts_ss; } static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) { struct gsm_bts_model *model; llist_for_each_entry(model, &bts_models, list) { if (model->type == type) return model; } return NULL; } int gsm_bts_model_register(struct gsm_bts_model *model) { if (bts_model_find(model->type)) return -EEXIST; tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); llist_add_tail(&model->list, &bts_models); return 0; } const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" }, { 0, NULL } }; struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nr == nr) return trx; } return NULL; } /* Search for a BTS in the given Location Area; optionally start searching * with start_bts (for continuing to search after the first result) */ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, struct gsm_bts *start_bts) { int i; struct gsm_bts *bts; int skip = 0; if (start_bts) skip = 1; for (i = 0; i < net->num_bts; i++) { bts = gsm_bts_num(net, i); if (skip) { if (start_bts == bts) skip = 0; continue; } if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) return bts; } return NULL; } static const struct value_string bts_gprs_mode_names[] = { { BTS_GPRS_NONE, "none" }, { BTS_GPRS_GPRS, "gprs" }, { BTS_GPRS_EGPRS, "egprs" }, { 0, NULL } }; enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid) { int rc; rc = get_string_value(bts_gprs_mode_names, arg); if (valid) *valid = rc != -EINVAL; return rc; } const char *bts_gprs_mode_name(enum bts_gprs_mode mode) { return get_value_string(bts_gprs_mode_names, mode); } int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode) { if (mode != BTS_GPRS_NONE && !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_GPRS)) { return 0; } if (mode == BTS_GPRS_EGPRS && !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) { return 0; } return 1; } int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) { struct gsm_bts_model *model; model = bts_model_find(type); if (!model) return -EINVAL; bts->type = type; bts->model = model; if (model->start && !model->started) { int ret = model->start(bts->network); if (ret < 0) return ret; model->started = true; } switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: /* Set the default OML Stream ID to 0xff */ bts->oml_tei = 0xff; bts->c0->nominal_power = 23; break; case GSM_BTS_TYPE_RBS2000: INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); break; case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_UNKNOWN: case GSM_BTS_TYPE_NOKIA_SITE: /* Set default BTS reset timer */ bts->nokia.bts_reset_timer_cnf = 15; case _NUM_GSM_BTS_TYPE: break; } return 0; } struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, uint8_t bsic) { struct gsm_bts_model *model = bts_model_find(type); struct gsm_bts *bts; if (!model && type != GSM_BTS_TYPE_UNKNOWN) return NULL; bts = gsm_bts_alloc(net, net->num_bts); if (!bts) return NULL; net->num_bts++; bts->type = type; bts->model = model; bts->bsic = bsic; llist_add_tail(&bts->list, &net->bts_list); return bts; } void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) { *raid = (struct gprs_ra_id){ .mcc = bts->network->plmn.mcc, .mnc = bts->network->plmn.mnc, .mnc_3_digits = bts->network->plmn.mnc_3_digits, .lac = bts->location_area_code, .rac = bts->gprs.rac, }; } void gsm48_ra_id_by_bts(struct gsm48_ra_id *buf, struct gsm_bts *bts) { struct gprs_ra_id raid; gprs_ra_id_by_bts(&raid, bts); gsm48_encode_ra(buf, &raid); } int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) { int ret; ret = 0; if (*str) { talloc_free(*str); *str = NULL; } regfree(reg); if (argc > 0) { *str = talloc_strdup(ctx, argv[0]); ret = regcomp(reg, argv[0], 0); /* handle compilation failures */ if (ret != 0) { talloc_free(*str); *str = NULL; } } return ret; } /* Assume there are only 256 possible bts */ osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256); static void depends_calc_index_bit(int bts_nr, int *idx, int *bit) { *idx = bts_nr / (8 * 4); *bit = bts_nr % (8 * 4); } void bts_depend_mark(struct gsm_bts *bts, int dep) { int idx, bit; depends_calc_index_bit(dep, &idx, &bit); bts->depends_on[idx] |= 1 << bit; } void bts_depend_clear(struct gsm_bts *bts, int dep) { int idx, bit; depends_calc_index_bit(dep, &idx, &bit); bts->depends_on[idx] &= ~(1 << bit); } int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) { int idx, bit; depends_calc_index_bit(other->nr, &idx, &bit); /* Check if there is a depends bit */ return (base->depends_on[idx] & (1 << bit)) > 0; } static int bts_is_online(struct gsm_bts *bts) { /* TODO: support E1 BTS too */ if (!is_ipaccess_bts(bts)) return 1; if (!bts->oml_link) return 0; return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED; } int bts_depend_check(struct gsm_bts *bts) { struct gsm_bts *other_bts; llist_for_each_entry(other_bts, &bts->network->bts_list, list) { if (!bts_depend_is_depedency(bts, other_bts)) continue; if (bts_is_online(other_bts)) continue; return 0; } return 1; } /* get the radio link timeout (based on SACCH decode errors, according * to algorithm specified in TS 05.08 section 5.2. A value of -1 * indicates we should use an infinitely long timeout, which only works * with OsmoBTS as the BTS implementation */ int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts) { const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; if (bts->infinite_radio_link_timeout) return -1; else { /* Encoding as per Table 10.5.21 of TS 04.08 */ return (cell_options->radio_link_timeout + 1) << 2; } } /* set the radio link timeout (based on SACCH decode errors, according * to algorithm specified in TS 05.08 Section 5.2. A value of -1 * indicates we should use an infinitely long timeout, which only works * with OsmoBTS as the BTS implementation */ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value) { struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; if (value < 0) bts->infinite_radio_link_timeout = true; else { bts->infinite_radio_link_timeout = false; /* Encoding as per Table 10.5.21 of TS 04.08 */ if (value < 4) value = 4; if (value > 64) value = 64; cell_options->radio_link_timeout = (value >> 2) - 1; } } bool classmark_is_r99(struct gsm_classmark *cm) { int rev_lev = 0; if (cm->classmark1_set) rev_lev = cm->classmark1.rev_lev; else if (cm->classmark2_len > 0) rev_lev = (cm->classmark2[0] >> 5) & 0x3; return rev_lev >= 2; } static const struct osmo_stat_item_desc bts_stat_desc[] = { { "chanloadavg", "Channel load average.", "%", 16, 0 }, { "T3122", "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator.", "s", 16, GSM_T3122_DEFAULT }, }; static const struct osmo_stat_item_group_desc bts_statg_desc = { .group_name_prefix = "bts", .group_description = "base transceiver station", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_items = ARRAY_SIZE(bts_stat_desc), .item_desc = bts_stat_desc, }; void gsm_abis_mo_reset(struct gsm_abis_mo *mo) { mo->nm_state.operational = NM_OPSTATE_NULL; mo->nm_state.availability = NM_AVSTATE_POWER_OFF; } static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts, uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3) { mo->bts = bts; mo->obj_class = obj_class; mo->obj_inst.bts_nr = p1; mo->obj_inst.trx_nr = p2; mo->obj_inst.ts_nr = p3; gsm_abis_mo_reset(mo); } const struct value_string bts_attribute_names[] = { OSMO_VALUE_STRING(BTS_TYPE_VARIANT), OSMO_VALUE_STRING(BTS_SUB_MODEL), OSMO_VALUE_STRING(TRX_PHY_VERSION), { 0, NULL } }; enum bts_attribute str2btsattr(const char *s) { return get_string_value(bts_attribute_names, s); } const char *btsatttr2str(enum bts_attribute v) { return get_value_string(bts_attribute_names, v); } const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = { { BTS_UNKNOWN, "unknown" }, { BTS_OSMO_LITECELL15, "osmo-bts-lc15" }, { BTS_OSMO_OCTPHY, "osmo-bts-octphy" }, { BTS_OSMO_SYSMO, "osmo-bts-sysmo" }, { BTS_OSMO_TRX, "omso-bts-trx" }, { 0, NULL } }; enum gsm_bts_type_variant str2btsvariant(const char *arg) { return get_string_value(osmo_bts_variant_names, arg); } const char *btsvariant2str(enum gsm_bts_type_variant v) { return get_value_string(osmo_bts_variant_names, v); } const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = { { GSM_BTS_TYPE_UNKNOWN, "unknown" }, { GSM_BTS_TYPE_BS11, "bs11" }, { GSM_BTS_TYPE_NANOBTS, "nanobts" }, { GSM_BTS_TYPE_RBS2000, "rbs2000" }, { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, { GSM_BTS_TYPE_OSMOBTS, "sysmobts" }, { 0, NULL } }; enum gsm_bts_type str2btstype(const char *arg) { return get_string_value(bts_type_names, arg); } const char *btstype2str(enum gsm_bts_type type) { return get_value_string(bts_type_names, type); } const struct value_string gsm_chreq_descs[] = { { GSM_CHREQ_REASON_EMERG, "emergency call" }, { GSM_CHREQ_REASON_PAG, "answer to paging" }, { GSM_CHREQ_REASON_CALL, "call re-establishment" }, { GSM_CHREQ_REASON_LOCATION_UPD,"Location updating" }, { GSM_CHREQ_REASON_PDCH, "one phase packet access" }, { GSM_CHREQ_REASON_OTHER, "other" }, { 0, NULL } }; const struct value_string gsm_pchant_names[13] = { { GSM_PCHAN_NONE, "NONE" }, { GSM_PCHAN_CCCH, "CCCH" }, { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, { GSM_PCHAN_TCH_F, "TCH/F" }, { GSM_PCHAN_TCH_H, "TCH/H" }, { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, { GSM_PCHAN_PDCH, "PDCH" }, { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" }, { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" }, { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH/F_TCH/H_PDCH" }, { 0, NULL } }; const struct value_string gsm_pchant_descs[13] = { { GSM_PCHAN_NONE, "Physical Channel not configured" }, { GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" }, { GSM_PCHAN_CCCH_SDCCH4, "FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)" }, { GSM_PCHAN_TCH_F, "TCH/F + FACCH/F + SACCH (Comb. I)" }, { GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" }, { GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" }, { GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" }, { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" }, { GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" }, { GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" }, { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" }, { GSM_PCHAN_TCH_F_TCH_H_PDCH, "Dynamic TCH/F or TCH/H or GPRS PDCH" }, { 0, NULL } }; const char *gsm_pchan_name(enum gsm_phys_chan_config c) { return get_value_string(gsm_pchant_names, c); } enum gsm_phys_chan_config gsm_pchan_parse(const char *name) { return get_string_value(gsm_pchant_names, name); } /* TODO: move to libosmocore, next to gsm_chan_t_names? */ const char *gsm_lchant_name(enum gsm_chan_t c) { return get_value_string(gsm_chan_t_names, c); } static const struct value_string lchan_s_names[] = { { LCHAN_S_NONE, "NONE" }, { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, { LCHAN_S_ACTIVE, "ACTIVE" }, { LCHAN_S_INACTIVE, "INACTIVE" }, { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, { 0, NULL } }; const char *gsm_lchans_name(enum gsm_lchan_state s) { return get_value_string(lchan_s_names, s); } static const struct value_string chreq_names[] = { { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, { GSM_CHREQ_REASON_PAG, "PAGING" }, { GSM_CHREQ_REASON_CALL, "CALL" }, { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, { GSM_CHREQ_REASON_OTHER, "OTHER" }, { 0, NULL } }; const char *gsm_chreq_name(enum gsm_chreq_reason_t c) { return get_value_string(chreq_names, c); } struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) { struct gsm_bts *bts; if (num >= net->num_bts) return NULL; llist_for_each_entry(bts, &net->bts_list, list) { if (bts->nr == num) return bts; } return NULL; } struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) { struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); int k; if (!trx) return NULL; trx->bts = bts; trx->nr = bts->num_trx++; trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, bts->nr, trx->nr, 0xff); gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, bts->nr, trx->nr, 0xff); for (k = 0; k < TRX_NR_TS; k++) { struct gsm_bts_trx_ts *ts = &trx->ts[k]; int l; ts->trx = trx; ts->nr = k; ts->pchan = GSM_PCHAN_NONE; ts->dyn.pchan_is = GSM_PCHAN_NONE; ts->dyn.pchan_want = GSM_PCHAN_NONE; ts->tsc = -1; gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, bts->nr, trx->nr, ts->nr); ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); ts->hopping.arfcns.data = ts->hopping.arfcns_data; ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); ts->hopping.ma.data = ts->hopping.ma_data; for (l = 0; l < TS_MAX_LCHAN; l++) { struct gsm_lchan *lchan; char *name; lchan = &ts->lchan[l]; lchan->ts = ts; lchan->nr = l; lchan->type = GSM_LCHAN_NONE; name = gsm_lchan_name_compute(lchan); lchan->name = talloc_strdup(trx, name); } } if (trx->nr != 0) trx->nominal_power = bts->c0->nominal_power; llist_add_tail(&trx->list, &bts->trx_list); return trx; } static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; static const uint8_t bts_cell_timer_default[] = { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; static const struct gprs_rlc_cfg rlc_cfg_default = { .parameter = { [RLC_T3142] = 20, [RLC_T3169] = 5, [RLC_T3191] = 5, [RLC_T3193] = 160, /* 10ms */ [RLC_T3195] = 5, [RLC_N3101] = 10, [RLC_N3103] = 4, [RLC_N3105] = 8, [CV_COUNTDOWN] = 15, [T_DL_TBF_EXT] = 250 * 10, /* ms */ [T_UL_TBF_EXT] = 250 * 10, /* ms */ }, .paging = { .repeat_time = 5 * 50, /* ms */ .repeat_count = 3, }, .cs_mask = 0x1fff, .initial_cs = 2, .initial_mcs = 6, }; /* Initialize those parts that don't require osmo-bsc specific dependencies. * This part is shared among the thin programs in osmo-bsc/src/utils/. * osmo-bsc requires further initialization that pulls in more dependencies (see * bsc_bts_alloc_register()). */ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) { struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); int i; if (!bts) return NULL; bts->nr = bts_num; bts->num_trx = 0; INIT_LLIST_HEAD(&bts->trx_list); bts->network = net; bts->ms_max_power = 15; /* dBm */ gsm_mo_init(&bts->mo, bts, NM_OC_BTS, bts->nr, 0xff, 0xff); gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { bts->gprs.nsvc[i].bts = bts; bts->gprs.nsvc[i].id = i; gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, bts->nr, i, 0xff); } memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, sizeof(bts->gprs.nse.timer)); gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, bts->nr, 0xff, 0xff); memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, sizeof(bts->gprs.cell.timer)); gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, bts->nr, 0xff, 0xff); memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, sizeof(bts->gprs.cell.rlc_cfg)); /* init statistics */ bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr); if (!bts->bts_ctrs) { talloc_free(bts); return NULL; } bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, 0); /* create our primary TRX */ bts->c0 = gsm_bts_trx_alloc(bts); if (!bts->c0) { rate_ctr_group_free(bts->bts_ctrs); osmo_stat_item_group_free(bts->bts_statg); talloc_free(bts); return NULL; } bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; bts->rach_b_thresh = -1; bts->rach_ldavg_slots = -1; bts->paging.free_chans_need = -1; INIT_LLIST_HEAD(&bts->paging.pending_requests); bts->features.data = &bts->_features_data[0]; bts->features.data_len = sizeof(bts->_features_data); /* si handling */ bts->bcch_change_mark = 1; bts->chan_load_avg = 0; /* timer overrides */ bts->T3122 = 0; /* not overriden by default */ bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; bts->dtxd = false; bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */ bts->neigh_list_manual_mode = 0; bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */ bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ bts->si_common.cell_sel_par.rxlev_acc_min = 0; bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; bts->si_common.si2quater_neigh_list.thresh_hi = 0; osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; bts->si_common.neigh_list.data_len = sizeof(bts->si_common.data.neigh_list); bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; bts->si_common.si5_neigh_list.data_len = sizeof(bts->si_common.data.si5_neigh_list); bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; bts->si_common.cell_alloc.data_len = sizeof(bts->si_common.data.cell_alloc); bts->si_common.rach_control.re = 1; /* no re-establishment */ bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ bts->si_common.rach_control.t2 = 4; /* no emergency calls */ bts->si_common.chan_desc.att = 1; /* attachment required */ bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ bts->si_common.chan_desc.t3212 = net->t3212; /* Use network's current value */ gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */ INIT_LLIST_HEAD(&bts->abis_queue); INIT_LLIST_HEAD(&bts->loc_list); /* Enable all codecs by default. These get reset to a more fine grained selection IF a * 'codec-support' config appears in the config file (see bsc_vty.c). */ bts->codec = (struct bts_codec_conf){ .hr = 1, .efr = 1, .amr = 1, }; return bts; } /* reset the state of all MO in the BTS */ void gsm_bts_mo_reset(struct gsm_bts *bts) { struct gsm_bts_trx *trx; unsigned int i; gsm_abis_mo_reset(&bts->mo); gsm_abis_mo_reset(&bts->site_mgr.mo); for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); gsm_abis_mo_reset(&bts->gprs.nse.mo); gsm_abis_mo_reset(&bts->gprs.cell.mo); llist_for_each_entry(trx, &bts->trx_list, list) { gsm_abis_mo_reset(&trx->mo); gsm_abis_mo_reset(&trx->bb_transc.mo); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; gsm_abis_mo_reset(&ts->mo); } } } struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) { struct gsm_bts_trx *trx; if (num >= bts->num_trx) return NULL; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nr == num) return trx; } return NULL; } static char ts2str[255]; char *gsm_trx_name(const struct gsm_bts_trx *trx) { if (!trx) snprintf(ts2str, sizeof(ts2str), "(trx=NULL)"); else snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", trx->bts->nr, trx->nr); return ts2str; } char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) { snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", ts->trx->bts->nr, ts->trx->nr, ts->nr); return ts2str; } /*! Log timeslot number with full pchan information */ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts) { switch (ts->pchan) { case GSM_PCHAN_TCH_F_TCH_H_PDCH: if (ts->dyn.pchan_is == ts->dyn.pchan_want) snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), gsm_pchan_name(ts->dyn.pchan_is)); else snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s" " switching %s -> %s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), gsm_pchan_name(ts->dyn.pchan_is), gsm_pchan_name(ts->dyn.pchan_want)); break; case GSM_PCHAN_TCH_F_PDCH: if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" : "TCH/F"); else snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s" " switching %s -> %s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan), (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" : "TCH/F", (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" : "TCH/F"); break; default: snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan)); break; } return ts2str; } char *gsm_lchan_name_compute(const struct gsm_lchan *lchan) { struct gsm_bts_trx_ts *ts = lchan->ts; snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); return ts2str; } /* obtain the MO structure for a given object instance */ static inline struct gsm_abis_mo * gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_bts_trx *trx; struct gsm_abis_mo *mo = NULL; switch (obj_class) { case NM_OC_BTS: mo = &bts->mo; break; case NM_OC_RADIO_CARRIER: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->mo; break; case NM_OC_BASEB_TRANSC: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bb_transc.mo; break; case NM_OC_CHANNEL: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); if (obj_inst->ts_nr >= TRX_NR_TS) return NULL; mo = &trx->ts[obj_inst->ts_nr].mo; break; case NM_OC_SITE_MANAGER: mo = &bts->site_mgr.mo; break; case NM_OC_BS11: switch (obj_inst->bts_nr) { case BS11_OBJ_CCLK: mo = &bts->bs11.cclk.mo; break; case BS11_OBJ_BBSIG: if (obj_inst->ts_nr > bts->num_trx) return NULL; trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bs11.bbsig.mo; break; case BS11_OBJ_PA: if (obj_inst->ts_nr > bts->num_trx) return NULL; trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); mo = &trx->bs11.pa.mo; break; default: return NULL; } break; case NM_OC_BS11_RACK: mo = &bts->bs11.rack.mo; break; case NM_OC_BS11_ENVABTSE: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) return NULL; mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; break; case NM_OC_GPRS_NSE: mo = &bts->gprs.nse.mo; break; case NM_OC_GPRS_CELL: mo = &bts->gprs.cell.mo; break; case NM_OC_GPRS_NSVC: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) return NULL; mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; break; } return mo; } /* obtain the gsm_nm_state data structure for a given object instance */ struct gsm_nm_state * gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_abis_mo *mo; mo = gsm_objclass2mo(bts, obj_class, obj_inst); if (!mo) return NULL; return &mo->nm_state; } /* obtain the in-memory data structure of a given object instance */ void * gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, const struct abis_om_obj_inst *obj_inst) { struct gsm_bts_trx *trx; void *obj = NULL; switch (obj_class) { case NM_OC_BTS: obj = bts; break; case NM_OC_RADIO_CARRIER: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); obj = trx; break; case NM_OC_BASEB_TRANSC: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); obj = &trx->bb_transc; break; case NM_OC_CHANNEL: if (obj_inst->trx_nr >= bts->num_trx) { return NULL; } trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); if (obj_inst->ts_nr >= TRX_NR_TS) return NULL; obj = &trx->ts[obj_inst->ts_nr]; break; case NM_OC_SITE_MANAGER: obj = &bts->site_mgr; break; case NM_OC_GPRS_NSE: obj = &bts->gprs.nse; break; case NM_OC_GPRS_CELL: obj = &bts->gprs.cell; break; case NM_OC_GPRS_NSVC: if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) return NULL; obj = &bts->gprs.nsvc[obj_inst->trx_nr]; break; } return obj; } /* See Table 10.5.25 of GSM04.08 */ uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan, uint8_t ts_nr, uint8_t lchan_nr) { uint8_t cbits, chan_nr; switch (pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_F_PDCH: OSMO_ASSERT(lchan_nr == 0); cbits = 0x01; break; case GSM_PCHAN_PDCH: OSMO_ASSERT(lchan_nr == 0); cbits = RSL_CHAN_OSMO_PDCH >> 3; break; case GSM_PCHAN_TCH_H: OSMO_ASSERT(lchan_nr < 2); cbits = 0x02; cbits += lchan_nr; break; case GSM_PCHAN_CCCH_SDCCH4: case GSM_PCHAN_CCCH_SDCCH4_CBCH: /* * As a special hack for BCCH, lchan_nr == 4 may be passed * here. This should never be sent in an RSL message. * See osmo-bts-xxx/oml.c:opstart_compl(). */ if (lchan_nr == CCCH_LCHAN) chan_nr = 0; else OSMO_ASSERT(lchan_nr < 4); cbits = 0x04; cbits += lchan_nr; break; case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: OSMO_ASSERT(lchan_nr < 8); cbits = 0x08; cbits += lchan_nr; break; default: case GSM_PCHAN_CCCH: OSMO_ASSERT(lchan_nr == 0); cbits = 0x10; break; } chan_nr = (cbits << 3) | (ts_nr & 0x7); return chan_nr; } uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) { enum gsm_phys_chan_config pchan = lchan->ts->pchan; if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) return gsm_lchan_as_pchan2chan_nr(lchan, lchan->ts->dyn.pchan_is); return gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr); } uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan, enum gsm_phys_chan_config as_pchan) { if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && as_pchan == GSM_PCHAN_PDCH) return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK); return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr); } /* return the gsm_lchan for the CBCH (if it exists at all) */ struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts) { struct gsm_lchan *lchan = NULL; struct gsm_bts_trx *trx = bts->c0; if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) lchan = &trx->ts[0].lchan[2]; else { int i; for (i = 0; i < 8; i++) { if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) { lchan = &trx->ts[i].lchan[2]; break; } } } return lchan; } /* determine logical channel based on TRX and channel number IE */ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, int *rc) { uint8_t ts_nr = chan_nr & 0x07; uint8_t cbits = chan_nr >> 3; uint8_t lch_idx; struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; bool ok = true; if (rc) *rc = -EINVAL; if (cbits == 0x01) { lch_idx = 0; /* TCH/F */ if (ts->pchan != GSM_PCHAN_TCH_F && ts->pchan != GSM_PCHAN_PDCH && ts->pchan != GSM_PCHAN_TCH_F_PDCH && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && (ts->dyn.pchan_is == GSM_PCHAN_TCH_F || ts->dyn.pchan_want == GSM_PCHAN_TCH_F))) ok = false; } else if ((cbits & 0x1e) == 0x02) { lch_idx = cbits & 0x1; /* TCH/H */ if (ts->pchan != GSM_PCHAN_TCH_H && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH && (ts->dyn.pchan_is == GSM_PCHAN_TCH_H || ts->dyn.pchan_want == GSM_PCHAN_TCH_H))) ok = false; } else if ((cbits & 0x1c) == 0x04) { lch_idx = cbits & 0x3; /* SDCCH/4 */ if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) ok = false; } else if ((cbits & 0x18) == 0x08) { lch_idx = cbits & 0x7; /* SDCCH/8 */ if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C && ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH) ok = false; } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { lch_idx = 0; if (ts->pchan != GSM_PCHAN_CCCH && ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) ok = false; /* FIXME: we should not return first sdcch4 !!! */ } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) { lch_idx = 0; if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) ok = false; } else return NULL; if (rc && ok) *rc = 0; return &ts->lchan[lch_idx]; } static const uint8_t subslots_per_pchan[] = { [GSM_PCHAN_NONE] = 0, [GSM_PCHAN_CCCH] = 0, [GSM_PCHAN_PDCH] = 0, [GSM_PCHAN_CCCH_SDCCH4] = 4, [GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_H] = 2, [GSM_PCHAN_SDCCH8_SACCH8C] = 8, [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4, [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8, /* * GSM_PCHAN_TCH_F_PDCH and GSM_PCHAN_TCH_F_TCH_H_PDCH should not be * part of this, those TS are handled according to their dynamic state. */ }; /*! Return the actual pchan type, also heeding dynamic TS. */ enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts) { switch (ts->pchan) { case GSM_PCHAN_TCH_F_TCH_H_PDCH: return ts->dyn.pchan_is; case GSM_PCHAN_TCH_F_PDCH: if (ts->flags & TS_F_PDCH_ACTIVE) return GSM_PCHAN_PDCH; else return GSM_PCHAN_TCH_F; default: return ts->pchan; } } /*! According to ts->pchan and possibly ts->dyn_pchan, return the number of * logical channels available in the timeslot. */ uint8_t ts_subslots(struct gsm_bts_trx_ts *ts) { return subslots_per_pchan[ts_pchan(ts)]; } static bool pchan_is_tch(enum gsm_phys_chan_config pchan) { switch (pchan) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: return true; default: return false; } } bool ts_is_tch(struct gsm_bts_trx_ts *ts) { return pchan_is_tch(ts_pchan(ts)); } bool trx_is_usable(const struct gsm_bts_trx *trx) { /* FIXME: How does this behave for BS-11 ? */ if (is_ipaccess_bts(trx->bts)) { if (!nm_is_running(&trx->mo.nm_state) || !nm_is_running(&trx->bb_transc.mo.nm_state)) return false; } return true; } void gsm_trx_mark_all_ts_uninitialized(struct gsm_bts_trx *trx) { int i; for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; ts->initialized = false; } } void gsm_bts_mark_all_ts_uninitialized(struct gsm_bts *bts) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) gsm_trx_mark_all_ts_uninitialized(trx); } /* Trigger initial timeslot actions iff both OML and RSL are setup. */ void gsm_ts_check_init(struct gsm_bts_trx_ts *ts) { struct gsm_bts *bts = ts->trx->bts; if (bts->model->oml_is_ts_ready && !bts->model->oml_is_ts_ready(ts)) return; if (!ts->trx->rsl_link) return; if (ts->initialized) return; ts->initialized = on_gsm_ts_init(ts); } void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, const struct gsm_lchan *lchan) { uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; cd->chan_nr = gsm_lchan2chan_nr(lchan); if (!lchan->ts->hopping.enabled) { cd->h0.tsc = gsm_ts_tsc(lchan->ts); cd->h0.h = 0; cd->h0.arfcn_high = arfcn >> 8; cd->h0.arfcn_low = arfcn & 0xff; } else { cd->h1.tsc = gsm_ts_tsc(lchan->ts); cd->h1.h = 1; cd->h1.maio_high = lchan->ts->hopping.maio >> 2; cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; cd->h1.hsn = lchan->ts->hopping.hsn; } } bool nm_is_running(const struct gsm_nm_state *s) { return (s->operational == NM_OPSTATE_ENABLED) && ( (s->availability == NM_AVSTATE_OK) || (s->availability == 0xff) ); } osmo-bsc-1.3.0/src/osmo-bsc/handover_cfg.c000066400000000000000000000046771332665256100203660ustar00rootroot00000000000000/* OsmoBSC handover configuration implementation */ /* (C) 2009-2010 by Andreas Eversberg * (C) 2009-2010 by Harald Welte * (C) 2017-2018 by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * * Author: Andreas Eversberg * Neels Hofmeyr * * 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 struct handover_cfg { struct handover_cfg *higher_level_cfg; #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY3, VTY4, VTY5, VTY6) \ TYPE NAME; \ bool has_##NAME; HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER }; struct handover_cfg *ho_cfg_init(void *ctx, struct handover_cfg *higher_level_cfg) { struct handover_cfg *ho = talloc_zero(ctx, struct handover_cfg); OSMO_ASSERT(ho); ho->higher_level_cfg = higher_level_cfg; return ho; } #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY_ARG_EVAL, VTY4, VTY5, VTY6) \ TYPE ho_get_##NAME(struct handover_cfg *ho) \ { \ if (ho->has_##NAME) \ return ho->NAME; \ if (ho->higher_level_cfg) \ return ho_get_##NAME(ho->higher_level_cfg); \ return VTY_ARG_EVAL(#DEFAULT_VAL); \ } \ \ void ho_set_##NAME(struct handover_cfg *ho, TYPE value) \ { \ ho->NAME = value; \ ho->has_##NAME = true; \ } \ \ bool ho_isset_##NAME(struct handover_cfg *ho) \ { \ return ho->has_##NAME; \ } \ \ void ho_clear_##NAME(struct handover_cfg *ho) \ { \ ho->has_##NAME = false; \ } \ \ bool ho_isset_on_parent_##NAME(struct handover_cfg *ho) \ { \ return ho->higher_level_cfg \ && (ho_isset_##NAME(ho->higher_level_cfg) \ || ho_isset_on_parent_##NAME(ho->higher_level_cfg)); \ } HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER osmo-bsc-1.3.0/src/osmo-bsc/handover_decision.c000066400000000000000000000211331332665256100214060ustar00rootroot00000000000000/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This * only implements the handover algorithm/decision, but not execution * of it */ /* (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 #include #include #include #include #include #include #include #include #include /* Find BTS by ARFCN and BSIC */ struct gsm_bts *bts_by_arfcn_bsic(const struct gsm_network *net, uint16_t arfcn, uint8_t bsic) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) { if (bts->c0->arfcn == arfcn && bts->bsic == bsic) return bts; } return NULL; } /* issue handover to a cell identified by ARFCN and BSIC */ static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, uint16_t arfcn, uint8_t bsic) { struct gsm_bts *new_bts; /* resolve the gsm_bts structure for the best neighbor */ /* FIXME: use some better heuristics here to determine which cell * using this ARFCN really is closest to the target cell. For * now we simply assume that each ARFCN will only be used by one * cell */ new_bts = bts_by_arfcn_bsic(lchan->ts->trx->bts->network, arfcn, bsic); if (!new_bts) { LOGP(DHODEC, LOGL_NOTICE, "unable to determine neighbor BTS " "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); return -EINVAL; } /* and actually try to handover to that cell */ return bsc_handover_start(HODEC1, lchan, new_bts, lchan->type); } /* did we get a RXLEV for a given cell in the given report? */ static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, uint16_t arfcn, uint8_t bsic) { int i; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; /* search for matching report */ if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) continue; mrc->flags |= MRC_F_PROCESSED; return mrc->rxlev; } return -ENODEV; } /* obtain averaged rxlev for given neighbor */ static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) { unsigned int i, idx; int avg = 0; /* reduce window to the actual number of existing measurements */ if (window > nmp->rxlev_cnt) window = nmp->rxlev_cnt; /* this should never happen */ if (window <= 0) { LOGP(DHODEC, LOGL_ERROR, "Requested Neighbor RxLev for invalid window size of %d\n", window); return 0; } idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), window); for (i = 0; i < window; i++) { int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); avg += nmp->rxlev[j]; } return avg / window; } /* find empty or evict bad neighbor */ static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) { int j, worst = 999999; struct neigh_meas_proc *nmp_worst = NULL; /* first try to find an empty/unused slot */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; if (!nmp->arfcn) return nmp; } /* no empty slot found. evict worst neighbor from list */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); if (!nmp_worst || avg < worst) { worst = avg; nmp_worst = nmp; } } return nmp_worst; } /* process neighbor cell measurement reports */ static void process_meas_neigh(struct gsm_meas_rep *mr) { int i, j, idx; /* for each reported cell, try to update global state */ for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; unsigned int idx; int rxlev; /* skip unused entries */ if (!nmp->arfcn) continue; rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); if (rxlev >= 0) { nmp->rxlev[idx] = rxlev; nmp->last_seen_nr = mr->nr; } else nmp->rxlev[idx] = 0; nmp->rxlev_cnt++; } /* iterate over list of reported cells, check if we did not * process all of them */ for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; struct neigh_meas_proc *nmp; if (mrc->flags & MRC_F_PROCESSED) continue; nmp = find_evict_neigh(mr->lchan); nmp->arfcn = mrc->arfcn; nmp->bsic = mrc->bsic; nmp->rxlev_cnt = 0; idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); nmp->rxlev[idx] = mrc->rxlev; nmp->rxlev_cnt++; nmp->last_seen_nr = mr->nr; mrc->flags |= MRC_F_PROCESSED; } } /* attempt to do a handover */ static int attempt_handover(struct gsm_meas_rep *mr) { struct gsm_bts *bts = mr->lchan->ts->trx->bts; struct neigh_meas_proc *best_cell = NULL; unsigned int best_better_db = 0; int i; if (!ho_get_ho_active(bts->ho)) return 0; /* find the best cell in this report that is at least RXLEV_HYST * better than the current serving cell */ for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; int avg, better; /* skip empty slots */ if (nmp->arfcn == 0) continue; /* caculate average rxlev for this cell over the window */ avg = neigh_meas_avg(nmp, ho_get_hodec1_rxlev_neigh_avg_win(bts->ho)); /* check if hysteresis is fulfilled */ if (avg < mr->dl.full.rx_lev + ho_get_hodec1_pwr_hysteresis(bts->ho)) continue; better = avg - mr->dl.full.rx_lev; if (better > best_better_db) { best_cell = nmp; best_better_db = better; } } if (!best_cell) return 0; return handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); } /* process an already parsed measurement report and decide if we want to * attempt a handover */ static void on_measurement_report(struct gsm_meas_rep *mr) { struct gsm_bts *bts = mr->lchan->ts->trx->bts; enum meas_rep_field dlev, dqual; int av_rxlev; unsigned int pwr_interval; /* If this cell does not use handover algorithm 1, then we're not responsible. */ if (ho_get_algorithm(bts->ho) != 1) return; /* we currently only do handover for TCH channels */ switch (mr->lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: break; default: return; } if (mr->flags & MEAS_REP_F_DL_DTX) { dlev = MEAS_REP_DL_RXLEV_SUB; dqual = MEAS_REP_DL_RXQUAL_SUB; } else { dlev = MEAS_REP_DL_RXLEV_FULL; dqual = MEAS_REP_DL_RXQUAL_FULL; } /* parse actual neighbor cell info */ if (mr->num_cell > 0 && mr->num_cell < 7) process_meas_neigh(mr); av_rxlev = get_meas_rep_avg(mr->lchan, dlev, ho_get_hodec1_rxlev_avg_win(bts->ho)); /* Interference HO */ if (rxlev2dbm(av_rxlev) > -85 && meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) { LOGPC(DHO, LOGL_INFO, "HO cause: Interference HO av_rxlev=%d dBm\n", rxlev2dbm(av_rxlev)); attempt_handover(mr); return; } /* Bad Quality */ if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) { LOGPC(DHO, LOGL_INFO, "HO cause: Bad Quality av_rxlev=%d dBm\n", rxlev2dbm(av_rxlev)); attempt_handover(mr); return; } /* Low Level */ if (rxlev2dbm(av_rxlev) <= -110) { LOGPC(DHO, LOGL_INFO, "HO cause: Low Level av_rxlev=%d dBm\n", rxlev2dbm(av_rxlev)); attempt_handover(mr); return; } /* Distance */ if (mr->ms_l1.ta > ho_get_hodec1_max_distance(bts->ho)) { LOGPC(DHO, LOGL_INFO, "HO cause: Distance av_rxlev=%d dBm ta=%d \n", rxlev2dbm(av_rxlev), mr->ms_l1.ta); attempt_handover(mr); return; } /* Power Budget AKA Better Cell */ pwr_interval = ho_get_hodec1_pwr_interval(bts->ho); /* handover_cfg.h defines pwr_interval as [1..99], but since we're using it in a modulo below, * assert non-zero to clarify. */ OSMO_ASSERT(pwr_interval); if ((mr->nr % pwr_interval) == pwr_interval - 1) attempt_handover(mr); } struct handover_decision_callbacks hodec1_callbacks = { .hodec_id = HODEC1, .on_measurement_report = on_measurement_report, }; void handover_decision_1_init(void) { handover_decision_callbacks_register(&hodec1_callbacks); } osmo-bsc-1.3.0/src/osmo-bsc/handover_decision_2.c000066400000000000000000001611441332665256100216360ustar00rootroot00000000000000/* Handover Decision Algorithm 2 for intra-BSC (inter-BTS) handover, public API for OsmoBSC. */ /* (C) 2009 by Andreas Eversberg * (C) 2017-2018 by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * * Author: Andreas Eversberg * Neels Hofmeyr * * 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 #define LOGPHOBTS(bts, level, fmt, args...) \ LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args) #define LOGPHOLCHAN(lchan, level, fmt, args...) \ LOGP(DHODEC, level, "(lchan %u.%u%u%u %s %s) (subscr %s) " fmt, \ lchan->ts->trx->bts->nr, \ lchan->ts->trx->nr, \ lchan->ts->nr, \ lchan->nr, \ gsm_lchant_name(lchan->type), \ gsm48_chan_mode_name(lchan->tch_mode), \ bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \ ## args) #define LOGPHOLCHANTOBTS(lchan, new_bts, level, fmt, args...) \ LOGP(DHODEC, level, "(lchan %u.%u%u%u %s %s)->(BTS %u) (subscr %s) " fmt, \ lchan->ts->trx->bts->nr, \ lchan->ts->trx->nr, \ lchan->ts->nr, \ lchan->nr, \ gsm_lchant_name(lchan->type), \ gsm48_chan_mode_name(lchan->tch_mode), \ new_bts->nr, \ bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \ ## args) #define REQUIREMENT_A_TCHF 0x01 #define REQUIREMENT_B_TCHF 0x02 #define REQUIREMENT_C_TCHF 0x04 #define REQUIREMENT_A_TCHH 0x10 #define REQUIREMENT_B_TCHH 0x20 #define REQUIREMENT_C_TCHH 0x40 #define REQUIREMENT_TCHF_MASK (REQUIREMENT_A_TCHF | REQUIREMENT_B_TCHF | REQUIREMENT_C_TCHF) #define REQUIREMENT_TCHH_MASK (REQUIREMENT_A_TCHH | REQUIREMENT_B_TCHH | REQUIREMENT_C_TCHH) #define REQUIREMENT_A_MASK (REQUIREMENT_A_TCHF | REQUIREMENT_A_TCHH) #define REQUIREMENT_B_MASK (REQUIREMENT_B_TCHF | REQUIREMENT_B_TCHH) #define REQUIREMENT_C_MASK (REQUIREMENT_C_TCHF | REQUIREMENT_C_TCHH) struct ho_candidate { struct gsm_lchan *lchan; /* candidate for whom */ struct gsm_bts *bts; /* target BTS */ uint8_t requirements; /* what is fulfilled */ int avg; /* average RX level */ }; enum ho_reason { HO_REASON_INTERFERENCE, HO_REASON_BAD_QUALITY, HO_REASON_LOW_RXLEVEL, HO_REASON_MAX_DISTANCE, HO_REASON_BETTER_CELL, HO_REASON_CONGESTION, }; static const struct value_string ho_reason_names[] = { { HO_REASON_INTERFERENCE, "interference (bad quality)" }, { HO_REASON_BAD_QUALITY, "bad quality" }, { HO_REASON_LOW_RXLEVEL, "low rxlevel" }, { HO_REASON_MAX_DISTANCE, "maximum allowed distance" }, { HO_REASON_BETTER_CELL, "better cell" }, { HO_REASON_CONGESTION, "congestion" }, {0, NULL} }; static const char *ho_reason_name(int value) { return get_value_string(ho_reason_names, value); } static bool hodec2_initialized = false; static enum ho_reason global_ho_reason; static void congestion_check_cb(void *arg); /* This function gets called on ho2 init, whenever the congestion check interval is changed, and also * when the timer has fired to trigger again after the next congestion check timeout. */ static void reinit_congestion_timer(struct gsm_network *net) { int congestion_check_interval_s; bool was_active; /* Don't setup timers from VTY config parsing before the main program has actually initialized * the data structures. */ if (!hodec2_initialized) return; was_active = net->hodec2.congestion_check_timer.active; if (was_active) osmo_timer_del(&net->hodec2.congestion_check_timer); congestion_check_interval_s = net->hodec2.congestion_check_interval_s; if (congestion_check_interval_s < 1) { if (was_active) LOGP(DHODEC, LOGL_NOTICE, "HO algorithm 2: Disabling congestion check\n"); return; } LOGP(DHODEC, LOGL_DEBUG, "HO algorithm 2: next periodical congestion check in %u seconds\n", congestion_check_interval_s); osmo_timer_setup(&net->hodec2.congestion_check_timer, congestion_check_cb, net); osmo_timer_schedule(&net->hodec2.congestion_check_timer, congestion_check_interval_s, 0); } void hodec2_on_change_congestion_check_interval(struct gsm_network *net, unsigned int new_interval) { net->hodec2.congestion_check_interval_s = new_interval; reinit_congestion_timer(net); } static void conn_penalty_time_add(struct gsm_subscriber_connection *conn, struct gsm_bts *bts, int penalty_time) { if (!conn->hodec2.penalty_timers) { conn->hodec2.penalty_timers = penalty_timers_init(conn); OSMO_ASSERT(conn->hodec2.penalty_timers); } penalty_timers_add(conn->hodec2.penalty_timers, bts, penalty_time); } static unsigned int conn_penalty_time_remaining(struct gsm_subscriber_connection *conn, struct gsm_bts *bts) { if (!conn->hodec2.penalty_timers) return 0; return penalty_timers_remaining(conn->hodec2.penalty_timers, bts); } /* did we get a RXLEV for a given cell in the given report? Mark matches as MRC_F_PROCESSED. */ static struct gsm_meas_rep_cell *cell_in_rep(struct gsm_meas_rep *mr, uint16_t arfcn, uint8_t bsic) { int i; for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; if (mrc->arfcn != arfcn) continue; if (mrc->bsic != bsic) continue; return mrc; } return NULL; } /* obtain averaged rxlev for given neighbor */ static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) { unsigned int i, idx; int avg = 0; /* reduce window to the actual number of existing measurements */ if (window > nmp->rxlev_cnt) window = nmp->rxlev_cnt; /* this should never happen */ if (window <= 0) return 0; idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), window); for (i = 0; i < window; i++) { int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); avg += nmp->rxlev[j]; } return avg / window; } /* Find empty slot or the worst neighbor. */ static struct neigh_meas_proc *find_unused_or_worst_neigh(struct gsm_lchan *lchan) { struct neigh_meas_proc *nmp_worst = NULL; int worst; int j; /* First try to find an empty/unused slot. */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; if (!nmp->arfcn) return nmp; } /* No empty slot found. Return worst neighbor to be evicted. */ worst = 0; /* (overwritten on first loop, but avoid compiler warning) */ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); if (nmp_worst && avg >= worst) continue; worst = avg; nmp_worst = nmp; } return nmp_worst; } /* process neighbor cell measurement reports */ static void process_meas_neigh(struct gsm_meas_rep *mr) { int i, j, idx; /* For each reported cell, try to update measurements we already have from previous reports. */ for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; unsigned int idx; struct gsm_meas_rep_cell *mrc; /* skip unused entries */ if (!nmp->arfcn) continue; mrc = cell_in_rep(mr, nmp->arfcn, nmp->bsic); idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); if (mrc) { nmp->rxlev[idx] = mrc->rxlev; nmp->last_seen_nr = mr->nr; mrc->flags |= MRC_F_PROCESSED; } else { nmp->rxlev[idx] = 0; } nmp->rxlev_cnt++; } /* Add cells that we don't know about yet, if necessary overwriting previous records that reflect * cells with worse receive levels */ for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; struct neigh_meas_proc *nmp; if (mrc->flags & MRC_F_PROCESSED) continue; nmp = find_unused_or_worst_neigh(mr->lchan); nmp->arfcn = mrc->arfcn; nmp->bsic = mrc->bsic; nmp->rxlev_cnt = 0; idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); nmp->rxlev[idx] = mrc->rxlev; nmp->rxlev_cnt++; nmp->last_seen_nr = mr->nr; LOGPHOLCHAN(mr->lchan, LOGL_DEBUG, "neigh %u new in report rxlev=%d last_seen_nr=%u\n", nmp->arfcn, mrc->rxlev, nmp->last_seen_nr); mrc->flags |= MRC_F_PROCESSED; } } static bool codec_type_is_supported(struct gsm_subscriber_connection *conn, enum gsm0808_speech_codec_type type) { int i; struct gsm0808_speech_codec_list *clist = &conn->codec_list; if (!conn->codec_list_present) { /* We don't have a list of supported codecs. This should never happen. */ LOGPHOLCHAN(conn->lchan, LOGL_ERROR, "No Speech Codec List present, accepting all codecs\n"); return true; } for (i = 0; i < clist->len; i++) { if (clist->codec[i].type == type) return true; } LOGPHOLCHAN(conn->lchan, LOGL_DEBUG, "Codec not supported by MS or not allowed by MSC: %s\n", gsm0808_speech_codec_type_name(type)); return false; } /* * Check what requirements the given cell fulfills. * A bit mask of fulfilled requirements is returned. * * Target cell requirement A -- ability to service the call * * In order to successfully handover/assign to a better cell, the target cell * must be able to continue the current call. Therefore the cell must fulfill * the following criteria: * * * The handover must be enabled for the target cell, if it differs from the * originating cell. * * The assignment must be enabled for the cell, if it equals the current * cell. * * The handover penalty timer must not run for the cell. * * If FR, EFR or HR codec is used, the cell must support this codec. * * If FR or EFR codec is used, the cell must have a TCH/F slot type * available. * * If HR codec is used, the cell must have a TCH/H slot type available. * * If AMR codec is used, the cell must have a TCH/F slot available, if AFS * is supported by mobile and BTS. * * If AMR codec is used, the cell must have a TCH/H slot available, if AHS * is supported by mobile and BTS. * * osmo-nitb with built-in MNCC application: * o If AMR codec is used, the cell must support AMR codec with equal codec * rate or rates. (not meaning TCH types) * * If defined, the number of maximum unsynchronized handovers to this cell * may not be exceeded. (This limits processing load for random access * bursts.) * * * Target cell requirement B -- avoid congestion * * In order to prevent congestion of a target cell, the cell must fulfill the * requirement A, but also: * * * The minimum free channels, that are defined for that cell must be * maintained after handover/assignment. * * The minimum free channels are defined for TCH/F and TCH/H slot types * individually. * * * Target cell requirement C -- balance congestion * * In order to balance congested cells, the target cell must fulfill the * requirement A, but also: * * * The target cell (which is congested also) must have more or equal free * slots after handover/assignment. * * The number of free slots are checked for TCH/F and TCH/H slot types * individually. */ static uint8_t check_requirements(struct gsm_lchan *lchan, struct gsm_bts *bts, int tchf_count, int tchh_count) { int count; uint8_t requirement = 0; unsigned int penalty_time; struct gsm_bts *current_bts = lchan->ts->trx->bts; /* Requirement A */ /* the handover/assignment must not be disabled */ if (current_bts == bts) { if (!ho_get_hodec2_as_active(bts->ho)) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "Assignment disabled\n"); return 0; } } else { if (!ho_get_ho_active(bts->ho)) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, handover is disabled in target BTS\n"); return 0; } } /* the handover penalty timer must not run for this bts */ penalty_time = conn_penalty_time_remaining(lchan->conn, bts); if (penalty_time) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, target BTS still in penalty time" " (%u seconds left)\n", penalty_time); return 0; } /* compatibility check for codecs. * if so, the candidates for full rate and half rate are selected */ switch (lchan->tch_mode) { case GSM48_CMODE_SPEECH_V1: switch (lchan->type) { case GSM_LCHAN_TCH_F: /* mandatory */ requirement |= REQUIREMENT_A_TCHF; break; case GSM_LCHAN_TCH_H: if (!bts->codec.hr) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "tch_mode='%s' type='%s' not supported\n", get_value_string(gsm48_chan_mode_names, lchan->tch_mode), gsm_lchant_name(lchan->type)); break; } if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR1)) requirement |= REQUIREMENT_A_TCHH; break; default: LOGPHOLCHAN(lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n", get_value_string(gsm48_chan_mode_names, lchan->tch_mode)); return 0; } break; case GSM48_CMODE_SPEECH_EFR: if (!bts->codec.efr) { LOGPHOBTS(bts, LOGL_DEBUG, "EFR not supported\n"); break; } if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR2)) requirement |= REQUIREMENT_A_TCHF; break; case GSM48_CMODE_SPEECH_AMR: if (!bts->codec.amr) { LOGPHOBTS(bts, LOGL_DEBUG, "AMR not supported\n"); break; } if (codec_type_is_supported(lchan->conn, GSM0808_SCT_FR3)) requirement |= REQUIREMENT_A_TCHF; if (codec_type_is_supported(lchan->conn, GSM0808_SCT_HR3)) requirement |= REQUIREMENT_A_TCHH; break; default: LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "Not even considering: src is not a SPEECH mode lchan\n"); return 0; } /* no candidate, because new cell is incompatible */ if (!requirement) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because codec of MS and BTS are incompatible\n"); return 0; } /* remove slot types that are not available */ if (!tchf_count && requirement & REQUIREMENT_A_TCHF) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "removing TCH/F, since all TCH/F lchans are in use\n"); requirement &= ~(REQUIREMENT_A_TCHF); } if (!tchh_count && requirement & REQUIREMENT_A_TCHH) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "removing TCH/H, since all TCH/H lchans are in use\n"); requirement &= ~(REQUIREMENT_A_TCHH); } if (!requirement) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, because no suitable slots available\n"); return 0; } /* omit same channel type on same BTS (will not change anything) */ if (bts == current_bts) { switch (lchan->type) { case GSM_LCHAN_TCH_F: requirement &= ~(REQUIREMENT_A_TCHF); break; case GSM_LCHAN_TCH_H: requirement &= ~(REQUIREMENT_A_TCHH); break; default: break; } if (!requirement) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "Reassignment within cell not an option, no differing channel types available\n"); return 0; } } #ifdef LEGACY // This was useful in osmo-nitb. We're in osmo-bsc now and have no idea whether the osmo-msc does // internal or external call control. Maybe a future config switch wants to add this behavior? /* Built-in call control requires equal codec rates. Remove rates that are not equal. */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && current_bts->network->mncc_recv != mncc_sock_from_cc) { switch (lchan->type) { case GSM_LCHAN_TCH_F: if ((requirement & REQUIREMENT_A_TCHF) && !!memcmp(¤t_bts->mr_full, &bts->mr_full, sizeof(struct amr_multirate_conf))) requirement &= ~(REQUIREMENT_A_TCHF); if ((requirement & REQUIREMENT_A_TCHH) && !!memcmp(¤t_bts->mr_full, &bts->mr_half, sizeof(struct amr_multirate_conf))) requirement &= ~(REQUIREMENT_A_TCHH); break; case GSM_LCHAN_TCH_H: if ((requirement & REQUIREMENT_A_TCHF) && !!memcmp(¤t_bts->mr_half, &bts->mr_full, sizeof(struct amr_multirate_conf))) requirement &= ~(REQUIREMENT_A_TCHF); if ((requirement & REQUIREMENT_A_TCHH) && !!memcmp(¤t_bts->mr_half, &bts->mr_half, sizeof(struct amr_multirate_conf))) requirement &= ~(REQUIREMENT_A_TCHH); break; default: break; } if (!requirement) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, cannot provide identical codec rate\n"); return 0; } } #endif /* the maximum number of unsynchonized handovers must no be exceeded */ if (current_bts != bts && bsc_ho_count(bts, true) >= ho_get_hodec2_ho_max(bts->ho)) { LOGPHOLCHANTOBTS(lchan, bts, LOGL_DEBUG, "not a candidate, number of allowed handovers (%d) would be exceeded\n", ho_get_hodec2_ho_max(bts->ho)); return 0; } /* Requirement B */ /* the minimum free timeslots that are defined for this cell must * be maintained _after_ handover/assignment */ if (requirement & REQUIREMENT_A_TCHF) { if (tchf_count - 1 >= ho_get_hodec2_tchf_min_slots(bts->ho)) requirement |= REQUIREMENT_B_TCHF; } if (requirement & REQUIREMENT_A_TCHH) { if (tchh_count - 1 >= ho_get_hodec2_tchh_min_slots(bts->ho)) requirement |= REQUIREMENT_B_TCHH; } /* Requirement C */ /* the nr of free timeslots of the target cell must be >= the * free slots of the current cell _after_ handover/assignment */ count = bts_count_free_ts(current_bts, (lchan->type == GSM_LCHAN_TCH_H) ? GSM_PCHAN_TCH_H : GSM_PCHAN_TCH_F); if (requirement & REQUIREMENT_A_TCHF) { if (tchf_count - 1 >= count + 1) requirement |= REQUIREMENT_C_TCHF; } if (requirement & REQUIREMENT_A_TCHH) { if (tchh_count - 1 >= count + 1) requirement |= REQUIREMENT_C_TCHH; } /* return mask of fulfilled requirements */ return requirement; } /* Trigger handover or assignment depending on the target BTS */ static int trigger_handover_or_assignment(struct gsm_lchan *lchan, struct gsm_bts *new_bts, uint8_t requirements) { struct gsm_bts *current_bts = lchan->ts->trx->bts; int afs_bias = 0; bool full_rate = false; /* afs_bias becomes > 0, if AFS is used and is improved */ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) afs_bias = ho_get_hodec2_afs_bias_rxlev(new_bts->ho); /* select TCH rate, prefer TCH/F if AFS is improved */ switch (lchan->type) { case GSM_LCHAN_TCH_F: /* keep on full rate, if TCH/F is a candidate */ if ((requirements & REQUIREMENT_TCHF_MASK)) { if (current_bts == new_bts) { LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n"); return 0; } full_rate = true; break; } /* change to half rate */ if (!(requirements & REQUIREMENT_TCHH_MASK)) { LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR, "neither TCH/F nor TCH/H requested, aborting ho/as\n"); return -EINVAL; } break; case GSM_LCHAN_TCH_H: /* change to full rate if AFS is improved and a candidate */ if (afs_bias > 0 && (requirements & REQUIREMENT_TCHF_MASK)) { full_rate = true; LOGPHOLCHAN(lchan, LOGL_DEBUG, "[Improve AHS->AFS]\n"); break; } /* change to full rate if the only candidate */ if ((requirements & REQUIREMENT_TCHF_MASK) && !(requirements & REQUIREMENT_TCHH_MASK)) { full_rate = true; break; } /* keep on half rate */ if (!(requirements & REQUIREMENT_TCHH_MASK)) { LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR, "neither TCH/F nor TCH/H requested, aborting ho/as\n"); return -EINVAL; } if (current_bts == new_bts) { LOGPHOLCHAN(lchan, LOGL_INFO, "Not performing assignment: Already on target type\n"); return 0; } break; default: LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_ERROR, "lchan is neither TCH/F nor TCH/H, aborting ho/as\n"); return -EINVAL; } /* trigger handover or assignment */ if (current_bts == new_bts) LOGPHOLCHAN(lchan, LOGL_NOTICE, "Triggering assignment to %s, due to %s\n", full_rate ? "TCH/F" : "TCH/H", ho_reason_name(global_ho_reason)); else LOGPHOLCHANTOBTS(lchan, new_bts, LOGL_INFO, "Triggering handover to %s, due to %s\n", full_rate ? "TCH/F" : "TCH/H", ho_reason_name(global_ho_reason)); return bsc_handover_start(HODEC2, lchan, current_bts == new_bts? NULL : new_bts, full_rate? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H); } /* debug collected candidates */ static inline void debug_candidate(struct gsm_lchan *lchan, struct ho_candidate *candidate, struct gsm_bts *neighbor, int8_t rxlev, int tchf_count, int tchh_count) { #define HO_CANDIDATE_FMT(tchx, TCHX) "TCH/" #TCHX "={free %d (want %d), [%s%s%s]%s}" #define HO_CANDIDATE_ARGS(tchx, TCHX) \ tch##tchx##_count, ho_get_hodec2_tch##tchx##_min_slots(candidate->bts->ho), \ candidate->requirements & REQUIREMENT_A_TCH##TCHX ? "A" : \ (candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == 0? "-" : "", \ candidate->requirements & REQUIREMENT_B_TCH##TCHX ? "B" : "", \ candidate->requirements & REQUIREMENT_B_TCH##TCHX ? "C" : "", \ (candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == 0 ? " not a candidate" : \ ((candidate->requirements & REQUIREMENT_TCH##TCHX##_MASK) == REQUIREMENT_A_TCH##TCHX ? \ " more congestion" : \ (candidate->requirements & REQUIREMENT_B_TCH##TCHX ? \ " good" : \ /* now has to be candidate->requirements & REQUIREMENT_C_TCHX != 0: */ \ " less-or-equal congestion")) if (neighbor) LOGPHOLCHANTOBTS(lchan, neighbor, LOGL_DEBUG, "RX level %d -> %d; " HO_CANDIDATE_FMT(f, F) "; " HO_CANDIDATE_FMT(h, H) "\n", rxlev2dbm(rxlev), rxlev2dbm(candidate->avg), HO_CANDIDATE_ARGS(f, F), HO_CANDIDATE_ARGS(h, H)); else LOGPHOLCHANTOBTS(lchan, lchan->ts->trx->bts, LOGL_DEBUG, "RX level %d; " HO_CANDIDATE_FMT(f, F) "; " HO_CANDIDATE_FMT(h, H) "\n", rxlev2dbm(candidate->avg), HO_CANDIDATE_ARGS(f, F), HO_CANDIDATE_ARGS(h, H)); } /* add candidate for re-assignment within the current cell */ static void collect_assignment_candidate(struct gsm_lchan *lchan, struct ho_candidate *clist, unsigned int *candidates, int av_rxlev) { struct gsm_bts *bts = lchan->ts->trx->bts; int tchf_count, tchh_count; struct ho_candidate *c; tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F); tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H); c = &clist[*candidates]; c->lchan = lchan; c->bts = bts; c->requirements = check_requirements(lchan, bts, tchf_count, tchh_count); c->avg = av_rxlev; debug_candidate(lchan, c, NULL, 0, tchf_count, tchh_count); (*candidates)++; } /* add candidates for handover to all neighbor cells */ static void collect_handover_candidate(struct gsm_lchan *lchan, struct neigh_meas_proc *nmp, struct ho_candidate *clist, unsigned int *candidates, bool include_weaker_rxlev, int av_rxlev, int *neighbors_count) { struct gsm_bts *bts = lchan->ts->trx->bts; int tchf_count, tchh_count; struct gsm_bts *neighbor_bts; int avg; struct ho_candidate *c; int min_rxlev; /* skip empty slots */ if (nmp->arfcn == 0) return; if (neighbors_count) (*neighbors_count)++; /* skip if measurement report is old */ if (nmp->last_seen_nr != lchan->meas_rep_last_seen_nr) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "neighbor ARFCN %u measurement report is old" " (nmp->last_seen_nr=%u lchan->meas_rep_last_seen_nr=%u)\n", nmp->arfcn, nmp->last_seen_nr, lchan->meas_rep_last_seen_nr); return; } neighbor_bts = bts_by_arfcn_bsic(bts->network, nmp->arfcn, nmp->bsic); if (!neighbor_bts) { LOGPHOBTS(bts, LOGL_DEBUG, "neighbor ARFCN %u does not belong to this network\n", nmp->arfcn); return; } /* in case we have measurements of our bts, due to misconfiguration */ if (neighbor_bts == bts) { LOGPHOBTS(bts, LOGL_ERROR, "Configuration error: this BTS appears as its own neighbor\n"); return; } /* caculate average rxlev for this cell over the window */ avg = neigh_meas_avg(nmp, ho_get_hodec2_rxlev_neigh_avg_win(bts->ho)); /* Heed rxlev hysteresis only if the RXLEV/RXQUAL/TA levels of the MS aren't critically bad and * we're just looking for an improvement. If levels are critical, we desperately need a handover * and thus skip the hysteresis check. */ if (!include_weaker_rxlev) { unsigned int pwr_hyst = ho_get_hodec2_pwr_hysteresis(bts->ho); if (avg <= (av_rxlev + pwr_hyst)) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "BTS %d is not a candidate, because RX level (%d) is lower" " or equal than current RX level (%d) + hysteresis (%d)\n", neighbor_bts->nr, rxlev2dbm(avg), rxlev2dbm(av_rxlev), pwr_hyst); return; } } /* if the minimum level is not reached */ min_rxlev = ho_get_hodec2_min_rxlev(neighbor_bts->ho); if (rxlev2dbm(avg) < min_rxlev) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "BTS %d is not a candidate, because RX level (%d) is lower" " than its minimum required RX level (%d)\n", neighbor_bts->nr, rxlev2dbm(avg), min_rxlev); return; } tchf_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_F); tchh_count = bts_count_free_ts(neighbor_bts, GSM_PCHAN_TCH_H); c = &clist[*candidates]; c->lchan = lchan; c->bts = neighbor_bts; c->requirements = check_requirements(lchan, neighbor_bts, tchf_count, tchh_count); c->avg = avg; debug_candidate(lchan, c, neighbor_bts, av_rxlev, tchf_count, tchh_count); (*candidates)++; } static void collect_candidates_for_lchan(struct gsm_lchan *lchan, struct ho_candidate *clist, unsigned int *candidates, int *_av_rxlev, bool include_weaker_rxlev) { struct gsm_bts *bts = lchan->ts->trx->bts; int av_rxlev; bool assignment; bool handover; int neighbors_count = 0; unsigned int rxlev_avg_win = ho_get_hodec2_rxlev_avg_win(bts->ho); OSMO_ASSERT(candidates); /* caculate average rxlev for this cell over the window */ av_rxlev = get_meas_rep_avg(lchan, ho_get_hodec2_full_tdma(bts->ho) ? MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB, rxlev_avg_win); if (_av_rxlev) *_av_rxlev = av_rxlev; /* in case there is no measurment report (yet) */ if (av_rxlev < 0) { LOGPHOLCHAN(lchan, LOGL_DEBUG, "Not collecting candidates, not enough measurements" " (got %d, want %u)\n", lchan->meas_rep_count, rxlev_avg_win); return; } assignment = ho_get_hodec2_as_active(bts->ho); handover = ho_get_ho_active(bts->ho); if (assignment) collect_assignment_candidate(lchan, clist, candidates, av_rxlev); if (handover) { int i; for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) { collect_handover_candidate(lchan, &lchan->neigh_meas[i], clist, candidates, include_weaker_rxlev, av_rxlev, &neighbors_count); } } } /* * Search for a alternative / better cell. * * Do not trigger handover/assignment on slots which have already ongoing * handover/assignment processes. If no AFS improvement offset is given, try to * maintain the same TCH rate, if available. * Do not perform this process, if handover and assignment are disabled for * the current cell. * Do not perform handover, if the minimum acceptable RX level * is not reched for this cell. * * If one or more 'better cells' are available, check the current and neighbor * cell measurements in descending order of their RX levels (down-link): * * * Select the best candidate that fulfills requirement B (no congestion * after handover/assignment) and trigger handover or assignment. * * If no candidate fulfills requirement B, select the best candidate that * fulfills requirement C (less or equally congested cells after handover) * and trigger handover or assignment. * * If no candidate fulfills requirement C, do not perform handover nor * assignment. * * If the RX level (down-link) or RX quality (down-link) of the current cell is * below minimum acceptable level, or if the maximum allowed timing advance is * reached or exceeded, check the RX levels (down-link) of the current and * neighbor cells in descending order of their levels: (bad BTS case) * * * Select the best candidate that fulfills requirement B (no congestion after * handover/assignment) and trigger handover or assignment. * * If no candidate fulfills requirement B, select the best candidate that * fulfills requirement C (less or equally congested cells after handover) * and trigger handover or assignment. * * If no candidate fulfills requirement C, select the best candidate that * fulfills requirement A (ignore congestion after handover or assignment) * and trigger handover or assignment. * * If no candidate fulfills requirement A, do not perform handover nor * assignment. * * RX levels (down-link) of current and neighbor cells: * * * The RX levels of the current cell and neighbor cells are improved by a * given offset, if AFS (AMR on TCH/F) is used or is a candidate for * handover/assignment. * * If AMR is used, the requirement for handover is checked for TCH/F and * TCH/H. Both results (if any) are used as a candidate. * * If AMR is used, the requirement for assignment to a different TCH slot * rate is checked. The result (if available) is used as a candidate. * * If minimum RXLEV, minimum RXQUAL or maximum TA are exceeded, the caller should pass * include_weaker_rxlev=true so that handover is performed despite congestion. */ static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev) { struct gsm_bts *bts = lchan->ts->trx->bts; int ahs = (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && lchan->type == GSM_LCHAN_TCH_H); int av_rxlev; struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)]; unsigned int candidates = 0; int i; struct ho_candidate *best_cand = NULL; unsigned int best_better_db; bool best_applied_afs_bias = false; int better; /* check for disabled handover/assignment at the current cell */ if (!ho_get_hodec2_as_active(bts->ho) && !ho_get_ho_active(bts->ho)) { LOGP(DHODEC, LOGL_INFO, "Skipping, Handover and Assignment both disabled in this cell\n"); return 0; } collect_candidates_for_lchan(lchan, clist, &candidates, &av_rxlev, include_weaker_rxlev); /* If assignment is disabled and no neighbor cell report exists, or no neighbor cell qualifies, * we may not even have any candidates. */ if (!candidates) goto no_candidates; /* select best candidate that fulfills requirement B: no congestion after HO */ best_better_db = 0; for (i = 0; i < candidates; i++) { int afs_bias; if (!(clist[i].requirements & REQUIREMENT_B_MASK)) continue; better = clist[i].avg - av_rxlev; /* Apply AFS bias? */ afs_bias = 0; if (ahs && (clist[i].requirements & REQUIREMENT_B_TCHF)) afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); better += afs_bias; if (better > best_better_db) { best_cand = &clist[i]; best_better_db = better; best_applied_afs_bias = afs_bias? true : false; } } /* perform handover, if there is a candidate */ if (best_cand) { LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d%s\n", rxlev2dbm(best_cand->avg), best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : ""); return trigger_handover_or_assignment(lchan, best_cand->bts, best_cand->requirements & REQUIREMENT_B_MASK); } /* select best candidate that fulfills requirement C: less or equal congestion after HO */ best_better_db = 0; for (i = 0; i < candidates; i++) { int afs_bias; if (!(clist[i].requirements & REQUIREMENT_C_MASK)) continue; better = clist[i].avg - av_rxlev; /* Apply AFS bias? */ afs_bias = 0; if (ahs && (clist[i].requirements & REQUIREMENT_C_TCHF)) afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); better += afs_bias; if (better > best_better_db) { best_cand = &clist[i]; best_better_db = better; best_applied_afs_bias = afs_bias? true : false; } } /* perform handover, if there is a candidate */ if (best_cand) { LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d%s\n", rxlev2dbm(best_cand->avg), best_applied_afs_bias? " (applied AHS -> AFS rxlev bias)" : ""); return trigger_handover_or_assignment(lchan, best_cand->bts, best_cand->requirements & REQUIREMENT_C_MASK); } /* we are done in case the MS RXLEV/RXQUAL/TA aren't critical and we're avoiding congestion. */ if (!include_weaker_rxlev) goto no_candidates; /* Select best candidate that fulfills requirement A: can service the call. * From above we know that there are no options that avoid congestion. Here we're trying to find * *any* free lchan that has no critically low RXLEV and is able to handle the MS. */ best_better_db = 0; for (i = 0; i < candidates; i++) { int afs_bias; if (!(clist[i].requirements & REQUIREMENT_A_MASK)) continue; better = clist[i].avg - av_rxlev; /* Apply AFS bias? */ afs_bias = 0; if (ahs && (clist[i].requirements & REQUIREMENT_A_TCHF)) afs_bias = ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); better += afs_bias; if (better > best_better_db) { best_cand = &clist[i]; best_better_db = better; best_applied_afs_bias = afs_bias? true : false; } } /* perform handover, if there is a candidate */ if (best_cand) { LOGPHOLCHANTOBTS(lchan, best_cand->bts, LOGL_INFO, "Best candidate, RX level %d" " with greater congestion found%s\n", rxlev2dbm(best_cand->avg), best_applied_afs_bias ? " (applied AHS -> AFS rxlev bias)" : ""); return trigger_handover_or_assignment(lchan, best_cand->bts, best_cand->requirements & REQUIREMENT_A_MASK); } /* Damn, all is congested, has too low RXLEV or cannot service the voice call due to codec * restrictions or because all lchans are taken. */ no_candidates: if (include_weaker_rxlev) LOGPHOLCHAN(lchan, LOGL_INFO, "No alternative lchan found\n"); else LOGPHOLCHAN(lchan, LOGL_INFO, "No better/less congested neighbor cell found\n"); return 0; } /* * Handover/assignment check, if measurement report is received * * Do not trigger handover/assignment on slots which have already ongoing * handover/assignment processes. * * In case of handover triggered because maximum allowed timing advance is * exceeded, the handover penalty timer is started for the originating cell. * */ static void on_measurement_report(struct gsm_meas_rep *mr) { struct gsm_lchan *lchan = mr->lchan; struct gsm_bts *bts = lchan->ts->trx->bts; int av_rxlev = -EINVAL, av_rxqual = -EINVAL; unsigned int pwr_interval; /* we currently only do handover for TCH channels */ switch (mr->lchan->type) { case GSM_LCHAN_TCH_F: case GSM_LCHAN_TCH_H: break; default: return; } if (log_check_level(DHODEC, LOGL_DEBUG)) { int i; LOGPHOLCHAN(lchan, LOGL_DEBUG, "MEASUREMENT REPORT (%d neighbors)\n", mr->num_cell); for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; LOGPHOLCHAN(lchan, LOGL_DEBUG, " %d: arfcn=%u bsic=%u neigh_idx=%u rxlev=%u flags=%x\n", i, mrc->arfcn, mrc->bsic, mrc->neigh_idx, mrc->rxlev, mrc->flags); } } /* parse actual neighbor cell info */ if (mr->num_cell > 0 && mr->num_cell < 7) process_meas_neigh(mr); /* check for ongoing handover/assignment */ if (!lchan->conn) { LOGPHOLCHAN(lchan, LOGL_ERROR, "Skipping, No subscriber connection???\n"); return; } if (lchan->conn->secondary_lchan) { LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Initial Assignment is still ongoing\n"); return; } if (lchan->conn->ho) { LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Handover already triggered\n"); return; } /* get average levels. if not enought measurements yet, value is < 0 */ av_rxlev = get_meas_rep_avg(lchan, ho_get_hodec2_full_tdma(bts->ho) ? MEAS_REP_DL_RXLEV_FULL : MEAS_REP_DL_RXLEV_SUB, ho_get_hodec2_rxlev_avg_win(bts->ho)); av_rxqual = get_meas_rep_avg(lchan, ho_get_hodec2_full_tdma(bts->ho) ? MEAS_REP_DL_RXQUAL_FULL : MEAS_REP_DL_RXQUAL_SUB, ho_get_hodec2_rxqual_avg_win(bts->ho)); if (av_rxlev < 0 && av_rxqual < 0) { LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Not enough recent measurements\n"); return; } /* improve levels in case of AFS, if defined */ if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { int av_rxlev_was = av_rxlev; int av_rxqual_was = av_rxqual; int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho); int rxqual_bias = ho_get_hodec2_afs_bias_rxqual(bts->ho); if (av_rxlev >= 0) av_rxlev = av_rxlev + rxlev_bias; if (av_rxqual >= 0) av_rxqual = OSMO_MAX(0, av_rxqual - rxqual_bias); LOGPHOLCHAN(lchan, LOGL_DEBUG, "Avg RX level = %d dBm, %+d dBm AFS bias = %d dBm;" " Avg RX quality = %d%s, %+d AFS bias = %d\n", rxlev2dbm(av_rxlev_was), rxlev_bias, rxlev2dbm(av_rxlev), OSMO_MAX(-1, av_rxqual_was), av_rxqual_was < 0 ? " (invalid)" : "", -rxqual_bias, OSMO_MAX(-1, av_rxqual)); } else { LOGPHOLCHAN(lchan, LOGL_DEBUG, "Avg RX level = %d dBm; Avg RX quality = %d%s\n", rxlev2dbm(av_rxlev), OSMO_MAX(-1, av_rxqual), av_rxqual < 0 ? " (invalid)" : ""); } /* Bad Quality */ if (av_rxqual >= 0 && av_rxqual > ho_get_hodec2_min_rxqual(bts->ho)) { if (rxlev2dbm(av_rxlev) > -85) { global_ho_reason = HO_REASON_INTERFERENCE; LOGPHOLCHAN(lchan, LOGL_INFO, "Trying handover/assignment" " due to interference (bad quality)\n"); } else { global_ho_reason = HO_REASON_BAD_QUALITY; LOGPHOLCHAN(lchan, LOGL_INFO, "Trying handover/assignment due to bad quality\n"); } find_alternative_lchan(lchan, true); return; } /* Low Level */ if (av_rxlev >= 0 && rxlev2dbm(av_rxlev) < ho_get_hodec2_min_rxlev(bts->ho)) { global_ho_reason = HO_REASON_LOW_RXLEVEL; LOGPHOLCHAN(lchan, LOGL_NOTICE, "RX level is TOO LOW: %d < %d\n", rxlev2dbm(av_rxlev), ho_get_hodec2_min_rxlev(bts->ho)); find_alternative_lchan(lchan, true); return; } /* Max Distance */ if (lchan->meas_rep_count > 0 && lchan->rqd_ta > ho_get_hodec2_max_distance(bts->ho)) { global_ho_reason = HO_REASON_MAX_DISTANCE; LOGPHOLCHAN(lchan, LOGL_NOTICE, "TA is TOO HIGH: %u > %d\n", lchan->rqd_ta, ho_get_hodec2_max_distance(bts->ho)); /* start penalty timer to prevent comming back too * early. it must be started before selecting a better cell, * so there is no assignment selected, due to running * penalty timer. */ conn_penalty_time_add(lchan->conn, bts, ho_get_hodec2_penalty_max_dist(bts->ho)); find_alternative_lchan(lchan, true); return; } /* pwr_interval's range is 1-99, clarifying that no div-zero shall happen in modulo below: */ pwr_interval = ho_get_hodec2_pwr_interval(bts->ho); OSMO_ASSERT(pwr_interval); /* try handover to a better cell */ if (av_rxlev >= 0 && (mr->nr % pwr_interval) == 0) { global_ho_reason = HO_REASON_BETTER_CELL; find_alternative_lchan(lchan, false); } } /* * Handover/assignment check after timer timeout: * * Even if handover process tries to prevent a congestion, a cell might get * congested due to new call setups or handovers to prevent loss of radio link. * A cell is congested, if not the minimum number of free slots are available. * The minimum number can be defined for TCH/F and TCH/H individually. * * Do not perform congestion check, if no minimum free slots are defined for * a cell. * Do not trigger handover/assignment on slots which have already ongoing * handover/assignment processes. If no AFS improvement offset is given, try to * maintain the same TCH rate, if available. * Do not perform this process, if handover and assignment are disabled for * the current cell. * Do not perform handover, if the minimum acceptable RX level * is not reched for this cell. * Only check candidates that will solve/reduce congestion. * * If a cell is congested, all slots are checked for all their RX levels * (down-link) of the current and neighbor cell measurements in descending * order of their RX levels: * * * Select the best candidate that fulfills requirement B (no congestion after * handover/assignment), trigger handover or assignment. Candidates that will * cause an assignment from AHS (AMR on TCH/H) to AFS (AMR on TCH/F) are * omitted. * o This process repeated until the minimum required number of free slots * are restored or if all cell measurements are checked. The process ends * then, otherwise: * * Select the worst candidate that fulfills requirement B, trigger * assignment. Note that only assignment candidates for changing from AHS to * AFS are left. * o This process repeated until the minimum required number of free slots * are restored or if all cell measurements are checked. The process ends * then, otherwise: * * Select the best candidates that fulfill requirement C (less or equally * congested cells after handover/assignment), trigger handover or * assignment. Candidates that will cause an assignment from AHS (AMR on * TCH/H) to AFS (AMR on TCH/F) are omitted. * o This process repeated until the minimum required number of free slots * are restored or if all cell measurements are checked. The process ends * then, otherwise: * * Select the worst candidate that fulfills requirement C, trigger * assignment. Note that only assignment candidates for changing from AHS to * AFS are left. * o This process repeated until the minimum required number of free slots * are restored or if all cell measurements are checked. */ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int tchh_congestion) { struct gsm_lchan *lc; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; int i, j; struct ho_candidate *clist; unsigned int candidates; struct ho_candidate *best_cand = NULL, *worst_cand = NULL; struct gsm_lchan *delete_lchan = NULL; unsigned int best_avg_db, worst_avg_db; int avg; int rc = 0; int any_ho = 0; int is_improved = 0; if (tchf_congestion < 0) tchf_congestion = 0; if (tchh_congestion < 0) tchh_congestion = 0; LOGPHOBTS(bts, LOGL_INFO, "congested: %d TCH/F and %d TCH/H should be moved\n", tchf_congestion, tchh_congestion); /* allocate array of all bts */ clist = talloc_zero_array(tall_bsc_ctx, struct ho_candidate, bts->num_trx * 8 * 2 * (1 + ARRAY_SIZE(lc->neigh_meas))); if (!clist) return 0; candidates = 0; /* loop through all active lchan and collect candidates */ llist_for_each_entry(trx, &bts->trx_list, list) { if (!trx_is_usable(trx)) continue; for (i = 0; i < 8; i++) { ts = &trx->ts[i]; if (!ts_is_usable(ts)) continue; /* (Do not consider dynamic TS that are in PDCH mode) */ switch (ts_pchan(ts)) { case GSM_PCHAN_TCH_F: lc = &ts->lchan[0]; /* omit if channel not active */ if (lc->type != GSM_LCHAN_TCH_F || lc->state != LCHAN_S_ACTIVE) break; /* omit if there is an ongoing ho/as */ if (!lc->conn || lc->conn->secondary_lchan || lc->conn->ho) break; /* We desperately want to resolve congestion, ignore rxlev when * collecting candidates by passing include_weaker_rxlev=true. */ collect_candidates_for_lchan(lc, clist, &candidates, NULL, true); break; case GSM_PCHAN_TCH_H: for (j = 0; j < 2; j++) { lc = &ts->lchan[j]; /* omit if channel not active */ if (lc->type != GSM_LCHAN_TCH_H || lc->state != LCHAN_S_ACTIVE) continue; /* omit of there is an ongoing ho/as */ if (!lc->conn || lc->conn->secondary_lchan || lc->conn->ho) continue; /* We desperately want to resolve congestion, ignore rxlev when * collecting candidates by passing include_weaker_rxlev=true. */ collect_candidates_for_lchan(lc, clist, &candidates, NULL, true); } break; default: break; } } } if (!candidates) { LOGPHOBTS(bts, LOGL_DEBUG, "No neighbor cells qualify to solve congestion\n"); goto exit; } if (log_check_level(DHODEC, LOGL_DEBUG)) { LOGPHOBTS(bts, LOGL_DEBUG, "Considering %u candidates to solve congestion:\n", candidates); for (i = 0; i < candidates; i++) { LOGPHOLCHANTOBTS(clist[i].lchan, clist[i].bts, LOGL_DEBUG, "#%d: req=0x%x avg-rxlev=%d\n", i, clist[i].requirements, clist[i].avg); } } #if 0 next_b1: #endif /* select best candidate that fulfills requirement B, * omit change from AHS to AFS */ best_avg_db = 0; for (i = 0; i < candidates; i++) { /* delete subscriber that just have handovered */ if (clist[i].lchan == delete_lchan) clist[i].lchan = NULL; /* omit all subscribers that are handovered */ if (!clist[i].lchan) continue; if (!(clist[i].requirements & REQUIREMENT_B_MASK)) continue; /* omit assignment from AHS to AFS */ if (clist[i].lchan->ts->trx->bts == clist[i].bts && clist[i].lchan->type == GSM_LCHAN_TCH_H && (clist[i].requirements & REQUIREMENT_B_TCHF)) continue; /* omit candidates that will not solve/reduce congestion */ if (clist[i].lchan->type == GSM_LCHAN_TCH_F && tchf_congestion <= 0) continue; if (clist[i].lchan->type == GSM_LCHAN_TCH_H && tchh_congestion <= 0) continue; avg = clist[i].avg; /* improve AHS */ if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && clist[i].lchan->type == GSM_LCHAN_TCH_H && (clist[i].requirements & REQUIREMENT_B_TCHF)) { avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); is_improved = 1; } else is_improved = 0; LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d best_avg_db=%d\n", i, avg, best_avg_db); if (avg > best_avg_db) { best_cand = &clist[i]; best_avg_db = avg; } } /* perform handover, if there is a candidate */ if (best_cand) { any_ho = 1; LOGPHOLCHAN(best_cand->lchan, LOGL_DEBUG, "Best candidate BTS %u (RX level %d%s) without congestion found\n", best_cand->bts->nr, rxlev2dbm(best_cand->avg), is_improved ? ", RX quality improved by AHS->AFS" : ""); trigger_handover_or_assignment(best_cand->lchan, best_cand->bts, best_cand->requirements & REQUIREMENT_B_MASK); #if 0 /* if there is still congestion, mark lchan as deleted * and redo this process */ if (best_cand->lchan->type == GSM_LCHAN_TCH_H) tchh_congestion--; else tchf_congestion--; if (tchf_congestion > 0 || tchh_congestion > 0) { delete_lchan = best_cand->lchan; best_cand = NULL; goto next_b1; } #else /* must exit here, because triggering handover/assignment * will cause change in requirements. more check for this * bts is performed in the next iteration. */ #endif goto exit; } #if 0 next_b2: #endif /* select worst candidate that fulfills requirement B, * select candidates that change from AHS to AFS only */ if (tchh_congestion > 0) { /* since this will only check half rate channels, it will * only need to be checked, if tchh is congested */ worst_avg_db = 999; for (i = 0; i < candidates; i++) { /* delete subscriber that just have handovered */ if (clist[i].lchan == delete_lchan) clist[i].lchan = NULL; /* omit all subscribers that are handovered */ if (!clist[i].lchan) continue; if (!(clist[i].requirements & REQUIREMENT_B_MASK)) continue; /* omit all but assignment from AHS to AFS */ if (clist[i].lchan->ts->trx->bts != clist[i].bts || clist[i].lchan->type != GSM_LCHAN_TCH_H || !(clist[i].requirements & REQUIREMENT_B_TCHF)) continue; avg = clist[i].avg; /* improve AHS */ if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && clist[i].lchan->type == GSM_LCHAN_TCH_H) { avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); is_improved = 1; } else is_improved = 0; if (avg < worst_avg_db) { worst_cand = &clist[i]; worst_avg_db = avg; } } } /* perform handover, if there is a candidate */ if (worst_cand) { any_ho = 1; LOGP(DHODEC, LOGL_INFO, "Worst candidate for assignment " "(RX level %d%s) from TCH/H -> TCH/F without congestion " "found\n", rxlev2dbm(worst_cand->avg), is_improved ? ", RX quality improved by AHS->AFS" : ""); trigger_handover_or_assignment(worst_cand->lchan, worst_cand->bts, worst_cand->requirements & REQUIREMENT_B_MASK); #if 0 /* if there is still congestion, mark lchan as deleted * and redo this process */ tchh_congestion--; if (tchh_congestion > 0) { delete_lchan = worst_cand->lchan; best_cand = NULL; goto next_b2; } #else /* must exit here, because triggering handover/assignment * will cause change in requirements. more check for this * bts is performed in the next iteration. */ #endif goto exit; } #if 0 next_c1: #endif /* select best candidate that fulfills requirement C, * omit change from AHS to AFS */ best_avg_db = 0; for (i = 0; i < candidates; i++) { /* delete subscriber that just have handovered */ if (clist[i].lchan == delete_lchan) clist[i].lchan = NULL; /* omit all subscribers that are handovered */ if (!clist[i].lchan) continue; if (!(clist[i].requirements & REQUIREMENT_C_MASK)) continue; /* omit assignment from AHS to AFS */ if (clist[i].lchan->ts->trx->bts == clist[i].bts && clist[i].lchan->type == GSM_LCHAN_TCH_H && (clist[i].requirements & REQUIREMENT_C_TCHF)) continue; /* omit candidates that will not solve/reduce congestion */ if (clist[i].lchan->type == GSM_LCHAN_TCH_F && tchf_congestion <= 0) continue; if (clist[i].lchan->type == GSM_LCHAN_TCH_H && tchh_congestion <= 0) continue; avg = clist[i].avg; /* improve AHS */ if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && clist[i].lchan->type == GSM_LCHAN_TCH_H && (clist[i].requirements & REQUIREMENT_C_TCHF)) { avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); is_improved = 1; } else is_improved = 0; if (avg > best_avg_db) { best_cand = &clist[i]; best_avg_db = avg; } } /* perform handover, if there is a candidate */ if (best_cand) { any_ho = 1; LOGP(DHODEC, LOGL_INFO, "Best candidate BTS %d (RX level %d) " "with less or equal congestion found\n", best_cand->bts->nr, rxlev2dbm(best_cand->avg)); if (is_improved) LOGP(DHODEC, LOGL_INFO, "(is improved due to " "AHS -> AFS)\n"); trigger_handover_or_assignment(best_cand->lchan, best_cand->bts, best_cand->requirements & REQUIREMENT_C_MASK); #if 0 /* if there is still congestion, mark lchan as deleted * and redo this process */ if (best_cand->lchan->type == GSM_LCHAN_TCH_H) tchh_congestion--; else tchf_congestion--; if (tchf_congestion > 0 || tchh_congestion > 0) { delete_lchan = best_cand->lchan; best_cand = NULL; goto next_c1; } #else /* must exit here, because triggering handover/assignment * will cause change in requirements. more check for this * bts is performed in the next iteration. */ #endif goto exit; } LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a best candidate that fulfills requirement C" " (omitting change from AHS to AFS)\n"); #if 0 next_c2: #endif /* select worst candidate that fulfills requirement C, * select candidates that change from AHS to AFS only */ if (tchh_congestion > 0) { /* since this will only check half rate channels, it will * only need to be checked, if tchh is congested */ worst_avg_db = 999; for (i = 0; i < candidates; i++) { /* delete subscriber that just have handovered */ if (clist[i].lchan == delete_lchan) clist[i].lchan = NULL; /* omit all subscribers that are handovered */ if (!clist[i].lchan) continue; if (!(clist[i].requirements & REQUIREMENT_C_MASK)) continue; /* omit all but assignment from AHS to AFS */ if (clist[i].lchan->ts->trx->bts != clist[i].bts || clist[i].lchan->type != GSM_LCHAN_TCH_H || !(clist[i].requirements & REQUIREMENT_C_TCHF)) continue; avg = clist[i].avg; /* improve AHS */ if (clist[i].lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && clist[i].lchan->type == GSM_LCHAN_TCH_H) { avg += ho_get_hodec2_afs_bias_rxlev(clist[i].bts->ho); is_improved = 1; } else is_improved = 0; LOGP(DHODEC, LOGL_DEBUG, "candidate %d: avg=%d worst_avg_db=%d\n", i, avg, worst_avg_db); if (avg < worst_avg_db) { worst_cand = &clist[i]; worst_avg_db = avg; } } } /* perform handover, if there is a candidate */ if (worst_cand) { any_ho = 1; LOGP(DHODEC, LOGL_INFO, "Worst candidate for assignment " "(RX level %d%s) from TCH/H -> TCH/F with less or equal " "congestion found\n", rxlev2dbm(worst_cand->avg), is_improved ? ", RX quality improved by AHS->AFS" : ""); trigger_handover_or_assignment(worst_cand->lchan, worst_cand->bts, worst_cand->requirements & REQUIREMENT_C_MASK); #if 0 /* if there is still congestion, mark lchan as deleted * and redo this process */ tchh_congestion--; if (tchh_congestion > 0) { delete_lchan = worst_cand->lchan; worst_cand = NULL; goto next_c2; } #else /* must exit here, because triggering handover/assignment * will cause change in requirements. more check for this * bts is performed in the next iteration. */ #endif goto exit; } LOGPHOBTS(bts, LOGL_DEBUG, "Did not find a worst candidate that fulfills requirement C," " selecting candidates that change from AHS to AFS only\n"); exit: /* free array */ talloc_free(clist); if (tchf_congestion <= 0 && tchh_congestion <= 0) LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d solved!\n", bts->nr); else if (any_ho) LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d reduced!\n", bts->nr); else LOGP(DHODEC, LOGL_INFO, "Congestion at BTS %d can't be reduced/solved!\n", bts->nr); return rc; } static void bts_congestion_check(struct gsm_bts *bts) { int min_free_tchf, min_free_tchh; int tchf_count, tchh_count; global_ho_reason = HO_REASON_CONGESTION; /* only check BTS if TRX 0 is usable */ if (!trx_is_usable(bts->c0)) { LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: TRX 0 not usable\n"); return; } /* only check BTS if handover or assignment is enabled */ if (!ho_get_hodec2_as_active(bts->ho) && !ho_get_ho_active(bts->ho)) { LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: Assignment and Handover both disabled\n"); return; } min_free_tchf = ho_get_hodec2_tchf_min_slots(bts->ho); min_free_tchh = ho_get_hodec2_tchh_min_slots(bts->ho); /* only check BTS with congestion level set */ if (!min_free_tchf && !min_free_tchh) { LOGPHOBTS(bts, LOGL_DEBUG, "No congestion check: no minimum for free TCH/F nor TCH/H set\n"); return; } tchf_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_F); tchh_count = bts_count_free_ts(bts, GSM_PCHAN_TCH_H); LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n", tchf_count, min_free_tchf, tchh_count, min_free_tchh); /* only check BTS if congested */ if (tchf_count >= min_free_tchf && tchh_count >= min_free_tchh) { LOGPHOBTS(bts, LOGL_DEBUG, "Not congested\n"); return; } LOGPHOBTS(bts, LOGL_DEBUG, "Attempting to resolve congestion...\n"); bts_resolve_congestion(bts, min_free_tchf - tchf_count, min_free_tchh - tchh_count); } void hodec2_congestion_check(struct gsm_network *net) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) bts_congestion_check(bts); } static void congestion_check_cb(void *arg) { struct gsm_network *net = arg; hodec2_congestion_check(net); reinit_congestion_timer(net); } void on_ho_chan_activ_nack(struct bsc_handover *ho) { struct gsm_bts *new_bts = ho->new_lchan->ts->trx->bts; LOGPHO(ho, LOGL_ERROR, "Channel Activate Nack for %s, starting penalty timer\n", ho->inter_cell? "Handover" : "Assignment"); /* if channel failed, wait 10 seconds before allowing to retry handover */ conn_penalty_time_add(ho->old_lchan->conn, new_bts, 10); /* FIXME configurable */ } void on_ho_failure(struct bsc_handover *ho) { struct gsm_bts *old_bts = ho->old_lchan->ts->trx->bts; struct gsm_bts *new_bts = ho->new_lchan->ts->trx->bts; struct gsm_subscriber_connection *conn = ho->old_lchan->conn; if (!conn) { LOGPHO(ho, LOGL_ERROR, "HO failure, but no conn\n"); return; } if (conn->hodec2.failures >= ho_get_hodec2_retries(old_bts->ho)) { int penalty = ho->inter_cell ? ho_get_hodec2_penalty_failed_ho(old_bts->ho) : ho_get_hodec2_penalty_failed_as(old_bts->ho); LOGPHO(ho, LOGL_NOTICE, "%s failed, starting penalty timer (%d s)\n", ho->inter_cell ? "Handover" : "Assignment", penalty); conn->hodec2.failures = 0; conn_penalty_time_add(conn, new_bts, penalty); } else { conn->hodec2.failures++; LOGPHO(ho, LOGL_NOTICE, "%s failed, allowing handover decision to try again" " (%d/%d attempts)\n", ho->inter_cell ? "Handover" : "Assignment", conn->hodec2.failures, ho_get_hodec2_retries(old_bts->ho)); } } struct handover_decision_callbacks hodec2_callbacks = { .hodec_id = 2, .on_measurement_report = on_measurement_report, .on_ho_chan_activ_nack = on_ho_chan_activ_nack, .on_ho_failure = on_ho_failure, }; void hodec2_init(struct gsm_network *net) { handover_decision_callbacks_register(&hodec2_callbacks); hodec2_initialized = true; reinit_congestion_timer(net); } osmo-bsc-1.3.0/src/osmo-bsc/handover_logic.c000066400000000000000000000317121332665256100207120ustar00rootroot00000000000000/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not * actually implement the handover algorithm/decision, but executes a * handover decision */ /* (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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static LLIST_HEAD(bsc_handovers); static LLIST_HEAD(handover_decision_callbacks); static void handover_free(struct bsc_handover *ho) { osmo_timer_del(&ho->T3103); llist_del(&ho->list); talloc_free(ho); } static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; llist_for_each_entry(ho, &bsc_handovers, list) { if (ho->new_lchan == new_lchan) return ho; } return NULL; } static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) { struct bsc_handover *ho; llist_for_each_entry(ho, &bsc_handovers, list) { if (ho->old_lchan == old_lchan) return ho; } return NULL; } /*! Hand over the specified logical channel to the specified new BTS and possibly change the lchan type. * This is the main entry point for the actual handover algorithm, after the decision whether to initiate * HO to a specific BTS. To not change the lchan type, pass old_lchan->type. */ int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts, enum gsm_chan_t new_lchan_type) { int rc; struct gsm_subscriber_connection *conn; struct bsc_handover *ho; static uint8_t ho_ref = 0; bool do_assignment; OSMO_ASSERT(old_lchan); /* don't attempt multiple handovers for the same lchan at * the same time */ if (bsc_ho_by_old_lchan(old_lchan)) return -EBUSY; conn = old_lchan->conn; if (!conn) { LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); return -ENOSPC; } if (!new_bts) new_bts = old_lchan->ts->trx->bts; OSMO_ASSERT(new_bts); do_assignment = (new_bts == old_lchan->ts->trx->bts); ho = talloc_zero(conn, struct bsc_handover); if (!ho) { LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); return -ENOMEM; } ho->from_hodec_id = from_hodec_id; ho->old_lchan = old_lchan; ho->new_bts = new_bts; ho->new_lchan_type = new_lchan_type; ho->ho_ref = ho_ref++; ho->inter_cell = !do_assignment; ho->async = true; llist_add(&ho->list, &bsc_handovers); conn->ho = ho; rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HO_START, NULL); if (rc < 0) { LOGPHO(ho, LOGL_ERROR, "Failed to trigger handover, conn state does not allow it\n"); conn->ho = NULL; talloc_free(ho); } return rc; } /*! Start actual handover. Call bsc_handover_start() instead; The only legal caller is the GSCON FSM in * bsc_subscr_conn_fsm.c. */ int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn) { int rc; struct gsm_network *network = conn->network; struct bsc_handover *ho = conn->ho; struct gsm_lchan *old_lchan; struct gsm_lchan *new_lchan; if (!ho) { LOGP(DHO, LOGL_ERROR, "%s: Requested to start handover, but conn->ho is NULL\n", bsc_subscr_name(conn->bsub)); return -EINVAL; } OSMO_ASSERT(ho->old_lchan && ho->new_bts); if (ho->old_lchan->conn != conn) { LOGP(DHO, LOGL_ERROR, "%s: Requested to start handover, but the lchan does not belong to this conn\n", bsc_subscr_name(conn->bsub)); return -EINVAL; } rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]); ho->new_lchan = lchan_alloc(ho->new_bts, ho->new_lchan_type, 0); if (!ho->new_lchan) { LOGP(DHO, LOGL_NOTICE, "No free channel for %s\n", gsm_lchant_name(ho->new_lchan_type)); rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]); return -ENOSPC; } LOGPHO(ho, LOGL_INFO, "Triggering %s\n", ho->inter_cell? "Handover" : "Assignment"); /* copy some parameters from old lchan */ old_lchan = ho->old_lchan; new_lchan = ho->new_lchan; memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); if (!ho->inter_cell) { new_lchan->ms_power = old_lchan->ms_power; new_lchan->rqd_ta = old_lchan->rqd_ta; } else { new_lchan->ms_power = ms_pwr_ctl_lvl(ho->new_bts->band, ho->new_bts->ms_max_power); /* FIXME: do we have a better idea of the timing advance? */ //new_lchan->rqd_ta = old_lchan->rqd_ta; } new_lchan->bs_power = old_lchan->bs_power; new_lchan->rsl_cmode = old_lchan->rsl_cmode; new_lchan->tch_mode = old_lchan->tch_mode; memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, sizeof(new_lchan->mr_ms_lv)); memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, sizeof(new_lchan->mr_bts_lv)); new_lchan->conn = conn; rc = rsl_chan_activate_lchan(new_lchan, ho->async ? RSL_ACT_INTER_ASYNC : RSL_ACT_INTER_SYNC, ho->ho_ref); if (rc < 0) { LOGPHO(ho, LOGL_INFO, "%s Failure: activate lchan rc = %d\n", ho->inter_cell? "Handover" : "Assignment", rc); lchan_free(new_lchan); ho->new_lchan = NULL; bsc_clear_handover(conn, 0); return rc; } rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ return 0; } /* clear any operation for this connection */ void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) { struct bsc_handover *ho = conn->ho; if (!ho) return; if (ho->new_lchan) { ho->new_lchan->conn = NULL; if (free_lchan) lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); ho->new_lchan = NULL; } handover_free(ho); conn->ho = NULL; } /* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ static void ho_T3103_cb(void *_ho) { struct bsc_handover *ho = _ho; struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; DEBUGP(DHO, "HO T3103 expired\n"); rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]); /* Inform the GSCON FSM about the timed out handover */ osmo_fsm_inst_dispatch(ho->old_lchan->conn->fi, GSCON_EV_HO_TIMEOUT, NULL); bsc_clear_handover(ho->old_lchan->conn, 1); } /* RSL has acknowledged activation of the new lchan */ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; /* we need to check if this channel activation is related to * a handover at all (and if, which particular handover) */ ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) return -ENODEV; LOGPHO(ho, LOGL_INFO, "Channel Activate Ack, send %s COMMAND\n", ho->inter_cell? "HANDOVER" : "ASSIGNMENT"); /* we can now send the 04.08 HANDOVER COMMAND to the MS * using the old lchan */ gsm48_send_ho_cmd(ho->old_lchan, new_lchan, new_lchan->ms_power, ho->ho_ref); /* start T3103. We can continue either with T3103 expiration, * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho); osmo_timer_schedule(&ho->T3103, 10, 0); /* create a RTP connection */ if (is_ipaccess_bts(new_lchan->ts->trx->bts)) rsl_ipacc_crcx(new_lchan); return 0; } /* RSL has not acknowledged activation of the new lchan */ static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; struct handover_decision_callbacks *hdc; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { /* This lchan is not involved in a handover. */ return 0; } hdc = handover_decision_callbacks_get(ho->from_hodec_id); if (hdc && hdc->on_ho_chan_activ_nack) hdc->on_ho_chan_activ_nack(ho); bsc_clear_handover(new_lchan->conn, 0); return 0; } /* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) { struct gsm_network *net; struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } net = new_lchan->ts->trx->bts->network; LOGPHO(ho, LOGL_INFO, "%s Complete\n", ho->inter_cell ? "Handover" : "Assignment"); rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]); osmo_timer_del(&ho->T3103); /* Replace the ho lchan with the primary one */ if (ho->old_lchan != new_lchan->conn->lchan) LOGPHO(ho, LOGL_ERROR, "Primary lchan changed during handover.\n"); if (new_lchan->conn->ho != ho) LOGPHO(ho, LOGL_ERROR, "Handover channel changed during this handover.\n"); new_lchan->conn->lchan = new_lchan; ho->old_lchan->conn = NULL; lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); handover_free(ho); new_lchan->conn->ho = NULL; /* Inform the GSCON FSM that the handover is complete */ osmo_fsm_inst_dispatch(new_lchan->conn->fi, GSCON_EV_HO_COMPL, NULL); return 0; } /* GSM 04.08 HANDOVER FAIL has been received */ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) { struct gsm_network *net = old_lchan->ts->trx->bts->network; struct bsc_handover *ho; struct handover_decision_callbacks *hdc; ho = bsc_ho_by_old_lchan(old_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } hdc = handover_decision_callbacks_get(ho->from_hodec_id); if (hdc && hdc->on_ho_failure) hdc->on_ho_failure(ho); rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]); bsc_clear_handover(ho->new_lchan->conn, 1); /* Inform the GSCON FSM that the handover failed */ osmo_fsm_inst_dispatch(old_lchan->conn->fi, GSCON_EV_HO_FAIL, NULL); return 0; } /* GSM 08.58 HANDOVER DETECT has been received */ static int ho_rsl_detect(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) { LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); return -ENODEV; } LOGPHO(ho, LOGL_DEBUG, "Handover RACH detected\n"); /* This is just for logging on the DHO category. The actual MGCP switchover happens in * osmo_bsc_mgcp.c by receiving the same S_LCHAN_HANDOVER_DETECT signal. * (Calling mgcp_handover() directly currently breaks linking in utils/...) */ return 0; } static int ho_meas_rep(struct gsm_meas_rep *mr) { struct handover_decision_callbacks *hdc; enum hodec_id hodec_id = ho_get_algorithm(mr->lchan->ts->trx->bts->ho); hdc = handover_decision_callbacks_get(hodec_id); if (!hdc || !hdc->on_measurement_report) return 0; hdc->on_measurement_report(mr); return 0; } static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct lchan_signal_data *lchan_data; struct gsm_lchan *lchan; lchan_data = signal_data; switch (subsys) { case SS_LCHAN: lchan = lchan_data->lchan; switch (signal) { case S_LCHAN_ACTIVATE_ACK: return ho_chan_activ_ack(lchan); case S_LCHAN_ACTIVATE_NACK: return ho_chan_activ_nack(lchan); case S_LCHAN_HANDOVER_DETECT: return ho_rsl_detect(lchan); case S_LCHAN_HANDOVER_COMPL: return ho_gsm48_ho_compl(lchan); case S_LCHAN_HANDOVER_FAIL: return ho_gsm48_ho_fail(lchan); case S_LCHAN_MEAS_REP: return ho_meas_rep(lchan_data->mr); } break; default: break; } return 0; } /* Return the old lchan or NULL. This is meant for audio handling */ struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan) { struct bsc_handover *ho; ho = bsc_ho_by_new_lchan(new_lchan); if (!ho) return NULL; return ho->old_lchan; } static __attribute__((constructor)) void on_dso_load_ho_logic(void) { osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL); } /* Count number of currently ongoing handovers * inter_cell: if true, count only handovers between two cells. If false, count only handovers within one * cell. */ int bsc_ho_count(struct gsm_bts *bts, bool inter_cell) { struct bsc_handover *ho; int count = 0; llist_for_each_entry(ho, &bsc_handovers, list) { if (ho->inter_cell != inter_cell) continue; if (ho->new_lchan->ts->trx->bts == bts) count++; } return count; } void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc) { llist_add_tail(&hdc->entry, &handover_decision_callbacks); } struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id) { struct handover_decision_callbacks *hdc; llist_for_each_entry(hdc, &handover_decision_callbacks, entry) { if (hdc->hodec_id == hodec_id) return hdc; } return NULL; } osmo-bsc-1.3.0/src/osmo-bsc/handover_vty.c000066400000000000000000000127651332665256100204460ustar00rootroot00000000000000/* OsmoBSC interface to quagga VTY for handover parameters */ /* (C) 2009-2010 by Andreas Eversberg * (C) 2009-2010 by Harald Welte * (C) 2017-2018 by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * * Author: Andreas Eversberg * Neels Hofmeyr * * 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 struct handover_cfg *ho_cfg_from_vty(struct vty *vty) { switch (vty->node) { case GSMNET_NODE: return gsmnet_from_vty(vty)->ho; case BTS_NODE: OSMO_ASSERT(vty->index); return ((struct gsm_bts *)vty->index)->ho; default: OSMO_ASSERT(false); } } #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, \ VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, \ VTY_WRITE_FMT, VTY_WRITE_CONV, \ VTY_DOC) \ DEFUN(cfg_ho_##NAME, cfg_ho_##NAME##_cmd, \ VTY_CMD_PREFIX VTY_CMD " (" VTY_CMD_ARG "|default)", \ VTY_DOC \ "Use default (" #DEFAULT_VAL "), remove explicit setting on this node\n") \ { \ struct handover_cfg *ho = ho_cfg_from_vty(vty); \ const char *val = argv[0]; \ if (!strcmp(val, "default")) { \ const char *msg; \ if (ho_isset_##NAME(ho)) {\ ho_clear_##NAME(ho); \ msg = "setting removed, now is"; \ } else \ msg = "already was unset, still is"; \ vty_out(vty, "%% '" VTY_CMD_PREFIX VTY_CMD "' %s " VTY_WRITE_FMT "%s%s", \ msg, VTY_WRITE_CONV( ho_get_##NAME(ho) ), \ ho_isset_on_parent_##NAME(ho)? " (set on higher level node)" : "", \ VTY_NEWLINE); \ } \ else \ ho_set_##NAME(ho, VTY_ARG_EVAL(val)); \ return CMD_SUCCESS; \ } HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER /* Aliases of 'handover' for 'handover1' for backwards compat */ #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, \ VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, \ VTY_WRITE_FMT, VTY_WRITE_CONV, \ VTY_DOC) \ ALIAS_DEPRECATED(cfg_ho_##NAME, cfg_ho_##NAME##_cmd_alias, \ "handover " VTY_CMD " (" VTY_CMD_ARG "|default)", \ "Legacy alias for 'handover1': " VTY_DOC \ "Use default (" #DEFAULT_VAL "), remove explicit setting on this node\n"); HODEC1_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER static inline const int a2congestion_check_interval(const char *arg) { if (!strcmp(arg, "disabled")) return 0; return atoi(arg); } static inline const char *congestion_check_interval2a(int val) { static char str[9]; if (val < 1 || snprintf(str, sizeof(str), "%d", val) >= sizeof(str)) return "disabled"; return str; } DEFUN(cfg_net_ho_congestion_check_interval, cfg_net_ho_congestion_check_interval_cmd, "handover2 congestion-check (disabled|<1-999>|now)", HO_CFG_STR_HANDOVER2 "Configure congestion check interval" HO_CFG_STR_2 "Disable congestion checking, do not handover based on cell overload\n" "Congestion check interval in seconds (default " OSMO_STRINGIFY_VAL(HO_CFG_CONGESTION_CHECK_DEFAULT) ")\n" "Manually trigger a congestion check to run right now\n") { if (!strcmp(argv[0], "now")) { hodec2_congestion_check(gsmnet_from_vty(vty)); return CMD_SUCCESS; } hodec2_on_change_congestion_check_interval(gsmnet_from_vty(vty), a2congestion_check_interval(argv[0])); return CMD_SUCCESS; } static void ho_vty_write(struct vty *vty, const char *indent, struct handover_cfg *ho) { #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, \ VTY_CMD_PREFIX, VTY_CMD, VTY_CMD_ARG, VTY_ARG_EVAL, \ VTY_WRITE_FMT, VTY_WRITE_CONV, \ VTY_DOC) \ if (ho_isset_##NAME(ho)) \ vty_out(vty, "%s" VTY_CMD_PREFIX VTY_CMD " " VTY_WRITE_FMT "%s", indent, \ VTY_WRITE_CONV( ho_get_##NAME(ho) ), VTY_NEWLINE); HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER } void ho_vty_write_bts(struct vty *vty, struct gsm_bts *bts) { ho_vty_write(vty, " ", bts->ho); } void ho_vty_write_net(struct vty *vty, struct gsm_network *net) { ho_vty_write(vty, " ", net->ho); if (net->hodec2.congestion_check_interval_s != HO_CFG_CONGESTION_CHECK_DEFAULT) vty_out(vty, " handover2 congestion-check %s%s", congestion_check_interval2a(net->hodec2.congestion_check_interval_s), VTY_NEWLINE); } static void ho_vty_init_cmds(int parent_node) { #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY3, VTY4, VTY5, VTY6) \ install_element(parent_node, &cfg_ho_##NAME##_cmd); HO_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER /* Aliases of 'handover' for 'handover1' for backwards compat */ #define HO_CFG_ONE_MEMBER(TYPE, NAME, DEFAULT_VAL, VTY0, VTY1, VTY2, VTY3, VTY4, VTY5, VTY6) \ install_element(parent_node, &cfg_ho_##NAME##_cmd_alias); HODEC1_CFG_ALL_MEMBERS #undef HO_CFG_ONE_MEMBER } void ho_vty_init() { ho_vty_init_cmds(GSMNET_NODE); install_element(GSMNET_NODE, &cfg_net_ho_congestion_check_interval_cmd); ho_vty_init_cmds(BTS_NODE); } osmo-bsc-1.3.0/src/osmo-bsc/meas_feed.c000066400000000000000000000116601332665256100176370ustar00rootroot00000000000000/* UDP-Feed of measurement reports */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct meas_feed_state { struct osmo_wqueue wqueue; char scenario[31+1]; char *dst_host; uint16_t dst_port; }; static struct meas_feed_state g_mfs = {}; static int process_meas_rep(struct gsm_meas_rep *mr) { struct msgb *msg; struct meas_feed_meas *mfm; struct bsc_subscr *bsub; /* ignore measurements as long as we don't know who it is */ if (!mr->lchan) { LOGP(DMEAS, LOGL_DEBUG, "meas_feed: no lchan, not sending report\n"); return 0; } if (!mr->lchan->conn) { LOGP(DMEAS, LOGL_DEBUG, "meas_feed: lchan without conn, not sending report\n"); return 0; } bsub = mr->lchan->conn->bsub; msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed"); if (!msg) return 0; /* fill in the header */ mfm = (struct meas_feed_meas *) msgb_put(msg, sizeof(*mfm)); mfm->hdr.msg_type = MEAS_FEED_MEAS; mfm->hdr.version = MEAS_FEED_VERSION; /* fill in MEAS_FEED_MEAS specific header */ if (bsub) osmo_strlcpy(mfm->imsi, bsub->imsi, sizeof(mfm->imsi)); /* This used to be a human readable meaningful name set in the old osmo-nitb's subscriber * database. Now we're several layers away from that (and possibly don't even have a name in * osmo-hlr either), hence this is a legacy item now that we should leave empty ... *but*: * here in the BSC we often don't know the subscriber's full identity information. For example, * we might only know the TMSI, and hence would pass an empty IMSI above. So after all, feed * bsc_subscr_name(), which possibly will feed the IMSI again, but in case only the TMSI is known * would add that to the information set as "TMSI:0x12345678". */ osmo_strlcpy(mfm->name, bsc_subscr_name(bsub), sizeof(mfm->name)); osmo_strlcpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario)); /* copy the entire measurement report */ memcpy(&mfm->mr, mr, sizeof(mfm->mr)); /* copy channel information */ /* we assume that the measurement report always belong to some timeslot */ mfm->lchan_type = (uint8_t)mr->lchan->type; mfm->pchan_type = (uint8_t)mr->lchan->ts->pchan; mfm->bts_nr = mr->lchan->ts->trx->bts->nr; mfm->trx_nr = mr->lchan->ts->trx->nr; mfm->ts_nr = mr->lchan->ts->nr; mfm->ss_nr = mr->lchan->nr; /* and send it to the socket */ if (osmo_wqueue_enqueue(&g_mfs.wqueue, msg) != 0) { LOGP(DMEAS, LOGL_ERROR, "meas_feed %s: sending measurement report failed\n", gsm_lchan_name(mr->lchan)); msgb_free(msg); } else LOGP(DMEAS, LOGL_DEBUG, "meas_feed %s: sent measurement report\n", gsm_lchan_name(mr->lchan)); return 0; } static int meas_feed_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct lchan_signal_data *sdata = signal_data; if (subsys != SS_LCHAN) return 0; if (signal == S_LCHAN_MEAS_REP) process_meas_rep(sdata->mr); return 0; } static int feed_write_cb(struct osmo_fd *ofd, struct msgb *msg) { return write(ofd->fd, msgb_data(msg), msgb_length(msg)); } static int feed_read_cb(struct osmo_fd *ofd) { int rc; char buf[256]; rc = read(ofd->fd, buf, sizeof(buf)); ofd->fd &= ~BSC_FD_READ; return rc; } int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port) { int rc; int already_initialized = 0; if (g_mfs.wqueue.bfd.fd) already_initialized = 1; if (already_initialized && !strcmp(dst_host, g_mfs.dst_host) && dst_port == g_mfs.dst_port) return 0; if (!already_initialized) { osmo_wqueue_init(&g_mfs.wqueue, 10); g_mfs.wqueue.write_cb = feed_write_cb; g_mfs.wqueue.read_cb = feed_read_cb; osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL); LOGP(DMEAS, LOGL_DEBUG, "meas_feed: registered signal callback\n"); } if (already_initialized) { osmo_wqueue_clear(&g_mfs.wqueue); osmo_fd_unregister(&g_mfs.wqueue.bfd); close(g_mfs.wqueue.bfd.fd); /* don't set to zero, as that would mean 'not yet initialized' */ g_mfs.wqueue.bfd.fd = -1; } rc = osmo_sock_init_ofd(&g_mfs.wqueue.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, dst_host, dst_port, OSMO_SOCK_F_CONNECT); if (rc < 0) return rc; g_mfs.wqueue.bfd.when &= ~BSC_FD_READ; if (g_mfs.dst_host) talloc_free(g_mfs.dst_host); g_mfs.dst_host = talloc_strdup(NULL, dst_host); g_mfs.dst_port = dst_port; return 0; } void meas_feed_cfg_get(char **host, uint16_t *port) { *port = g_mfs.dst_port; *host = g_mfs.dst_host; } void meas_feed_scenario_set(const char *name) { osmo_strlcpy(g_mfs.scenario, name, sizeof(g_mfs.scenario)); } const char *meas_feed_scenario_get(void) { return g_mfs.scenario; } osmo-bsc-1.3.0/src/osmo-bsc/meas_rep.c000066400000000000000000000063571332665256100175310ustar00rootroot00000000000000/* Measurement Report Processing */ /* (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 #include static int get_field(const struct gsm_meas_rep *rep, enum meas_rep_field field) { switch (field) { case MEAS_REP_DL_RXLEV_FULL: if (!(rep->flags & MEAS_REP_F_DL_VALID)) return -EINVAL; return rep->dl.full.rx_lev; case MEAS_REP_DL_RXLEV_SUB: if (!(rep->flags & MEAS_REP_F_DL_VALID)) return -EINVAL; return rep->dl.sub.rx_lev; case MEAS_REP_DL_RXQUAL_FULL: if (!(rep->flags & MEAS_REP_F_DL_VALID)) return -EINVAL; return rep->dl.full.rx_qual; case MEAS_REP_DL_RXQUAL_SUB: if (!(rep->flags & MEAS_REP_F_DL_VALID)) return -EINVAL; return rep->dl.sub.rx_qual; case MEAS_REP_UL_RXLEV_FULL: return rep->ul.full.rx_lev; case MEAS_REP_UL_RXLEV_SUB: return rep->ul.sub.rx_lev; case MEAS_REP_UL_RXQUAL_FULL: return rep->ul.full.rx_qual; case MEAS_REP_UL_RXQUAL_SUB: return rep->ul.sub.rx_qual; } return 0; } unsigned int calc_initial_idx(unsigned int array_size, unsigned int meas_rep_idx, unsigned int num_values) { int offs, idx; /* from which element do we need to start if we're interested * in an average of 'num' elements */ offs = meas_rep_idx - num_values; if (offs < 0) idx = array_size + offs; else idx = offs; return idx; } /* obtain an average over the last 'num' fields in the meas reps */ int get_meas_rep_avg(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int num) { unsigned int i, idx; int avg = 0, valid_num = 0; if (num < 1) return -EINVAL; if (num > lchan->meas_rep_count) return -EINVAL; idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, num); for (i = 0; i < num; i++) { int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); int val = get_field(&lchan->meas_rep[j], field); if (val >= 0) { avg += val; valid_num++; } } if (valid_num == 0) return -EINVAL; return avg / valid_num; } /* Check if N out of M last values for FIELD are >= bd */ int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, enum meas_rep_field field, unsigned int n, unsigned int m, int be) { unsigned int i, idx; int count = 0; idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, m); for (i = 0; i < m; i++) { int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); int val = get_field(&lchan->meas_rep[j], field); if (val >= be) /* implies that val < 0 will not count */ count++; if (count >= n) return 1; } return 0; } osmo-bsc-1.3.0/src/osmo-bsc/net_init.c000066400000000000000000000044611332665256100175410ustar00rootroot00000000000000/* (C) 2008-2010 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 /* Initialize the bare minimum of struct gsm_network, minimizing required dependencies. * This part is shared among the thin programs in osmo-bsc/src/utils/. * osmo-bsc requires further initialization that pulls in more dependencies (see bsc_network_init()). */ struct gsm_network *gsm_network_init(void *ctx) { struct gsm_network *net = talloc_zero(ctx, struct gsm_network); if (!net) return NULL; net->plmn = (struct osmo_plmn_id){ .mcc = 1, .mnc = 1, }; net->dyn_ts_allow_tch_f = true; /* Permit a compile-time default of A5/3 and A5/1 */ net->a5_encryption_mask = (1 << 3) | (1 << 1); /* Use 30 min periodic update interval as sane default */ net->t3212 = 5; INIT_LLIST_HEAD(&net->subscr_conns); net->bsc_subscribers = talloc_zero(net, struct llist_head); INIT_LLIST_HEAD(net->bsc_subscribers); INIT_LLIST_HEAD(&net->bts_list); net->num_bts = 0; net->T3101 = GSM_T3101_DEFAULT; net->T3103 = GSM_T3103_DEFAULT; net->T3105 = GSM_T3105_DEFAULT; net->T3107 = GSM_T3107_DEFAULT; net->T3109 = GSM_T3109_DEFAULT; net->T3111 = GSM_T3111_DEFAULT; net->T3113 = GSM_T3113_DEFAULT; net->T3115 = GSM_T3115_DEFAULT; net->T3117 = GSM_T3117_DEFAULT; net->T3119 = GSM_T3119_DEFAULT; net->T3122 = GSM_T3122_DEFAULT; net->T3141 = GSM_T3141_DEFAULT; net->T10 = GSM_T10_DEFAULT; net->T7 = GSM_T7_DEFAULT; net->T8 = GSM_T8_DEFAULT; net->T101 = GSM_T101_DEFAULT; return net; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_api.c000066400000000000000000000356561332665256100203770ustar00rootroot00000000000000/* (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-2011 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 /* Check if we have a proper connection to the MSC */ static bool msc_connected(struct gsm_subscriber_connection *conn) { /* No subscriber conn at all */ if (!conn) return false; /* Connection to MSC not established */ if (!conn->sccp.msc) return false; /* Reset procedure not (yet) executed */ if (a_reset_conn_ready(conn->sccp.msc->a.reset_fsm) == false) return false; return true; } static int complete_layer3(struct gsm_subscriber_connection *conn, struct msgb *msg, struct bsc_msc_data *msc); static struct osmo_cell_global_id *cgi_for_msc(struct bsc_msc_data *msc, struct gsm_bts *bts) { static struct osmo_cell_global_id cgi; cgi.lai.plmn = msc->network->plmn; if (msc->core_plmn.mcc != GSM_MCC_MNC_INVALID) cgi.lai.plmn.mcc = msc->core_plmn.mcc; if (msc->core_plmn.mnc != GSM_MCC_MNC_INVALID) { cgi.lai.plmn.mnc = msc->core_plmn.mnc; cgi.lai.plmn.mnc_3_digits = msc->core_plmn.mnc_3_digits; } cgi.lai.lac = (msc->core_lac != -1) ? msc->core_lac : bts->location_area_code; cgi.cell_identity = (msc->core_ci != -1) ? msc->core_ci : bts->cell_identity; return &cgi; } static void bsc_maybe_lu_reject(struct gsm_subscriber_connection *conn, int con_type, int cause) { struct msgb *msg; /* ignore cm service request or such */ if (con_type != FLT_CON_TYPE_LU) return; msg = gsm48_create_loc_upd_rej(cause); if (!msg) { LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); return; } msg->lchan = conn->lchan; gsm0808_submit_dtap(conn, msg, 0, 0); } static int bsc_filter_initial(struct osmo_bsc_data *bsc, struct bsc_msc_data *msc, struct gsm_subscriber_connection *conn, struct msgb *msg, char **imsi, int *con_type, int *lu_cause) { struct bsc_filter_request req; struct bsc_filter_reject_cause cause; struct gsm48_hdr *gh = msgb_l3(msg); int rc; req.ctx = conn; req.black_list = NULL; req.access_lists = bsc_access_lists(); req.local_lst_name = msc->acc_lst_name; req.global_lst_name = conn_get_bts(conn)->network->bsc_data->acc_lst_name; req.bsc_nr = 0; rc = bsc_msg_filter_initial(gh, msgb_l3len(msg), &req, con_type, imsi, &cause); *lu_cause = cause.lu_reject_cause; return rc; } static int bsc_filter_data(struct gsm_subscriber_connection *conn, struct msgb *msg, int *lu_cause) { struct bsc_filter_request req; struct gsm48_hdr *gh = msgb_l3(msg); struct bsc_filter_reject_cause cause; int rc; req.ctx = conn; req.black_list = NULL; req.access_lists = bsc_access_lists(); req.local_lst_name = conn->sccp.msc->acc_lst_name; req.global_lst_name = conn_get_bts(conn)->network->bsc_data->acc_lst_name; req.bsc_nr = 0; rc = bsc_msg_filter_data(gh, msgb_l3len(msg), &req, &conn->filter_state, &cause); *lu_cause = cause.lu_reject_cause; return rc; } /*! BTS->MSC: tell MSC a SAPI was not established. */ void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) { int rc; struct msgb *resp; if (!conn || !msc_connected(conn)) return; LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci); resp = gsm0808_create_sapi_reject(dlci); rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); if (rc != 0) msgb_free(resp); } /*! MS->MSC: Tell MSC that ciphering has been enabled. */ void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) { int rc; struct msgb *resp; if (!msc_connected(conn)) return; LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n"); resp = gsm0808_create_cipher_complete(msg, chosen_encr); rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); if (rc != 0) msgb_free(resp); } static void bsc_send_ussd_no_srv(struct gsm_subscriber_connection *conn, struct msgb *msg, const char *text) { struct gsm48_hdr *gh; int8_t pdisc; uint8_t mtype; int drop_message = 1; if (!text) return; if (!msg || msgb_l3len(msg) < sizeof(*gh)) return; gh = msgb_l3(msg); pdisc = gsm48_hdr_pdisc(gh); mtype = gsm48_hdr_msg_type(gh); /* Is CM service request? */ if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { struct gsm48_service_request *cm; cm = (struct gsm48_service_request *) &gh->data[0]; /* Is type SMS or call? */ if (cm->cm_service_type == GSM48_CMSERV_SMS) drop_message = 0; else if (cm->cm_service_type == GSM48_CMSERV_MO_CALL_PACKET) drop_message = 0; } if (drop_message) { LOGP(DMSC, LOGL_DEBUG, "Skipping (not sending) USSD message: '%s'\n", text); return; } LOGP(DMSC, LOGL_INFO, "Sending CM Service Accept\n"); gsm48_tx_mm_serv_ack(conn); LOGP(DMSC, LOGL_INFO, "Sending USSD message: '%s'\n", text); bsc_send_ussd_notify(conn, 1, text); bsc_send_ussd_release_complete(conn); } /*! MS->MSC: New MM context with L3 payload. */ int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel) { struct bsc_msc_data *msc; LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n"); /* find the MSC link we want to use */ msc = bsc_find_msc(conn, msg); if (!msc) { LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n"); bsc_send_ussd_no_srv(conn, msg, conn_get_bts(conn)->network->bsc_data->ussd_no_msc_txt); return -1; } return complete_layer3(conn, msg, msc); } static int complete_layer3(struct gsm_subscriber_connection *conn, struct msgb *msg, struct bsc_msc_data *msc) { int con_type, rc, lu_cause; char *imsi = NULL; struct msgb *resp; enum bsc_con ret; /* Check the filter */ rc = bsc_filter_initial(msc->network->bsc_data, msc, conn, msg, &imsi, &con_type, &lu_cause); if (rc < 0) { bsc_maybe_lu_reject(conn, con_type, lu_cause); return BSC_API_CONN_POL_REJECT; } /* allocate resource for a new connection */ ret = osmo_bsc_sigtran_new_conn(conn, msc); if (ret != BSC_CON_SUCCESS) { /* allocation has failed */ if (ret == BSC_CON_REJECT_NO_LINK) bsc_send_ussd_no_srv(conn, msg, msc->ussd_msc_lost_txt); else if (ret == BSC_CON_REJECT_RF_GRACE) bsc_send_ussd_no_srv(conn, msg, msc->ussd_grace_txt); return BSC_API_CONN_POL_REJECT; } /* TODO: also extract TMSI. We get an IMSI only when an initial L3 Complete comes in that * contains an IMSI. We filter by IMSI. A TMSI identity is never returned here, see e.g. * _cr_check_loc_upd() and other similar functions called from bsc_msg_filter_initial(). */ if (imsi) { conn->filter_state.imsi = talloc_steal(conn, imsi); if (conn->bsub) { /* Already a subscriber on L3 Complete? Should never happen... */ if (conn->bsub->imsi[0] && strcmp(conn->bsub->imsi, imsi)) LOGP(DMSC, LOGL_ERROR, "Subscriber's IMSI changes from %s to %s\n", conn->bsub->imsi, imsi); bsc_subscr_set_imsi(conn->bsub, imsi); } else conn->bsub = bsc_subscr_find_or_create_by_imsi(msc->network->bsc_subscribers, imsi); } conn->filter_state.con_type = con_type; /* check return value, if failed check msg for and send USSD */ bsc_scan_bts_msg(conn, msg); resp = gsm0808_create_layer3_2(msg, cgi_for_msc(conn->sccp.msc, conn_get_bts(conn)), NULL); if (!resp) { LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); return BSC_API_CONN_POL_REJECT; } osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, resp); return BSC_API_CONN_POL_ACCEPT; } /* * Plastic surgery... we want to give up the current connection */ static int move_to_msc(struct gsm_subscriber_connection *_conn, struct msgb *msg, struct bsc_msc_data *msc) { /* * 1. Give up the old connection. * This happens by sending a clear request to the MSC, * it should end with the MSC releasing the connection. */ bsc_clear_request(_conn, 0); /* * 2. Attempt to create a new connection to the local * MSC. If it fails the caller will need to handle this * properly. */ if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) { gsm0808_clear(_conn); //bsc_subscr_con_free(_conn); return 1; } return 2; } static int handle_cc_setup(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gsm48_hdr_pdisc(gh); uint8_t mtype = gsm48_hdr_msg_type(gh); struct bsc_msc_data *msc; struct gsm_mncc_number called; struct tlv_parsed tp; unsigned payload_len; char _dest_nr[35]; /* * Do we have a setup message here? if not return fast. */ if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP) return 0; payload_len = msgb_l3len(msg) - sizeof(*gh); tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n"); return -1; } memset(&called, 0, sizeof(called)); gsm48_decode_called(&called, TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); if (called.plan != 1 && called.plan != 0) return 0; if (called.plan == 1 && called.type == 1) { _dest_nr[0] = _dest_nr[1] = '0'; memcpy(_dest_nr + 2, called.number, sizeof(called.number)); } else memcpy(_dest_nr, called.number, sizeof(called.number)); /* * Check if the connection should be moved... */ llist_for_each_entry(msc, &conn_get_bts(conn)->network->bsc_data->mscs, entry) { if (msc->type != MSC_CON_TYPE_LOCAL) continue; if (!msc->local_pref) continue; if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0) continue; return move_to_msc(conn, msg, msc); } return 0; } /*! MS->BSC/MSC: Um L3 message. */ void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { int lu_cause; if (!msc_connected(conn)) return; LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id); /* * We might want to move this connection to a new MSC. Ask someone * to handle it. If it was handled we will return. */ if (handle_cc_setup(conn, msg) >= 1) return; /* Check the filter */ if (bsc_filter_data(conn, msg, &lu_cause) < 0) { bsc_maybe_lu_reject(conn, conn->filter_state.con_type, lu_cause); bsc_clear_request(conn, 0); return; } bsc_scan_bts_msg(conn, msg); /* Store link_id in msg->cb */ OBSC_LINKID_CB(msg) = link_id; osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_DTAP, msg); } /*! BSC->MSC: Assignment of lchan successful. */ void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) { if (!msc_connected(conn)) return; conn->lchan->abis_ip.ass_compl.rr_cause = rr_cause; if (is_ipaccess_bts(conn_get_bts(conn)) && conn->user_plane.rtp_ip) { /* NOTE: In a network that makes use of an IPA base station * and AoIP, we have to wait until the BTS reports its RTP * IP/Port combination back to BSC via RSL. Unfortunately, the * IPA protocol sends its Abis assignment complete message * before it sends its RTP IP/Port via IPACC. So we will now * postpone the AoIP assignment completed message until we * know the RTP IP/Port combination. */ LOGP(DMSC, LOGL_INFO, "POSTPONE MSC ASSIGN COMPL\n"); conn->lchan->abis_ip.ass_compl.valid = true; } else { /* NOTE: Send the A assignment complete message immediately. */ LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n"); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RR_ASS_COMPL, NULL); } } /*! BSC->MSC: Assignment of lchan failed. */ void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) { LOGPFSML(conn->fi, LOGL_ERROR, "Assignment failure: BSSMAP: '%s' from RR: '%s'\n", gsm0808_cause_name(cause), rr_cause ? rr_cause_name(*rr_cause) : "(none)"); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RR_ASS_FAIL, &cause); } /*! BSC->MSC: RR conn has been cleared. */ int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { int rc; struct msgb *resp; if (!msc_connected(conn)) return 1; LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n"); resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); return 1; } rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); if (rc != 0) msgb_free(resp); return 1; } /*! BSC->MSC: Classmark Update. */ void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) { int rc; struct msgb *resp; if (!msc_connected(conn)) return; resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len); rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); if (rc != 0) msgb_free(resp); } /*! Configure the multirate setting on this channel. */ void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate) { struct bsc_msc_data *msc; struct gsm48_multi_rate_conf *ms_conf, *bts_conf; if (!conn) { LOGP(DMSC, LOGL_ERROR, "No msc data available on conn %p. Audio will be broken.\n", conn); return; } msc = conn->sccp.msc; /* initialize the data structure */ lchan->mr_ms_lv[0] = sizeof(*ms_conf); lchan->mr_bts_lv[0] = sizeof(*bts_conf); ms_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_ms_lv[1]; bts_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_bts_lv[1]; memset(ms_conf, 0, sizeof(*ms_conf)); memset(bts_conf, 0, sizeof(*bts_conf)); bts_conf->ver = ms_conf->ver = 1; bts_conf->icmi = ms_conf->icmi = 1; /* maybe gcc see's it is copy of _one_ byte */ bts_conf->m4_75 = ms_conf->m4_75 = msc->amr_conf.m4_75; bts_conf->m5_15 = ms_conf->m5_15 = msc->amr_conf.m5_15; bts_conf->m5_90 = ms_conf->m5_90 = msc->amr_conf.m5_90; bts_conf->m6_70 = ms_conf->m6_70 = msc->amr_conf.m6_70; bts_conf->m7_40 = ms_conf->m7_40 = msc->amr_conf.m7_40; bts_conf->m7_95 = ms_conf->m7_95 = msc->amr_conf.m7_95; if (full_rate) { bts_conf->m10_2 = ms_conf->m10_2 = msc->amr_conf.m10_2; bts_conf->m12_2 = ms_conf->m12_2 = msc->amr_conf.m12_2; } /* now copy this into the bts structure */ memcpy(lchan->mr_bts_lv, lchan->mr_ms_lv, sizeof(lchan->mr_ms_lv)); } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_audio.c000066400000000000000000000057451332665256100207230ustar00rootroot00000000000000/* * ipaccess audio handling * * (C) 2009-2010 by Holger Hans Peter Freyther * (C) 2009-2010 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include static int handle_abisip_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct gsm_subscriber_connection *con; struct gsm_lchan *lchan = signal_data; int rc; uint32_t rtp_ip; if (subsys != SS_ABISIP) return 0; con = lchan->conn; if (!con) return 0; switch (signal) { case S_ABISIP_CRCX_ACK: /* we can ask it to connect now */ LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n", con->user_plane.rtp_port, lchan->abis_ip.conn_id); /* If AoIP is in use, the rtp_ip, which has been communicated * via the A interface as connect_ip */ if(con->user_plane.rtp_ip) rtp_ip = con->user_plane.rtp_ip; else rtp_ip = ntohl(INADDR_ANY); rc = rsl_ipacc_mdcx(lchan, rtp_ip, con->user_plane.rtp_port, lchan->abis_ip.rtp_payload2); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc); return rc; } break; case S_ABISIP_MDCX_ACK: if (con->ho) { LOGPHO(con->ho, LOGL_DEBUG, "BTS sent MDCX ACK\n"); /* No need to do anything for handover here. As soon as a HANDOVER DETECT * happens, handover_logic.c and bsc_subscr_conn_fsm.c will trigger the * MGCP MDCX towards MGW by receiving an S_LCHAN_HANDOVER_DETECT signal. */ } else if (is_ipaccess_bts(conn_get_bts(con)) && con->user_plane.rtp_ip) { /* NOTE: This is only relevant on AoIP networks with * IPA based base stations. See also osmo_bsc_api.c, * function bsc_assign_compl() */ LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL (POSTPONED)\n"); osmo_fsm_inst_dispatch(con->fi, GSCON_EV_RR_ASS_COMPL, NULL); } break; } return 0; } int osmo_bsc_audio_init(struct gsm_network *net) { osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, net); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_bssap.c000066400000000000000000000676661332665256100207440ustar00rootroot00000000000000/* GSM 08.08 BSSMAP handling */ /* (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-2012 by On-Waves * (C) 2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define IP_V4_ADDR_LEN 4 /* * helpers for the assignment command */ static int bssmap_handle_reset_ack(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { LOGP(DMSC, LOGL_NOTICE, "RESET ACK from MSC: %s\n", osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); /* Inform the FSM that controls the RESET/RESET-ACK procedure * that we have successfully received the reset-ack message */ a_reset_ack_confirm(msc->a.reset_fsm); return 0; } /* Handle MSC sided reset */ static int bssmap_handle_reset(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { LOGP(DMSC, LOGL_NOTICE, "RESET from MSC: %s\n", osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr)); /* Instruct the bsc to close all open sigtran connections and to * close all active channels on the BTS side as well */ osmo_bsc_sigtran_reset(msc); /* Drop all ongoing paging requests that this MSC has created on any BTS */ paging_flush_network(msc->network, msc); /* Inform the MSC that we have received the reset request and * that we acted accordingly */ osmo_bsc_sigtran_tx_reset_ack(msc); return 0; } /* Page a subscriber based on TMSI and LAC via the specified BTS. * The msc parameter is the MSC which issued the corresponding paging request. * Log an error if paging failed. */ static void page_subscriber(struct bsc_msc_data *msc, struct gsm_bts *bts, uint32_t tmsi, uint32_t lac, const char *mi_string, uint8_t chan_needed) { struct bsc_subscr *subscr; int ret; LOGP(DMSC, LOGL_INFO, "Paging request from MSC BTS: %d IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", bts->nr, mi_string, tmsi, tmsi, lac); subscr = bsc_subscr_find_or_create_by_imsi(msc->network->bsc_subscribers, mi_string); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Paging request failed: Could not allocate subscriber for %s\n", mi_string); return; } subscr->lac = lac; subscr->tmsi = tmsi; ret = bsc_grace_paging_request(msc->network->bsc_data->rf_ctrl->policy, subscr, chan_needed, msc, bts); if (ret == 0) LOGP(DMSC, LOGL_ERROR, "Paging request failed: BTS: %d IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", bts->nr, mi_string, tmsi, tmsi, lac); /* the paging code has grabbed its own references */ bsc_subscr_put(subscr); } static void page_all_bts(struct bsc_msc_data *msc, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) page_subscriber(msc, bts, tmsi, GSM_LAC_RESERVED_ALL_BTS, mi_string, chan_needed); } static void page_cgi(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { int i; for (i = 0; i < cil->id_list_len; i++) { struct osmo_cell_global_id *id = &cil->id_list[i].global; if (!osmo_plmn_cmp(&id->lai.plmn, &msc->network->plmn)) { int paged = 0; struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) { if (bts->location_area_code != id->lai.lac) continue; if (bts->cell_identity != id->cell_identity) continue; page_subscriber(msc, bts, tmsi, id->lai.lac, mi_string, chan_needed); paged = 1; } if (!paged) { LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d and CI %d not found\n", mi_string, id->lai.lac, id->cell_identity); } } else { LOGP(DMSC, LOGL_DEBUG, "Paging IMSI %s: MCC-MNC in Cell Identifier List " "(%s) do not match our network (%s)\n", mi_string, osmo_plmn_name(&id->lai.plmn), osmo_plmn_name2(&msc->network->plmn)); } } } static void page_lac_and_ci(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { int i; for (i = 0; i < cil->id_list_len; i++) { struct osmo_lac_and_ci_id *id = &cil->id_list[i].lac_and_ci; int paged = 0; struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) { if (bts->location_area_code != id->lac) continue; if (bts->cell_identity != id->ci) continue; page_subscriber(msc, bts, tmsi, id->lac, mi_string, chan_needed); paged = 1; } if (!paged) { LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d and CI %d not found\n", mi_string, id->lac, id->ci); } } } static void page_ci(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { int i; for (i = 0; i < cil->id_list_len; i++) { uint16_t ci = cil->id_list[i].ci; int paged = 0; struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) { if (bts->cell_identity != ci) continue; page_subscriber(msc, bts, tmsi, GSM_LAC_RESERVED_ALL_BTS, mi_string, chan_needed); paged = 1; } if (!paged) { LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with CI %d not found\n", mi_string, ci); } } } static void page_lai_and_lac(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { int i; for (i = 0; i < cil->id_list_len; i++) { struct osmo_location_area_id *id = &cil->id_list[i].lai_and_lac; if (!osmo_plmn_cmp(&id->plmn, &msc->network->plmn)) { int paged = 0; struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) { if (bts->location_area_code != id->lac) continue; page_subscriber(msc, bts, tmsi, id->lac, mi_string, chan_needed); paged = 1; } if (!paged) { LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d not found\n", mi_string, id->lac); } } else { LOGP(DMSC, LOGL_DEBUG, "Paging IMSI %s: MCC-MNC in Cell Identifier List " "(%s) do not match our network (%s)\n", mi_string, osmo_plmn_name(&id->plmn), osmo_plmn_name2(&msc->network->plmn)); } } } static void page_lac(struct bsc_msc_data *msc, struct gsm0808_cell_id_list2 *cil, uint32_t tmsi, const char *mi_string, uint8_t chan_needed) { int i; for (i = 0; i < cil->id_list_len; i++) { uint16_t lac = cil->id_list[i].lac; int paged = 0; struct gsm_bts *bts; llist_for_each_entry(bts, &msc->network->bts_list, list) { if (bts->location_area_code != lac) continue; page_subscriber(msc, bts, tmsi, lac, mi_string, chan_needed); paged = 1; } if (!paged) { LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: BTS with LAC %d not found\n", mi_string, lac); } } } /* GSM 08.08 § 3.2.1.19 */ static int bssmap_handle_paging(struct bsc_msc_data *msc, struct msgb *msg, unsigned int payload_length) { struct tlv_parsed tp; char mi_string[GSM48_MI_SIZE]; uint32_t tmsi = GSM_RESERVED_TMSI; uint8_t data_length; int remain; const uint8_t *data; uint8_t chan_needed = RSL_CHANNEED_ANY; struct gsm0808_cell_id_list2 cil; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); remain = payload_length - 1; if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n"); return -1; } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); return -1; } remain -= TLVP_LEN(&tp, GSM0808_IE_IMSI); if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n"); return -1; } if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) && TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) { tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI)); remain -= TLVP_LEN(&tp, GSM0808_IE_TMSI); } if (remain <= 0) { LOGP(DMSC, LOGL_ERROR, "Payload too short.\n"); return -1; } /* * parse the IMSI */ gsm48_mi_to_string(mi_string, sizeof(mi_string), TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI)); /* * There are various cell identifier list types defined at 3GPP TS § 08.08, we don't support all * of them yet. To not disrupt paging operation just because we're lacking some implementation, * interpret any unknown cell identifier type as "page the entire BSS". */ data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); if (gsm0808_dec_cell_id_list2(&cil, data, data_length) < 0) { LOGP(DMSC, LOGL_ERROR, "Paging IMSI %s: Could not parse Cell Identifier List\n", mi_string); return -1; } remain = 0; if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); } rate_ctr_inc(&msc->network->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]); switch (cil.id_discr) { case CELL_IDENT_NO_CELL: page_all_bts(msc, tmsi, mi_string, chan_needed); break; case CELL_IDENT_WHOLE_GLOBAL: page_cgi(msc, &cil, tmsi, mi_string, chan_needed); break; case CELL_IDENT_LAC_AND_CI: page_lac_and_ci(msc, &cil, tmsi, mi_string, chan_needed); break; case CELL_IDENT_CI: page_ci(msc, &cil, tmsi, mi_string, chan_needed); break; case CELL_IDENT_LAI_AND_LAC: page_lai_and_lac(msc, &cil, tmsi, mi_string, chan_needed); break; case CELL_IDENT_LAC: page_lac(msc, &cil, tmsi, mi_string, chan_needed); break; case CELL_IDENT_BSS: if (data_length != 1) { LOGP(DMSC, LOGL_ERROR, "Paging IMSI %s: Cell Identifier List for BSS (0x%x)" " has invalid length: %u, paging entire BSS anyway (%s)\n", mi_string, CELL_IDENT_BSS, data_length, osmo_hexdump(data, data_length)); } page_all_bts(msc, tmsi, mi_string, chan_needed); break; default: LOGP(DMSC, LOGL_NOTICE, "Paging IMSI %s: unimplemented Cell Identifier List (0x%x)," " paging entire BSS instead (%s)\n", mi_string, cil.id_discr, osmo_hexdump(data, data_length)); page_all_bts(msc, tmsi, mi_string, chan_needed); break; } return 0; } /* select the best cipher permitted by the intersection of both masks */ static int select_best_cipher(uint8_t msc_mask, uint8_t bsc_mask) { uint8_t intersection = msc_mask & bsc_mask; int i; for (i = 7; i >= 0; i--) { if (intersection & (1 << i)) return i; } return -1; } /* * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick * the cipher to be used for this. In case we are already using * a cipher we will have to send cipher mode reject to the MSC, * otherwise we will have to pick something that we and the MS * is supporting. Currently we are doing it in a rather static * way by picking one encryption or no encryption. */ static int bssmap_handle_cipher_mode(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int payload_length) { uint16_t len; struct gsm_network *network = NULL; const uint8_t *data; struct tlv_parsed tp; struct msgb *resp; int reject_cause = -1; int include_imeisv = 1; const uint8_t *enc_key; uint16_t enc_key_len; uint8_t enc_bits_msc; int chosen_cipher; if (!conn) { LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); goto reject; } if (conn->ciphering_handled) { LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); goto reject; } conn->ciphering_handled = 1; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); goto reject; } /* * check if our global setting is allowed * - Currently we check for A5/0 and A5/1 * - Copy the key if that is necessary * - Otherwise reject */ len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); if (len < 1) { LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); goto reject; } network = conn_get_bts(conn)->network; data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); enc_bits_msc = data[0]; enc_key = &data[1]; enc_key_len = len - 1; if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; /* Identical to the GSM0808_IE_ENCRYPTION_INFORMATION above: * a5_encryption == 0 --> 0x01 * a5_encryption == 1 --> 0x02 * a5_encryption == 2 --> 0x04 ... */ enc_bits_msc = data[0]; /* The bit-mask of permitted ciphers from the MSC (sent in ASSIGNMENT COMMAND) is intersected * with the vty-configured mask a the BSC. Finally, the best (highest) possible cipher is * chosen. */ chosen_cipher = select_best_cipher(enc_bits_msc, network->a5_encryption_mask); if (chosen_cipher < 0) { LOGP(DMSC, LOGL_ERROR, "Reject: no overlapping A5 ciphers between BSC (0x%02x) " "and MSC (0x%02x)\n", network->a5_encryption_mask, enc_bits_msc); reject_cause = GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED; goto reject; } /* To complete the confusion, gsm0808_cipher_mode again expects the encryption as a number * from 0 to 7. */ if (gsm0808_cipher_mode(conn, chosen_cipher, enc_key, enc_key_len, include_imeisv)) { reject_cause = GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC; goto reject; } return 0; reject: resp = gsm0808_create_cipher_reject(reject_cause); if (!resp) { LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); return -1; } osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); return -1; } /* handle LCLS specific IES in BSSMAP ASS REQ */ static void bssmap_handle_ass_req_lcls(struct gsm_subscriber_connection *conn, const struct tlv_parsed *tp) { const struct tlv_p_entry *tlv; const uint8_t *config, *control; tlv = TLVP_GET(tp, GSM0808_IE_GLOBAL_CALL_REF); if (tlv) { if (tlv->len > sizeof(conn->lcls.global_call_ref)) LOGPFSML(conn->fi, LOGL_ERROR, "Global Call Ref IE of %u bytes is too long\n", tlv->len); else { LOGPFSM(conn->fi, "Setting GCR to %s\n", osmo_hexdump_nospc(tlv->val, tlv->len)); memcpy(&conn->lcls.global_call_ref, tlv->val, tlv->len); conn->lcls.global_call_ref_len = tlv->len; } } config = TLVP_VAL_MINLEN(tp, GSM0808_IE_LCLS_CONFIG, 1); control = TLVP_VAL_MINLEN(tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1); if (config || control) { LOGPFSM(conn->fi, "BSSMAP ASS REQ contains LCLS (%s / %s)\n", config ? gsm0808_lcls_config_name(*config) : "NULL", control ? gsm0808_lcls_control_name(*control) : "NULL"); } /* Update the LCLS state with Config + CSC (if any) */ lcls_update_config(conn, config, control); /* Do not attempt to perform correlation yet, as during processing of the ASS REQ * we don't have the MGCP/MGW connections yet, and hence couldn't enable LS. */ } /* TS 48.008 3.2.1.91 */ static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { struct msgb *resp; struct tlv_parsed tp; const uint8_t *config, *control; int rc; OSMO_ASSERT(conn); rc = tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); if (rc < 0) { LOGPFSML(conn->fi, LOGL_ERROR, "Error parsing TLVs of LCLS CONNT CTRL: %s\n", msgb_hexdump(msg)); return rc; } config = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONFIG, 1); control = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1); LOGPFSM(conn->fi, "Rx LCLS CONNECT CTRL (%s / %s)\n", config ? gsm0808_lcls_config_name(*config) : "NULL", control ? gsm0808_lcls_control_name(*control) : "NULL"); if (conn->lcls.global_call_ref_len == 0) { LOGPFSML(conn->fi, LOGL_ERROR, "Ignoring LCLS as no GCR was set before\n"); return 0; } /* Update the LCLS state with Config + CSC (if any) */ lcls_update_config(conn, TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONFIG, 1), TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1)); lcls_apply_config(conn); LOGPFSM(conn->fi, "Tx LCLS CONNECT CTRL ACK (%s)\n", gsm0808_lcls_status_name(lcls_get_status(conn))); resp = gsm0808_create_lcls_conn_ctrl_ack(lcls_get_status(conn)); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); return 0; } /* * Handle the assignment request message. * * See §3.2.1.1 for the message type */ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { struct msgb *resp; struct bsc_msc_data *msc; struct tlv_parsed tp; uint8_t timeslot = 0; uint8_t multiplex = 0; enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; int full_rate = -1; bool aoip = false; struct sockaddr_storage rtp_addr; struct gsm0808_channel_type ct; struct gsm0808_speech_codec_list *scl_ptr = NULL; uint8_t cause; int rc; if (!conn) { LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); return -1; } msc = conn->sccp.msc; if (msc->a.asp_proto != OSMO_SS7_ASP_PROT_IPA) aoip = true; tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); /* Check for channel type element, if its missing, immediately reject */ if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n"); cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; goto reject; } /* Decode Channel Type element */ rc = gsm0808_dec_channel_type(&ct, TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE), TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "unable to decode channel type.\n"); cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } bssmap_handle_ass_req_lcls(conn, &tp); /* Currently we only support a limited subset of all * possible channel types, such as multi-slot or CSD */ switch (ct.ch_indctr) { case GSM0808_CHAN_DATA: LOGP(DMSC, LOGL_ERROR, "Unsupported channel type, currently only speech is supported!\n"); cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP; goto reject; case GSM0808_CHAN_SPEECH: if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { /* CIC is permitted in both AoIP and SCCPlite */ conn->user_plane.cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); timeslot = conn->user_plane.cic & 0x1f; multiplex = (conn->user_plane.cic & ~0x1f) >> 5; } else { if (!aoip) { /* no CIC but SCCPlite: illegal */ LOGP(DMSC, LOGL_ERROR, "SCCPlite MSC, but no CIC in ASSIGN REQ?\n"); cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; goto reject; } } if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { if (!aoip) { /* SCCPlite and AoIP transport address: illegal */ LOGP(DMSC, LOGL_ERROR, "AoIP Transport address over IPA ?!?\n"); cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } /* Decode AoIP transport address element */ rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR), TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR)); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "Unable to decode AoIP transport address.\n"); cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } } else { if (aoip) { /* no AoIP transport level address but AoIP transport: illegal */ LOGP(DMSC, LOGL_ERROR, "AoIP transport address missing in ASSIGN REQ, " "audio would not work; rejecting\n"); cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; goto reject; } } /* Decode speech codec list (AoIP) */ conn->codec_list_present = false; if (aoip) { /* Check for speech codec list element */ if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) { LOGP(DMSC, LOGL_ERROR, "Mandatory speech codec list not present.\n"); cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; goto reject; } /* Decode Speech Codec list */ rc = gsm0808_dec_speech_codec_list(&conn->codec_list, TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST), TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST)); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "Unable to decode speech codec list\n"); cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } conn->codec_list_present = true; scl_ptr = &conn->codec_list; } /* Match codec information from the assignment command against the * local preferences of the BSC */ rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc, conn_get_bts(conn)); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type =" " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n", ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len)); /* TODO: actually output codec names, e.g. implement * gsm0808_permitted_speech_names[] and iterate perm_spch. */ cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL; goto reject; } DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type =" " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n", full_rate? "full rate" : "half rate", get_value_string(gsm48_chan_mode_names, chan_mode), ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len)); /* Forward the assignment request to lower layers */ if (aoip) { /* Store network side RTP connection information, we will * process this address later after we have established an RTP * connection to the BTS. This is just for organizational * reasons, functional wise it would not matter when exactly * the network side RTP connection is made, as long it is made * before we return with the assignment complete message. */ memcpy(&conn->user_plane.aoip_rtp_addr_remote, &rtp_addr, sizeof(rtp_addr)); } else { /* Note: In the sccp-lite case we to not perform any mgcp operation, * (the MSC does that for us). We set conn->rtp_ip to 0 and check * on this later. By this we know that we have to behave accordingly * to sccp-lite. */ conn->user_plane.rtp_port = mgcp_timeslot_to_port(multiplex, timeslot, msc->rtp_base); conn->user_plane.rtp_ip = 0; } conn->user_plane.chan_mode = chan_mode; conn->user_plane.full_rate = full_rate; osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL); break; case GSM0808_CHAN_SIGN: conn->user_plane.chan_mode = GSM48_CMODE_SIGN; osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL); break; default: cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS; goto reject; } return 0; reject: resp = gsm0808_create_assignment_failure(cause, NULL); OSMO_ASSERT(resp); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); return -1; } static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { int ret = 0; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l4h[0])); switch (msg->l4h[0]) { case BSS_MAP_MSG_RESET_ACKNOWLEDGE: ret = bssmap_handle_reset_ack(msc, msg, length); break; case BSS_MAP_MSG_RESET: ret = bssmap_handle_reset(msc, msg, length); break; case BSS_MAP_MSG_PAGING: ret = bssmap_handle_paging(msc, msg, length); break; default: LOGP(DMSC, LOGL_NOTICE, "Received unimplemented BSSMAP UDT %s\n", gsm0808_bssmap_name(msg->l4h[0])); break; } return ret; } static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { int ret = 0; if (length < 1) { LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l4h[0])); switch (msg->l4h[0]) { case BSS_MAP_MSG_CLEAR_CMD: osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CLEAR_CMD, msg); break; case BSS_MAP_MSG_CIPHER_MODE_CMD: ret = bssmap_handle_cipher_mode(conn, msg, length); break; case BSS_MAP_MSG_ASSIGMENT_RQST: ret = bssmap_handle_assignm_req(conn, msg, length); break; case BSS_MAP_MSG_LCLS_CONNECT_CTRL: ret = bssmap_handle_lcls_connect_ctrl(conn, msg, length); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l4h[0])); break; } return ret; } static int dtap_rcvmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { struct dtap_header *header; struct msgb *gsm48; uint8_t *data; int rc, dtap_rc; LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", osmo_hexdump(msg->l3h, length)); if (!conn) { LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); return -1; } header = (struct dtap_header *) msg->l3h; if (sizeof(*header) >= length) { LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); return -1; } if (header->length > length - sizeof(*header)) { LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); return -1; } LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); /* forward the data */ gsm48 = gsm48_msgb_alloc_name("GSM 04.08 DTAP RCV"); if (!gsm48) { LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); return -1; } gsm48->l3h = gsm48->data; data = msgb_put(gsm48, length - sizeof(*header)); memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); /* pass it to the filter for extra actions */ rc = bsc_scan_msc_msg(conn, gsm48); /* Store link_id in msgb->cb */ OBSC_LINKID_CB(msg) = header->link_id; dtap_rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, gsm48); if (rc == BSS_SEND_USSD) bsc_send_welcome_ussd(conn); return dtap_rc; } int bsc_handle_udt(struct bsc_msc_data *msc, struct msgb *msgb, unsigned int length) { struct bssmap_header *bs; LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n", osmo_hexdump(msgb->l3h, length)); if (length < sizeof(*bs)) { LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); return -1; } bs = (struct bssmap_header *) msgb->l3h; if (bs->length < length - sizeof(*bs)) return -1; switch (bs->type) { case BSSAP_MSG_BSS_MANAGEMENT: msgb->l4h = &msgb->l3h[sizeof(*bs)]; bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs)); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(bs->type)); } return 0; } int bsc_handle_dt(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int len) { if (len < sizeof(struct bssmap_header)) { LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); } switch (msg->l3h[0]) { case BSSAP_MSG_BSS_MANAGEMENT: msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); break; case BSSAP_MSG_DTAP: dtap_rcvmsg(conn, msg, len); break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l3h[0])); } return -1; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_ctrl.c000066400000000000000000000470461332665256100205660ustar00rootroot00000000000000/* (C) 2011 by Daniel Willmann * (C) 2011 by Holger Hans Peter Freyther * (C) 2011 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include #include #include /* Obtain SS7 application server currently handling given MSC (DPC) */ static struct osmo_ss7_as *msc_get_ss7_as(struct bsc_msc_data *msc) { struct osmo_ss7_route *rt; struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(msc->a.sccp); rt = osmo_ss7_route_lookup(ss7, msc->a.msc_addr.pc); if (!rt) return NULL; return rt->dest.as; } /* Encode a CTRL command and send it to the given ASP * \param[in] asp ASP through which we shall send the encoded message * \param[in] cmd decoded CTRL command to be encoded and sent. Ownership is *NOT* * transferred, to permit caller to send the same CMD to several ASPs. * Caller must hence free 'cmd' itself. * \returns 0 on success; negative on error */ static int sccplite_asp_ctrl_cmd_send(struct osmo_ss7_asp *asp, struct ctrl_cmd *cmd) { /* this is basically like libosmoctrl:ctrl_cmd_send(), not for a dedicated * CTRL connection but for the CTRL piggy-back on the IPA/SCCPlite link */ struct msgb *msg; /* don't attempt to send CTRL on a non-SCCPlite ASP */ if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) return 0; msg = ctrl_cmd_make(cmd); if (!msg) return -1; ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL); ipa_prepend_header(msg, IPAC_PROTO_OSMO); return osmo_ss7_asp_send(asp, msg); } /* Ownership of 'cmd' is *NOT* transferred, to permit caller to send the same CMD to several ASPs. * Caller must hence free 'cmd' itself. */ static int sccplite_msc_ctrl_cmd_send(struct bsc_msc_data *msc, struct ctrl_cmd *cmd) { struct osmo_ss7_as *as; struct osmo_ss7_asp *asp; unsigned int i; as = msc_get_ss7_as(msc); if (!as) return -1; /* don't attempt to send CTRL on a non-SCCPlite AS */ if (as->cfg.proto != OSMO_SS7_ASP_PROT_IPA) return 0; /* FIXME: unify with xua_as_transmit_msg() and perform proper ASP lookup */ for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { asp = as->cfg.asps[i]; if (!asp) continue; /* FIXME: deal with multiple ASPs per AS */ return sccplite_asp_ctrl_cmd_send(asp, cmd); } return -1; } /* receive + process a CTRL command from the piggy-back on the IPA/SCCPlite link */ int bsc_sccplite_rx_ctrl(struct osmo_ss7_asp *asp, struct msgb *msg) { struct ctrl_cmd *cmd; bool parse_failed; int rc; /* caller has already ensured ipaccess_head + ipaccess_head_ext */ OSMO_ASSERT(msg->l2h); /* prase raw (ASCII) CTRL command into ctrl_cmd */ cmd = ctrl_cmd_parse3(asp, msg, &parse_failed); OSMO_ASSERT(cmd); msgb_free(msg); if (cmd->type == CTRL_TYPE_ERROR && parse_failed) goto send_reply; /* handle the CTRL command */ ctrl_cmd_handle(bsc_gsmnet->ctrl, cmd, bsc_gsmnet); send_reply: rc = sccplite_asp_ctrl_cmd_send(asp, cmd); talloc_free(cmd); return rc; } void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_data *msc_data) { struct ctrl_cmd *trap; struct ctrl_handle *ctrl; ctrl = msc_data->network->ctrl; trap = ctrl_cmd_trap(cmd); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n"); return; } ctrl_cmd_send_to_all(ctrl, trap); sccplite_msc_ctrl_cmd_send(msc_data, trap); talloc_free(trap); } CTRL_CMD_DEFINE_RO(msc_connection_status, "connection_status"); static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data) { struct bsc_msc_data *msc = (struct bsc_msc_data *)cmd->node; struct osmo_ss7_as *as; const char *as_state_name; if (msc == NULL) { cmd->reply = "msc not found"; return CTRL_CMD_ERROR; } as = msc_get_ss7_as(msc); if (!as) { cmd->reply = "AS not found for MSC"; return CTRL_CMD_ERROR; } as_state_name = osmo_fsm_inst_state_name(as->fi); if (!strcmp(as_state_name, "AS_ACTIVE")) cmd->reply = "connected"; else cmd->reply = "disconnected"; return CTRL_CMD_REPLY; } /* Backwards compat. */ CTRL_CMD_DEFINE_RO(msc0_connection_status, "msc_connection_status"); static int msc_connection_status = 0; /* XXX unused */ static int get_msc0_connection_status(struct ctrl_cmd *cmd, void *data) { struct bsc_msc_data *msc = osmo_msc_data_find(bsc_gsmnet, 0); void *old_node = cmd->node; int rc; cmd->node = msc; rc = get_msc_connection_status(cmd, data); cmd->node = old_node; return rc; } static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ctrl_cmd *cmd; struct gsm_network *gsmnet = (struct gsm_network *)handler_data; if (signal == S_MSC_LOST && msc_connection_status == 1) { LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n"); msc_connection_status = 0; } else if (signal == S_MSC_CONNECTED && msc_connection_status == 0) { LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n"); msc_connection_status = 1; } else { return 0; } cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); return 0; } cmd->id = "0"; cmd->variable = "msc_connection_status"; get_msc0_connection_status(cmd, NULL); ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); talloc_free(cmd); return 0; } CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status"); static int bts_connection_status = 0; static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data) { if (bts_connection_status) cmd->reply = "connected"; else cmd->reply = "disconnected"; return CTRL_CMD_REPLY; } static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct ctrl_cmd *cmd; struct gsm_network *gsmnet = (struct gsm_network *)handler_data; struct gsm_bts *bts; int bts_current_status; if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) { return 0; } bts_current_status = 0; /* Check if OML on at least one BTS is up */ llist_for_each_entry(bts, &gsmnet->bts_list, list) { if (bts->oml_link) { bts_current_status = 1; break; } } if (bts_connection_status == 0 && bts_current_status == 1) { LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n"); } else if (bts_connection_status == 1 && bts_current_status == 0) { LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n"); } else { return 0; } cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); return 0; } bts_connection_status = bts_current_status; cmd->id = "0"; cmd->variable = "bts_connection_status"; get_bts_connection_status(cmd, NULL); ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); talloc_free(cmd); return 0; } static int get_bts_loc(struct ctrl_cmd *cmd, void *data); static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_data *msc) { struct ctrl_cmd *cmd; const char *oper, *admin, *policy; cmd = ctrl_cmd_create(msc, CTRL_TYPE_TRAP); if (!cmd) { LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); return; } cmd->id = "0"; cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr); /* Prepare the location reply */ cmd->node = bts; get_bts_loc(cmd, NULL); oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); cmd->reply = talloc_asprintf_append(cmd->reply, ",%s,%s,%s,%s,%s", oper, admin, policy, osmo_mcc_name(bts->network->plmn.mcc), osmo_mnc_name(bts->network->plmn.mnc, bts->network->plmn.mnc_3_digits)); osmo_bsc_send_trap(cmd, msc); talloc_free(cmd); } void bsc_gen_location_state_trap(struct gsm_bts *bts) { struct bsc_msc_data *msc; llist_for_each_entry(msc, &bts->network->bsc_data->mscs, entry) generate_location_state_trap(bts, msc); } static int location_equal(struct bts_location *a, struct bts_location *b) { return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) && (a->lon == b->lon) && (a->height == b->height)); } static void cleanup_locations(struct llist_head *locations) { struct bts_location *myloc, *tmp; int invalpos = 0, i = 0; LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n"); llist_for_each_entry_safe(myloc, tmp, locations, list) { i++; if (i > 3) { LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n"); llist_del(&myloc->list); talloc_free(myloc); } else if (myloc->valid == BTS_LOC_FIX_INVALID) { /* Only capture the newest of subsequent invalid positions */ invalpos++; if (invalpos > 1) { LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n"); invalpos--; i--; llist_del(&myloc->list); talloc_free(myloc); } } else { invalpos = 0; } } LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i); } CTRL_CMD_DEFINE(bts_loc, "location"); static int get_bts_loc(struct ctrl_cmd *cmd, void *data) { struct bts_location *curloc; struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } if (llist_empty(&bts->loc_list)) { cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0"); return CTRL_CMD_REPLY; } else { curloc = llist_entry(bts->loc_list.next, struct bts_location, list); } cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp, get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_bts_loc(struct ctrl_cmd *cmd, void *data) { char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp; struct bts_location *curloc, *lastloc; int ret; struct gsm_bts *bts = (struct gsm_bts *) cmd->node; if (!bts) { cmd->reply = "bts not found."; return CTRL_CMD_ERROR; } tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; curloc = talloc_zero(tall_bsc_ctx, struct bts_location); if (!curloc) { talloc_free(tmp); goto oom; } INIT_LLIST_HEAD(&curloc->list); tstamp = strtok_r(tmp, ",", &saveptr); valid = strtok_r(NULL, ",", &saveptr); lat = strtok_r(NULL, ",", &saveptr); lon = strtok_r(NULL, ",", &saveptr); height = strtok_r(NULL, "\0", &saveptr); curloc->tstamp = atol(tstamp); curloc->valid = get_string_value(bts_loc_fix_names, valid); curloc->lat = atof(lat); curloc->lon = atof(lon); curloc->height = atof(height); talloc_free(tmp); lastloc = llist_entry(bts->loc_list.next, struct bts_location, list); /* Add location to the end of the list */ llist_add(&curloc->list, &bts->loc_list); ret = get_bts_loc(cmd, data); if (!location_equal(curloc, lastloc)) bsc_gen_location_state_trap(bts); cleanup_locations(&bts->loc_list); return ret; oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp; time_t tstamp; int valid; double lat, lon, height __attribute__((unused)); tmp = talloc_strdup(cmd, value); if (!tmp) return 1; tstampstr = strtok_r(tmp, ",", &saveptr); validstr = strtok_r(NULL, ",", &saveptr); latstr = strtok_r(NULL, ",", &saveptr); lonstr = strtok_r(NULL, ",", &saveptr); heightstr = strtok_r(NULL, "\0", &saveptr); if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) || (lonstr == NULL) || (heightstr == NULL)) goto err; tstamp = atol(tstampstr); valid = get_string_value(bts_loc_fix_names, validstr); lat = atof(latstr); lon = atof(lonstr); height = atof(heightstr); talloc_free(tmp); tmp = NULL; if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) || (lon < -180) || (lon > 180) || (valid < 0)) { goto err; } return 0; err: talloc_free(tmp); cmd->reply = talloc_strdup(cmd, "The format is ,(invalid|fix2d|fix3d),,,"); return 1; } CTRL_CMD_DEFINE(net_timezone, "timezone"); static int get_net_timezone(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net = (struct gsm_network*)cmd->node; struct gsm_tz *tz = &net->tz; if (tz->override) cmd->reply = talloc_asprintf(cmd, "%d,%d,%d", tz->hr, tz->mn, tz->dst); else cmd->reply = talloc_asprintf(cmd, "off"); if (!cmd->reply) { cmd->reply = "OOM"; return CTRL_CMD_ERROR; } return CTRL_CMD_REPLY; } static int set_net_timezone(struct ctrl_cmd *cmd, void *data) { char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0; int override; struct gsm_network *net = (struct gsm_network*)cmd->node; tmp = talloc_strdup(cmd, cmd->value); if (!tmp) goto oom; hourstr = strtok_r(tmp, ",", &saveptr); minstr = strtok_r(NULL, ",", &saveptr); dststr = strtok_r(NULL, ",", &saveptr); override = 0; if (hourstr != NULL) override = strcasecmp(hourstr, "off") != 0; struct gsm_tz *tz = &net->tz; tz->override = override; if (override) { tz->hr = hourstr ? atol(hourstr) : 0; tz->mn = minstr ? atol(minstr) : 0; tz->dst = dststr ? atol(dststr) : 0; } talloc_free(tmp); tmp = NULL; return get_net_timezone(cmd, data); oom: cmd->reply = "OOM"; return CTRL_CMD_ERROR; } static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr, *hourstr, *minstr, *dststr, *tmp; int override, tz_hours, tz_mins, tz_dst; tmp = talloc_strdup(cmd, value); if (!tmp) return 1; hourstr = strtok_r(tmp, ",", &saveptr); minstr = strtok_r(NULL, ",", &saveptr); dststr = strtok_r(NULL, ",", &saveptr); if (hourstr == NULL) goto err; override = strcasecmp(hourstr, "off") != 0; if (!override) { talloc_free(tmp); return 0; } if (minstr == NULL || dststr == NULL) goto err; tz_hours = atol(hourstr); tz_mins = atol(minstr); tz_dst = atol(dststr); talloc_free(tmp); tmp = NULL; if ((tz_hours < -19) || (tz_hours > 19) || (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) || (tz_dst < 0) || (tz_dst > 2)) goto err; return 0; err: talloc_free(tmp); cmd->reply = talloc_strdup(cmd, "The format is ,, or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2"); return 1; } CTRL_CMD_DEFINE_WO_NOVRF(net_notification, "notification"); static int set_net_notification(struct ctrl_cmd *cmd, void *data) { struct ctrl_cmd *trap; struct gsm_network *net; net = cmd->node; trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); goto handled; } trap->id = "0"; trap->variable = "notification"; trap->reply = talloc_strdup(trap, cmd->value); /* * This should only be sent to local systems. In the future * we might even ask for systems to register to receive * the notifications. */ ctrl_cmd_send_to_all(net->ctrl, trap); talloc_free(trap); handled: return CTRL_CMD_HANDLED; } CTRL_CMD_DEFINE_WO_NOVRF(net_inform_msc, "inform-msc-v1"); static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data) { struct gsm_network *net; struct bsc_msc_data *msc; net = cmd->node; llist_for_each_entry(msc, &net->bsc_data->mscs, entry) { struct ctrl_cmd *trap; trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); if (!trap) { LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); continue; } trap->id = "0"; trap->variable = "inform-msc-v1"; trap->reply = talloc_strdup(trap, cmd->value); sccplite_msc_ctrl_cmd_send(msc, trap); talloc_free(trap); } return CTRL_CMD_HANDLED; } CTRL_CMD_DEFINE_WO(net_ussd_notify, "ussd-notify-v1"); static int set_net_ussd_notify(struct ctrl_cmd *cmd, void *data) { struct gsm_subscriber_connection *conn; struct gsm_network *net; char *saveptr = NULL; char *cic_str, *alert_str, *text_str; int cic, alert; /* Verify has done the test for us */ cic_str = strtok_r(cmd->value, ",", &saveptr); alert_str = strtok_r(NULL, ",", &saveptr); text_str = strtok_r(NULL, ",", &saveptr); if (!cic_str || !alert_str || !text_str) { cmd->reply = "Programming issue. How did this pass verify?"; return CTRL_CMD_ERROR; } cmd->reply = "No connection found"; cic = atoi(cic_str); alert = atoi(alert_str); net = cmd->node; llist_for_each_entry(conn, &net->subscr_conns, entry) { if (conn->user_plane.cic != cic) continue; /* * This is a hack. My E71 does not like to immediately * receive a release complete on a TCH. So schedule a * release complete to clear any previous attempt. The * right thing would be to track invokeId and only send * the release complete when we get a returnResultLast * for this invoke id. */ bsc_send_ussd_release_complete(conn); bsc_send_ussd_notify(conn, alert, text_str); cmd->reply = "Found a connection"; break; } return CTRL_CMD_REPLY; } static int verify_net_ussd_notify(struct ctrl_cmd *cmd, const char *value, void *data) { char *saveptr = NULL; char *inp, *cic, *alert, *text; OSMO_ASSERT(cmd); inp = talloc_strdup(cmd, value); cic = strtok_r(inp, ",", &saveptr); alert = strtok_r(NULL, ",", &saveptr); text = strtok_r(NULL, ",", &saveptr); talloc_free(inp); if (!cic || !alert || !text) return 1; return 0; } static int msc_signal_handler(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct msc_signal_data *msc; struct gsm_network *net; struct gsm_bts *bts; if (subsys != SS_MSC) return 0; if (signal != S_MSC_AUTHENTICATED) return 0; msc = signal_data; net = msc->data->network; llist_for_each_entry(bts, &net->bts_list, list) generate_location_state_trap(bts, msc->data); return 0; } int bsc_ctrl_cmds_install(struct gsm_network *net) { int rc; rc = bsc_base_ctrl_cmds_install(); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_MSC, &cmd_msc_connection_status); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc0_connection_status); if (rc) goto end; rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net); if (rc) goto end; rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc); if (rc) goto end; rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_ussd_notify); if (rc) goto end; rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net); end: return rc; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_filter.c000066400000000000000000000226421332665256100211020ustar00rootroot00000000000000/* (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2011 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 static void handle_lu_request(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh; struct gsm48_loc_upd_req *lu; struct gsm48_loc_area_id lai; if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); return; } gh = msgb_l3(msg); lu = (struct gsm48_loc_upd_req *) gh->data; gsm48_generate_lai2(&lai, bts_lai(conn_get_bts(conn))); if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); conn->new_subscriber = 1; } } /* extract a subscriber from the paging response */ static struct bsc_subscr *extract_sub(struct gsm_subscriber_connection *conn, struct msgb *msg) { uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; struct gsm48_hdr *gh; struct gsm48_pag_resp *resp; struct bsc_subscr *subscr; if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); return NULL; } gh = msgb_l3(msg); resp = (struct gsm48_pag_resp *) &gh->data[0]; gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), mi_string, &mi_type); DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", gsm48_mi_type_name(mi_type), mi_string); switch (mi_type) { case GSM_MI_TYPE_TMSI: subscr = bsc_subscr_find_by_tmsi(conn->network->bsc_subscribers, tmsi_from_string(mi_string)); break; case GSM_MI_TYPE_IMSI: subscr = bsc_subscr_find_by_imsi(conn->network->bsc_subscribers, mi_string); break; default: subscr = NULL; break; } return subscr; } /* we will need to stop the paging request */ static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct bsc_subscr *subscr = extract_sub(conn, msg); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); return -1; } paging_request_stop(&conn->network->bts_list, conn_get_bts(conn), subscr, conn, msg); bsc_subscr_put(subscr); return 0; } static int is_cm_service_for_emerg(struct msgb *msg) { struct gsm48_service_request *cm; struct gsm48_hdr *gh = msgb_l3(msg); if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) { LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n"); return 0; } cm = (struct gsm48_service_request *) &gh->data[0]; return cm->cm_service_type == GSM48_CMSERV_EMERGENCY; } struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh; int8_t pdisc; uint8_t mtype; struct osmo_bsc_data *bsc; struct bsc_msc_data *msc, *pag_msc; struct bsc_subscr *subscr; int is_emerg = 0; bsc = conn->network->bsc_data; if (msgb_l3len(msg) < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n"); return NULL; } gh = msgb_l3(msg); pdisc = gsm48_hdr_pdisc(gh); mtype = gsm48_hdr_msg_type(gh); /* * We are asked to select a MSC here but they are not equal. We * want to respond to a paging request on the MSC where we got the * request from. This is where we need to decide where this connection * will go. */ if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) goto paging; else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { is_emerg = is_cm_service_for_emerg(msg); goto round_robin; } else goto round_robin; round_robin: llist_for_each_entry(msc, &bsc->mscs, entry) { if (!msc->is_authenticated) continue; if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL) continue; if (is_emerg && !msc->allow_emerg) continue; /* force round robin by moving it to the end */ llist_move_tail(&msc->entry, &bsc->mscs); return msc; } return NULL; paging: subscr = extract_sub(conn, msg); if (!subscr) { LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n"); return NULL; } pag_msc = paging_get_msc(conn_get_bts(conn), subscr); bsc_subscr_put(subscr); llist_for_each_entry(msc, &bsc->mscs, entry) { if (msc != pag_msc) continue; /* * We don't check if the MSC is connected. In case it * is not the connection will be dropped. */ /* force round robin by moving it to the end */ llist_move_tail(&msc->entry, &bsc->mscs); return msc; } LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n"); return NULL; } /** * This is used to scan a message for extra functionality of the BSC. This * includes scanning for location updating requests/acceptd and then send * a welcome USSD message to the subscriber. */ int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); uint8_t pdisc = gsm48_hdr_pdisc(gh); uint8_t mtype = gsm48_hdr_msg_type(gh); if (pdisc == GSM48_PDISC_MM) { if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) handle_lu_request(conn, msg); } else if (pdisc == GSM48_PDISC_RR) { if (mtype == GSM48_MT_RR_PAG_RESP) handle_page_resp(conn, msg); } return 0; } static int send_welcome_ussd(struct gsm_subscriber_connection *conn) { if (!conn->sccp.msc->ussd_welcome_txt) { LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n"); return 0; } return BSS_SEND_USSD; } int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) { bsc_send_ussd_notify(conn, 1, conn->sccp.msc->ussd_welcome_txt); bsc_send_ussd_release_complete(conn); return 0; } static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn, uint8_t *data, unsigned int length) { struct tlv_parsed tp; int parse_res; struct gsm_bts *bts = conn_get_bts(conn); int tzunits; uint8_t tzbsd = 0; uint8_t dst = 0; parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, data, length, 0, 0); if (parse_res <= 0 && parse_res != -3) /* FIXME: -3 means unknown IE error, so this accepts messages * with unknown IEs. But parsing has aborted with the unknown * IE and the message is broken or parsed incompletely. */ return 0; /* Is TZ patching enabled? */ struct gsm_tz *tz = &bts->network->tz; if (!tz->override) return 0; /* Convert tz.hr and tz.mn to units */ if (tz->hr < 0) { tzunits = -tz->hr*4; tzbsd |= 0x08; } else tzunits = tz->hr*4; tzunits = tzunits + (tz->mn/15); tzbsd |= (tzunits % 10)*0x10 + (tzunits / 10); /* Convert DST value */ if (tz->dst >= 0 && tz->dst <= 2) dst = tz->dst; if (TLVP_PRESENT(&tp, GSM48_IE_UTC)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Local time zone' from 0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_UTC)[6], tzbsd); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_UTC)))[0] = tzbsd; } if (TLVP_PRESENT(&tp, GSM48_IE_NET_TIME_TZ)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Universal time and local time zone' TZ from " "0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)[6], tzbsd); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)))[6] = tzbsd; } #ifdef GSM48_IE_NET_DST if (TLVP_PRESENT(&tp, GSM48_IE_NET_DST)) { LOGP(DMSC, LOGL_DEBUG, "Changing 'Network daylight saving time' from " "0x%02x to 0x%02x.\n", TLVP_VAL(&tp, GSM48_IE_NET_DST)[0], dst); ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_DST)))[0] = dst; } #endif return 0; } static int has_core_identity(struct bsc_msc_data *msc) { if (msc->core_plmn.mnc != GSM_MCC_MNC_INVALID) return 1; if (msc->core_plmn.mcc != GSM_MCC_MNC_INVALID) return 1; if (msc->core_lac != -1) return 1; if (msc->core_ci != -1) return 1; return 0; } /** * Messages coming back from the MSC. */ int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct bsc_msc_data *msc; struct gsm48_loc_area_id *lai; struct gsm48_hdr *gh; uint8_t pdisc; uint8_t mtype; int length = msgb_l3len(msg); if (length < sizeof(*gh)) { LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); return -1; } gh = (struct gsm48_hdr *) msgb_l3(msg); length -= (const char *)&gh->data[0] - (const char *)gh; pdisc = gsm48_hdr_pdisc(gh); if (pdisc != GSM48_PDISC_MM) return 0; mtype = gsm48_hdr_msg_type(gh); msc = conn->sccp.msc; if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { if (has_core_identity(msc)) { if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { /* overwrite LAI in the message */ lai = (struct gsm48_loc_area_id *) &gh->data[0]; gsm48_generate_lai2(lai, bts_lai(conn_get_bts(conn))); } } if (conn->new_subscriber) return send_welcome_ussd(conn); return 0; } else if (mtype == GSM48_MT_MM_INFO) { bsc_patch_mm_info(conn, &gh->data[0], length); } return 0; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_grace.c000066400000000000000000000102061332665256100206670ustar00rootroot00000000000000/* * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 #include #include #include #include #include #include int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts) { if (bts->excl_from_rf_lock) return 1; return network->bsc_data->rf_ctrl->policy == S_RF_ON; } /* Return value is like paging_request_bts(): * returns 1 on success (one BTS was paged); 0 in case of error (e.g. TRX down) */ static int locked_paging_bts(struct gsm_bts *bts, struct bsc_subscr *subscr, int chan_needed, struct bsc_msc_data *msc) { /* Return error if the BTS is not excluded from the lock. */ if (!bts->excl_from_rf_lock) return 0; /* in case of no lac patching is in place, check the BTS */ if (msc->core_lac == -1 && subscr->lac != bts->location_area_code) return 0; return paging_request_bts(bts, subscr, chan_needed, msc); } /** * Page a subscriber in an MSC. * \param[in] rf_policy if not S_RF_ON, page only BTSs which are not excluded from the RF lock * \param[in] subscr subscriber we want to page * \param[in] chan_needed value of the GSM0808_IE_CHANNEL_NEEDED IE * \param[in] msc MSC which has issued this paging * \param[in] bts The BTS to issue the paging on * \returns 1 if paging was issued to the BTS, 0 if not */ int bsc_grace_paging_request(enum signal_rf rf_policy, struct bsc_subscr *subscr, int chan_needed, struct bsc_msc_data *msc, struct gsm_bts *bts) { if (rf_policy == S_RF_ON) return paging_request_bts(bts, subscr, chan_needed, msc); return locked_paging_bts(bts, subscr, chan_needed, msc); } static int handle_sub(struct gsm_lchan *lchan, const char *text) { struct gsm_subscriber_connection *conn; /* only send it to TCH */ if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) return -1; /* only send on the primary channel */ conn = lchan->conn; if (!conn) return -1; if (conn->lchan != lchan) return -1; /* only when active */ if (lchan->state != LCHAN_S_ACTIVE) return -1; bsc_send_ussd_notify(conn, 0, text); bsc_send_ussd_release_complete(conn); return 0; } /* * The place to handle the grace mode. Right now we will send * USSD messages to the subscriber, in the future we might start * a timer to have different modes for the grace period. */ static int handle_grace(struct gsm_network *network) { int ts_nr, lchan_nr; struct gsm_bts *bts; struct gsm_bts_trx *trx; if (!network->bsc_data->mid_call_txt) return 0; llist_for_each_entry(bts, &network->bts_list, list) { llist_for_each_entry(trx, &bts->trx_list, list) { for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { handle_sub(&ts->lchan[lchan_nr], network->bsc_data->mid_call_txt); } } } } return 0; } static int handle_rf_signal(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct rf_signal_data *sig; if (subsys != SS_RF) return -1; sig = signal_data; if (signal == S_RF_GRACE) handle_grace(sig->net); return 0; } static __attribute__((constructor)) void on_dso_load_grace(void) { osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL); } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_lcls.c000066400000000000000000000556301332665256100205550ustar00rootroot00000000000000/* (C) 2018 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 #include #include #include #include #include #include #include #include #include #include #include struct value_string lcls_event_names[] = { { LCLS_EV_UPDATE_CFG_CSC, "UPDATE_CFG_CSC" }, { LCLS_EV_APPLY_CFG_CSC, "APPLY_CFG_CSC" }, { LCLS_EV_CORRELATED, "CORRELATED" }, { LCLS_EV_OTHER_ENABLED, "OTHER_ENABLED" }, { LCLS_EV_OTHER_BREAK, "OTHER_BREAK" }, { LCLS_EV_OTHER_DEAD, "OTHER_DEAD" }, { 0, NULL } }; /*********************************************************************** * Utility functions ***********************************************************************/ enum gsm0808_lcls_status lcls_get_status(struct gsm_subscriber_connection *conn) { if (!conn->lcls.fi) return 0xff; switch (conn->lcls.fi->state) { case ST_NO_LCLS: return 0xff; case ST_NOT_YET_LS: return GSM0808_LCLS_STS_NOT_YET_LS; case ST_NOT_POSSIBLE_LS: return GSM0808_LCLS_STS_NOT_POSSIBLE_LS; case ST_NO_LONGER_LS: return GSM0808_LCLS_STS_NO_LONGER_LS; case ST_REQ_LCLS_NOT_SUPP: return GSM0808_LCLS_STS_REQ_LCLS_NOT_SUPP; case ST_LOCALLY_SWITCHED: case ST_LOCALLY_SWITCHED_WAIT_BREAK: case ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK: return GSM0808_LCLS_STS_LOCALLY_SWITCHED; } OSMO_ASSERT(0); } static void lcls_send_notify(struct gsm_subscriber_connection *conn) { enum gsm0808_lcls_status status = lcls_get_status(conn); struct msgb *msg; if (status == 0xff) return; LOGPFSM(conn->lcls.fi, "Sending BSSMAP LCLS NOTIFICATION (%s)\n", gsm0808_lcls_status_name(status)); msg = gsm0808_create_lcls_notification(status, false); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, msg); } static struct gsm_subscriber_connection * find_conn_with_same_gcr(struct gsm_subscriber_connection *conn_local) { struct gsm_network *net = conn_local->network; struct gsm_subscriber_connection *conn_other; llist_for_each_entry(conn_other, &net->subscr_conns, entry) { /* don't report back the same connection */ if (conn_other == conn_local) continue; /* don't consider any conn where GCR length is not the same as before */ if (conn_other->lcls.global_call_ref_len != conn_local->lcls.global_call_ref_len) continue; if (!memcmp(conn_other->lcls.global_call_ref, conn_local->lcls.global_call_ref, conn_local->lcls.global_call_ref_len)) return conn_other; } return NULL; } static bool lcls_is_supported_config(enum gsm0808_lcls_config cfg) { /* this is the only configuration that we support for now */ if (cfg == GSM0808_LCLS_CFG_BOTH_WAY) return true; else return false; } /* LCLS Call Leg Correlation as per 23.284 4.3 / 48.008 3.1.33.2.1 */ static int lcls_perform_correlation(struct gsm_subscriber_connection *conn_local) { struct gsm_subscriber_connection *conn_other; /* We can only correlate if a GCR is present */ OSMO_ASSERT(conn_local->lcls.global_call_ref_len); /* We can only correlate if we're not in active LS */ OSMO_ASSERT(conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED && conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK && conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK); conn_other = conn_local->lcls.other; if (conn_other) { LOGPFSM(conn_local->lcls.fi, "Breaking previous correlation with %s\n", osmo_fsm_inst_name(conn_other->lcls.fi)); OSMO_ASSERT(conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED && conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK && conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK); conn_local->lcls.other->lcls.other = NULL; conn_local->lcls.other = NULL; } conn_other = find_conn_with_same_gcr(conn_local); if (!conn_other) { /* we found no other call with same GCR: not possible */ LOGPFSM(conn_local->lcls.fi, "Unsuccessful correlation\n"); return -ENODEV; } /* store pointer to "other" in "local" */ conn_local->lcls.other = conn_other; LOGPFSM(conn_local->lcls.fi, "Successfully correlated with %s\n", osmo_fsm_inst_name(conn_other->lcls.fi)); /* notify other conn about our correlation */ osmo_fsm_inst_dispatch(conn_other->lcls.fi, LCLS_EV_CORRELATED, conn_local); return 0; } struct lcls_cfg_csc { enum gsm0808_lcls_config config; enum gsm0808_lcls_control control; }; /* Update the connections LCLS configuration and return old/previous configuration. * \returns (staticallly allocated) old configuration; NULL if new config not supported */ static struct lcls_cfg_csc *update_lcls_cfg_csc(struct gsm_subscriber_connection *conn, struct lcls_cfg_csc *new_cfg_csc) { static struct lcls_cfg_csc old_cfg_csc; old_cfg_csc.config = conn->lcls.config; old_cfg_csc.control = conn->lcls.control; if (new_cfg_csc->config != 0xff) { if (!lcls_is_supported_config(new_cfg_csc->config)) return NULL; if (conn->lcls.config != new_cfg_csc->config) { /* TODO: logging */ conn->lcls.config = new_cfg_csc->config; } } if (new_cfg_csc->control != 0xff) { if (conn->lcls.control != new_cfg_csc->control) { /* TODO: logging */ conn->lcls.control = new_cfg_csc->control; } } return &old_cfg_csc; } /* Attempt to update conn->lcls with the new config/csc provided. If new config is * unsupported, change into LCLS NOT SUPPORTED state and return -EINVAL. */ static int lcls_handle_cfg_update(struct gsm_subscriber_connection *conn, void *data) { struct lcls_cfg_csc *new_cfg_csc, *old_cfg_csc; new_cfg_csc = (struct lcls_cfg_csc *) data; old_cfg_csc = update_lcls_cfg_csc(conn, new_cfg_csc); if (!old_cfg_csc) { osmo_fsm_inst_state_chg(conn->lcls.fi, ST_REQ_LCLS_NOT_SUPP, 0, 0); return -EINVAL; } return 0; } /* notify the LCLS FSM about new LCLS Config and/or CSC */ void lcls_update_config(struct gsm_subscriber_connection *conn, const uint8_t *config, const uint8_t *control) { struct lcls_cfg_csc new_cfg = { .config = 0xff, .control = 0xff, }; /* nothing to update, skip it */ if (!config && !control) return; if (config) new_cfg.config = *config; if (control) new_cfg.control = *control; osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_UPDATE_CFG_CSC, &new_cfg); } /* apply the configuration, may be changed before by lcls_update_config */ void lcls_apply_config(struct gsm_subscriber_connection *conn) { osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_APPLY_CFG_CSC, NULL); } static void lcls_break_local_switching(struct gsm_subscriber_connection *conn) { struct mgcp_conn_peer peer; struct sockaddr_in *sin; LOGPFSM(conn->lcls.fi, "=== HERE IS WHERE WE DISABLE LCLS\n"); if (!conn->user_plane.fi_msc) { /* the MGCP FSM has died, e.g. due to some MGCP/SDP parsing error */ LOGPFSML(conn->lcls.fi, LOGL_NOTICE, "Cannot disable LCLS without MSC-side MGCP FSM\n"); return; } sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote; OSMO_ASSERT(sin->sin_family == AF_INET); memset(&peer, 0, sizeof(peer)); peer.port = htons(sin->sin_port); osmo_strlcpy(peer.addr, inet_ntoa(sin->sin_addr), sizeof(peer.addr)); bsc_subscr_pick_codec(&peer, conn); mgcp_conn_modify(conn->user_plane.fi_msc, 0, &peer); } static bool lcls_enable_possible(struct gsm_subscriber_connection *conn) { struct gsm_subscriber_connection *other_conn = conn->lcls.other; OSMO_ASSERT(other_conn); if (!lcls_is_supported_config(conn->lcls.config)) { LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported local config\n"); return false; } if (!lcls_is_supported_config(other_conn->lcls.config)) { LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported other config\n"); return false; } if (conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) { LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient local control\n"); return false; } if (other_conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) { LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient other control\n"); return false; } return true; } /*********************************************************************** * State callback functions ***********************************************************************/ static void lcls_no_lcls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; /* we're just starting and cannot yet have a correlated call */ OSMO_ASSERT(conn->lcls.other == NULL); if (conn->sccp.msc->lcls_mode == BSC_LCLS_MODE_DISABLED) { LOGPFSML(fi, LOGL_DEBUG, "LCLS disabled for this MSC, ignoring %s\n", osmo_fsm_event_name(fi->fsm, event)); return; } /* If there's no GCR set, we can never leave this state */ if (conn->lcls.global_call_ref_len == 0) { LOGPFSML(fi, LOGL_NOTICE, "No GCR set, ignoring %s\n", osmo_fsm_event_name(fi->fsm, event)); return; } switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) return; return; case LCLS_EV_APPLY_CFG_CSC: if (conn->lcls.config == 0xff) return; if (lcls_perform_correlation(conn) != 0) { /* Correlation leads to no result: Not Possible to LS */ osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); return; } /* we now have two correlated calls */ OSMO_ASSERT(conn->lcls.other); if (lcls_enable_possible(conn)) { /* Local Switching now active */ osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); } else { /* Couldn't be enabled: Not yet LS */ osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); } break; default: OSMO_ASSERT(0); break; } } static void lcls_not_yet_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; /* not yet locally switched means that we have correlation but no instruction * to actually connect them yet */ OSMO_ASSERT(conn->lcls.other); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) return; return; case LCLS_EV_APPLY_CFG_CSC: if (lcls_enable_possible(conn)) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); } break; case LCLS_EV_OTHER_ENABLED: OSMO_ASSERT(conn->lcls.other == data); if (lcls_enable_possible(conn)) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); } else { /* we couldn't enable our side, so ask other side to break */ osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); } break; case LCLS_EV_CORRELATED: /* other call informs us that he correlated with us */ conn->lcls.other = data; break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); break; default: OSMO_ASSERT(0); break; } } static void lcls_not_possible_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; OSMO_ASSERT(conn->lcls.other == NULL); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) return; return; case LCLS_EV_APPLY_CFG_CSC: if (lcls_perform_correlation(conn) != 0) { /* no correlation result: Remain in NOT_POSSIBLE_LS */ return; } /* we now have two correlated calls */ OSMO_ASSERT(conn->lcls.other); if (lcls_enable_possible(conn)) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); } else { osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); } break; case LCLS_EV_CORRELATED: /* other call informs us that he correlated with us */ conn->lcls.other = data; osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); /* Send NOTIFY about the fact that correlation happened */ lcls_send_notify(conn); break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; break; default: OSMO_ASSERT(0); break; } } static void lcls_no_longer_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; OSMO_ASSERT(conn->lcls.other); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) return; if (lcls_enable_possible(conn)) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); } break; case LCLS_EV_OTHER_ENABLED: OSMO_ASSERT(conn->lcls.other == data); if (lcls_enable_possible(conn)) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); } else { /* we couldn't enable our side, so ask other side to break */ osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); } break; case LCLS_EV_CORRELATED: /* other call informs us that he correlated with us */ conn->lcls.other = data; break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); break; default: OSMO_ASSERT(0); break; } } static void lcls_req_lcls_not_supp_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; /* we could have a correlated other call or not */ switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) return; //FIXME osmo_fsm_inst_state_chg(fi, return; case LCLS_EV_APPLY_CFG_CSC: if (lcls_perform_correlation(conn) != 0) { osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); return; } /* we now have two correlated calls */ OSMO_ASSERT(conn->lcls.other); if (!lcls_is_supported_config(conn->lcls.config)) return; if (lcls_enable_possible(conn)) osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); else osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); break; case LCLS_EV_CORRELATED: /* other call informs us that he correlated with us */ conn->lcls.other = data; break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; break; default: OSMO_ASSERT(0); break; } } static void lcls_locally_switched_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; OSMO_ASSERT(conn->lcls.other); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) { lcls_break_local_switching(conn); return; } break; case LCLS_EV_APPLY_CFG_CSC: if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) { osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); /* FIXME: what if there's a new config included? */ return; } /* TODO: Handle any changes of "config" once we support bi-casting etc. */ break; case LCLS_EV_OTHER_BREAK: OSMO_ASSERT(conn->lcls.other == data); osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_BREAK, 0, 0); break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); break; default: OSMO_ASSERT(0); break; } } static void lcls_locally_switched_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct gsm_subscriber_connection *conn = fi->priv; struct gsm_subscriber_connection *conn_other = conn->lcls.other; struct mgcp_conn_peer peer; struct sockaddr_in *sin; OSMO_ASSERT(conn_other); LOGPFSM(fi, "=== HERE IS WHERE WE ENABLE LCLS\n"); if (!conn->user_plane.fi_msc) { LOGPFSML(fi, LOGL_ERROR, "Cannot enable LCLS without MSC-side MGCP FSM. FIXME\n"); return; } sin = (struct sockaddr_in *)&conn_other->user_plane.aoip_rtp_addr_local; OSMO_ASSERT(sin->sin_family == AF_INET); memset(&peer, 0, sizeof(peer)); peer.port = htons(sin->sin_port); osmo_strlcpy(peer.addr, inet_ntoa(sin->sin_addr), sizeof(peer.addr)); bsc_subscr_pick_codec(&peer, conn); mgcp_conn_modify(conn->user_plane.fi_msc, 0, &peer); } static void lcls_locally_switched_wait_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; OSMO_ASSERT(conn->lcls.other); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) { lcls_break_local_switching(conn); return; } break; case LCLS_EV_APPLY_CFG_CSC: if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) { lcls_break_local_switching(conn); osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0); osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); /* no NOTIFY here, as the caller will be returning status in LCLS-CTRL-ACK */ /* FIXME: what if there's a new config included? */ return; } /* TODO: Handle any changes of "config" once we support bi-casting etc. */ break; case LCLS_EV_OTHER_BREAK: /* we simply ignore it, must be a re-transmission */ break; case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); conn->lcls.other = NULL; break; default: lcls_locally_switched_fn(fi, event, data); break; } } static void lcls_locally_switched_wait_other_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; OSMO_ASSERT(conn->lcls.other); switch (event) { case LCLS_EV_UPDATE_CFG_CSC: if (lcls_handle_cfg_update(conn, data) != 0) { lcls_break_local_switching(conn); return; } /* TODO: Handle any changes of "config" once we support bi-casting etc. */ break; case LCLS_EV_OTHER_BREAK: case LCLS_EV_OTHER_DEAD: OSMO_ASSERT(conn->lcls.other == data); lcls_break_local_switching(conn); osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0); /* Send LCLS-NOTIFY to inform MSC */ lcls_send_notify(conn); break; default: lcls_locally_switched_fn(fi, event, data); break; } } static void lcls_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) { struct gsm_subscriber_connection *conn = fi->priv; if (conn->lcls.other) { /* inform the "other" side that we're dead, so it can disabe LS and send NOTIFY */ if (conn->lcls.other->fi) osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_DEAD, conn); conn->lcls.other = NULL; } } /*********************************************************************** * FSM Definition ***********************************************************************/ #define S(x) (1 << (x)) static const struct osmo_fsm_state lcls_fsm_states[] = { [ST_NO_LCLS] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC), .out_state_mask = S(ST_NO_LCLS) | S(ST_NOT_YET_LS) | S(ST_NOT_POSSIBLE_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED), .name = "NO_LCLS", .action = lcls_no_lcls_fn, }, [ST_NOT_YET_LS] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_CORRELATED) | S(LCLS_EV_OTHER_ENABLED) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NOT_YET_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED), .name = "NOT_YET_LS", .action = lcls_not_yet_ls_fn, }, [ST_NOT_POSSIBLE_LS] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_CORRELATED), .out_state_mask = S(ST_NOT_YET_LS) | S(ST_NOT_POSSIBLE_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED), .name = "NOT_POSSIBLE_LS", .action = lcls_not_possible_ls_fn, }, [ST_NO_LONGER_LS] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_CORRELATED) | S(LCLS_EV_OTHER_ENABLED) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NO_LONGER_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED), .name = "NO_LONGER_LS", .action = lcls_no_longer_ls_fn, }, [ST_REQ_LCLS_NOT_SUPP] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_CORRELATED) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NOT_YET_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED), .name = "REQ_LCLS_NOT_SUPP", .action = lcls_req_lcls_not_supp_fn, }, [ST_LOCALLY_SWITCHED] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_OTHER_BREAK) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NO_LONGER_LS) | S(ST_NOT_POSSIBLE_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED_WAIT_BREAK) | S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK) | S(ST_LOCALLY_SWITCHED), .name = "LOCALLY_SWITCHED", .action = lcls_locally_switched_fn, .onenter = lcls_locally_switched_onenter, }, /* received an "other" break, waiting for the local break */ [ST_LOCALLY_SWITCHED_WAIT_BREAK] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_APPLY_CFG_CSC) | S(LCLS_EV_OTHER_BREAK) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NO_LONGER_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED) | S(ST_LOCALLY_SWITCHED_WAIT_BREAK), .name = "LOCALLY_SWITCHED_WAIT_BREAK", .action = lcls_locally_switched_wait_break_fn, }, /* received a local break, waiting for the "other" break */ [ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK] = { .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | S(LCLS_EV_OTHER_BREAK) | S(LCLS_EV_OTHER_DEAD), .out_state_mask = S(ST_NO_LONGER_LS) | S(ST_REQ_LCLS_NOT_SUPP) | S(ST_LOCALLY_SWITCHED) | S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK), .name = "LOCALLY_SWITCHED_WAIT_OTHER_BREAK", .action = lcls_locally_switched_wait_other_break_fn, }, }; struct osmo_fsm lcls_fsm = { .name = "LCLS", .states = lcls_fsm_states, .num_states = ARRAY_SIZE(lcls_fsm_states), .allstate_event_mask = 0, .allstate_action = NULL, .cleanup = lcls_fsm_cleanup, .timer_cb = NULL, .log_subsys = DLCLS, .event_names = lcls_event_names, }; osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_main.c000066400000000000000000000572551332665256100205510ustar00rootroot00000000000000/* (C) 2008-2009 by Harald Welte * (C) 2009-2011 by Holger Hans Peter Freyther * (C) 2009-2011 by On-Waves * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include #include #include #include #include #include #include "../../bscconfig.h" struct gsm_network *bsc_gsmnet = 0; static const char *config_file = "osmo-bsc.cfg"; static const char *rf_ctrl = NULL; static int daemonize = 0; static LLIST_HEAD(access_lists); struct llist_head *bsc_access_lists(void) { return &access_lists; } static void print_usage() { printf("Usage: osmo-bsc\n"); } static void print_help() { printf(" Some useful help...\n"); printf(" -h --help This text.\n"); printf(" -D --daemonize Fork the process into a background daemon.\n"); printf(" -d --debug option --debug=DRLL:DMM:DRR:DRSL:DNM enable debugging.\n"); printf(" -s --disable-color Disable coloring log in stderr.\n"); printf(" -T --timestamp Print a timestamp in the debug output.\n"); printf(" -V --version Print the version of OsmoBSC.\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -l --local IP The local address of the MGCP.\n"); printf(" -e --log-level number Set a global loglevel.\n"); printf(" -r --rf-ctl NAME A unix domain socket to listen for cmds.\n"); printf(" -t --testmode A special mode to provoke failures at the MSC.\n"); } static void handle_options(int argc, char **argv) { while (1) { int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"debug", 1, 0, 'd'}, {"daemonize", 0, 0, 'D'}, {"config-file", 1, 0, 'c'}, {"disable-color", 0, 0, 's'}, {"timestamp", 0, 0, 'T'}, {"version", 0, 0, 'V' }, {"local", 1, 0, 'l'}, {"log-level", 1, 0, 'e'}, {"rf-ctl", 1, 0, 'r'}, {"testmode", 0, 0, 't'}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:DsTVc:e:r:t", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_usage(); print_help(); exit(0); case 's': log_set_use_color(osmo_stderr_target, 0); break; case 'd': log_parse_category_mask(osmo_stderr_target, optarg); break; case 'D': daemonize = 1; break; case 'c': config_file = optarg; break; case 'T': log_set_print_timestamp(osmo_stderr_target, 1); break; case 'V': print_version(1); exit(0); break; case 'e': log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case 'r': rf_ctrl = optarg; break; default: /* ignore */ break; } } } /* Callback function for NACK on the OML NM */ static int oml_msg_nack(struct nm_nack_signal_data *nack) { if (nack->mt == NM_MT_GET_ATTR_NACK) { LOGP(DNM, LOGL_ERROR, "BTS%u does not support Get Attributes " "OML message.\n", nack->bts->nr); return 0; } if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " "Was the bts type and frequency properly specified?\n"); else LOGP(DNM, LOGL_ERROR, "Got %s NACK going to drop the OML links.\n", abis_nm_nack_name(nack->mt)); if (!nack->bts) { LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); return 0; } if (is_ipaccess_bts(nack->bts)) ipaccess_drop_oml(nack->bts); return 0; } /* Callback function to be called every time we receive a signal from NM */ static int nm_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct nm_nack_signal_data *nack; switch (signal) { case S_NM_NACK: nack = signal_data; return oml_msg_nack(nack); default: break; } return 0; } /* Produce a MA as specified in 10.5.2.21 */ static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) { /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs * and the MA */ struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; struct bitvec *ts_arfcn = &ts->hopping.arfcns; struct bitvec *ma = &ts->hopping.ma; unsigned int num_cell_arfcns, bitnum, n_chan; int i; /* re-set the MA to all-zero */ ma->cur_bit = 0; ts->hopping.ma_len = 0; memset(ma->data, 0, ma->data_len); if (!ts->hopping.enabled) return 0; /* count the number of ARFCNs in the cell channel allocation */ num_cell_arfcns = 0; for (i = 0; i < 1024; i++) { if (bitvec_get_bit_pos(cell_chan, i)) num_cell_arfcns++; } /* pad it to octet-aligned number of bits */ ts->hopping.ma_len = num_cell_arfcns / 8; if (num_cell_arfcns % 8) ts->hopping.ma_len++; n_chan = 0; for (i = 0; i < 1024; i++) { if (!bitvec_get_bit_pos(cell_chan, i)) continue; /* set the corresponding bit in the MA */ bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; if (bitvec_get_bit_pos(ts_arfcn, i)) bitvec_set_bit_pos(ma, bitnum, 1); else bitvec_set_bit_pos(ma, bitnum, 0); n_chan++; } /* ARFCN 0 is special: It is coded last in the bitmask */ if (bitvec_get_bit_pos(cell_chan, 0)) { n_chan++; /* set the corresponding bit in the MA */ bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; if (bitvec_get_bit_pos(ts_arfcn, 0)) bitvec_set_bit_pos(ma, bitnum, 1); else bitvec_set_bit_pos(ma, bitnum, 0); } return 0; } static void bootstrap_rsl(struct gsm_bts_trx *trx) { unsigned int i; LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " "on ARFCN %u using MCC-MNC %s LAC=%u CID=%u BSIC=%u\n", trx->bts->nr, trx->nr, trx->arfcn, osmo_plmn_name(&bsc_gsmnet->plmn), trx->bts->location_area_code, trx->bts->cell_identity, trx->bts->bsic); if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { rsl_nokia_si_begin(trx); } /* * Trigger ACC ramping before sending system information to BTS. * This ensures that RACH control in system information is configured correctly. * TRX 0 should be usable and unlocked, otherwise starting ACC ramping is pointless. */ if (trx_is_usable(trx) && trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) acc_ramp_trigger(&trx->bts->acc_ramp); gsm_bts_trx_set_system_infos(trx); if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { /* channel unspecific, power reduction in 2 dB steps */ rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); rsl_nokia_si_end(trx); } for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; generate_ma_for_ts(ts); gsm_ts_check_init(ts); } } /* Callback function to be called every time we receive a signal from INPUT */ static int inp_sig_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { struct input_signal_data *isd = signal_data; struct gsm_bts_trx *trx = isd->trx; int ts_no, lchan_no; /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */ const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, }; const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, }; /* we should not request more attributes than we're ready to handle */ OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR); OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR); if (subsys != SS_L_INPUT) return -EINVAL; LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, get_value_string(e1inp_signal_names, signal)); switch (signal) { case S_L_INP_TEI_UP: if (isd->link_type == E1INP_SIGN_OML) { /* TODO: this is required for the Nokia BTS, hopping is configured during OML, other MA is not set. */ struct gsm_bts_trx *cur_trx; /* was static in system_information.c */ extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); uint8_t ca[20]; /* has to be called before generate_ma_for_ts to set bts->si_common.cell_alloc */ generate_cell_chan_list(ca, trx->bts); /* Request generic BTS-level attributes */ abis_nm_get_attr(trx->bts, NM_OC_BTS, 0xFF, 0xFF, 0xFF, bts_attr, sizeof(bts_attr)); llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { int i; /* Request TRX-level attributes */ abis_nm_get_attr(cur_trx->bts, NM_OC_BASEB_TRANSC, 0, cur_trx->nr, 0xFF, trx_attr, sizeof(trx_attr)); for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) generate_ma_for_ts(&cur_trx->ts[i]); } } if (isd->link_type == E1INP_SIGN_RSL) bootstrap_rsl(trx); break; case S_L_INP_TEI_DN: LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); if (isd->link_type == E1INP_SIGN_OML) rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_OML_FAIL]); else if (isd->link_type == E1INP_SIGN_RSL) { rate_ctr_inc(&trx->bts->bts_ctrs->ctr[BTS_CTR_BTS_RSL_FAIL]); acc_ramp_abort(&trx->bts->acc_ramp); } /* * free all allocated channels. change the nm_state so the * trx and trx_ts becomes unusable and chan_alloc.c can not * allocate from it. */ for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { if (ts->lchan[lchan_no].state != LCHAN_S_NONE) lchan_free(&ts->lchan[lchan_no]); lchan_reset(&ts->lchan[lchan_no]); } } gsm_bts_mo_reset(trx->bts); abis_nm_clear_queue(trx->bts); break; default: break; } return 0; } static int bootstrap_bts(struct gsm_bts *bts) { int i, n; if (!bts->model) return -EFAULT; if (bts->model->start && !bts->model->started) { int ret = bts->model->start(bts->network); if (ret < 0) return ret; bts->model->started = true; } /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ switch (bts->band) { case GSM_BAND_1800: if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); return -EINVAL; } break; case GSM_BAND_1900: if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); return -EINVAL; } break; case GSM_BAND_900: if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || bts->c0->arfcn > 1023) { LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n"); return -EINVAL; } break; case GSM_BAND_850: if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); return -EINVAL; } break; default: LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); return -EINVAL; } /* Control Channel Description is set from vty/config */ /* T3212 is set from vty/config */ /* Set ccch config by looking at ts config */ for (n=0, i=0; i<8; i++) n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; /* Indicate R99 MSC in SI3 */ bts->si_common.chan_desc.mscr = 1; switch (n) { case 0: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; /* Limit reserved block to 2 on combined channel according to 3GPP TS 44.018 Table 10.5.2.11.1 */ if (bts->si_common.chan_desc.bs_ag_blks_res > 2) { LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, " "reducing BS-AG-BLKS-RES value %d -> 2\n", bts->si_common.chan_desc.bs_ag_blks_res); bts->si_common.chan_desc.bs_ag_blks_res = 2; } break; case 1: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; break; case 2: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; break; case 3: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; break; case 4: bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; break; default: LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); return -EINVAL; } bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ bts->si_common.cell_sel_par.acs = 0; bts->si_common.ncc_permitted = 0xff; bts->chan_load_samples_idx = 0; /* ACC ramping is initialized from vty/config */ /* Initialize the BTS state */ gsm_bts_mo_reset(bts); return 0; } static int bsc_network_configure(const char *config_file) { struct gsm_bts *bts; int rc; rc = vty_read_config_file(config_file, NULL); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); return rc; } /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(), OSMO_VTY_PORT_NITB_BSC); if (rc < 0) return rc; osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { rc = bootstrap_bts(bts); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n"); return rc; } rc = e1_reconfig_bts(bts); if (rc < 0) { LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n"); return rc; } } return 0; } static int bsc_vty_go_parent(struct vty *vty) { switch (vty->node) { case GSMNET_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; case BTS_NODE: vty->node = GSMNET_NODE; { /* set vty->index correctly ! */ struct gsm_bts *bts = vty->index; vty->index = bts->network; vty->index_sub = NULL; } break; case TRX_NODE: vty->node = BTS_NODE; { /* set vty->index correctly ! */ struct gsm_bts_trx *trx = vty->index; vty->index = trx->bts; vty->index_sub = &trx->bts->description; } break; case TS_NODE: vty->node = TRX_NODE; { /* set vty->index correctly ! */ struct gsm_bts_trx_ts *ts = vty->index; vty->index = ts->trx; vty->index_sub = &ts->trx->description; } break; case OML_NODE: case OM2K_NODE: vty->node = ENABLE_NODE; /* NOTE: this only works because it's not part of the config * tree, where outer commands are searched via vty_go_parent() * and only (!) executed when a matching one is found. */ talloc_free(vty->index); vty->index = NULL; break; case OM2K_CON_GROUP_NODE: vty->node = BTS_NODE; { struct con_group *cg = vty->index; struct gsm_bts *bts = cg->bts; vty->index = bts; vty->index_sub = &bts->description; } break; case BSC_NODE: case MSC_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; default: osmo_ss7_vty_go_parent(vty); } return vty->node; } static int bsc_vty_is_config_node(struct vty *vty, int node) { /* Check if libosmo-sccp declares the node in * question as config node */ if (osmo_ss7_is_config_node(vty, node)) return 1; switch (node) { /* add items that are not config */ case OML_NODE: case OM2K_NODE: case CONFIG_NODE: return 0; default: return 1; } } static struct vty_app_info vty_info = { .name = "OsmoBSC", .copyright = "Copyright (C) 2008-2018 Harald Welte, Holger Freyther\r\n" "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n" "License AGPLv3+: GNU AGPL version 3 or later \r\n" "This is free software: you are free to change and redistribute it.\r\n" "There is NO WARRANTY, to the extent permitted by law.\r\n", .version = PACKAGE_VERSION, .go_parent_cb = bsc_vty_go_parent, .is_config_node = bsc_vty_is_config_node, }; extern int bsc_shutdown_net(struct gsm_network *net); static void signal_handler(int signal) { fprintf(stdout, "signal %u received\n", signal); switch (signal) { case SIGINT: case SIGTERM: bsc_shutdown_net(bsc_gsmnet); osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); sleep(3); exit(0); break; case SIGABRT: /* in case of abort, we want to obtain a talloc report * and then return to the caller, who will abort the process */ case SIGUSR1: talloc_report(tall_vty_ctx, stderr); talloc_report_full(tall_bsc_ctx, stderr); break; case SIGUSR2: if (!bsc_gsmnet->bsc_data) return; break; default: break; } } static const struct log_info_cat osmo_bsc_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRR] = { .name = "DRR", .description = "Layer3 Radio Resource (RR)", .color = "\033[1;34m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRSL] = { .name = "DRSL", .description = "A-bis Radio Signalling Link (RSL)", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DNM] = { .name = "DNM", .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_INFO, }, [DPAG] = { .name = "DPAG", .description = "Paging Subsystem", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMEAS] = { .name = "DMEAS", .description = "Radio Measurement Processing", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DHO] = { .name = "DHO", .description = "Hand-Over Process", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DHODEC] = { .name = "DHODEC", .description = "Hand-Over Decision", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DNAT] = { .name = "DNAT", .description = "GSM 08.08 NAT/Multiplexer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCTRL] = { .name = "DCTRL", .description = "Control interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DFILTER] = { .name = "DFILTER", .description = "BSC/NAT IMSI based filtering", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DPCU] = { .name = "DPCU", .description = "PCU Interface", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DLCLS] = { .name = "DLCLS", .description = "Local Call, Local Switch", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) { const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR]; if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0 && bsub && bsub == tar->filter_data[LOG_FLT_BSC_SUBSCR]) return 1; return 0; } const struct log_info log_info = { .filter_fn = filter_fn, .cat = osmo_bsc_categories, .num_cat = ARRAY_SIZE(osmo_bsc_categories), }; extern void *tall_paging_ctx; extern void *tall_fle_ctx; extern void *tall_sigh_ctx; extern void *tall_tqe_ctx; extern void *tall_ctr_ctx; int main(int argc, char **argv) { struct bsc_msc_data *msc; struct osmo_bsc_data *data; int rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "osmo-bsc"); msgb_talloc_ctx_init(tall_bsc_ctx, 0); osmo_xua_msg_tall_ctx_init(tall_bsc_ctx); vty_info.tall_ctx = tall_bsc_ctx; tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, "bs11_file_list_entry"); tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); osmo_init_logging2(tall_bsc_ctx, &log_info); osmo_stats_init(tall_bsc_ctx); /* Allocate global gsm_network struct */ rc = bsc_network_alloc(); if (rc) { fprintf(stderr, "Allocation failed. exiting.\n"); exit(1); } bsc_gsmnet->mgw.conf = talloc_zero(bsc_gsmnet, struct mgcp_client_conf); mgcp_client_conf_init(bsc_gsmnet->mgw.conf); bts_init(); libosmo_abis_init(tall_bsc_ctx); /* enable filters */ /* This needs to precede handle_options() */ vty_init(&vty_info); bsc_vty_init(bsc_gsmnet); bsc_msg_acc_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE); ctrl_vty_init(tall_bsc_ctx); logging_vty_add_deprecated_subsys(tall_bsc_ctx, "cc"); logging_vty_add_deprecated_subsys(tall_bsc_ctx, "mgcp"); /* Initalize SS7 */ osmo_ss7_init(); osmo_ss7_vty_init_asp(tall_bsc_ctx); /* parse options */ handle_options(argc, argv); /* seed the PRNG */ srand(time(NULL)); /* Read the config */ rc = bsc_network_configure(config_file); if (rc < 0) { fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); exit(1); } /* start control interface after reading config for * ctrl_vty_get_bind_addr() */ bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_NITB_BSC); if (!bsc_gsmnet->ctrl) { fprintf(stderr, "Failed to init the control interface. Exiting.\n"); exit(1); } rc = bsc_ctrl_cmds_install(bsc_gsmnet); if (rc < 0) { fprintf(stderr, "Failed to install control commands. Exiting.\n"); exit(1); } data = bsc_gsmnet->bsc_data; if (rf_ctrl) osmo_talloc_replace_string(data, &data->rf_ctrl_name, rf_ctrl); data->rf_ctrl = osmo_bsc_rf_create(data->rf_ctrl_name, bsc_gsmnet); if (!data->rf_ctrl) { fprintf(stderr, "Failed to create the RF service.\n"); exit(1); } llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { if (osmo_bsc_msc_init(msc) != 0) { LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n"); exit(1); } } bsc_gsmnet->mgw.client = mgcp_client_init(bsc_gsmnet, bsc_gsmnet->mgw.conf); if (mgcp_client_connect(bsc_gsmnet->mgw.client)) { LOGP(DNM, LOGL_ERROR, "MGW connect failed at (%s:%u)\n", bsc_gsmnet->mgw.conf->remote_addr, bsc_gsmnet->mgw.conf->remote_port); exit(1); } if (osmo_bsc_sigtran_init(&bsc_gsmnet->bsc_data->mscs) != 0) { LOGP(DNM, LOGL_ERROR, "Failed to initalize sigtran backhaul.\n"); exit(1); } if (osmo_bsc_audio_init(bsc_gsmnet) != 0) { LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n"); exit(1); } handover_decision_1_init(); hodec2_init(bsc_gsmnet); signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } while (1) { osmo_select_main(0); } return 0; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_msc.c000066400000000000000000000053771332665256100204050ustar00rootroot00000000000000/* * Handle the connection to the MSC. This include ping/timeout/reconnect * (C) 2008-2018 by Harald Welte * (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include int osmo_bsc_msc_init(struct bsc_msc_data *data) { /* FIXME: This is a leftover from the old architecture that used * sccp-lite with osmocom specific authentication. Since we now * changed to AoIP the connected status and the authentication * status is managed differently. However osmo_bsc_filter.c still * needs the flags to be set to one. See also: OS#3112 */ data->is_authenticated = 1; return 0; } struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) { struct bsc_msc_data *msc_data; llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry) if (msc_data->nr == nr) return msc_data; return NULL; } struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) { struct bsc_msc_data *msc_data; /* check if there is already one */ msc_data = osmo_msc_data_find(net, nr); if (msc_data) return msc_data; msc_data = talloc_zero(net, struct bsc_msc_data); if (!msc_data) return NULL; llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); /* Init back pointer */ msc_data->network = net; msc_data->core_plmn = (struct osmo_plmn_id){ .mcc = GSM_MCC_MNC_INVALID, .mnc = GSM_MCC_MNC_INVALID, }; msc_data->core_ci = -1; msc_data->core_lac = -1; msc_data->rtp_base = 4000; msc_data->nr = nr; msc_data->allow_emerg = 1; msc_data->a.asp_proto = OSMO_SS7_ASP_PROT_M3UA; /* Defaults for the audio setup */ msc_data->amr_conf.m5_90 = 1; return msc_data; } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_sigtran.c000066400000000000000000000434201332665256100212610ustar00rootroot00000000000000/* (C) 2017 by sysmocom s.f.m.c. GmbH, Author: Philipp Maier * (C) 2017-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* A pointer to a list with all involved MSCs * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */ static struct llist_head *msc_list; #define RESET_INTERVAL 1 /* sek */ #define SCCP_MSG_MAXSIZE 1024 #define CS7_POINTCODE_DEFAULT_OFFSET 2 /* The SCCP stack will not assign connection IDs to us automatically, we * will do this ourselves using a counter variable, that counts one up * for every new connection */ static uint32_t conn_id_counter; /* Helper function to Check if the given connection id is already assigned */ static struct gsm_subscriber_connection *get_bsc_conn_by_conn_id(int conn_id) { conn_id &= 0xFFFFFF; struct gsm_subscriber_connection *conn; llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { if (conn->sccp.conn_id == conn_id) return conn; } return NULL; } /* Pick a free connection id */ static int pick_free_conn_id(const struct bsc_msc_data *msc) { int conn_id = conn_id_counter; int i; for (i = 0; i < 0xFFFFFF; i++) { conn_id++; conn_id &= 0xFFFFFF; if (get_bsc_conn_by_conn_id(conn_id) == false) { conn_id_counter = conn_id; return conn_id; } } return -1; } /* Send reset to MSC */ static void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc) { struct osmo_ss7_instance *ss7; struct msgb *msg; ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); msg = gsm0808_create_reset(); osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, &msc->a.msc_addr, msg); } /* Send reset-ack to MSC */ void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc) { struct osmo_ss7_instance *ss7; struct msgb *msg; OSMO_ASSERT(msc); ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); msg = gsm0808_create_reset_ack(); osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr, &msc->a.msc_addr, msg); } /* Find an MSC by its sigtran point code */ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr) { struct osmo_ss7_instance *ss7; struct bsc_msc_data *msc; llist_for_each_entry(msc, msc_list, entry) { if (memcmp(msc_addr, &msc->a.msc_addr, sizeof(*msc_addr)) == 0) return msc; } ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_ERROR, "Unable to find MSC data under address: %s\n", osmo_sccp_addr_name(ss7, msc_addr)); return NULL; } /* Send data to MSC, use the connection id which MSC it is */ static int handle_data_from_msc(struct gsm_subscriber_connection *conn, struct msgb *msg) { msg->l3h = msgb_l2(msg); return bsc_handle_dt(conn, msg, msgb_l2len(msg)); } /* Sent unitdata to MSC, use the point code to determine which MSC it is */ static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struct msgb *msg, const struct osmo_sccp_user *scu) { struct osmo_ss7_instance *ss7; struct bsc_msc_data *msc = get_msc_by_addr(msc_addr); int rc = -EINVAL; if (msc) { msg->l3h = msgb_l2(msg); rc = bsc_handle_udt(msc, msg, msgb_l2len(msg)); } else { ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu)); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "incoming unitdata data from unknown remote address: %s\n", osmo_sccp_addr_name(ss7, msc_addr)); } return rc; } /* Callback function, called by the SSCP stack when data arrives */ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; struct osmo_sccp_user *scu = _scu; struct gsm_subscriber_connection *conn; int rc = 0; switch (OSMO_PRIM_HDR(&scu_prim->oph)) { case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): /* Handle inbound UNITDATA */ DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); rc = handle_unitdata_from_msc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu); break; case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): /* Handle inbound connections */ DEBUGP(DMSC, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id); conn = bsc_subscr_con_allocate(bsc_gsmnet); if (conn) { conn->sccp.msc = get_msc_by_addr(&scu_prim->u.connect.calling_addr); /* MSC may be NULL, let the FSM deal with it */ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_IND, scu_prim); } else LOGP(DMSC, LOGL_ERROR, "Unable to alloc subscr_conn for inbound N-CONNECT.ind\n"); break; case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): /* Handle outbound connection confirmation */ DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); conn = get_bsc_conn_by_conn_id(scu_prim->u.connect.conn_id); if (conn) { osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, scu_prim); conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED; if (msgb_l2len(oph->msg) > 0) handle_data_from_msc(conn, oph->msg); } else { LOGP(DMSC, LOGL_ERROR, "N-CONNET.cfm(%u, %s) for unknown conn?!?\n", scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); } break; case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): /* Handle incoming connection oriented data */ DEBUGP(DMSC, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); /* Incoming data is a sign of a vital connection */ conn = get_bsc_conn_by_conn_id(scu_prim->u.data.conn_id); if (conn) { a_reset_conn_success(conn->sccp.msc->a.reset_fsm); handle_data_from_msc(conn, oph->msg); } break; case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause); /* indication of disconnect */ conn = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id); if (conn) { conn->sccp.state = SUBSCR_SCCP_ST_NONE; if (msgb_l2len(oph->msg) > 0) handle_data_from_msc(conn, oph->msg); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_DISC_IND, scu_prim); } break; default: LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN operation %s on primitive %u\n", get_value_string(osmo_prim_op_names, oph->operation), oph->primitive); break; } msgb_free(oph->msg); return rc; } /* Allocate resources to make a new connection oriented sigtran connection * (not the connection ittself!) */ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc) { struct osmo_ss7_instance *ss7; struct gsm_bts *bts = conn_get_bts(conn); OSMO_ASSERT(conn); OSMO_ASSERT(msc); ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "Initializing resources for new SIGTRAN connection to MSC: %s...\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); if (a_reset_conn_ready(msc->a.reset_fsm) == false) { LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); return BSC_CON_REJECT_NO_LINK; } if (!bsc_grace_allow_new_connection(bts->network, bts)) { LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); return BSC_CON_REJECT_RF_GRACE; } conn->sccp.msc = msc; return BSC_CON_SUCCESS; } /* Open a new connection oriented sigtran connection */ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct osmo_ss7_instance *ss7; struct bsc_msc_data *msc; int conn_id; int rc; OSMO_ASSERT(conn); OSMO_ASSERT(msg); OSMO_ASSERT(conn->sccp.msc); OSMO_ASSERT(conn->sccp.conn_id == -1); msc = conn->sccp.msc; if (a_reset_conn_ready(msc->a.reset_fsm) == false) { LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); return -EINVAL; } conn->sccp.conn_id = conn_id = pick_free_conn_id(msc); if (conn->sccp.conn_id < 0) { LOGP(DMSC, LOGL_ERROR, "Unable to allocate SCCP Connection ID\n"); return -1; } LOGP(DMSC, LOGL_DEBUG, "Allocated new connection id: %d\n", conn->sccp.conn_id); ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "Opening new SIGTRAN connection (id=%i) to MSC: %s\n", conn_id, osmo_sccp_addr_name(ss7, &msc->a.msc_addr)); rc = osmo_sccp_tx_conn_req_msg(msc->a.sccp_user, conn_id, &msc->a.bsc_addr, &msc->a.msc_addr, msg); if (rc >= 0) conn->sccp.state = SUBSCR_SCCP_ST_WAIT_CONN_CONF; return rc; } /* Send data to MSC */ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { struct osmo_ss7_instance *ss7; int conn_id; int rc; struct bsc_msc_data *msc; OSMO_ASSERT(conn); OSMO_ASSERT(msg); OSMO_ASSERT(conn->sccp.msc); msc = conn->sccp.msc; /* Log the type of the message we are sending. This is just * informative, do not stop if detecting the type fails */ if (msg->len >= 3) { switch (msg->data[0]) { case BSSAP_MSG_BSS_MANAGEMENT: LOGP(DMSC, LOGL_INFO, "Tx MSC %s\n", gsm0808_bssmap_name(msg->data[2])); break; case BSSAP_MSG_DTAP: LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP\n"); break; default: LOGP(DMSC, LOGL_ERROR, "Tx MSC (unknwon message type)\n"); } } else LOGP(DMSC, LOGL_ERROR, "Tx MSC (message too short)\n"); if (a_reset_conn_ready(msc->a.reset_fsm) == false) { LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n"); return -EINVAL; } conn_id = conn->sccp.conn_id; ss7 = osmo_ss7_instance_find(msc->a.cs7_instance); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_DEBUG, "Sending connection (id=%i) oriented data to MSC: %s (%s)\n", conn_id, osmo_sccp_addr_name(ss7, &msc->a.msc_addr), osmo_hexdump(msg->data, msg->len)); rc = osmo_sccp_tx_data_msg(msc->a.sccp_user, conn_id, msg); return rc; } /* Send an USSD notification in case we loose the connection to the MSC */ static void bsc_notify_msc_lost(struct gsm_subscriber_connection *conn) { /* Check if sccp conn is still present */ if (!conn) return; /* check for config string */ if (!conn->sccp.msc->ussd_msc_lost_txt) return; if (conn->sccp.msc->ussd_msc_lost_txt[0] == '\0') return; /* send USSD notification */ bsc_send_ussd_notify(conn, 1, conn->sccp.msc->ussd_msc_lost_txt); bsc_send_ussd_release_complete(conn); } /* Close all open sigtran connections and channels */ void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc) { struct gsm_subscriber_connection *conn, *conn_temp; OSMO_ASSERT(msc); /* Close all open connections */ llist_for_each_entry_safe(conn, conn_temp, &bsc_gsmnet->subscr_conns, entry) { /* We only may close connections which actually belong to this * MSC. All other open connections are left untouched */ if (conn->sccp.msc == msc) { /* Notify active connection users via USSD that the MSC is down */ bsc_notify_msc_lost(conn); /* Take down all occopied RF channels */ /* Disconnect all Sigtran connections */ /* Delete subscriber connection */ osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL); } } } /* Callback function: Close all open connections */ static void osmo_bsc_sigtran_reset_cb(const void *priv) { struct bsc_msc_data *msc = (struct bsc_msc_data*) priv; /* Shut down all ongoing traffic */ osmo_bsc_sigtran_reset(msc); /* Send reset to MSC */ osmo_bsc_sigtran_tx_reset(msc); } /* Default point-code to be used as local address (BSC) */ #define BSC_DEFAULT_PC "0.23.3" /* Default point-code to be used as remote address (MSC) */ #define MSC_DEFAULT_PC "0.23.1" static int asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg); /* Initalize osmo sigtran backhaul */ int osmo_bsc_sigtran_init(struct llist_head *mscs) { bool free_attempt_used = false; bool fail_on_next_invalid_cfg = false; struct bsc_msc_data *msc; char msc_name[32]; uint32_t default_pc; osmo_ss7_register_rx_unknown_cb(&asp_rx_unknown); OSMO_ASSERT(mscs); msc_list = mscs; llist_for_each_entry(msc, msc_list, entry) { snprintf(msc_name, sizeof(msc_name), "msc-%u", msc->nr); LOGP(DMSC, LOGL_NOTICE, "Initializing SCCP connection to MSC %s\n", msc_name); /* Check if the VTY could determine a valid CS7 instance, * use safe default in case none is set */ if (msc->a.cs7_instance_valid == false) { msc->a.cs7_instance = 0; if (fail_on_next_invalid_cfg) goto fail_auto_cofiguration; free_attempt_used = true; } LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, A-Interface: %u\n", msc->a.cs7_instance); /* Pre-Check if there is an ss7 instance present */ if (osmo_ss7_instance_find(msc->a.cs7_instance) == NULL) { if (fail_on_next_invalid_cfg) goto fail_auto_cofiguration; free_attempt_used = true; } /* SS7 Protocol stack */ default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC); msc->a.sccp = osmo_sccp_simple_client_on_ss7_id(msc, msc->a.cs7_instance, msc_name, default_pc, msc->a.asp_proto, 0, NULL, 0, NULL); if (!msc->a.sccp) return -EINVAL; /* If unset, use default local SCCP address */ if (!msc->a.bsc_addr.presence) osmo_sccp_local_addr_by_instance(&msc->a.bsc_addr, msc->a.sccp, OSMO_SCCP_SSN_BSSAP); if (!osmo_sccp_check_addr(&msc->a.bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { LOGP(DMSC, LOGL_ERROR, "(%s) A-interface: invalid local (BSC) SCCP address: %s\n", msc_name, osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.bsc_addr)); return -EINVAL; } /* If unset, use default SCCP address for the MSC */ if (!msc->a.msc_addr.presence) osmo_sccp_make_addr_pc_ssn(&msc->a.msc_addr, osmo_ss7_pointcode_parse(NULL, MSC_DEFAULT_PC), OSMO_SCCP_SSN_BSSAP); if (!osmo_sccp_check_addr(&msc->a.msc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { LOGP(DMSC, LOGL_ERROR, "(%s) A-interface: invalid remote (MSC) SCCP address: %s\n", msc_name, osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.msc_addr)); return -EINVAL; } LOGP(DMSC, LOGL_NOTICE, "(%s) A-interface: local (BSC) SCCP address: %s\n", msc_name, osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.bsc_addr)); LOGP(DMSC, LOGL_NOTICE, "(%s) A-interface: remote (MSC) SCCP address: %s\n", msc_name, osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.msc_addr)); /* Bind SCCP user */ msc->a.sccp_user = osmo_sccp_user_bind(msc->a.sccp, msc_name, sccp_sap_up, msc->a.bsc_addr.ssn); if (!msc->a.sccp_user) return -EINVAL; /* Start MSC-Reset procedure */ msc->a.reset_fsm = a_reset_alloc(msc, msc_name, osmo_bsc_sigtran_reset_cb, msc); if (!msc->a.reset_fsm) return -EINVAL; /* If we have detected that the SS7 configuration of the MSC we have just initalized * was incomplete or completely missing, we can not tolerate another incomplete * configuration. The reson for this is that we do only specify exactly one default * pointcode pair. We also specify localhost as default IP-Address. If we have wanted * to support multiple MSCs with automatic configuration we would be forced to invent * a complex ruleset how to allocate the pointcodes and respective IP-Addresses. * Furthermore, the situation where a single BSC is connected to multiple MSCs * is a very rare situation anyway. In this case we expect the user to experienced * enough to create a valid SS7/CS7 VTY configuration that does not lack any * components */ if (free_attempt_used) fail_on_next_invalid_cfg = true; } return 0; fail_auto_cofiguration: LOGP(DMSC, LOGL_ERROR, "A-interface: More than one invalid/inclomplete configuration detected, unable to revover - check config file!\n"); return -EINVAL; } /* this function receives all messages received on an ASP for a PPID / StreamID that * libosmo-sigtran doesn't know about, such as piggy-backed CTRL and/or MGCP */ static int asp_rx_unknown(struct osmo_ss7_asp *asp, int ppid_mux, struct msgb *msg) { struct ipaccess_head *iph; struct ipaccess_head_ext *iph_ext; if (asp->cfg.proto != OSMO_SS7_ASP_PROT_IPA) { msgb_free(msg); return 0; } switch (ppid_mux) { case IPAC_PROTO_OSMO: if (msg->len < sizeof(*iph) + sizeof(*iph_ext)) { LOGP(DMSC, LOGL_ERROR, "The message is too short.\n"); msgb_free(msg); return -EINVAL; } iph = (struct ipaccess_head *) msg->data; iph_ext = (struct ipaccess_head_ext *) iph->data; msg->l2h = iph_ext->data; switch (iph_ext->proto) { case IPAC_PROTO_EXT_CTRL: return bsc_sccplite_rx_ctrl(asp, msg); } break; default: break; } msgb_free(msg); return 0; /* OSMO_SS7_UNKNOWN? */ } osmo-bsc-1.3.0/src/osmo-bsc/osmo_bsc_vty.c000066400000000000000000000665061332665256100204460ustar00rootroot00000000000000/* Osmo BSC VTY Configuration */ /* (C) 2009-2015 by Holger Hans Peter Freyther * (C) 2009-2014 by On-Waves * (C) 2018 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 #include #include #include #include #include #include #include #include #include #include #define IPA_STR "IP.ACCESS specific\n" static const struct value_string bsc_lcls_mode_names[] = { { BSC_LCLS_MODE_DISABLED, "disabled" }, { BSC_LCLS_MODE_MGW_LOOP, "mgw-loop" }, { 0, NULL } }; static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty) { return bsc_gsmnet->bsc_data; } static struct bsc_msc_data *bsc_msc_data(struct vty *vty) { return vty->index; } static struct cmd_node bsc_node = { BSC_NODE, "%s(config-bsc)# ", 1, }; static struct cmd_node msc_node = { MSC_NODE, "%s(config-msc)# ", 1, }; DEFUN(cfg_net_msc, cfg_net_msc_cmd, "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n") { int index = argc == 1 ? atoi(argv[0]) : 0; struct bsc_msc_data *msc; msc = osmo_msc_data_alloc(bsc_gsmnet, index); if (!msc) { vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE); return CMD_WARNING; } vty->index = msc; vty->node = MSC_NODE; return CMD_SUCCESS; } DEFUN(cfg_net_bsc, cfg_net_bsc_cmd, "bsc", "Configure BSC\n") { vty->node = BSC_NODE; return CMD_SUCCESS; } static void write_msc_amr_options(struct vty *vty, struct bsc_msc_data *msc) { #define WRITE_AMR(vty, msc, name, var) \ vty_out(vty, " amr-config %s %s%s", \ name, msc->amr_conf.var ? "allowed" : "forbidden", \ VTY_NEWLINE); WRITE_AMR(vty, msc, "12_2k", m12_2); WRITE_AMR(vty, msc, "10_2k", m10_2); WRITE_AMR(vty, msc, "7_95k", m7_95); WRITE_AMR(vty, msc, "7_40k", m7_40); WRITE_AMR(vty, msc, "6_70k", m6_70); WRITE_AMR(vty, msc, "5_90k", m5_90); WRITE_AMR(vty, msc, "5_15k", m5_15); WRITE_AMR(vty, msc, "4_75k", m4_75); #undef WRITE_AMR } static void write_msc(struct vty *vty, struct bsc_msc_data *msc) { vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); if (msc->core_plmn.mnc != GSM_MCC_MNC_INVALID) vty_out(vty, " core-mobile-network-code %s%s", osmo_mnc_name(msc->core_plmn.mnc, msc->core_plmn.mnc_3_digits), VTY_NEWLINE); if (msc->core_plmn.mcc != GSM_MCC_MNC_INVALID) vty_out(vty, " core-mobile-country-code %s%s", osmo_mcc_name(msc->core_plmn.mcc), VTY_NEWLINE); if (msc->core_lac != -1) vty_out(vty, " core-location-area-code %d%s", msc->core_lac, VTY_NEWLINE); if (msc->core_ci != -1) vty_out(vty, " core-cell-identity %d%s", msc->core_ci, VTY_NEWLINE); vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE); if (msc->ussd_welcome_txt) vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-welcome-text%s", VTY_NEWLINE); if (msc->ussd_msc_lost_txt && msc->ussd_msc_lost_txt[0]) vty_out(vty, " bsc-msc-lost-text %s%s", msc->ussd_msc_lost_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-msc-lost-text%s", VTY_NEWLINE); if (msc->ussd_grace_txt && msc->ussd_grace_txt[0]) vty_out(vty, " bsc-grace-text %s%s", msc->ussd_grace_txt, VTY_NEWLINE); else vty_out(vty, " no bsc-grace-text%s", VTY_NEWLINE); if (msc->audio_length != 0) { int i; vty_out(vty, " codec-list "); for (i = 0; i < msc->audio_length; ++i) { if (i != 0) vty_out(vty, " "); if (msc->audio_support[i]->hr) vty_out(vty, "hr%.1u", msc->audio_support[i]->ver); else vty_out(vty, "fr%.1u", msc->audio_support[i]->ver); } vty_out(vty, "%s", VTY_NEWLINE); } vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ? "normal" : "local", VTY_NEWLINE); vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ? "allow" : "deny", VTY_NEWLINE); if (msc->local_pref) vty_out(vty, " local-prefix %s%s", msc->local_pref, VTY_NEWLINE); if (msc->acc_lst_name) vty_out(vty, " access-list-name %s%s", msc->acc_lst_name, VTY_NEWLINE); /* write amr options */ write_msc_amr_options(vty, msc); /* write sccp connection configuration */ if (msc->a.bsc_addr_name) { vty_out(vty, " bsc-addr %s%s", msc->a.bsc_addr_name, VTY_NEWLINE); } if (msc->a.msc_addr_name) { vty_out(vty, " msc-addr %s%s", msc->a.msc_addr_name, VTY_NEWLINE); } vty_out(vty, " asp-protocol %s%s", osmo_ss7_asp_protocol_name(msc->a.asp_proto), VTY_NEWLINE); vty_out(vty, " lcls-mode %s%s", get_value_string(bsc_lcls_mode_names, msc->lcls_mode), VTY_NEWLINE); /* write MGW configuration */ mgcp_client_config_write(vty, " "); } static int config_write_msc(struct vty *vty) { struct bsc_msc_data *msc; struct osmo_bsc_data *bsc = osmo_bsc_data(vty); llist_for_each_entry(msc, &bsc->mscs, entry) write_msc(vty, msc); return CMD_SUCCESS; } static int config_write_bsc(struct vty *vty) { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); vty_out(vty, "bsc%s", VTY_NEWLINE); if (bsc->mid_call_txt) vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE); vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE); if (bsc->rf_ctrl_name) vty_out(vty, " bsc-rf-socket %s%s", bsc->rf_ctrl_name, VTY_NEWLINE); if (bsc->auto_off_timeout != -1) vty_out(vty, " bsc-auto-rf-off %d%s", bsc->auto_off_timeout, VTY_NEWLINE); if (bsc->ussd_no_msc_txt && bsc->ussd_no_msc_txt[0]) vty_out(vty, " missing-msc-text %s%s", bsc->ussd_no_msc_txt, VTY_NEWLINE); else vty_out(vty, " no missing-msc-text%s", VTY_NEWLINE); if (bsc->acc_lst_name) vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); bsc_msg_acc_lst_write(vty); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_ncc, cfg_net_bsc_ncc_cmd, "core-mobile-network-code <1-999>", "Use this network code for the core network\n" "MNC value\n") { struct bsc_msc_data *data = bsc_msc_data(vty); uint16_t mnc; bool mnc_3_digits; if (osmo_mnc_from_str(argv[0], &mnc, &mnc_3_digits)) { vty_out(vty, "%% Error decoding MNC: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } data->core_plmn.mnc = mnc; data->core_plmn.mnc_3_digits = mnc_3_digits; return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mcc, cfg_net_bsc_mcc_cmd, "core-mobile-country-code <1-999>", "Use this country code for the core network\n" "MCC value\n") { uint16_t mcc; struct bsc_msc_data *data = bsc_msc_data(vty); if (osmo_mcc_from_str(argv[0], &mcc)) { vty_out(vty, "%% Error decoding MCC: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } data->core_plmn.mcc = mcc; return CMD_SUCCESS; } DEFUN(cfg_net_bsc_lac, cfg_net_bsc_lac_cmd, "core-location-area-code <0-65535>", "Use this location area code for the core network\n" "LAC value\n") { struct bsc_msc_data *data = bsc_msc_data(vty); data->core_lac = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_ci, cfg_net_bsc_ci_cmd, "core-cell-identity <0-65535>", "Use this cell identity for the core network\n" "CI value\n") { struct bsc_msc_data *data = bsc_msc_data(vty); data->core_ci = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_rtp_base, cfg_net_bsc_rtp_base_cmd, "ip.access rtp-base <1-65000>", IPA_STR "Set the rtp-base port for the RTP stream\n" "Port number\n") { struct bsc_msc_data *data = bsc_msc_data(vty); data->rtp_base = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_codec_list, cfg_net_bsc_codec_list_cmd, "codec-list .LIST", "Set the allowed audio codecs\n" "List of audio codecs, e.g. fr3 fr1 hr3\n") { struct bsc_msc_data *data = bsc_msc_data(vty); int i; /* free the old list... if it exists */ if (data->audio_support) { talloc_free(data->audio_support); data->audio_support = NULL; data->audio_length = 0; } /* create a new array */ data->audio_support = talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc); data->audio_length = argc; for (i = 0; i < argc; ++i) { /* check for hrX or frX */ if (strlen(argv[i]) != 3 || argv[i][1] != 'r' || (argv[i][0] != 'h' && argv[i][0] != 'f') || argv[i][2] < 0x30 || argv[i][2] > 0x39) goto error; data->audio_support[i] = talloc_zero(data->audio_support, struct gsm_audio_support); data->audio_support[i]->ver = atoi(argv[i] + 2); if (strncmp("hr", argv[i], 2) == 0) data->audio_support[i]->hr = 1; else if (strncmp("fr", argv[i], 2) == 0) data->audio_support[i]->hr = 0; } return CMD_SUCCESS; error: vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", argv[i], VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } DEFUN(cfg_net_msc_welcome_ussd, cfg_net_msc_welcome_ussd_cmd, "bsc-welcome-text .TEXT", "Set the USSD notification to be sent\n" "Text to be sent\n") { struct bsc_msc_data *data = bsc_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_welcome_ussd, cfg_net_msc_no_welcome_ussd_cmd, "no bsc-welcome-text", NO_STR "Clear the USSD notification to be sent\n") { struct bsc_msc_data *data = bsc_msc_data(vty); talloc_free(data->ussd_welcome_txt); data->ussd_welcome_txt = NULL; return CMD_SUCCESS; } DEFUN(cfg_net_msc_lost_ussd, cfg_net_msc_lost_ussd_cmd, "bsc-msc-lost-text .TEXT", "Set the USSD notification to be sent on MSC connection loss\n" "Text to be sent\n") { struct bsc_msc_data *data = bsc_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_msc_lost_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_lost_ussd, cfg_net_msc_no_lost_ussd_cmd, "no bsc-msc-lost-text", NO_STR "Clear the USSD notification to be sent on MSC connection loss\n") { struct bsc_msc_data *data = bsc_msc_data(vty); talloc_free(data->ussd_msc_lost_txt); data->ussd_msc_lost_txt = 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_grace_ussd, cfg_net_msc_grace_ussd_cmd, "bsc-grace-text .TEXT", "Set the USSD notification to be sent when the MSC has entered the grace period\n" "Text to be sent\n") { struct bsc_msc_data *data = bsc_msc_data(vty); char *str = argv_concat(argv, argc, 0); if (!str) return CMD_WARNING; osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_grace_txt, str); talloc_free(str); return CMD_SUCCESS; } DEFUN(cfg_net_msc_no_grace_ussd, cfg_net_msc_no_grace_ussd_cmd, "no bsc-grace-text", NO_STR "Clear the USSD notification to be sent when the MSC has entered the grace period\n") { struct bsc_msc_data *data = bsc_msc_data(vty); talloc_free(data->ussd_grace_txt); data->ussd_grace_txt = NULL; return CMD_SUCCESS; } DEFUN(cfg_net_bsc_missing_msc_ussd, cfg_net_bsc_missing_msc_ussd_cmd, "missing-msc-text .TEXT", "Set the USSD notification to be send when a MSC has not been found.\n" "Text to be sent\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; osmo_talloc_replace_string(data, &data->ussd_no_msc_txt, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_no_missing_msc_text, cfg_net_bsc_no_missing_msc_text_cmd, "no missing-msc-text", NO_STR "Clear the USSD notification to be send when a MSC has not been found.\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); talloc_free(data->ussd_no_msc_txt); data->ussd_no_msc_txt = 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_type, cfg_net_msc_type_cmd, "type (normal|local)", "Select the MSC type\n" "Plain GSM MSC\n" "Special MSC for local call routing\n") { struct bsc_msc_data *data = bsc_msc_data(vty); if (strcmp(argv[0], "normal") == 0) data->type = MSC_CON_TYPE_NORMAL; else if (strcmp(argv[0], "local") == 0) data->type = MSC_CON_TYPE_LOCAL; return CMD_SUCCESS; } DEFUN(cfg_net_msc_emerg, cfg_net_msc_emerg_cmd, "allow-emergency (allow|deny)", "Allow CM ServiceRequests with type emergency\n" "Allow\n" "Deny\n") { struct bsc_msc_data *data = bsc_msc_data(vty); data->allow_emerg = strcmp("allow", argv[0]) == 0; return CMD_SUCCESS; } DEFUN(cfg_net_msc_local_prefix, cfg_net_msc_local_prefix_cmd, "local-prefix REGEXP", "Prefix for local numbers\n" "REGEXP used\n") { struct bsc_msc_data *msc = bsc_msc_data(vty); if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) { vty_out(vty, "%%Failed to parse the regexp: '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } #define AMR_CONF_STR "AMR Multirate Configuration\n" #define AMR_COMMAND(name) \ DEFUN(cfg_net_msc_amr_##name, \ cfg_net_msc_amr_##name##_cmd, \ "amr-config " #name "k (allowed|forbidden)", \ AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \ { \ struct bsc_msc_data *msc = bsc_msc_data(vty); \ \ msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \ return CMD_SUCCESS; \ } AMR_COMMAND(12_2) AMR_COMMAND(10_2) AMR_COMMAND(7_95) AMR_COMMAND(7_40) AMR_COMMAND(6_70) AMR_COMMAND(5_90) AMR_COMMAND(5_15) AMR_COMMAND(4_75) DEFUN(cfg_msc_acc_lst_name, cfg_msc_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { struct bsc_msc_data *msc = bsc_msc_data(vty); osmo_talloc_replace_string(msc, &msc->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_msc_no_acc_lst_name, cfg_msc_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Remove the access list from the NAT.\n") { struct bsc_msc_data *msc = bsc_msc_data(vty); if (msc->acc_lst_name) { talloc_free(msc->acc_lst_name); msc->acc_lst_name = NULL; } return CMD_SUCCESS; } /* Make sure only standard SSN numbers are used. If no ssn number is * configured, silently apply the default SSN */ static void enforce_standard_ssn(struct vty *vty, struct osmo_sccp_addr *addr) { if (addr->presence & OSMO_SCCP_ADDR_T_SSN) { if (addr->ssn != OSMO_SCCP_SSN_BSSAP) vty_out(vty, "setting an SSN (%u) different from the standard (%u) is not allowd, will use standard SSN for address: %s%s", addr->ssn, OSMO_SCCP_SSN_BSSAP, osmo_sccp_addr_dump(addr), VTY_NEWLINE); } addr->presence |= OSMO_SCCP_ADDR_T_SSN; addr->ssn = OSMO_SCCP_SSN_BSSAP; } DEFUN(cfg_msc_cs7_bsc_addr, cfg_msc_cs7_bsc_addr_cmd, "bsc-addr NAME", "Calling Address (local address of this BSC)\n" "SCCP address name\n") { struct bsc_msc_data *msc = bsc_msc_data(vty); const char *bsc_addr_name = argv[0]; struct osmo_ss7_instance *ss7; ss7 = osmo_sccp_addr_by_name(&msc->a.bsc_addr, bsc_addr_name); if (!ss7) { vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", bsc_addr_name, VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } /* Prevent mixing addresses from different CS7/SS7 instances */ if (msc->a.cs7_instance_valid) { if (msc->a.cs7_instance != ss7->cfg.id) { vty_out(vty, "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s", bsc_addr_name, VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } } msc->a.cs7_instance = ss7->cfg.id; msc->a.cs7_instance_valid = true; enforce_standard_ssn(vty, &msc->a.bsc_addr); msc->a.bsc_addr_name = talloc_strdup(msc, bsc_addr_name); return CMD_SUCCESS; } DEFUN(cfg_msc_cs7_msc_addr, cfg_msc_cs7_msc_addr_cmd, "msc-addr NAME", "Called Address (remote address of the MSC)\n" "SCCP address name\n") { struct bsc_msc_data *msc = bsc_msc_data(vty); const char *msc_addr_name = argv[0]; struct osmo_ss7_instance *ss7; ss7 = osmo_sccp_addr_by_name(&msc->a.msc_addr, msc_addr_name); if (!ss7) { vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", msc_addr_name, VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } /* Prevent mixing addresses from different CS7/SS7 instances */ if (msc->a.cs7_instance_valid) { if (msc->a.cs7_instance != ss7->cfg.id) { vty_out(vty, "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s", msc_addr_name, VTY_NEWLINE); return CMD_ERR_INCOMPLETE; } } msc->a.cs7_instance = ss7->cfg.id; msc->a.cs7_instance_valid = true; enforce_standard_ssn(vty, &msc->a.msc_addr); msc->a.msc_addr_name = talloc_strdup(msc, msc_addr_name); return CMD_SUCCESS; } DEFUN(cfg_msc_cs7_asp_proto, cfg_msc_cs7_asp_proto_cmd, "asp-protocol (m3ua|sua|ipa)", "A interface protocol to use for this MSC)\n" "MTP3 User Adaptation\n" "SCCP User Adaptation\n" "IPA Multiplex (SCCP Lite)\n") { struct bsc_msc_data *msc = bsc_msc_data(vty); msc->a.asp_proto = get_string_value(osmo_ss7_asp_protocol_vals, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_msc_lcls_mode, cfg_net_msc_lcls_mode_cmd, "lcls-mode (disabled|mgw-loop)", "Configure 3GPP LCLS (Local Call, Local Switch)\n" "Disable LCLS for all calls of this MSC\n" "Enable LCLS with loopping traffic in MGW\n") { struct bsc_msc_data *data = bsc_msc_data(vty); data->lcls_mode = get_string_value(bsc_lcls_mode_names, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mid_call_text, cfg_net_bsc_mid_call_text_cmd, "mid-call-text .TEXT", "Set the USSD notification sent to running calls when switching from Grace to Off.\n" "Text to be sent\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); char *txt = argv_concat(argv, argc, 0); if (!txt) return CMD_WARNING; osmo_talloc_replace_string(data, &data->mid_call_txt, txt); talloc_free(txt); return CMD_SUCCESS; } DEFUN(cfg_net_bsc_mid_call_timeout, cfg_net_bsc_mid_call_timeout_cmd, "mid-call-timeout NR", "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->mid_call_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_rf_socket, cfg_net_rf_socket_cmd, "bsc-rf-socket PATH", "Set the filename for the RF control interface.\n" "RF Control path\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); osmo_talloc_replace_string(data, &data->rf_ctrl_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_rf_off_time, cfg_net_rf_off_time_cmd, "bsc-auto-rf-off <1-65000>", "Disable RF on MSC Connection\n" "Timeout\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->auto_off_timeout = atoi(argv[0]); return CMD_SUCCESS; } DEFUN(cfg_net_no_rf_off_time, cfg_net_no_rf_off_time_cmd, "no bsc-auto-rf-off", NO_STR "Disable RF on MSC Connection\n") { struct osmo_bsc_data *data = osmo_bsc_data(vty); data->auto_off_timeout = -1; return CMD_SUCCESS; } DEFUN(cfg_bsc_acc_lst_name, cfg_bsc_acc_lst_name_cmd, "access-list-name NAME", "Set the name of the access list to use.\n" "The name of the to be used access list.") { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); osmo_talloc_replace_string(bsc, &bsc->acc_lst_name, argv[0]); return CMD_SUCCESS; } DEFUN(cfg_bsc_no_acc_lst_name, cfg_bsc_no_acc_lst_name_cmd, "no access-list-name", NO_STR "Remove the access list from the BSC\n") { struct osmo_bsc_data *bsc = osmo_bsc_data(vty); if (bsc->acc_lst_name) { talloc_free(bsc->acc_lst_name); bsc->acc_lst_name = NULL; } return CMD_SUCCESS; } DEFUN(show_statistics, show_statistics_cmd, "show statistics", SHOW_STR "Statistics about the BSC\n") { openbsc_vty_print_statistics(vty, bsc_gsmnet); return CMD_SUCCESS; } DEFUN(show_mscs, show_mscs_cmd, "show mscs", SHOW_STR "MSC Connections and State\n") { struct bsc_msc_data *msc; llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { vty_out(vty, "%d %s %s ", msc->a.cs7_instance, osmo_ss7_asp_protocol_name(msc->a.asp_proto), osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.bsc_addr)); vty_out(vty, "%s%s", osmo_sccp_inst_addr_name(msc->a.sccp, &msc->a.msc_addr), VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(show_pos, show_pos_cmd, "show position", SHOW_STR "Position information of the BTS\n") { struct gsm_bts *bts; struct bts_location *curloc; struct tm time; char timestr[50]; llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { if (llist_empty(&bts->loc_list)) { vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr, VTY_NEWLINE); continue; } curloc = llist_entry(bts->loc_list.next, struct bts_location, list); if (gmtime_r(&curloc->tstamp, &time) == NULL) { vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, VTY_NEWLINE); continue; } if (asctime_r(&time, timestr) == NULL) { vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, VTY_NEWLINE); continue; } /* Last character in asctime is \n */ timestr[strlen(timestr)-1] = 0; vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr, get_value_string(bts_loc_fix_names, curloc->valid), timestr, VTY_NEWLINE); vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon, curloc->height, VTY_NEWLINE); } return CMD_SUCCESS; } DEFUN(gen_position_trap, gen_position_trap_cmd, "generate-location-state-trap <0-255>", "Generate location state report\n" "BTS to report\n") { int bts_nr; struct gsm_bts *bts; struct gsm_network *net = bsc_gsmnet; bts_nr = atoi(argv[0]); if (bts_nr >= net->num_bts) { vty_out(vty, "%% can't find BTS '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } bts = gsm_bts_num(net, bts_nr); bsc_gen_location_state_trap(bts); return CMD_SUCCESS; } DEFUN(logging_fltr_imsi, logging_fltr_imsi_cmd, "logging filter imsi IMSI", LOGGING_STR FILTER_STR "Filter log messages by IMSI\n" "IMSI to be used as filter\n") { struct bsc_subscr *bsc_subscr; struct log_target *tgt = osmo_log_vty2tgt(vty); const char *imsi = argv[0]; bsc_subscr = bsc_subscr_find_by_imsi(bsc_gsmnet->bsc_subscribers, imsi); if (!bsc_subscr) { vty_out(vty, "%%no subscriber with IMSI(%s)%s", imsi, VTY_NEWLINE); return CMD_WARNING; } log_set_filter_bsc_subscr(tgt, bsc_subscr); return CMD_SUCCESS; } static void dump_one_sub(struct vty *vty, struct bsc_subscr *bsub) { vty_out(vty, " %15s %08x %5u %d%s", bsub->imsi, bsub->tmsi, bsub->lac, bsub->use_count, VTY_NEWLINE); } DEFUN(show_subscr_all, show_subscr_all_cmd, "show subscriber all", SHOW_STR "Display information about subscribers\n" "All Subscribers\n") { struct bsc_subscr *bsc_subscr; vty_out(vty, " IMSI TMSI LAC Use%s", VTY_NEWLINE); /* " 001010123456789 ffffffff 65534 1" */ llist_for_each_entry(bsc_subscr, bsc_gsmnet->bsc_subscribers, entry) dump_one_sub(vty, bsc_subscr); return CMD_SUCCESS; } #define LEGACY_STR "This command has no effect, it is kept to support legacy config files\n" DEFUN_DEPRECATED(cfg_net_msc_ping_time, cfg_net_msc_ping_time_cmd, "timeout-ping ARG", LEGACY_STR "-\n") { vty_out(vty, "%% timeout-ping / timeout-pong config is deprecated and has no effect%s", VTY_NEWLINE); return CMD_WARNING; } ALIAS_DEPRECATED(cfg_net_msc_ping_time, cfg_net_msc_no_ping_time_cmd, "no timeout-ping [ARG]", NO_STR LEGACY_STR "-\n"); ALIAS_DEPRECATED(cfg_net_msc_ping_time, cfg_net_msc_pong_time_cmd, "timeout-pong ARG", LEGACY_STR "-\n"); DEFUN_DEPRECATED(cfg_net_msc_dest, cfg_net_msc_dest_cmd, "dest A.B.C.D <1-65000> <0-255>", LEGACY_STR "-\n" "-\n" "-\n") { vty_out(vty, "%% dest config is deprecated and has no effect%s", VTY_NEWLINE); return CMD_WARNING; } ALIAS_DEPRECATED(cfg_net_msc_dest, cfg_net_msc_no_dest_cmd, "no dest A.B.C.D <1-65000> <0-255>", NO_STR LEGACY_STR "-\n" "-\n" "-\n"); int bsc_vty_init_extra(void) { struct gsm_network *net = bsc_gsmnet; install_element(CONFIG_NODE, &cfg_net_msc_cmd); install_element(CONFIG_NODE, &cfg_net_bsc_cmd); install_node(&bsc_node, config_write_bsc); install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd); install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd); install_element(BSC_NODE, &cfg_net_rf_socket_cmd); install_element(BSC_NODE, &cfg_net_rf_off_time_cmd); install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd); install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd); install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd); install_element(BSC_NODE, &cfg_bsc_acc_lst_name_cmd); install_element(BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); install_node(&msc_node, config_write_msc); install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd); install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd); install_element(MSC_NODE, &cfg_net_bsc_lac_cmd); install_element(MSC_NODE, &cfg_net_bsc_ci_cmd); install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd); install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd); install_element(MSC_NODE, &cfg_net_msc_dest_cmd); install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd); install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_lost_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_grace_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_no_grace_ussd_cmd); install_element(MSC_NODE, &cfg_net_msc_type_cmd); install_element(MSC_NODE, &cfg_net_msc_emerg_cmd); install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_12_2_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_10_2_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_7_95_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_7_40_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_6_70_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_5_90_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd); install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd); install_element(MSC_NODE, &cfg_net_msc_lcls_mode_cmd); install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd); install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd); install_element(MSC_NODE, &cfg_msc_cs7_bsc_addr_cmd); install_element(MSC_NODE, &cfg_msc_cs7_msc_addr_cmd); install_element(MSC_NODE, &cfg_msc_cs7_asp_proto_cmd); /* Deprecated: ping time config, kept to support legacy config files. */ install_element(MSC_NODE, &cfg_net_msc_no_ping_time_cmd); install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd); install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); install_element_ve(&show_statistics_cmd); install_element_ve(&show_mscs_cmd); install_element_ve(&show_pos_cmd); install_element_ve(&logging_fltr_imsi_cmd); install_element_ve(&show_subscr_all_cmd); install_element(ENABLE_NODE, &gen_position_trap_cmd); install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); mgcp_client_vty_init(net, MSC_NODE, net->mgw.conf); return 0; } osmo-bsc-1.3.0/src/osmo-bsc/paging.c000066400000000000000000000337611332665256100172020ustar00rootroot00000000000000/* Paging helper and manager.... */ /* (C) 2009,2013 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 . * */ /* * Relevant specs: * 12.21: * - 9.4.12 for CCCH Local Threshold * * 05.58: * - 8.5.2 CCCH Load indication * - 9.3.15 Paging Load * * Approach: * - Send paging command to subscriber * - On Channel Request we will remember the reason * - After the ACK we will request the identity * - Then we will send assign the gsm_subscriber and * - and call a callback */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *tall_paging_ctx = NULL; #define PAGING_TIMER 0, 500000 /* * TODO MSCSPLIT: the paging in libbsc is closely tied to MSC land in that the * MSC realm callback functions used to be invoked from the BSC/BTS level. So * this entire file needs to be rewired for use with an A interface. */ /* * Kill one paging request update the internal list... */ static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, struct gsm_paging_request *to_be_deleted) { osmo_timer_del(&to_be_deleted->T3113); llist_del(&to_be_deleted->entry); bsc_subscr_put(to_be_deleted->bsub); talloc_free(to_be_deleted); } static void page_ms(struct gsm_paging_request *request) { uint8_t mi[128]; unsigned int mi_len; unsigned int page_group; struct gsm_bts *bts = request->bts; log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub); LOGP(DPAG, LOGL_INFO, "(bts=%d) Going to send paging commands: imsi: %s tmsi: " "0x%08x for ch. type %d (attempt %d)\n", bts->nr, request->bsub->imsi, request->bsub->tmsi, request->chan_type, request->attempts); if (request->bsub->tmsi == GSM_RESERVED_TMSI) mi_len = gsm48_generate_mid_from_imsi(mi, request->bsub->imsi); else mi_len = gsm48_generate_mid_from_tmsi(mi, request->bsub->tmsi); page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(request->bsub->imsi)); gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); log_set_context(LOG_CTX_BSC_SUBSCR, NULL); } static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) { if (llist_empty(&paging_bts->pending_requests)) return; if (!osmo_timer_pending(&paging_bts->work_timer)) osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); } static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); static void paging_give_credit(void *data) { struct gsm_bts_paging_state *paging_bts = data; LOGP(DPAG, LOGL_NOTICE, "(bts=%d) No PCH LOAD IND, adding 20 slots)\n", paging_bts->bts->nr); paging_bts->available_slots = 20; paging_handle_pending_requests(paging_bts); } /*! count the number of free channels for given RSL channel type required * \param[in] BTS on which we shall count * \param[in] rsl_type the RSL channel needed type * \returns number of free channels matching \a rsl_type in \a bts */ static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) { struct pchan_load pl; int count; memset(&pl, 0, sizeof(pl)); bts_chan_load(&pl, bts); switch (rsl_type) { case RSL_CHANNEED_TCH_F: case RSL_CHANNEED_TCH_ForH: goto count_tch; break; case RSL_CHANNEED_SDCCH: goto count_sdcch; break; case RSL_CHANNEED_ANY: default: if (bts->network->pag_any_tch) goto count_tch; else goto count_sdcch; break; } return 0; /* could available SDCCH */ count_sdcch: count = 0; count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; return bts->paging.free_chans_need > count; count_tch: count = 0; count += pl.pchan[GSM_PCHAN_TCH_F].total - pl.pchan[GSM_PCHAN_TCH_F].used; if (bts->network->neci) count += pl.pchan[GSM_PCHAN_TCH_H].total - pl.pchan[GSM_PCHAN_TCH_H].used; return bts->paging.free_chans_need > count; } /* * This is kicked by the periodic PAGING LOAD Indicator * coming from abis_rsl.c * * We attempt to iterate once over the list of items but * only upto available_slots. */ static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) { struct gsm_paging_request *request = NULL; /* * Determine if the pending_requests list is empty and * return then. */ if (llist_empty(&paging_bts->pending_requests)) { /* since the list is empty, no need to reschedule the timer */ return; } /* * In case the BTS does not provide us with load indication and we * ran out of slots, call an autofill routine. It might be that the * BTS did not like our paging messages and then we have counted down * to zero and we do not get any messages. */ if (paging_bts->available_slots == 0) { osmo_timer_setup(&paging_bts->credit_timer, paging_give_credit, paging_bts); osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); return; } request = llist_entry(paging_bts->pending_requests.next, struct gsm_paging_request, entry); /* we need to determine the number of free channels */ if (paging_bts->free_chans_need != -1) { if (can_send_pag_req(request->bts, request->chan_type) != 0) goto skip_paging; } /* Skip paging if the bts is down. */ if (!request->bts->oml_link) goto skip_paging; /* handle the paging request now */ page_ms(request); paging_bts->available_slots--; request->attempts++; /* take the current and add it to the back */ llist_del(&request->entry); llist_add_tail(&request->entry, &paging_bts->pending_requests); skip_paging: osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); } static void paging_worker(void *data) { struct gsm_bts_paging_state *paging_bts = data; paging_handle_pending_requests(paging_bts); } /*! initialize the bts paging state, if it hasn't been initialized yet */ static void paging_init_if_needed(struct gsm_bts *bts) { if (bts->paging.bts) return; bts->paging.bts = bts; /* This should be initialized only once. There is currently no code that sets bts->paging.bts * back to NULL, so let's just assert this one instead of graceful handling. */ OSMO_ASSERT(llist_empty(&bts->paging.pending_requests)); osmo_timer_setup(&bts->paging.work_timer, paging_worker, &bts->paging); /* Large number, until we get a proper message */ bts->paging.available_slots = 20; } /*! do we have any pending paging requests for given subscriber? */ static int paging_pending_request(struct gsm_bts_paging_state *bts, struct bsc_subscr *bsub) { struct gsm_paging_request *req; llist_for_each_entry(req, &bts->pending_requests, entry) { if (bsub == req->bsub) return 1; } return 0; } /*! Call-back once T3113 (paging timeout) expires for given paging_request */ static void paging_T3113_expired(void *data) { struct gsm_paging_request *req = (struct gsm_paging_request *)data; log_set_context(LOG_CTX_BSC_SUBSCR, req->bsub); LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", req, bsc_subscr_name(req->bsub)); /* must be destroyed before calling cbfn, to prevent double free */ rate_ctr_inc(&req->bts->bts_ctrs->ctr[BTS_CTR_PAGING_EXPIRED]); /* destroy it now. Do not access req afterwards */ paging_remove_request(&req->bts->paging, req); } /*! Start paging + paging timer for given subscriber on given BTS * \param bts BTS on which to page * \param[in] bsub subscriber we want to page * \param[in] type type of radio channel we're requirign * \param[in] msc MSC which has issue this paging * \returns 0 on success, negative on error */ static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, int type, struct bsc_msc_data *msc) { struct gsm_bts_paging_state *bts_entry = &bts->paging; struct gsm_paging_request *req; rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_ATTEMPTED]); if (paging_pending_request(bts_entry, bsub)) { LOGP(DPAG, LOGL_INFO, "(bts=%d) Paging request already pending for %s\n", bts->nr, bsc_subscr_name(bsub)); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_PAGING_ALREADY]); return -EEXIST; } LOGP(DPAG, LOGL_DEBUG, "(bts=%d) Start paging of subscriber %s\n", bts->nr, bsc_subscr_name(bsub)); req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); OSMO_ASSERT(req); req->bsub = bsc_subscr_get(bsub); req->bts = bts; req->chan_type = type; req->msc = msc; osmo_timer_setup(&req->T3113, paging_T3113_expired, req); osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); llist_add_tail(&req->entry, &bts_entry->pending_requests); paging_schedule_if_needed(bts_entry); return 0; } /*! Handle PAGING request from MSC for one (matching) BTS * \param bts BTS on which to page * \param[in] bsub subscriber we want to page * \param[in] type type of radio channel we're requirign * \param[in] msc MSC which has issue this paging * returns 1 on success; 0 in case of error (e.g. TRX down) */ int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, int type, struct bsc_msc_data *msc) { int rc; /* skip all currently inactive TRX */ if (!trx_is_usable(bts->c0)) return 0; /* maybe it is the first time we use it */ paging_init_if_needed(bts); /* Trigger paging, pass any error to the caller */ rc = _paging_request(bts, bsub, type, msc); if (rc < 0) return 0; return 1; } /*! Stop paging a given subscriber on a given BTS. * If \a conn is non-NULL, we also call the paging call-back function * to notify the paging originator that paging has completed. * \param[in] bts BTS on which we shall stop paging * \param[in] bsub subscriber which we shall stop paging * \param[in] conn connection to the subscriber (if any) * \param[in] msg message received from subscrbier (if any) * \returns 0 if an active paging request was stopped, an error code otherwise. */ /* we consciously ignore the type of the request here */ static int _paging_request_stop(struct gsm_bts *bts, struct bsc_subscr *bsub, struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts_paging_state *bts_entry = &bts->paging; struct gsm_paging_request *req, *req2; paging_init_if_needed(bts); llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, entry) { if (req->bsub == bsub) { /* now give up the data structure */ paging_remove_request(&bts->paging, req); LOGP(DPAG, LOGL_DEBUG, "(bts=%d) Stop paging %s\n", bts->nr, bsc_subscr_name(bsub)); return 0; } } return -ENOENT; } /*! Stop paging on all other bts' * \param[in] bts_list list of BTSs to iterate * \param[in] _bts BTS which has received a paging response * \param[in] bsub subscriber * \param[in] msgb L3 message that we have received from \a bsub on \a _bts */ void paging_request_stop(struct llist_head *bts_list, struct gsm_bts *_bts, struct bsc_subscr *bsub, struct gsm_subscriber_connection *conn, struct msgb *msg) { struct gsm_bts *bts; log_set_context(LOG_CTX_BSC_SUBSCR, bsub); conn->bsub = bsc_subscr_get(bsub); /* Stop this first and dispatch the request */ if (_bts) { if (_paging_request_stop(_bts, bsub, conn, msg) == 0) { rate_ctr_inc(&_bts->bts_ctrs->ctr[BTS_CTR_PAGING_RESPONDED]); rate_ctr_inc(&_bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_RESPONDED]); } } /* Make sure to cancel this everywhere else */ llist_for_each_entry(bts, bts_list, list) { /* Sort of an optimization. */ if (bts == _bts) continue; _paging_request_stop(bts, bsub, NULL, NULL); } } /*! Update the BTS paging buffer slots on given BTS */ void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) { paging_init_if_needed(bts); osmo_timer_del(&bts->paging.credit_timer); bts->paging.available_slots = free_slots; paging_schedule_if_needed(&bts->paging); } /*! Count the number of pending paging requests on given BTS */ unsigned int paging_pending_requests_nr(struct gsm_bts *bts) { unsigned int requests = 0; struct gsm_paging_request *req; paging_init_if_needed(bts); llist_for_each_entry(req, &bts->paging.pending_requests, entry) ++requests; return requests; } /*! Find any paging data for the given subscriber at the given BTS. */ struct bsc_msc_data *paging_get_msc(struct gsm_bts *bts, struct bsc_subscr *bsub) { struct gsm_paging_request *req; llist_for_each_entry(req, &bts->paging.pending_requests, entry) if (req->bsub == bsub) return req->msc; return NULL; } /*! Flush all paging requests at a given BTS for a given MSC (or NULL if all MSC should be flushed). */ void paging_flush_bts(struct gsm_bts *bts, struct bsc_msc_data *msc) { struct gsm_paging_request *req, *req2; paging_init_if_needed(bts); llist_for_each_entry_safe(req, req2, &bts->paging.pending_requests, entry) { if (msc && req->msc != msc) continue; /* now give up the data structure */ LOGP(DPAG, LOGL_DEBUG, "(bts=%d) Stop paging %s (flush)\n", bts->nr, bsc_subscr_name(req->bsub)); paging_remove_request(&bts->paging, req); } } /*! Flush all paging requests issued by \a msc on any BTS in \a net */ void paging_flush_network(struct gsm_network *net, struct bsc_msc_data *msc) { struct gsm_bts *bts; llist_for_each_entry(bts, &net->bts_list, list) paging_flush_bts(bts, msc); } osmo-bsc-1.3.0/src/osmo-bsc/pcu_sock.c000066400000000000000000000463301332665256100175370ustar00rootroot00000000000000/* pcu_sock.c: Connect from PCU via unix domain socket */ /* (C) 2008-2010 by Harald Welte * (C) 2009-2012 by Andreas Eversberg * (C) 2012 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg); uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx); int pcu_direct = 1; static const char *sapi_string[] = { [PCU_IF_SAPI_RACH] = "RACH", [PCU_IF_SAPI_AGCH] = "AGCH", [PCU_IF_SAPI_PCH] = "PCH", [PCU_IF_SAPI_BCCH] = "BCCH", [PCU_IF_SAPI_PDTCH] = "PDTCH", [PCU_IF_SAPI_PRACH] = "PRACH", [PCU_IF_SAPI_PTCCH] = "PTCCH", [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT", }; /* Check if BTS has a PCU connection */ static bool pcu_connected(struct gsm_bts *bts) { struct pcu_sock_state *state = bts->pcu_state; if (!state) return false; if (state->conn_bfd.fd <= 0) return false; return true; } /* * PCU messages */ /* Set up an message buffer to package an pcu interface message */ struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) { struct msgb *msg; struct gsm_pcu_if *pcu_prim; msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx"); if (!msg) return NULL; msgb_put(msg, sizeof(struct gsm_pcu_if)); pcu_prim = (struct gsm_pcu_if *) msg->data; pcu_prim->msg_type = msg_type; pcu_prim->bts_nr = bts_nr; return msg; } /* Helper function exclusivly used by pcu_if_signal_cb() */ static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { if (ts->pchan == GSM_PCHAN_PDCH) return true; if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { /* When we're busy deactivating the PDCH, we first set * DEACT_PENDING, tell the PCU about it and wait for a * response. So DEACT_PENDING means "no PDCH" to the PCU. * Similarly, when we're activating PDCH, we set the * ACT_PENDING and wait for an activation response from the * PCU, so ACT_PENDING means "is PDCH". */ if (ts->flags & TS_F_PDCH_ACTIVE) return !(ts->flags & TS_F_PDCH_DEACT_PENDING); else return (ts->flags & TS_F_PDCH_ACT_PENDING); } if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { /* * When we're busy de-/activating the PDCH, we first set * ts->dyn.pchan_want, tell the PCU about it and wait for a * response. So only care about dyn.pchan_want here. */ return ts->dyn.pchan_want == GSM_PCHAN_PDCH; } return false; } /* Send BTS properties to the PCU */ static int pcu_tx_info_ind(struct gsm_bts *bts) { struct msgb *msg; struct gsm_pcu_if *pcu_prim; struct gsm_pcu_if_info_ind *info_ind; struct gprs_rlc_cfg *rlcc; struct gsm_bts_gprs_nsvc *nsvc; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; int i, j; OSMO_ASSERT(bts); OSMO_ASSERT(bts->network); LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr); rlcc = &bts->gprs.cell.rlc_cfg; msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr); if (!msg) return -ENOMEM; pcu_prim = (struct gsm_pcu_if *) msg->data; info_ind = &pcu_prim->u.info_ind; info_ind->version = PCU_IF_VERSION; info_ind->flags |= PCU_IF_FLAG_ACTIVE; if (pcu_direct) info_ind->flags |= PCU_IF_FLAG_SYSMO; /* RAI */ info_ind->mcc = bts->network->plmn.mcc; info_ind->mnc = bts->network->plmn.mnc; info_ind->mnc_3_digits = bts->network->plmn.mnc_3_digits; info_ind->lac = bts->location_area_code; info_ind->rac = bts->gprs.rac; /* NSE */ info_ind->nsei = bts->gprs.nse.nsei; memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7); memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11); /* cell attributes */ info_ind->cell_id = bts->cell_identity; info_ind->repeat_time = rlcc->paging.repeat_time; info_ind->repeat_count = rlcc->paging.repeat_count; info_ind->bvci = bts->gprs.cell.bvci; info_ind->t3142 = rlcc->parameter[RLC_T3142]; info_ind->t3169 = rlcc->parameter[RLC_T3169]; info_ind->t3191 = rlcc->parameter[RLC_T3191]; info_ind->t3193_10ms = rlcc->parameter[RLC_T3193]; info_ind->t3195 = rlcc->parameter[RLC_T3195]; info_ind->n3101 = rlcc->parameter[RLC_N3101]; info_ind->n3103 = rlcc->parameter[RLC_N3103]; info_ind->n3105 = rlcc->parameter[RLC_N3105]; info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN]; if (rlcc->cs_mask & (1 << GPRS_CS1)) info_ind->flags |= PCU_IF_FLAG_CS1; if (rlcc->cs_mask & (1 << GPRS_CS2)) info_ind->flags |= PCU_IF_FLAG_CS2; if (rlcc->cs_mask & (1 << GPRS_CS3)) info_ind->flags |= PCU_IF_FLAG_CS3; if (rlcc->cs_mask & (1 << GPRS_CS4)) info_ind->flags |= PCU_IF_FLAG_CS4; if (bts->gprs.mode == BTS_GPRS_EGPRS) { if (rlcc->cs_mask & (1 << GPRS_MCS1)) info_ind->flags |= PCU_IF_FLAG_MCS1; if (rlcc->cs_mask & (1 << GPRS_MCS2)) info_ind->flags |= PCU_IF_FLAG_MCS2; if (rlcc->cs_mask & (1 << GPRS_MCS3)) info_ind->flags |= PCU_IF_FLAG_MCS3; if (rlcc->cs_mask & (1 << GPRS_MCS4)) info_ind->flags |= PCU_IF_FLAG_MCS4; if (rlcc->cs_mask & (1 << GPRS_MCS5)) info_ind->flags |= PCU_IF_FLAG_MCS5; if (rlcc->cs_mask & (1 << GPRS_MCS6)) info_ind->flags |= PCU_IF_FLAG_MCS6; if (rlcc->cs_mask & (1 << GPRS_MCS7)) info_ind->flags |= PCU_IF_FLAG_MCS7; if (rlcc->cs_mask & (1 << GPRS_MCS8)) info_ind->flags |= PCU_IF_FLAG_MCS8; if (rlcc->cs_mask & (1 << GPRS_MCS9)) info_ind->flags |= PCU_IF_FLAG_MCS9; } #warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs" info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT]; #warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs" info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT]; info_ind->initial_cs = rlcc->initial_cs; info_ind->initial_mcs = rlcc->initial_mcs; /* NSVC */ for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) { nsvc = &bts->gprs.nsvc[i]; info_ind->nsvci[i] = nsvc->nsvci; info_ind->local_port[i] = nsvc->local_port; info_ind->remote_port[i] = nsvc->remote_port; info_ind->remote_ip[i] = nsvc->remote_ip; } for (i = 0; i < ARRAY_SIZE(info_ind->trx); i++) { trx = gsm_bts_trx_num(bts, i); if (!trx) continue; info_ind->trx[i].hlayer1 = 0x2342; info_ind->trx[i].pdch_mask = 0; info_ind->trx[i].arfcn = trx->arfcn; for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { ts = &trx->ts[j]; if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED && ts_should_be_pdch(ts)) { info_ind->trx[i].pdch_mask |= (1 << j); info_ind->trx[i].tsc[j] = (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7; LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: " "available (tsc=%d arfcn=%d)\n", trx->nr, ts->nr, info_ind->trx[i].tsc[j], info_ind->trx[i].arfcn); } } } return pcu_sock_send(bts, msg); } void pcu_info_update(struct gsm_bts *bts) { if (pcu_connected(bts)) pcu_tx_info_ind(bts); } /* Forward rach indication to PCU */ int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, uint8_t is_11bit, enum ph_burst_type burst_type) { struct msgb *msg; struct gsm_pcu_if *pcu_prim; struct gsm_pcu_if_rach_ind *rach_ind; /* Bail if no PCU is connected */ if (!pcu_connected(bts)) { LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not " "connected!\n", bts->nr); return -ENODEV; } LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, " "fn=%d\n", qta, ra, fn); msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr); if (!msg) return -ENOMEM; pcu_prim = (struct gsm_pcu_if *) msg->data; rach_ind = &pcu_prim->u.rach_ind; rach_ind->sapi = PCU_IF_SAPI_RACH; rach_ind->ra = ra; rach_ind->qta = qta; rach_ind->fn = fn; rach_ind->is_11bit = is_11bit; rach_ind->burst_type = burst_type; return pcu_sock_send(bts, msg); } /* Confirm the sending of an immediate assignment to the pcu */ int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli) { struct msgb *msg; struct gsm_pcu_if *pcu_prim; struct gsm_pcu_if_data_cnf_dt *data_cnf_dt; LOGP(DPCU, LOGL_INFO, "Sending PCH confirm with direct TLLI\n"); msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_DT, bts->nr); if (!msg) return -ENOMEM; pcu_prim = (struct gsm_pcu_if *) msg->data; data_cnf_dt = &pcu_prim->u.data_cnf_dt; data_cnf_dt->sapi = PCU_IF_SAPI_PCH; data_cnf_dt->tlli = tlli; return pcu_sock_send(bts, msg); } /* we need to decode the raw RR paging messsage (see PCU code * Encoding::write_paging_request) and extract the mobile identity * (P-TMSI) from it */ static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group, const uint8_t *raw_rr_msg) { struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) raw_rr_msg; uint8_t chan_needed; unsigned int mi_len; uint8_t *mi; int rc; switch (p1->msg_type) { case GSM48_MT_RR_PAG_REQ_1: chan_needed = (p1->cneed2 << 2) | p1->cneed1; mi_len = p1->data[0]; mi = p1->data+1; LOGP(DPCU, LOGL_ERROR, "PCU Sends paging " "request type %02x (chan_needed=%02x, mi_len=%u, mi=%s)\n", p1->msg_type, chan_needed, mi_len, osmo_hexdump_nospc(mi,mi_len)); /* NOTE: We will have to add 2 to mi_len and subtract 2 from * the mi pointer because rsl_paging_cmd() will perform the * reverse operations. This is because rsl_paging_cmd() is * normally expected to chop off the element identifier (0xC0) * and the length field. In our parameter, we do not have * those fields included. */ rc = rsl_paging_cmd(bts, paging_group, mi_len+2, mi-2, chan_needed, true); break; case GSM48_MT_RR_PAG_REQ_2: case GSM48_MT_RR_PAG_REQ_3: LOGP(DPCU, LOGL_ERROR, "PCU Sends unsupported paging " "request type %02x\n", p1->msg_type); rc = -EINVAL; break; default: LOGP(DPCU, LOGL_ERROR, "PCU Sends unknown paging " "request type %02x\n", p1->msg_type); rc = -EINVAL; break; } return rc; } static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type, struct gsm_pcu_if_data *data_req) { struct msgb *msg; char imsi_digit_buf[4]; uint32_t tlli = -1; uint8_t pag_grp; int rc = 0; LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d " "block=%d data=%s\n", sapi_string[data_req->sapi], data_req->arfcn, data_req->block_nr, osmo_hexdump(data_req->data, data_req->len)); switch (data_req->sapi) { case PCU_IF_SAPI_PCH: /* the first three bytes are the last three digits of * the IMSI, which we need to compute the paging group */ imsi_digit_buf[0] = data_req->data[0]; imsi_digit_buf[1] = data_req->data[1]; imsi_digit_buf[2] = data_req->data[2]; imsi_digit_buf[3] = '\0'; LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf); pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc, str_to_imsi(imsi_digit_buf)); pcu_rx_rr_paging(bts, pag_grp, data_req->data+3); break; case PCU_IF_SAPI_AGCH: msg = msgb_alloc(data_req->len, "pcu_agch"); if (!msg) { rc = -ENOMEM; break; } msg->l3h = msgb_put(msg, data_req->len); memcpy(msg->l3h, data_req->data, data_req->len); if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) { msgb_free(msg); rc = -EIO; } break; case PCU_IF_SAPI_AGCH_DT: /* DT = direct tlli. A tlli is prefixed */ if (data_req->len < 5) { LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " "invalid/small length %d\n", data_req->len); break; } memcpy(&tlli, data_req->data, 4); msg = msgb_alloc(data_req->len - 4, "pcu_agch"); if (!msg) { rc = -ENOMEM; break; } msg->l3h = msgb_put(msg, data_req->len - 4); memcpy(msg->l3h, data_req->data + 4, data_req->len - 4); if (bts->type == GSM_BTS_TYPE_RBS2000) rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data); else rc = rsl_imm_assign_cmd(bts, msg->len, msg->data); if (rc) { msgb_free(msg); rc = -EIO; } break; default: LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " "unsupported sapi %d\n", data_req->sapi); rc = -EINVAL; } return rc; } static int pcu_rx(struct gsm_network *net, uint8_t msg_type, struct gsm_pcu_if *pcu_prim) { int rc = 0; struct gsm_bts *bts; /* FIXME: allow multiple BTS */ bts = llist_entry(net->bts_list.next, struct gsm_bts, list); switch (msg_type) { case PCU_IF_MSG_DATA_REQ: case PCU_IF_MSG_PAG_REQ: rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req); break; default: LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n", msg_type); rc = -EINVAL; } return rc; } /* * PCU socket interface */ static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg) { struct pcu_sock_state *state = bts->pcu_state; struct osmo_fd *conn_bfd; struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data; if (!state) { if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) LOGP(DPCU, LOGL_INFO, "PCU socket not created, " "dropping message\n"); msgb_free(msg); return -EINVAL; } conn_bfd = &state->conn_bfd; if (conn_bfd->fd <= 0) { if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, " "dropping message\n"); msgb_free(msg); return -EIO; } msgb_enqueue(&state->upqueue, msg); conn_bfd->when |= BSC_FD_WRITE; return 0; } static void pcu_sock_close(struct pcu_sock_state *state) { struct osmo_fd *bfd = &state->conn_bfd; struct gsm_bts *bts; struct gsm_bts_trx *trx; struct gsm_bts_trx_ts *ts; int i, j; /* FIXME: allow multiple BTS */ bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list); LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n"); close(bfd->fd); bfd->fd = -1; osmo_fd_unregister(bfd); /* re-enable the generation of ACCEPT for new connections */ state->listen_bfd.when |= BSC_FD_READ; #if 0 /* remove si13, ... */ bts->si_valid &= ~(1 << SYSINFO_TYPE_13); osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); #endif /* release PDCH */ for (i = 0; i < 8; i++) { trx = gsm_bts_trx_num(bts, i); if (!trx) break; for (j = 0; j < 8; j++) { ts = &trx->ts[j]; if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED && ts->pchan == GSM_PCHAN_PDCH) { printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n"); } } } /* flush the queue */ while (!llist_empty(&state->upqueue)) { struct msgb *msg = msgb_dequeue(&state->upqueue); msgb_free(msg); } } static int pcu_sock_read(struct osmo_fd *bfd) { struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; struct gsm_pcu_if *pcu_prim; struct msgb *msg; int rc; msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx"); if (!msg) return -ENOMEM; pcu_prim = (struct gsm_pcu_if *) msg->tail; rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); if (rc == 0) goto close; if (rc < 0) { if (errno == EAGAIN) return 0; goto close; } rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim); /* as we always synchronously process the message in pcu_rx() and * its callbacks, we can free the message here. */ msgb_free(msg); return rc; close: msgb_free(msg); pcu_sock_close(state); return -1; } static int pcu_sock_write(struct osmo_fd *bfd) { struct pcu_sock_state *state = bfd->data; int rc; while (!llist_empty(&state->upqueue)) { struct msgb *msg, *msg2; struct gsm_pcu_if *pcu_prim; /* peek at the beginning of the queue */ msg = llist_entry(state->upqueue.next, struct msgb, list); pcu_prim = (struct gsm_pcu_if *)msg->data; bfd->when &= ~BSC_FD_WRITE; /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ if (!msgb_length(msg)) { LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO " "bytes!\n", pcu_prim->msg_type); goto dontsend; } /* try to send it over the socket */ rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); if (rc == 0) goto close; if (rc < 0) { if (errno == EAGAIN) { bfd->when |= BSC_FD_WRITE; break; } goto close; } dontsend: /* _after_ we send it, we can deueue */ msg2 = msgb_dequeue(&state->upqueue); assert(msg == msg2); msgb_free(msg); } return 0; close: pcu_sock_close(state); return -1; } static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags) { int rc = 0; if (flags & BSC_FD_READ) rc = pcu_sock_read(bfd); if (rc < 0) return rc; if (flags & BSC_FD_WRITE) rc = pcu_sock_write(bfd); return rc; } /* accept connection comming from PCU */ static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags) { struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; struct osmo_fd *conn_bfd = &state->conn_bfd; struct sockaddr_un un_addr; socklen_t len; int rc; len = sizeof(un_addr); rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); if (rc < 0) { LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n"); return -1; } if (conn_bfd->fd >= 0) { LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have " "another active connection ?!?\n"); /* We already have one PCU connected, this is all we support */ state->listen_bfd.when &= ~BSC_FD_READ; close(rc); return 0; } conn_bfd->fd = rc; conn_bfd->when = BSC_FD_READ; conn_bfd->cb = pcu_sock_cb; conn_bfd->data = state; if (osmo_fd_register(conn_bfd) != 0) { LOGP(DPCU, LOGL_ERROR, "Failed to register new connection " "fd\n"); close(conn_bfd->fd); conn_bfd->fd = -1; return -1; } LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n"); return 0; } /* Open connection to PCU */ int pcu_sock_init(const char *path, struct gsm_bts *bts) { struct pcu_sock_state *state; struct osmo_fd *bfd; int rc; state = talloc_zero(NULL, struct pcu_sock_state); if (!state) return -ENOMEM; INIT_LLIST_HEAD(&state->upqueue); state->net = bts->network; state->conn_bfd.fd = -1; bfd = &state->listen_bfd; bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND); if (bfd->fd < 0) { LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n", strerror(errno)); talloc_free(state); return -1; } bfd->when = BSC_FD_READ; bfd->cb = pcu_sock_accept; bfd->data = state; rc = osmo_fd_register(bfd); if (rc < 0) { LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n", rc); close(bfd->fd); talloc_free(state); return rc; } LOGP(DPCU, LOGL_INFO, "Started listening on PCU socket: %s\n", path); bts->pcu_state = state; return 0; } /* Close connection to PCU */ void pcu_sock_exit(struct gsm_bts *bts) { struct pcu_sock_state *state = bts->pcu_state; struct osmo_fd *bfd, *conn_bfd; if (!state) return; conn_bfd = &state->conn_bfd; if (conn_bfd->fd > 0) pcu_sock_close(state); bfd = &state->listen_bfd; close(bfd->fd); osmo_fd_unregister(bfd); talloc_free(state); bts->pcu_state = NULL; } osmo-bsc-1.3.0/src/osmo-bsc/penalty_timers.c000066400000000000000000000061731332665256100207710ustar00rootroot00000000000000/* (C) 2018 by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * * Author: Neels Hofmeyr * * 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 struct penalty_timers { struct llist_head timers; }; struct penalty_timer { struct llist_head entry; const void *for_object; unsigned int timeout; }; static unsigned int time_now(void) { time_t now; time(&now); /* FIXME: use monotonic clock */ return (unsigned int)now; } struct penalty_timers *penalty_timers_init(void *ctx) { struct penalty_timers *pt = talloc_zero(ctx, struct penalty_timers); if (!pt) return NULL; INIT_LLIST_HEAD(&pt->timers); return pt; } void penalty_timers_add(struct penalty_timers *pt, const void *for_object, int timeout) { struct penalty_timer *timer; unsigned int now; unsigned int then; now = time_now(); if (timeout <= 0) return; then = now + timeout; /* timer already running for that BTS? */ llist_for_each_entry(timer, &pt->timers, entry) { if (timer->for_object != for_object) continue; /* raise, if running timer will timeout earlier or has timed * out already, otherwise keep later timeout */ if (timer->timeout < then) timer->timeout = then; return; } /* add new timer */ timer = talloc_zero(pt, struct penalty_timer); if (!timer) return; timer->for_object = for_object; timer->timeout = then; llist_add_tail(&timer->entry, &pt->timers); } unsigned int penalty_timers_remaining(struct penalty_timers *pt, const void *for_object) { struct penalty_timer *timer; unsigned int now = time_now(); unsigned int max_remaining = 0; llist_for_each_entry(timer, &pt->timers, entry) { unsigned int remaining; if (timer->for_object != for_object) continue; if (now >= timer->timeout) continue; remaining = timer->timeout - now; if (remaining > max_remaining) max_remaining = remaining; } return max_remaining; } void penalty_timers_clear(struct penalty_timers *pt, const void *for_object) { struct penalty_timer *timer, *timer2; llist_for_each_entry_safe(timer, timer2, &pt->timers, entry) { if (for_object && timer->for_object != for_object) continue; llist_del(&timer->entry); talloc_free(timer); } } void penalty_timers_free(struct penalty_timers **pt_p) { struct penalty_timers *pt = *pt_p; if (!pt) return; penalty_timers_clear(pt, NULL); talloc_free(pt); *pt_p = NULL; } osmo-bsc-1.3.0/src/osmo-bsc/rest_octets.c000066400000000000000000000541241332665256100202670ustar00rootroot00000000000000/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, * rest octet handling according to * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (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 #include #include #include #include #include #include #include #include #include /* generate SI1 rest octets */ int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 1; if (nch_pos) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, *nch_pos, 5); } else bitvec_set_bit(&bv, L); if (is1800_net) bitvec_set_bit(&bv, L); else bitvec_set_bit(&bv, H); bitvec_spare_padding(&bv, 6); return bv.data_len; } /* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */ static inline bool append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) { const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; unsigned i, skip = 0; size_t offset = bts->e_offset; int16_t rem = budget - 6; /* account for mandatory stop bit and THRESH_E-UTRAN_high */ uint8_t earfcn_budget; if (budget <= 6) return false; OSMO_ASSERT(budget <= SI2Q_MAX_LEN); /* first we have to properly adjust budget requirements */ if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ rem -= 4; else rem--; if (e->thresh_lo_valid) /* THRESH_E-UTRAN_low: */ rem -= 6; else rem--; if (e->qrxlm_valid) /* E-UTRAN_QRXLEVMIN: */ rem -= 6; else rem--; if (rem < 0) return false; /* now we can proceed with actually adding EARFCNs within adjusted budget limit */ for (i = 0; i < e->length; i++) { if (e->arfcn[i] != OSMO_EARFCN_INVALID) { if (skip < offset) { skip++; /* ignore EARFCNs added on previous calls */ } else { earfcn_budget = 17; /* compute budget per-EARFCN */ if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) earfcn_budget++; else earfcn_budget += 4; if (rem - earfcn_budget < 0) break; else { bts->e_offset++; rem -= earfcn_budget; if (rem < 0) return false; bitvec_set_bit(bv, 1); /* EARFCN: */ bitvec_set_uint(bv, e->arfcn[i], 16); if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) bitvec_set_bit(bv, 0); else { /* Measurement Bandwidth: 9.1.54 */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, e->meas_bw[i], 3); } } } } } /* stop bit - end of EARFCN + Measurement Bandwidth sequence */ bitvec_set_bit(bv, 0); /* Note: we don't support different EARFCN arrays each with different priority, threshold etc. */ if (e->prio_valid) { /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, e->prio, 3); } else bitvec_set_bit(bv, 0); /* THRESH_E-UTRAN_high */ bitvec_set_uint(bv, e->thresh_hi, 5); if (e->thresh_lo_valid) { /* THRESH_E-UTRAN_low: */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, e->thresh_lo, 5); } else bitvec_set_bit(bv, 0); if (e->qrxlm_valid) { /* E-UTRAN_QRXLEVMIN: */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, e->qrxlm, 5); } else bitvec_set_bit(bv, 0); return true; } static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) { bool appended; unsigned int old = bv->cur_bit; /* save current position to make rollback possible */ int rem = budget - 25; if (rem <= 0) return; OSMO_ASSERT(budget <= SI2Q_MAX_LEN); /* Additions in Rel-5: */ bitvec_set_bit(bv, H); /* No 3G Additional Measurement Param. Descr. */ bitvec_set_bit(bv, 0); /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */ bitvec_set_bit(bv, 0); /* Additions in Rel-6: */ bitvec_set_bit(bv, H); /* 3G_CCN_ACTIVE */ bitvec_set_bit(bv, 0); /* Additions in Rel-7: */ bitvec_set_bit(bv, H); /* No 700_REPORTING_OFFSET */ bitvec_set_bit(bv, 0); /* No 810_REPORTING_OFFSET */ bitvec_set_bit(bv, 0); /* Additions in Rel-8: */ bitvec_set_bit(bv, H); /* Priority and E-UTRAN Parameters Description */ bitvec_set_bit(bv, 1); /* No Serving Cell Priority Parameters Descr. */ bitvec_set_bit(bv, 0); /* No 3G Priority Parameters Description */ bitvec_set_bit(bv, 0); /* E-UTRAN Parameters Description */ bitvec_set_bit(bv, 1); /* E-UTRAN_CCN_ACTIVE */ bitvec_set_bit(bv, 0); /* E-UTRAN_Start: 9.1.54 */ bitvec_set_bit(bv, 1); /* E-UTRAN_Stop: 9.1.54 */ bitvec_set_bit(bv, 1); /* No E-UTRAN Measurement Parameters Descr. */ bitvec_set_bit(bv, 0); /* No GPRS E-UTRAN Measurement Param. Descr. */ bitvec_set_bit(bv, 0); /* Note: each of next 3 "repeated" structures might be repeated any (0, 1, 2...) times - we only support 1 and 0 */ /* Repeated E-UTRAN Neighbour Cells */ bitvec_set_bit(bv, 1); appended = append_eutran_neib_cell(bv, bts, rem); if (!appended) { /* appending is impossible within current budget: rollback */ bv->cur_bit = old; return; } /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */ bitvec_set_bit(bv, 0); /* Note: following 2 repeated structs are not supported ATM */ /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */ bitvec_set_bit(bv, 0); /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */ bitvec_set_bit(bv, 0); /* Priority and E-UTRAN Parameters Description ends here */ /* No 3G CSG Description */ bitvec_set_bit(bv, 0); /* No E-UTRAN CSG Description */ bitvec_set_bit(bv, 0); /* No Additions in Rel-9: */ bitvec_set_bit(bv, L); } static inline int f0_helper(int *sc, size_t length, uint8_t *chan_list) { int w[RANGE_ENC_MAX_ARFCNS] = { 0 }; return range_encode(ARFCN_RANGE_1024, sc, length, w, 0, chan_list); } /* Estimate how many bits it'll take to append single FDD UARFCN */ static inline int append_utran_fdd_length(uint16_t u, const int *sc, size_t sc_len, size_t length) { uint8_t chan_list[16] = { 0 }; int tmp[sc_len], f0; memcpy(tmp, sc, sizeof(tmp)); f0 = f0_helper(tmp, length, chan_list); if (f0 < 0) return f0; return 21 + range1024_p(length); } /* Append single FDD UARFCN */ static inline int append_utran_fdd(struct bitvec *bv, uint16_t u, int *sc, size_t length) { uint8_t chan_list[16] = { 0 }; int f0 = f0_helper(sc, length, chan_list); if (f0 < 0) return f0; /* Repeated UTRAN FDD Neighbour Cells */ bitvec_set_bit(bv, 1); /* FDD-ARFCN */ bitvec_set_bit(bv, 0); bitvec_set_uint(bv, u, 14); /* FDD_Indic0: parameter value '0000000000' is a member of the set? */ bitvec_set_bit(bv, f0); /* NR_OF_FDD_CELLS */ bitvec_set_uint(bv, length, 5); f0 = bv->cur_bit; bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list); bv->cur_bit = f0 + range1024_p(length); return 21 + range1024_p(length); } static inline int try_adding_uarfcn(struct bitvec *bv, struct gsm_bts *bts, uint16_t uarfcn, uint8_t num_sc, uint8_t start_pos, uint8_t budget) { int i, k, rc, a[bts->si_common.uarfcn_length]; if (budget < 23) return -ENOMEM; /* copy corresponding Scrambling Codes: range encoder make in-place modifications */ for (i = start_pos, k = 0; i < num_sc; a[k++] = bts->si_common.data.scramble_list[i++]); /* estimate bit length requirements */ rc = append_utran_fdd_length(uarfcn, a, bts->si_common.uarfcn_length, k); if (rc < 0) return rc; /* range encoder failure */ if (budget - rc <= 0) return -ENOMEM; /* we have ran out of budget in current SI2q */ /* compute next offset */ bts->u_offset += k; return budget - append_utran_fdd(bv, uarfcn, a, k); } /* Append multiple FDD UARFCNs */ static inline void append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) { const uint16_t *u = bts->si_common.data.uarfcn_list; int i, rem = budget - 7, st = bts->u_offset; /* account for constant bits right away */ uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */ OSMO_ASSERT(budget <= SI2Q_MAX_LEN); if (budget <= 7) return; /* 3G Neighbour Cell Description */ bitvec_set_bit(bv, 1); /* No Index_Start_3G */ bitvec_set_bit(bv, 0); /* No Absolute_Index_Start_EMR */ bitvec_set_bit(bv, 0); /* UTRAN FDD Description */ bitvec_set_bit(bv, 1); /* No Bandwidth_FDD */ bitvec_set_bit(bv, 0); for (i = bts->u_offset; i <= bts->si_common.uarfcn_length; i++) if (u[i] != cu) { /* we've reached new UARFCN */ rem = try_adding_uarfcn(bv, bts, cu, i, st, rem); if (rem < 0) break; if (i < bts->si_common.uarfcn_length) { cu = u[i]; st = i; } else break; } /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */ bitvec_set_bit(bv, 0); /* UTRAN TDD Description */ bitvec_set_bit(bv, 0); } /* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */ int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts) { int rc; struct bitvec bv; if (bts->si2q_count < bts->si2q_index) return -EINVAL; bv.data = data; bv.data_len = 20; bitvec_zero(&bv); /* BA_IND: Set to '0' as that's what we use for SI2xxx type, * whereas '1' is used for SI5xxx type messages. The point here * is to be able to correlate whether a given MS measurement * report was using the neighbor cells advertised in SI2 or in * SI5, as those two could very well be different */ bitvec_set_bit(&bv, 0); /* 3G_BA_IND */ bitvec_set_bit(&bv, 1); /* MP_CHANGE_MARK */ bitvec_set_bit(&bv, 0); /* SI2quater_INDEX */ bitvec_set_uint(&bv, bts->si2q_index, 4); /* SI2quater_COUNT */ bitvec_set_uint(&bv, bts->si2q_count, 4); /* No Measurement_Parameters Description */ bitvec_set_bit(&bv, 0); /* No GPRS_Real Time Difference Description */ bitvec_set_bit(&bv, 0); /* No GPRS_BSIC Description */ bitvec_set_bit(&bv, 0); /* No GPRS_REPORT PRIORITY Description */ bitvec_set_bit(&bv, 0); /* No GPRS_MEASUREMENT_Parameters Description */ bitvec_set_bit(&bv, 0); /* No NC Measurement Parameters */ bitvec_set_bit(&bv, 0); /* No extension (length) */ bitvec_set_bit(&bv, 0); rc = SI2Q_MAX_LEN - (bv.cur_bit + 3); if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0) append_uarfcns(&bv, bts, rc); else /* No 3G Neighbour Cell Description */ bitvec_set_bit(&bv, 0); /* No 3G Measurement Parameters Description */ bitvec_set_bit(&bv, 0); /* No GPRS_3G_MEASUREMENT Parameters Descr. */ bitvec_set_bit(&bv, 0); rc = SI2Q_MAX_LEN - bv.cur_bit; if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0) append_earfcn(&bv, bts, rc); else /* No Additions in Rel-5: */ bitvec_set_bit(&bv, L); bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); return bv.data_len; } /* Append selection parameters to bitvec */ static void append_selection_params(struct bitvec *bv, const struct gsm48_si_selection_params *sp) { if (sp->present) { bitvec_set_bit(bv, H); bitvec_set_bit(bv, sp->cbq); bitvec_set_uint(bv, sp->cell_resel_off, 6); bitvec_set_uint(bv, sp->temp_offs, 3); bitvec_set_uint(bv, sp->penalty_time, 5); } else bitvec_set_bit(bv, L); } /* Append power offset to bitvec */ static void append_power_offset(struct bitvec *bv, const struct gsm48_si_power_offset *po) { if (po->present) { bitvec_set_bit(bv, H); bitvec_set_uint(bv, po->power_offset, 2); } else bitvec_set_bit(bv, L); } /* Append GPRS indicator to bitvec */ static void append_gprs_ind(struct bitvec *bv, const struct gsm48_si3_gprs_ind *gi) { if (gi->present) { bitvec_set_bit(bv, H); bitvec_set_uint(bv, gi->ra_colour, 3); /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ bitvec_set_bit(bv, gi->si13_position); } else bitvec_set_bit(bv, L); } /* Generate SI2ter Rest Octests 3GPP TS 44.018 Table 10.5.2.33a.1 */ int rest_octets_si2ter(uint8_t *data) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 4; /* No SI2ter_MP_CHANGE_MARK */ bitvec_set_bit(&bv, L); bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); return bv.data_len; } /* Generate SI2bis Rest Octests 3GPP TS 44.018 Table 10.5.2.33.1 */ int rest_octets_si2bis(uint8_t *data) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 1; bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); return bv.data_len; } /* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 4; /* Optional Selection Parameters */ append_selection_params(&bv, &si3->selection_params); /* Optional Power Offset */ append_power_offset(&bv, &si3->power_offset); /* Do we have a SI2ter on the BCCH? */ if (si3->si2ter_indicator) bitvec_set_bit(&bv, H); else bitvec_set_bit(&bv, L); /* Early Classmark Sending Control */ if (si3->early_cm_ctrl) bitvec_set_bit(&bv, H); else bitvec_set_bit(&bv, L); /* Do we have a SI Type 9 on the BCCH? */ if (si3->scheduling.present) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si3->scheduling.where, 3); } else bitvec_set_bit(&bv, L); /* GPRS Indicator */ append_gprs_ind(&bv, &si3->gprs_ind); /* 3G Early Classmark Sending Restriction. If H, then controlled by * early_cm_ctrl above */ if (si3->early_cm_restrict_3g) bitvec_set_bit(&bv, L); else bitvec_set_bit(&bv, H); if (si3->si2quater_indicator) { bitvec_set_bit(&bv, H); /* indicator struct present */ bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */ } bitvec_spare_padding(&bv, (bv.data_len*8)-1); return bv.data_len; } static int append_lsa_params(struct bitvec *bv, const struct gsm48_lsa_params *lsa_params) { /* FIXME */ return -1; } /* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = len; /* SI4 Rest Octets O */ append_selection_params(&bv, &si4->selection_params); append_power_offset(&bv, &si4->power_offset); append_gprs_ind(&bv, &si4->gprs_ind); if (0 /* FIXME */) { /* H and SI4 Rest Octets S */ bitvec_set_bit(&bv, H); /* LSA Parameters */ if (si4->lsa_params.present) { bitvec_set_bit(&bv, H); append_lsa_params(&bv, &si4->lsa_params); } else bitvec_set_bit(&bv, L); /* Cell Identity */ if (1) { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si4->cell_id, 16); } else bitvec_set_bit(&bv, L); /* LSA ID Information */ if (0) { bitvec_set_bit(&bv, H); /* FIXME */ } else bitvec_set_bit(&bv, L); } else { /* L and break indicator */ bitvec_set_bit(&bv, L); bitvec_set_bit(&bv, si4->break_ind ? H : L); } return bv.data_len; } /* GSM 04.18 ETSI TS 101 503 V8.27.0 (2006-05) ::= {L | H } {L | H } { < DTM_support : bit == L > I < DTM_support : bit == H > < RAC : bit (8) > < MAX_LAPDm : bit (3) > } < Band indicator > { L | H < GPRS_MS_TXPWR_MAX_CCH : bit (5) > } ; */ int rest_octets_si6(uint8_t *data, bool is1800_net) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 1; /* no PCH/NCH info */ bitvec_set_bit(&bv, L); /* no VBS/VGCS options */ bitvec_set_bit(&bv, L); /* no DTM_support */ bitvec_set_bit(&bv, L); /* band indicator */ if (is1800_net) bitvec_set_bit(&bv, L); else bitvec_set_bit(&bv, H); /* no GPRS_MS_TXPWR_MAX_CCH */ bitvec_set_bit(&bv, L); bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); return bv.data_len; } /* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: < GPRS Mobile Allocation IE > ::= < HSN : bit (6) > { 0 | 1 < RFL number list : < RFL number list struct > > } { 0 < MA_LENGTH : bit (6) > < MA_BITMAP: bit (val(MA_LENGTH) + 1) > | 1 { 0 | 1 > } } ; < RFL number list struct > :: = < RFL_NUMBER : bit (4) > { 0 | 1 < RFL number list struct > } ; < ARFCN index list struct > ::= < ARFCN_INDEX : bit(6) > { 0 | 1 < ARFCN index list struct > } ; */ static int append_gprs_mobile_alloc(struct bitvec *bv) { /* Hopping Sequence Number */ bitvec_set_uint(bv, 0, 6); if (0) { /* We want to use a RFL number list */ bitvec_set_bit(bv, 1); /* FIXME: RFL number list */ } else bitvec_set_bit(bv, 0); if (0) { /* We want to use a MA_BITMAP */ bitvec_set_bit(bv, 0); /* FIXME: MA_LENGTH, MA_BITMAP, ... */ } else { bitvec_set_bit(bv, 1); if (0) { /* We want to provide an ARFCN index list */ bitvec_set_bit(bv, 1); /* FIXME */ } else bitvec_set_bit(bv, 0); } return 0; } static int encode_t3192(unsigned int t3192) { /* See also 3GPP TS 44.060 Table 12.24.2: GPRS Cell Options information element details */ if (t3192 == 0) return 3; else if (t3192 <= 80) return 4; else if (t3192 <= 120) return 5; else if (t3192 <= 160) return 6; else if (t3192 <= 200) return 7; else if (t3192 <= 500) return 0; else if (t3192 <= 1000) return 1; else if (t3192 <= 1500) return 2; else return -EINVAL; } static int encode_drx_timer(unsigned int drx) { if (drx == 0) return 0; else if (drx == 1) return 1; else if (drx == 2) return 2; else if (drx <= 4) return 3; else if (drx <= 8) return 4; else if (drx <= 16) return 5; else if (drx <= 32) return 6; else if (drx <= 64) return 7; else return -EINVAL; } /* GPRS Cell Options as per TS 04.60 Chapter 12.24 < GPRS Cell Options IE > ::= < NMO : bit(2) > < T3168 : bit(3) > < T3192 : bit(3) > < DRX_TIMER_MAX: bit(3) > < ACCESS_BURST_TYPE: bit > < CONTROL_ACK_TYPE : bit > < BS_CV_MAX: bit(4) > { 0 | 1 < PAN_DEC : bit(3) > < PAN_INC : bit(3) > < PAN_MAX : bit(3) > { 0 | 1 < Extension Length : bit(6) > < bit (val(Extension Length) + 1 & { < Extension Information > ! { bit ** = } } ; < Extension Information > ::= { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > < BEP_PERIOD : bit(4) > } < PFC_FEATURE_MODE : bit > < DTM_SUPPORT : bit > ** ; */ static int append_gprs_cell_opt(struct bitvec *bv, const struct gprs_cell_options *gco) { int t3192, drx_timer_max; t3192 = encode_t3192(gco->t3192); if (t3192 < 0) return t3192; drx_timer_max = encode_drx_timer(gco->drx_timer_max); if (drx_timer_max < 0) return drx_timer_max; bitvec_set_uint(bv, gco->nmo, 2); /* See also 3GPP TS 44.060 Table 12.24.2: GPRS Cell Options information element details */ bitvec_set_uint(bv, gco->t3168 / 500 - 1, 3); bitvec_set_uint(bv, t3192, 3); bitvec_set_uint(bv, drx_timer_max, 3); /* ACCESS_BURST_TYPE: Hard-code 8bit */ bitvec_set_bit(bv, 0); /* CONTROL_ACK_TYPE: */ bitvec_set_bit(bv, gco->ctrl_ack_type_use_block); bitvec_set_uint(bv, gco->bs_cv_max, 4); if (0) { /* hard-code no PAN_{DEC,INC,MAX} */ bitvec_set_bit(bv, 0); } else { /* copied from ip.access BSC protocol trace */ bitvec_set_bit(bv, 1); bitvec_set_uint(bv, 1, 3); /* DEC */ bitvec_set_uint(bv, 1, 3); /* INC */ bitvec_set_uint(bv, 15, 3); /* MAX */ } if (!gco->ext_info_present) { /* no extension information */ bitvec_set_bit(bv, 0); } else { /* extension information */ bitvec_set_bit(bv, 1); if (!gco->ext_info.egprs_supported) { /* 6bit length of extension */ bitvec_set_uint(bv, (1 + 3)-1, 6); /* EGPRS supported in the cell */ bitvec_set_bit(bv, 0); } else { /* 6bit length of extension */ bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); /* EGPRS supported in the cell */ bitvec_set_bit(bv, 1); /* 1bit EGPRS PACKET CHANNEL REQUEST */ if (gco->supports_egprs_11bit_rach == 0) { bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); } else { bitvec_set_bit(bv, 0); } /* 4bit BEP PERIOD */ bitvec_set_uint(bv, gco->ext_info.bep_period, 4); } bitvec_set_bit(bv, gco->ext_info.pfc_supported); bitvec_set_bit(bv, gco->ext_info.dtm_supported); bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); } return 0; } static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, const struct gprs_power_ctrl_pars *pcp) { bitvec_set_uint(bv, pcp->alpha, 4); bitvec_set_uint(bv, pcp->t_avg_w, 5); bitvec_set_uint(bv, pcp->t_avg_t, 5); bitvec_set_uint(bv, pcp->pc_meas_chan, 1); bitvec_set_uint(bv, pcp->n_avg_i, 4); } /* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13) { struct bitvec bv; memset(&bv, 0, sizeof(bv)); bv.data = data; bv.data_len = 20; if (0) { /* No rest octets */ bitvec_set_bit(&bv, L); } else { bitvec_set_bit(&bv, H); bitvec_set_uint(&bv, si13->bcch_change_mark, 3); bitvec_set_uint(&bv, si13->si_change_field, 4); if (1) { bitvec_set_bit(&bv, 0); } else { bitvec_set_bit(&bv, 1); bitvec_set_uint(&bv, si13->bcch_change_mark, 2); append_gprs_mobile_alloc(&bv); } /* PBCCH not present in cell: it shall never be indicated according to 3GPP TS 44.018 Table 10.5.2.37b.1 */ bitvec_set_bit(&bv, 0); bitvec_set_uint(&bv, si13->rac, 8); bitvec_set_bit(&bv, si13->spgc_ccch_sup); bitvec_set_uint(&bv, si13->prio_acc_thr, 3); bitvec_set_uint(&bv, si13->net_ctrl_ord, 2); append_gprs_cell_opt(&bv, &si13->cell_opts); append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars); /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */ bitvec_set_bit(&bv, H); /* added Release 99 */ /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS * was only added in this Release */ bitvec_set_bit(&bv, 1); } bitvec_spare_padding(&bv, (bv.data_len*8)-1); return bv.data_len; } osmo-bsc-1.3.0/src/osmo-bsc/system_information.c000066400000000000000000001025351332665256100216620ustar00rootroot00000000000000/* GSM 04.08 System Information (SI) encoding and decoding * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2010 by Harald Welte * (C) 2012 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the * array. DCS1800 and PCS1900 can not be used at the same time so conserve * memory and do the below. */ static int band_compatible(const struct gsm_bts *bts, int arfcn) { enum gsm_band band = gsm_arfcn2band(arfcn); /* normal case */ if (band == bts->band) return 1; /* deal with ARFCN_PCS not set */ if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) return 1; return 0; } static int is_dcs_net(const struct gsm_bts *bts) { if (bts->band == GSM_BAND_850) return 0; if (bts->band == GSM_BAND_1900) return 0; return 1; } /* Return p(n) for given NR_OF_TDD_CELLS - see Table 9.1.54.1a, 3GPP TS 44.018 */ unsigned range1024_p(unsigned n) { switch (n) { case 0: return 0; case 1: return 10; case 2: return 19; case 3: return 28; case 4: return 36; case 5: return 44; case 6: return 52; case 7: return 60; case 8: return 67; case 9: return 74; case 10: return 81; case 11: return 88; case 12: return 95; case 13: return 102; case 14: return 109; case 15: return 116; case 16: return 122; default: return 0; } } /* Return q(m) for given NR_OF_TDD_CELLS - see Table 9.1.54.1b, 3GPP TS 44.018 */ unsigned range512_q(unsigned m) { switch (m) { case 0: return 0; case 1: return 9; case 2: return 17; case 3: return 25; case 4: return 32; case 5: return 39; case 6: return 46; case 7: return 53; case 8: return 59; case 9: return 65; case 10: return 71; case 11: return 77; case 12: return 83; case 13: return 89; case 14: return 95; case 15: return 101; case 16: return 106; case 17: return 111; case 18: return 116; case 19: return 121; case 20: return 126; default: return 0; } } size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e) { unsigned i, ret = 0; if (!e) return 0; for (i = 0; i < e->length; i++) if (e->arfcn[i] != OSMO_EARFCN_INVALID) ret++; return ret; } /* generate SI2quater messages, return rest octets length of last generated message or negative error code */ static int make_si2quaters(struct gsm_bts *bts, bool counting) { int rc; bool memory_exceeded = true; struct gsm48_system_information_type_2quater *si2q; for (bts->si2q_index = 0; bts->si2q_index < SI2Q_MAX_NUM; bts->si2q_index++) { si2q = GSM_BTS_SI2Q(bts, bts->si2q_index); if (counting) { /* that's legitimate if we're called for counting purpose: */ if (bts->si2q_count < bts->si2q_index) bts->si2q_count = bts->si2q_index; } else { memset(si2q, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2q->header.l2_plen = GSM48_LEN2PLEN(22); si2q->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2q->header.skip_indicator = 0; si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater; } rc = rest_octets_si2quater(si2q->rest_octets, bts); if (rc < 0) return rc; if (bts->u_offset >= bts->si_common.uarfcn_length && bts->e_offset >= si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) { memory_exceeded = false; break; } } if (memory_exceeded) return -ENOMEM; return rc; } /* we generate SI2q rest octets twice to get proper estimation but it's one time cost anyway */ uint8_t si2q_num(struct gsm_bts *bts) { int rc = make_si2quaters(bts, true); uint8_t num = bts->si2q_index + 1; /* number of SI2quater messages */ /* N. B: si2q_num() should NEVER be called during actual SI2q rest octets generation we're not re-entrant because of the following code: */ bts->u_offset = 0; bts->e_offset = 0; if (rc < 0) return 0xFF; /* return impossible index as an indicator of error in generating SI2quater */ return num; } /* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */ static inline uint16_t encode_fdd(uint16_t scramble, bool diversity) { if (diversity) return scramble | (1 << 9); return scramble; } int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio, uint8_t qrx, uint8_t meas_bw) { struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID); if (r < 0) return r; if (e->thresh_hi && thresh_hi != e->thresh_hi) r = 1; e->thresh_hi = thresh_hi; if (thresh_lo != EARFCN_THRESH_LOW_INVALID) { if (e->thresh_lo_valid && e->thresh_lo != thresh_lo) r = EARFCN_THRESH_LOW_INVALID; e->thresh_lo = thresh_lo; e->thresh_lo_valid = true; } if (qrx != EARFCN_QRXLV_INVALID) { if (e->qrxlm_valid && e->qrxlm != qrx) r = EARFCN_QRXLV_INVALID + 1; e->qrxlm = qrx; e->qrxlm_valid = true; } if (prio != EARFCN_PRIO_INVALID) { if (e->prio_valid && e->prio != prio) r = EARFCN_PRIO_INVALID; e->prio = prio; e->prio_valid = true; } return r; } /* Scrambling Code as defined in 3GPP TS 25.213 is 9 bit long so number below is unreacheable upper bound */ #define SC_BOUND 600 /* Find position for a given UARFCN (take SC into consideration if it's available) in a sorted list N. B: we rely on the assumption that (uarfcn, scramble) tuple is unique in the lists */ static int uarfcn_sc_pos(const struct gsm_bts *bts, uint16_t uarfcn, uint16_t scramble) { const uint16_t *sc = bts->si_common.data.scramble_list; uint16_t i, scramble0 = encode_fdd(scramble, false), scramble1 = encode_fdd(scramble, true); for (i = 0; i < bts->si_common.uarfcn_length; i++) if (uarfcn == bts->si_common.data.uarfcn_list[i]) { if (scramble < SC_BOUND) { if (scramble0 == sc[i] || scramble1 == sc[i]) return i; } else return i; } return -1; } int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble) { uint16_t *ual = bts->si_common.data.uarfcn_list, *scl = bts->si_common.data.scramble_list; size_t len = bts->si_common.uarfcn_length; int pos = uarfcn_sc_pos(bts, arfcn, scramble); if (pos < 0) return -EINVAL; if (pos != len - 1) { /* move the tail if necessary */ memmove(ual + pos, ual + pos + 1, 2 * (len - pos + 1)); memmove(scl + pos, scl + pos + 1, 2 * (len - pos + 1)); } bts->si_common.uarfcn_length--; return 0; } int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity) { size_t len = bts->si_common.uarfcn_length, i; uint8_t si2q; int pos = uarfcn_sc_pos(bts, arfcn, scramble); uint16_t scr = diversity ? encode_fdd(scramble, true) : encode_fdd(scramble, false), *ual = bts->si_common.data.uarfcn_list, *scl = bts->si_common.data.scramble_list; if (len == MAX_EARFCN_LIST) return -ENOMEM; if (pos >= 0) return -EADDRINUSE; /* find the suitable position for arfcn if any */ pos = uarfcn_sc_pos(bts, arfcn, SC_BOUND); i = (pos < 0) ? len : pos; /* move the tail to make space for inserting if necessary */ if (i < len) { memmove(ual + i + 1, ual + i, (len - i) * 2); memmove(scl + i + 1, scl + i, (len - i) * 2); } /* insert into appropriate position */ ual[i] = arfcn; scl[i] = scr; bts->si_common.uarfcn_length++; /* try to generate SI2q */ si2q = si2q_num(bts); if (si2q <= SI2Q_MAX_NUM) { bts->si2q_count = si2q - 1; return 0; } /* rollback after unsuccessful generation */ bts_uarfcn_del(bts, arfcn, scramble); return -ENOSPC; } static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter, const bool pgsm, const int arfcn) { if (bts->force_combined_si) return !bis && !ter; if (!bis && !ter && band_compatible(bts, arfcn)) return 1; /* Correct but somehow broken with either the nanoBTS or the iPhone5 */ if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) return 1; if (ter && !band_compatible(bts, arfcn)) return 1; return 0; } /* Frequency Lists as per TS 04.08 10.5.2.13 */ /* 10.5.2.13.2: Bit map 0 format */ static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) { unsigned int byte, bit; if (arfcn > 124 || arfcn < 1) { LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); return -EINVAL; } /* the bitmask is from 1..124, not from 0..123 */ arfcn--; byte = arfcn / 8; bit = arfcn % 8; chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); return 0; } /* 10.5.2.13.7: Variable bit map format */ static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) { unsigned int byte, bit; unsigned int min_arfcn; unsigned int bitno; min_arfcn = (chan_list[0] & 1) << 9; min_arfcn |= chan_list[1] << 1; min_arfcn |= (chan_list[2] >> 7) & 1; /* The lower end of our bitmaks is always implicitly included */ if (arfcn == min_arfcn) return 0; if (((arfcn - min_arfcn) & 1023) > 111) { LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); return -EINVAL; } bitno = (arfcn - min_arfcn) & 1023; byte = bitno / 8; bit = bitno % 8; chan_list[2 + byte] |= 1 << (7 - bit); return 0; } /* generate a variable bitmap */ static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, bool bis, bool ter, int min, bool pgsm) { int i; /* set it to 'Variable bitmap format' */ chan_list[0] = 0x8e; chan_list[0] |= (min >> 9) & 1; chan_list[1] = (min >> 1); chan_list[2] = (min & 1) << 7; for (i = 0; i < bv->data_len*8; i++) { /* see notes in bitvec2freq_list */ if (bitvec_get_bit_pos(bv, i) && ((!bis && !ter && band_compatible(bts,i)) || (bis && pgsm && band_compatible(bts,i) && (i < 1 || i > 124)) || (ter && !band_compatible(bts, i)))) { int rc = freq_list_bmrel_set_arfcn(chan_list, i); if (rc < 0) return rc; } } return 0; } int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w, int f0, uint8_t *chan_list) { /* * Manipulate the ARFCN list according to the rules in J4 depending * on the selected range. */ int rc, f0_included; range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); rc = range_enc_arfcns(r, arfcns, arfcns_used, w, 0); if (rc < 0) return rc; /* Select the range and the amount of bits needed */ switch (r) { case ARFCN_RANGE_128: return range_enc_range128(chan_list, f0, w); case ARFCN_RANGE_256: return range_enc_range256(chan_list, f0, w); case ARFCN_RANGE_512: return range_enc_range512(chan_list, f0, w); case ARFCN_RANGE_1024: return range_enc_range1024(chan_list, f0, f0_included, w); default: return -ERANGE; }; return f0_included; } /* generate a frequency list with the range 512 format */ static inline int enc_freq_lst_range(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, bool bis, bool ter, bool pgsm) { int arfcns[RANGE_ENC_MAX_ARFCNS]; int w[RANGE_ENC_MAX_ARFCNS]; int arfcns_used = 0; int i, range, f0; /* * Select ARFCNs according to the rules in bitvec2freq_list */ for (i = 0; i < bv->data_len * 8; ++i) { /* More ARFCNs than the maximum */ if (arfcns_used > ARRAY_SIZE(arfcns)) return -1; /* Check if we can select it? */ if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) arfcns[arfcns_used++] = i; } /* * Check if the given list of ARFCNs can be encoded. */ range = range_enc_determine_range(arfcns, arfcns_used, &f0); if (range == ARFCN_RANGE_INVALID) return -2; memset(w, 0, sizeof(w)); return range_encode(range, arfcns, arfcns_used, w, f0, chan_list); } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, const struct gsm_bts *bts, bool bis, bool ter) { int i, rc, min = -1, max = -1, arfcns = 0; bool pgsm = false; memset(chan_list, 0, 16); if (bts->band == GSM_BAND_900 && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) pgsm = true; /* P-GSM-only handsets only support 'bit map 0 format' */ if (!bis && !ter && pgsm) { chan_list[0] = 0; for (i = 0; i < bv->data_len*8; i++) { if (i >= 1 && i <= 124 && bitvec_get_bit_pos(bv, i)) { rc = freq_list_bm0_set_arfcn(chan_list, i); if (rc < 0) return rc; } } return 0; } for (i = 0; i < bv->data_len*8; i++) { /* in case of SI2 or SI5 allow all neighbours in same band * in case of SI*bis, allow neighbours in same band ouside pgsm * in case of SI*ter, allow neighbours in different bands */ if (!bitvec_get_bit_pos(bv, i)) continue; if (!use_arfcn(bts, bis, ter, pgsm, i)) continue; /* count the arfcns we want to carry */ arfcns += 1; /* 955..1023 < 0..885 */ if (min < 0) min = i; if (i >= 955 && min < 955) min = i; if (i >= 955 && min >= 955 && i < min) min = i; if (i < 955 && min < 955 && i < min) min = i; if (max < 0) max = i; if (i < 955 && max >= 955) max = i; if (i >= 955 && max >= 955 && i > max) max = i; if (i < 955 && max < 955 && i > max) max = i; } if (max == -1) { /* Empty set, use 'bit map 0 format' */ chan_list[0] = 0; return 0; } /* Now find the best encoding */ if (((max - min) & 1023) <= 111) return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, ter, min, pgsm); /* Attempt to do the range encoding */ rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); if (rc >= 0) return 0; LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " "can not generate ARFCN list\n", min, max, arfcns); return -EINVAL; } /* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ /* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) { struct gsm_bts_trx *trx; struct bitvec *bv = &bts->si_common.cell_alloc; /* Zero-initialize the bit-vector */ memset(bv->data, 0, bv->data_len); /* first we generate a bitvec of all TRX ARFCN's in our BTS */ llist_for_each_entry(trx, &bts->trx_list, list) { unsigned int i, j; /* Always add the TRX's ARFCN */ bitvec_set_bit_pos(bv, trx->arfcn, 1); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; /* Add any ARFCNs present in hopping channels */ for (j = 0; j < 1024; j++) { if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) bitvec_set_bit_pos(bv, j, 1); } } } /* then we generate a GSM 04.08 frequency list from the bitvec */ return bitvec2freq_list(chan_list, bv, bts, false, false); } /*! generate a cell channel list as per Section 10.5.2.22 of 04.08 * \param[out] chan_list caller-provided output buffer * \param[in] bts BTS descriptor used for input data * \param[in] si5 Are we generating SI5xxx (true) or SI2xxx (false) * \param[in] bis Are we generating SIXbis (true) or not (false) * \param[in] ter Are we generating SIXter (true) or not (false) */ static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, bool si5, bool bis, bool ter) { struct gsm_bts *cur_bts; struct bitvec *bv; int rc; if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) bv = &bts->si_common.si5_neigh_list; else bv = &bts->si_common.neigh_list; /* Generate list of neighbor cells if we are in automatic mode */ if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { /* Zero-initialize the bit-vector */ memset(bv->data, 0, bv->data_len); /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { if (cur_bts == bts) continue; bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); } } /* then we generate a GSM 04.08 frequency list from the bitvec */ rc = bitvec2freq_list(chan_list, bv, bts, bis, ter); if (rc < 0) return rc; /* Set BA-IND depending on whether we're generating SI2 or SI5. * The point here is to be able to correlate whether a given MS * measurement report was using the neighbor cells advertised in * SI2 or in SI5, as those two could very well be different */ if (si5) chan_list[0] |= 0x10; else chan_list[0] &= ~0x10; return rc; } static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) { int n = 0, i; struct gsm_sysinfo_freq freq[1024]; memset(freq, 0, sizeof(freq)); gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); for (i = 0; i < 1024; i++) { if (freq[i].mask) { if (!n) LOGP(DRR, LOGL_INFO, "%s", text); LOGPC(DRR, LOGL_INFO, " %d", i); n++; } } if (n) LOGPC(DRR, LOGL_INFO, "\n"); return n; } static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_1 *si1 = (struct gsm48_system_information_type_1 *) GSM_BTS_SI(bts, t); memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si1->header.l2_plen = GSM48_LEN2PLEN(21); si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; si1->header.skip_indicator = 0; si1->header.system_information = GSM48_MT_RR_SYSINFO_1; rc = generate_cell_chan_list(si1->cell_channel_description, bts); if (rc < 0) return rc; list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); si1->rach_control = bts->si_common.rach_control; if (acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_apply(&si1->rach_control, &bts->acc_ramp); /* * SI1 Rest Octets (10.5.2.32), contains NCH position and band * indicator but that is not in the 04.08. */ rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); return sizeof(*si1) + rc; } static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, t); memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2->header.l2_plen = GSM48_LEN2PLEN(22); si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2->header.skip_indicator = 0; si2->header.system_information = GSM48_MT_RR_SYSINFO_2; rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, false, false, false); if (rc < 0) return rc; list_arfcn(si2->bcch_frequency_list, 0xce, "SI2 Neighbour cells in same band:"); si2->ncc_permitted = bts->si_common.ncc_permitted; si2->rach_control = bts->si_common.rach_control; if (acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_apply(&si2->rach_control, &bts->acc_ramp); return sizeof(*si2); } static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2bis *si2b = (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, t); int n; memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2b->header.l2_plen = GSM48_LEN2PLEN(22); si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2b->header.skip_indicator = 0; si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, false, true, false); if (rc < 0) return rc; n = list_arfcn(si2b->bcch_frequency_list, 0xce, "Neighbour cells in same band, but outside P-GSM:"); if (n) { /* indicate in SI2 and SI2bis: there is an extension */ struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2); si2->bcch_frequency_list[0] |= 0x20; si2b->bcch_frequency_list[0] |= 0x20; } else bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); si2b->rach_control = bts->si_common.rach_control; if (acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_apply(&si2b->rach_control, &bts->acc_ramp); /* SI2bis Rest Octets as per 3GPP TS 44.018 §10.5.2.33 */ rc = rest_octets_si2bis(si2b->rest_octets); return sizeof(*si2b) + rc; } static int generate_si2ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2ter *si2t = (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, t); int n; memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si2t->header.l2_plen = GSM48_LEN2PLEN(22); si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; si2t->header.skip_indicator = 0; si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, false, false, true); if (rc < 0) return rc; n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, "Neighbour cells in different band:"); if (!n) bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); /* SI2ter Rest Octets as per 3GPP TS 44.018 §10.5.2.33a */ rc = rest_octets_si2ter(si2t->rest_octets); return sizeof(*si2t) + rc; } /* SI2quater messages are optional - we only generate them when neighbor UARFCNs or EARFCNs are configured */ static inline bool si2quater_not_needed(struct gsm_bts *bts) { unsigned i = MAX_EARFCN_LIST; if (bts->si_common.si2quater_neigh_list.arfcn) for (i = 0; i < MAX_EARFCN_LIST; i++) if (bts->si_common.si2quater_neigh_list.arfcn[i] != OSMO_EARFCN_INVALID) break; if (!bts->si_common.uarfcn_length && i == MAX_EARFCN_LIST) { bts->si_valid &= ~(1 << SYSINFO_TYPE_2quater); /* mark SI2q as invalid if no (E|U)ARFCNs are present */ return true; } return false; } static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_2quater *si2q; if (si2quater_not_needed(bts)) /* generate rest_octets for SI2q only when necessary */ return GSM_MACBLOCK_LEN; bts->u_offset = 0; bts->e_offset = 0; bts->si2q_index = 0; bts->si2q_count = si2q_num(bts) - 1; rc = make_si2quaters(bts, false); if (rc < 0) return rc; OSMO_ASSERT(bts->si2q_count == bts->si2q_index); OSMO_ASSERT(bts->si2q_count <= SI2Q_MAX_NUM); return sizeof(*si2q) + rc; } static struct gsm48_si_ro_info si_info = { .selection_params = { .present = 0, }, .power_offset = { .present = 0, }, .si2ter_indicator = false, .early_cm_ctrl = true, .scheduling = { .present = 0, }, .gprs_ind = { .si13_position = 0, .ra_colour = 0, .present = 1, }, .early_cm_restrict_3g = false, .si2quater_indicator = false, .lsa_params = { .present = 0, }, .cell_id = 0, /* FIXME: doesn't the bts have this? */ .break_ind = 0, }; static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) GSM_BTS_SI(bts, t); memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si3->header.l2_plen = GSM48_LEN2PLEN(18); si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; si3->header.skip_indicator = 0; si3->header.system_information = GSM48_MT_RR_SYSINFO_3; si3->cell_identity = htons(bts->cell_identity); gsm48_generate_lai2(&si3->lai, bts_lai(bts)); si3->control_channel_desc = bts->si_common.chan_desc; si3->cell_options = bts->si_common.cell_options; si3->cell_sel_par = bts->si_common.cell_sel_par; si3->rach_control = bts->si_common.rach_control; if (acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_apply(&si3->rach_control, &bts->acc_ramp); /* allow/disallow DTXu */ gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true); if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) { LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); si_info.si2ter_indicator = true; } else { si_info.si2ter_indicator = false; } if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater)) { LOGP(DRR, LOGL_INFO, "SI 2quater is included, based on %zu EARFCNs and %zu UARFCNs.\n", si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length); si_info.si2quater_indicator = true; } else { si_info.si2quater_indicator = false; } si_info.early_cm_ctrl = bts->early_classmark_allowed; si_info.early_cm_restrict_3g = !bts->early_classmark_allowed_3g; /* SI3 Rest Octets (10.5.2.34), containing CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME Power Offset, 2ter Indicator, Early Classmark Sending, Scheduling if and WHERE, GPRS Indicator, SI13 position */ rc = rest_octets_si3(si3->rest_octets, &si_info); return sizeof(*si3) + rc; } static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts) { int rc; struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) GSM_BTS_SI(bts, t); struct gsm_lchan *cbch_lchan; uint8_t *restoct = si4->data; /* length of all IEs present except SI4 rest octets and l2_plen */ int l2_plen = sizeof(*si4) - 1; memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; si4->header.skip_indicator = 0; si4->header.system_information = GSM48_MT_RR_SYSINFO_4; gsm48_generate_lai2(&si4->lai, bts_lai(bts)); si4->cell_sel_par = bts->si_common.cell_sel_par; si4->rach_control = bts->si_common.rach_control; if (acc_ramp_is_enabled(&bts->acc_ramp)) acc_ramp_apply(&si4->rach_control, &bts->acc_ramp); /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ cbch_lchan = gsm_bts_get_cbch(bts); if (cbch_lchan) { struct gsm48_chan_desc cd; gsm48_lchan2chan_desc(&cd, cbch_lchan); tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3, (uint8_t *) &cd); l2_plen += 3 + 1; restoct += 3 + 1; /* we don't use hopping and thus don't need a CBCH MA */ } si4->header.l2_plen = GSM48_LEN2PLEN(l2_plen); /* SI4 Rest Octets (10.5.2.35), containing Optional Power offset, GPRS Indicator, Cell Identity, LSA ID, Selection Parameter */ rc = rest_octets_si4(restoct, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - restoct); return l2_plen + 1 + rc; } static int generate_si5(enum osmo_sysinfo_type t, struct gsm_bts *bts) { struct gsm48_system_information_type_5 *si5; uint8_t *output = GSM_BTS_SI(bts, t); int rc, l2_plen = 18; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: *output++ = GSM48_LEN2PLEN(l2_plen); l2_plen++; break; default: break; } si5 = (struct gsm48_system_information_type_5 *) output; /* l2 pseudo length, not part of msg: 18 */ si5->rr_protocol_discriminator = GSM48_PDISC_RR; si5->skip_indicator = 0; si5->system_information = GSM48_MT_RR_SYSINFO_5; rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, true, false, false); if (rc < 0) return rc; list_arfcn(si5->bcch_frequency_list, 0xce, "SI5 Neighbour cells in same band:"); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si5bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) { struct gsm48_system_information_type_5bis *si5b; uint8_t *output = GSM_BTS_SI(bts, t); int rc, l2_plen = 18; int n; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: *output++ = GSM48_LEN2PLEN(l2_plen); l2_plen++; break; default: break; } si5b = (struct gsm48_system_information_type_5bis *) output; /* l2 pseudo length, not part of msg: 18 */ si5b->rr_protocol_discriminator = GSM48_PDISC_RR; si5b->skip_indicator = 0; si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, true, true, false); if (rc < 0) return rc; n = list_arfcn(si5b->bcch_frequency_list, 0xce, "Neighbour cells in same band, but outside P-GSM:"); if (n) { /* indicate in SI5 and SI5bis: there is an extension */ struct gsm48_system_information_type_5 *si5 = (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5)+1; si5->bcch_frequency_list[0] |= 0x20; si5b->bcch_frequency_list[0] |= 0x20; } else bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) { struct gsm48_system_information_type_5ter *si5t; uint8_t *output = GSM_BTS_SI(bts, t); int rc, l2_plen = 18; int n; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: *output++ = GSM48_LEN2PLEN(l2_plen); l2_plen++; break; default: break; } si5t = (struct gsm48_system_information_type_5ter *) output; /* l2 pseudo length, not part of msg: 18 */ si5t->rr_protocol_discriminator = GSM48_PDISC_RR; si5t->skip_indicator = 0; si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, true, false, true); if (rc < 0) return rc; n = list_arfcn(si5t->bcch_frequency_list, 0x8e, "Neighbour cells in different band:"); if (!n) bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); /* 04.08 9.1.37: L2 Pseudo Length of 18 */ return l2_plen; } static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts) { struct gsm48_system_information_type_6 *si6; uint8_t *output = GSM_BTS_SI(bts, t); int l2_plen = 11; int rc; memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); /* ip.access nanoBTS needs l2_plen!! */ switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: *output++ = GSM48_LEN2PLEN(l2_plen); l2_plen++; break; default: break; } si6 = (struct gsm48_system_information_type_6 *) output; /* l2 pseudo length, not part of msg: 11 */ si6->rr_protocol_discriminator = GSM48_PDISC_RR; si6->skip_indicator = 0; si6->system_information = GSM48_MT_RR_SYSINFO_6; si6->cell_identity = htons(bts->cell_identity); gsm48_generate_lai2(&si6->lai, bts_lai(bts)); si6->cell_options = bts->si_common.cell_options; si6->ncc_permitted = bts->si_common.ncc_permitted; /* allow/disallow DTXu */ gsm48_set_dtx(&si6->cell_options, bts->dtxu, bts->dtxu, false); /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ rc = rest_octets_si6(si6->rest_octets, is_dcs_net(bts)); return l2_plen + rc; } static struct gsm48_si13_info si13_default = { .cell_opts = { .nmo = GPRS_NMO_II, .t3168 = 2000, .t3192 = 1500, .drx_timer_max = 3, .bs_cv_max = 15, .ctrl_ack_type_use_block = true, .ext_info_present = 0, .supports_egprs_11bit_rach = 0, .ext_info = { /* The values below are just guesses ! */ .egprs_supported = 0, .use_egprs_p_ch_req = 1, .bep_period = 5, .pfc_supported = 0, .dtm_supported = 0, .bss_paging_coordination = 0, }, }, .pwr_ctrl_pars = { .alpha = 0, /* a = 0.0 */ .t_avg_w = 16, .t_avg_t = 16, .pc_meas_chan = 0, /* downling measured on CCCH */ .n_avg_i = 8, }, .bcch_change_mark = 1, .si_change_field = 0, .rac = 0, /* needs to be patched */ .spgc_ccch_sup = 0, .net_ctrl_ord = 0, .prio_acc_thr = 6, }; static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts) { struct gsm48_system_information_type_13 *si13 = (struct gsm48_system_information_type_13 *) GSM_BTS_SI(bts, t); int ret; memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; si13->header.skip_indicator = 0; si13->header.system_information = GSM48_MT_RR_SYSINFO_13; si13_default.rac = bts->gprs.rac; si13_default.net_ctrl_ord = bts->gprs.net_ctrl_ord; si13_default.cell_opts.ctrl_ack_type_use_block = bts->gprs.ctrl_ack_type_use_block; /* Information about the other SIs */ si13_default.bcch_change_mark = bts->bcch_change_mark; si13_default.cell_opts.supports_egprs_11bit_rach = bts->gprs.supports_egprs_11bit_rach; ret = rest_octets_si13(si13->rest_octets, &si13_default); if (ret < 0) return ret; /* length is coded in bit 2 an up */ si13->header.l2_plen = 0x01; return sizeof (*si13) + ret; } typedef int (*gen_si_fn_t)(enum osmo_sysinfo_type t, struct gsm_bts *bts); static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { [SYSINFO_TYPE_1] = &generate_si1, [SYSINFO_TYPE_2] = &generate_si2, [SYSINFO_TYPE_2bis] = &generate_si2bis, [SYSINFO_TYPE_2ter] = &generate_si2ter, [SYSINFO_TYPE_2quater] = &generate_si2quater, [SYSINFO_TYPE_3] = &generate_si3, [SYSINFO_TYPE_4] = &generate_si4, [SYSINFO_TYPE_5] = &generate_si5, [SYSINFO_TYPE_5bis] = &generate_si5bis, [SYSINFO_TYPE_5ter] = &generate_si5ter, [SYSINFO_TYPE_6] = &generate_si6, [SYSINFO_TYPE_13] = &generate_si13, }; int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) { gen_si_fn_t gen_si; switch (bts->gprs.mode) { case BTS_GPRS_EGPRS: si13_default.cell_opts.ext_info_present = 1; si13_default.cell_opts.ext_info.egprs_supported = 1; /* fallthrough */ case BTS_GPRS_GPRS: si_info.gprs_ind.present = 1; break; case BTS_GPRS_NONE: si_info.gprs_ind.present = 0; break; } memcpy(&si_info.selection_params, &bts->si_common.cell_ro_sel_par, sizeof(struct gsm48_si_selection_params)); gen_si = gen_si_fn[si_type]; if (!gen_si) return -EINVAL; return gen_si(si_type, bts); } osmo-bsc-1.3.0/src/utils/000077500000000000000000000000001332665256100151755ustar00rootroot00000000000000osmo-bsc-1.3.0/src/utils/Makefile.am000066400000000000000000000042131332665256100172310ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ -I$(top_builddir) \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(SQLITE3_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMOMGCPCLIENT_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) noinst_HEADERS = \ meas_db.h \ $(NULL) bin_PROGRAMS = \ bs11_config \ isdnsync \ meas_json \ $(NULL) if HAVE_SQLITE3 bin_PROGRAMS += \ osmo-meas-udp2db \ $(NULL) if HAVE_PCAP bin_PROGRAMS += \ osmo-meas-pcap2db \ $(NULL) endif endif if HAVE_LIBCDK bin_PROGRAMS += \ meas_vis \ $(NULL) endif bs11_config_SOURCES = \ bs11_config.c \ stubs.c \ $(NULL) bs11_config_LDADD = \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/bts_siemens_bs11.o \ $(top_builddir)/src/osmo-bsc/e1_config.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(NULL) isdnsync_SOURCES = \ isdnsync.c \ $(NULL) meas_vis_SOURCES = \ meas_vis.c \ $(NULL) meas_vis_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ -lcdk \ -lncurses \ $(NULL) meas_vis_CFLAGS = \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(NULL) osmo_meas_pcap2db_SOURCES = \ meas_pcap2db.c \ meas_db.c \ $(NULL) osmo_meas_pcap2db_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(SQLITE3_LIBS) \ -lpcap \ $(NULL) osmo_meas_pcap2db_CFLAGS = \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) osmo_meas_udp2db_SOURCES = \ meas_udp2db.c \ meas_db.c \ $(NULL) osmo_meas_udp2db_LDADD = \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(SQLITE3_LIBS) \ $(NULL) osmo_meas_udp2db_CFLAGS = \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) meas_json_SOURCES = \ meas_json.c \ stubs.c \ $(NULL) meas_json_LDADD = \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(NULL) meas_json_CFLAGS = \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) osmo-bsc-1.3.0/src/utils/bs11_config.c000066400000000000000000000636701332665256100174500ustar00rootroot00000000000000/* Siemens BS-11 microBTS configuration tool */ /* (C) 2009-2010 by Harald Welte * All Rights Reserved * * This software is based on ideas (but not code) of BS11Config * (C) 2009 by Dieter Spaar * * 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 #include #include #include #include #include static void *tall_bs11cfg_ctx; static struct e1inp_sign_link *oml_link; /* state of our bs11_config application */ enum bs11cfg_state { STATE_NONE, STATE_LOGON_WAIT, STATE_LOGON_ACK, STATE_SWLOAD, STATE_QUERY, }; static enum bs11cfg_state bs11cfg_state = STATE_NONE; static char *command, *value; struct osmo_timer_list status_timer; static const uint8_t obj_li_attr[] = { NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, NM_ATT_BS11_L1_PROT_TYPE, 0x00, NM_ATT_BS11_LINE_CFG, 0x00, }; static const uint8_t obj_bbsig0_attr[] = { NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, NM_ATT_BS11_DIVERSITY, 0x01, 0x00, }; static const uint8_t obj_pa0_attr[] = { NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, }; static const char *trx1_password = "1111111111"; #define TEI_OML 25 /* dummy function to keep gsm_data.c happy */ struct osmo_counter *osmo_counter_alloc(const char *name) { return NULL; } int handle_serial_msg(struct msgb *rx_msg); /* create all objects for an initial configuration */ static int create_objects(struct gsm_bts *bts) { fprintf(stdout, "Crating Objects for minimal config\n"); abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), obj_li_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, sizeof(obj_bbsig0_attr), obj_bbsig0_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, sizeof(obj_pa0_attr), obj_pa0_attr); abis_nm_bs11_create_envaBTSE(bts, 0); abis_nm_bs11_create_envaBTSE(bts, 1); abis_nm_bs11_create_envaBTSE(bts, 2); abis_nm_bs11_create_envaBTSE(bts, 3); abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); sleep(1); abis_nm_bs11_set_trx1_pw(bts, trx1_password); sleep(1); return 0; } static int create_trx1(struct gsm_bts *bts) { uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; uint8_t *cur = bbsig1_attr; struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); if (!trx) trx = gsm_bts_trx_alloc(bts); fprintf(stdout, "Crating Objects for TRX1\n"); abis_nm_bs11_set_trx1_pw(bts, trx1_password); sleep(1); cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, (uint8_t *)trx1_password); memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, sizeof(bbsig1_attr), bbsig1_attr); abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, sizeof(obj_pa0_attr), obj_pa0_attr); abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); return 0; } static char *serial_port = "/dev/ttyUSB0"; static char *fname_safety = "BTSBMC76.SWI"; static char *fname_software = "HS011106.SWL"; static int delay_ms = 0; static int win_size = 8; static int param_disconnect = 0; static int param_restart = 0; static int param_forced = 0; static struct gsm_bts *g_bts; static int file_is_readable(const char *fname) { int rc; struct stat st; rc = stat(fname, &st); if (rc < 0) return 0; if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) return 1; return 0; } static int percent; static int percent_old; /* callback function passed to the ABIS OML code */ static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, void *data, void *param) { if (hook != GSM_HOOK_NM_SWLOAD) return 0; switch (event) { case NM_MT_LOAD_INIT_ACK: fprintf(stdout, "Software Load Initiate ACK\n"); break; case NM_MT_LOAD_INIT_NACK: fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); exit(5); break; case NM_MT_LOAD_END_ACK: if (data) { /* we did a safety load and must activate it */ abis_nm_software_activate(g_bts, fname_safety, swload_cbfn, g_bts); sleep(5); } break; case NM_MT_LOAD_END_NACK: fprintf(stderr, "ERROR: Software Load End NACK\n"); exit(3); break; case NM_MT_ACTIVATE_SW_NACK: fprintf(stderr, "ERROR: Activate Software NACK\n"); exit(4); break; case NM_MT_ACTIVATE_SW_ACK: bs11cfg_state = STATE_NONE; break; case NM_MT_LOAD_SEG_ACK: percent = abis_nm_software_load_status(g_bts); if (percent > percent_old) printf("Software Download Progress: %d%%\n", percent); percent_old = percent; break; } return 0; } static const struct value_string bs11_linkst_names[] = { { 0, "Down" }, { 1, "Up" }, { 2, "Restoring" }, { 0, NULL } }; static const char *linkstate_name(uint8_t linkstate) { return get_value_string(bs11_linkst_names, linkstate); } static const struct value_string mbccu_load_names[] = { { 0, "No Load" }, { 1, "Load BTSCAC" }, { 2, "Load BTSDRX" }, { 3, "Load BTSBBX" }, { 4, "Load BTSARC" }, { 5, "Load" }, { 0, NULL } }; static const char *mbccu_load_name(uint8_t linkstate) { return get_value_string(mbccu_load_names, linkstate); } static const char *bts_phase_name(uint8_t phase) { switch (phase) { case BS11_STATE_WARM_UP: case BS11_STATE_WARM_UP_2: return "Warm Up"; break; case BS11_STATE_LOAD_SMU_SAFETY: return "Load SMU Safety"; break; case BS11_STATE_LOAD_SMU_INTENDED: return "Load SMU Intended"; break; case BS11_STATE_LOAD_MBCCU: return "Load MBCCU"; break; case BS11_STATE_SOFTWARE_RQD: return "Software required"; break; case BS11_STATE_WAIT_MIN_CFG: case BS11_STATE_WAIT_MIN_CFG_2: return "Wait minimal config"; break; case BS11_STATE_MAINTENANCE: return "Maintenance"; break; case BS11_STATE_NORMAL: return "Normal"; break; case BS11_STATE_ABIS_LOAD: return "Abis load"; break; default: return "Unknown"; break; } } static const char *trx_power_name(uint8_t pwr) { switch (pwr) { case BS11_TRX_POWER_GSM_2W: return "2W (GSM)"; case BS11_TRX_POWER_GSM_250mW: return "250mW (GSM)"; case BS11_TRX_POWER_GSM_80mW: return "80mW (GSM)"; case BS11_TRX_POWER_GSM_30mW: return "30mW (GSM)"; case BS11_TRX_POWER_DCS_3W: return "3W (DCS)"; case BS11_TRX_POWER_DCS_1W6: return "1.6W (DCS)"; case BS11_TRX_POWER_DCS_500mW: return "500mW (DCS)"; case BS11_TRX_POWER_DCS_160mW: return "160mW (DCS)"; default: return "unknown value"; } } static const char *pll_mode_name(uint8_t mode) { switch (mode) { case BS11_LI_PLL_LOCKED: return "E1 Locked"; case BS11_LI_PLL_STANDALONE: return "Standalone"; default: return "unknown"; } } static const char *cclk_acc_name(uint8_t acc) { switch (acc) { case 0: /* Out of the demanded +/- 0.05ppm */ return "Medium"; case 1: /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ return "High"; default: return "unknown"; } } static const char *bport_lcfg_name(uint8_t lcfg) { switch (lcfg) { case BS11_LINE_CFG_STAR: return "Star"; case BS11_LINE_CFG_MULTIDROP: return "Multi-Drop"; default: return "unknown"; } } static const char *obj_name(struct abis_om_fom_hdr *foh) { static char retbuf[256]; retbuf[0] = 0; switch (foh->obj_class) { case NM_OC_BS11: strcat(retbuf, "BS11 "); switch (foh->obj_inst.bts_nr) { case BS11_OBJ_PA: sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", foh->obj_inst.ts_nr); break; case BS11_OBJ_LI: sprintf(retbuf+strlen(retbuf), "Line Interface "); break; case BS11_OBJ_CCLK: sprintf(retbuf+strlen(retbuf), "CCLK "); break; } break; case NM_OC_SITE_MANAGER: strcat(retbuf, "SITE MANAGER "); break; case NM_OC_BS11_BPORT: sprintf(retbuf+strlen(retbuf), "BPORT%u ", foh->obj_inst.bts_nr); break; } return retbuf; } static void print_state(struct tlv_parsed *tp) { if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { uint8_t phase, mbccu; if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); printf("PHASE: %u %-20s ", phase & 0xf, bts_phase_name(phase)); } if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); printf("MBCCU0: %-11s MBCCU1: %-11s ", mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); } } if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); } printf("\n"); } static int print_attr(struct tlv_parsed *tp) { if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { printf("\tBS-11 ESN PCB Serial Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); } if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { printf("\tBS-11 ESN Hardware Code Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); } if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { printf("\tBS-11 ESN Firmware Code Number: %s\n", TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); } #if 0 if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { printf("BS-11 Boot Software Version: %s\n", TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); } #endif if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); printf("\tE1 Channel: Port=%u Timeslot=%u ", chan[0], chan[1]); if (chan[2] == 0xff) printf("(Full Slot)\n"); else printf("Subslot=%u\n", chan[2]); } if (TLVP_PRESENT(tp, NM_ATT_TEI)) printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { printf("\tTRX Power: %s\n", trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); } if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { printf("\tPLL Mode: %s\n", pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); } if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); printf("\tPLL Set Value=%d, Work Value=%d\n", vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); } if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); } if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); printf("\tCCLK Type=%d\n", *acc); } if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); printf("\tLine Configuration: %s (%d)\n", bport_lcfg_name(*lcfg), *lcfg); } return 0; } static void cmd_query(void) { struct gsm_bts_trx *trx = g_bts->c0; bs11cfg_state = STATE_QUERY; abis_nm_bs11_get_serno(g_bts); abis_nm_bs11_get_oml_tei_ts(g_bts); abis_nm_bs11_get_pll_mode(g_bts); abis_nm_bs11_get_cclk(g_bts); abis_nm_bs11_get_trx_power(trx); trx = gsm_bts_trx_num(g_bts, 1); if (trx) abis_nm_bs11_get_trx_power(trx); abis_nm_bs11_get_bport_line_cfg(g_bts, 0); abis_nm_bs11_get_bport_line_cfg(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } /* handle a response from the BTS to a GET STATE command */ static int handle_state_resp(enum abis_bs11_phase state) { int rc = 0; switch (state) { case BS11_STATE_WARM_UP: case BS11_STATE_LOAD_SMU_SAFETY: case BS11_STATE_LOAD_SMU_INTENDED: case BS11_STATE_LOAD_MBCCU: break; case BS11_STATE_SOFTWARE_RQD: bs11cfg_state = STATE_SWLOAD; /* send safety load. Use g_bts as private 'param' * argument, so our swload_cbfn can distinguish * a safety load from a regular software */ if (file_is_readable(fname_safety)) rc = abis_nm_software_load(g_bts, 0xff, fname_safety, win_size, param_forced, swload_cbfn, g_bts); else fprintf(stderr, "No valid Safety Load file \"%s\"\n", fname_safety); break; case BS11_STATE_WAIT_MIN_CFG: case BS11_STATE_WAIT_MIN_CFG_2: bs11cfg_state = STATE_SWLOAD; rc = create_objects(g_bts); break; case BS11_STATE_MAINTENANCE: if (command) { if (!strcmp(command, "disconnect")) abis_nm_bs11_factory_logon(g_bts, 0); else if (!strcmp(command, "reconnect")) rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); else if (!strcmp(command, "software") && bs11cfg_state != STATE_SWLOAD) { bs11cfg_state = STATE_SWLOAD; /* send software (FIXME: over A-bis?) */ if (file_is_readable(fname_software)) rc = abis_nm_bs11_load_swl(g_bts, fname_software, win_size, param_forced, swload_cbfn); else fprintf(stderr, "No valid Software file \"%s\"\n", fname_software); } else if (!strcmp(command, "delete-trx1")) { printf("Locing BBSIG and PA objects of TRX1\n"); abis_nm_chg_adm_state(g_bts, NM_OC_BS11, BS11_OBJ_BBSIG, 0, 1, NM_STATE_LOCKED); abis_nm_chg_adm_state(g_bts, NM_OC_BS11, BS11_OBJ_PA, 0, 1, NM_STATE_LOCKED); sleep(1); printf("Deleting BBSIG and PA objects of TRX1\n"); abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "create-trx1")) { create_trx1(g_bts); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-e1-locked")) { abis_nm_bs11_set_pll_locked(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-standalone")) { abis_nm_bs11_set_pll_locked(g_bts, 0); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-setvalue")) { abis_nm_bs11_set_pll(g_bts, atoi(value)); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "pll-workvalue")) { /* To set the work value we need to login as FIELD */ abis_nm_bs11_factory_logon(g_bts, 0); sleep(1); abis_nm_bs11_infield_logon(g_bts, 1); sleep(1); abis_nm_bs11_set_pll(g_bts, atoi(value)); sleep(1); abis_nm_bs11_infield_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "oml-tei")) { abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); command = NULL; } else if (!strcmp(command, "restart")) { abis_nm_bs11_restart(g_bts); command = NULL; } else if (!strcmp(command, "query")) { cmd_query(); } else if (!strcmp(command, "create-bport1")) { abis_nm_bs11_create_bport(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "delete-bport1")) { abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); sleep(1); abis_nm_bs11_delete_bport(g_bts, 1); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport0-star")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport0-multidrop")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } else if (!strcmp(command, "bport1-multidrop")) { abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); sleep(1); abis_nm_bs11_factory_logon(g_bts, 0); command = NULL; } } break; case BS11_STATE_NORMAL: if (command) { if (!strcmp(command, "reconnect")) abis_nm_bs11_factory_logon(g_bts, 0); else if (!strcmp(command, "disconnect")) abis_nm_bs11_bsc_disconnect(g_bts, 0); else if (!strcmp(command, "query")) { cmd_query(); } } else if (param_disconnect) { param_disconnect = 0; abis_nm_bs11_bsc_disconnect(g_bts, 0); if (param_restart) { param_restart = 0; abis_nm_bs11_restart(g_bts); } } break; default: break; } return rc; } /* handle a fully-received message/packet from the RS232 port */ static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) { struct e1inp_sign_link *link = rx_msg->dst; struct abis_om_hdr *oh; struct abis_om_fom_hdr *foh; struct tlv_parsed tp; int rc = -1; #if 0 const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; if (rx_msg->len < LAPD_HDR_LEN + sizeof(struct abis_om_fom_hdr) + sizeof(struct abis_om_hdr)) { if (!memcmp(rx_msg->data + 2, too_fast, sizeof(too_fast))) { fprintf(stderr, "BS11 tells us we're too " "fast, try --delay bigger than %u\n", delay_ms); return -E2BIG; } else fprintf(stderr, "unknown BS11 message\n"); } #endif oh = (struct abis_om_hdr *) msgb_l2(rx_msg); foh = (struct abis_om_fom_hdr *) oh->data; switch (foh->msg_type) { case NM_MT_BS11_LMT_LOGON_ACK: printf("LMT LOGON: ACK\n\n"); if (bs11cfg_state == STATE_NONE) bs11cfg_state = STATE_LOGON_ACK; rc = abis_nm_bs11_get_state(g_bts); break; case NM_MT_BS11_LMT_LOGOFF_ACK: printf("LMT LOGOFF: ACK\n"); exit(0); break; case NM_MT_BS11_GET_STATE_ACK: rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); print_state(&tp); if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); break; case NM_MT_GET_ATTR_RESP: printf("\n%sATTRIBUTES:\n", obj_name(foh)); abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); rc = print_attr(&tp); //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); break; case NM_MT_BS11_SET_ATTR_ACK: printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); rc = 0; break; case NM_MT_BS11_SET_ATTR_NACK: printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", foh->obj_class, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); break; case NM_MT_GET_ATTR_NACK: printf("\n%sGET ATTR NACK\n", obj_name(foh)); break; case NM_MT_BS11_CREATE_OBJ_ACK: printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); break; case NM_MT_BS11_CREATE_OBJ_NACK: printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); break; case NM_MT_BS11_DELETE_OBJ_ACK: printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); break; case NM_MT_BS11_DELETE_OBJ_NACK: printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); break; default: rc = abis_nm_rcvmsg(rx_msg); } if (rc < 0) { perror("ERROR in main loop"); //break; } /* flush the queue of pending messages to be sent. */ abis_nm_queue_send_next(link->trx->bts); if (rc == 1) return rc; switch (bs11cfg_state) { case STATE_NONE: abis_nm_bs11_factory_logon(g_bts, 1); break; case STATE_LOGON_ACK: osmo_timer_schedule(&status_timer, 5, 0); break; default: break; } return rc; } void status_timer_cb(void *data) { abis_nm_bs11_get_state(g_bts); } static void print_banner(void) { printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); } static void print_help(void) { printf("bs11_config [options] [command]\n"); printf("\nSupported options:\n"); printf("\t-h --help\t\t\tPrint this help text\n"); printf("\t-p --port \t\tSpecify serial port\n"); printf("\t-s --software \t\tSpecify Software file\n"); printf("\t-S --safety \t\tSpecify Safety Load file\n"); printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); printf("\t-w --win-size \t\tSpecify Window Size\n"); printf("\t-f --forced\t\t\tForce Software Load\n"); printf("\nSupported commands:\n"); printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); printf("\trestart\t\t\tRestart the BTS\n"); printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); printf("\tpll-setvalue \tSet the PLL set value\n"); printf("\tpll-workvalue \tSet the PLL work value\n"); printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); } static void handle_options(int argc, char **argv) { int option_index = 0; print_banner(); while (1) { int c; static struct option long_options[] = { { "help", 0, 0, 'h' }, { "port", 1, 0, 'p' }, { "software", 1, 0, 's' }, { "safety", 1, 0, 'S' }, { "delay", 1, 0, 'd' }, { "disconnect", 0, 0, 'D' }, { "win-size", 1, 0, 'w' }, { "forced", 0, 0, 'f' }, { "restart", 0, 0, 'r' }, { "debug", 1, 0, 'b'}, }; c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_help(); exit(0); case 'p': serial_port = optarg; break; case 'b': log_parse_category_mask(osmo_stderr_target, optarg); break; case 's': fname_software = optarg; break; case 'S': fname_safety = optarg; break; case 'd': delay_ms = atoi(optarg); break; case 'w': win_size = atoi(optarg); break; case 'D': param_disconnect = 1; break; case 'f': param_forced = 1; break; case 'r': param_disconnect = 1; param_restart = 1; break; default: break; } } if (optind < argc) { command = argv[optind]; if (optind+1 < argc) value = argv[optind+1]; } } static int num_sigint; static void signal_handler(int signal) { fprintf(stdout, "\nsignal %u received\n", signal); switch (signal) { case SIGINT: num_sigint++; abis_nm_bs11_factory_logon(g_bts, 0); if (num_sigint >= 3) exit(0); break; } } static int bs11cfg_sign_link(struct msgb *msg) { msg->dst = oml_link; return abis_nm_bs11cfg_rcvmsg(msg); } struct e1inp_line_ops bs11cfg_e1inp_line_ops = { .sign_link = bs11cfg_sign_link, }; static const struct log_info_cat bs11_categories[] = { [DNM] = { .name = "DNM", .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_INFO, }, }; const struct log_info log_info = { .cat = bs11_categories, .num_cat = ARRAY_SIZE(bs11_categories), }; extern int bts_model_bs11_init(void); extern void *tall_fle_ctx; int main(int argc, char **argv) { struct gsm_network *gsmnet; struct e1inp_line *line; tall_bs11cfg_ctx = talloc_named_const(NULL, 0, "bs11-config"); tall_fle_ctx = talloc_named_const(tall_bs11cfg_ctx, 0, "bs11_file_list_entry"); msgb_talloc_ctx_init(tall_bs11cfg_ctx, 0); osmo_init_logging2(tall_bs11cfg_ctx, &log_info); handle_options(argc, argv); bts_model_bs11_init(); gsmnet = gsm_network_init(tall_bs11cfg_ctx); if (!gsmnet) { fprintf(stderr, "Unable to allocate gsm network\n"); exit(1); } g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, HARDCODED_BSIC); /* Override existing OML callback handler to set our own. */ g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; libosmo_abis_init(tall_bs11cfg_ctx); /* Initialize virtual E1 line over rs232. */ line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); if (!line) { fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); exit(1); } /* set the serial port. */ bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; line->driver = e1inp_driver_find("rs232"); if (!line->driver) { fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); exit(1); } e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); /* configure and create signalling link for OML. */ e1inp_ts_config_sign(&line->ts[0], line); g_bts->oml_link = oml_link = e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, g_bts->c0, TEI_OML, 0); e1inp_line_update(line); signal(SIGINT, &signal_handler); abis_nm_bs11_factory_logon(g_bts, 1); //abis_nm_bs11_get_serno(g_bts); osmo_timer_setup(&status_timer, status_timer_cb, NULL); while (1) { if (osmo_select_main(0) < 0) break; } abis_nm_bs11_factory_logon(g_bts, 0); exit(0); } /* Stub */ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } /* Stub */ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } osmo-bsc-1.3.0/src/utils/isdnsync.c000066400000000000000000000110571332665256100171770ustar00rootroot00000000000000/* isdnsync.c * * Author Andreas Eversberg * * 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 "mISDNif.h" #define MISDN_OLD_AF_COMPATIBILITY #define AF_COMPATIBILITY_FUNC #include "compat_af_isdn.h" int card = 0; int sock = -1; int mISDN_open(void) { int fd, ret; struct mISDN_devinfo devinfo; struct sockaddr_mISDN l2addr; fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); if (fd < 0) { fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); return fd; } devinfo.id = card; ret = ioctl(fd, IMGETDEVINFO, &devinfo); if (ret < 0) { fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); close(fd); return ret; } close(fd); if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); return ret; } fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); if (fd < 0) { fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); return fd; } l2addr.family = AF_ISDN; l2addr.dev = card; l2addr.channel = 0; l2addr.sapi = 0; l2addr.tei = 0; ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); if (ret < 0) { fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); close(fd); return ret; } sock = fd; return sock; } void mISDN_handle(void) { int ret; fd_set rfd; struct timeval tv; struct sockaddr_mISDN addr; socklen_t alen; unsigned char buffer[2048]; struct mISDNhead *hh = (struct mISDNhead *)buffer; int l1 = 0, l2 = 0, tei = 0; while(1) { again: FD_ZERO(&rfd); FD_SET(sock, &rfd); tv.tv_sec = 2; tv.tv_usec = 0; ret = select(sock+1, &rfd, NULL, NULL, &tv); if (ret < 0) { if (errno == EINTR) continue; fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); break; } if (FD_ISSET(sock, &rfd)) { alen = sizeof(addr); ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); if (ret < 0) { fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); } else if (ret < MISDN_HEADER_LEN) { fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); } else { switch(hh->prim) { case MPH_ACTIVATE_IND: case PH_ACTIVATE_IND: if (!l1) { printf("PH_ACTIVATE\n"); printf("*** Sync available from interface :-)\n"); l1 = 1; } goto again; break; case MPH_DEACTIVATE_IND: case PH_DEACTIVATE_IND: if (l1) { printf("PH_DEACTIVATE\n"); printf("*** Lost sync on interface :-(\n"); l1 = 0; } goto again; break; case DL_ESTABLISH_IND: case DL_ESTABLISH_CNF: printf("DL_ESTABLISH\n"); l2 = 1; goto again; break; case DL_RELEASE_IND: case DL_RELEASE_CNF: printf("DL_RELEASE\n"); l2 = 0; goto again; break; case DL_INFORMATION_IND: printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); tei = 1; break; default: // printf("prim %x\n", hh->prim); goto again; } } } if (tei && !l2) { hh->prim = DL_ESTABLISH_REQ; printf("-> activating layer 2\n"); sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); } } } int main(int argc, char *argv[]) { int ret; if (argc <= 1) { printf("Usage: %s \n\n", argv[0]); printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); return(0); } card = atoi(argv[1]); init_af_isdn(); if ((ret = mISDN_open() < 0)) return(ret); mISDN_handle(); close(sock); return 0; } osmo-bsc-1.3.0/src/utils/meas_db.c000066400000000000000000000222671332665256100167440ustar00rootroot00000000000000/* Routines for storing measurement reports in SQLite3 database */ /* (C) 2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 "meas_db.h" #define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)" #define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)" #define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?" struct meas_db_state { sqlite3 *db; sqlite3_stmt *stmt_ins_ud; sqlite3_stmt *stmt_ins_mr; sqlite3_stmt *stmt_upd_mr; }; /* macros to check for SQLite3 result codes */ #define _SCK_OK(db, call, exp) \ do { \ int rc = call; \ if (rc != exp) { \ fprintf(stderr,"SQL Error in line %u: %s\n", \ __LINE__, sqlite3_errmsg(db)); \ goto err_io; \ } \ } while (0) #define SCK_OK(db, call) _SCK_OK(db, call, SQLITE_OK) #define SCK_DONE(db, call) _SCK_OK(db, call, SQLITE_DONE) static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx, int uplink, const struct gsm_meas_rep_unidir *ud) { unsigned long rowid; SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2, rxlev2dbm(ud->full.rx_lev))); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 3, rxlev2dbm(ud->sub.rx_lev))); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 4, ud->full.rx_qual)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 5, ud->sub.rx_qual)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 6, dtx)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 7, uplink)); SCK_DONE(st->db, sqlite3_step(st->stmt_ins_ud)); SCK_OK(st->db, sqlite3_reset(st->stmt_ins_ud)); return sqlite3_last_insert_rowid(st->db); err_io: exit(1); } /* insert a measurement report into the database */ int meas_db_insert(struct meas_db_state *st, const char *imsi, const char *name, unsigned long timestamp, const char *scenario, const struct gsm_meas_rep *mr) { int rc; sqlite3_int64 rowid, ul_rowid, dl_rowid; SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp)); if (imsi) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2, imsi, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2)); if (name) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3, name, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3)); if (scenario) SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4, scenario, -1, SQLITE_STATIC)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power)); if (mr->flags & MEAS_REP_F_MS_TO) SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset)); else SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7)); if (mr->flags & MEAS_REP_F_FPC) SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1)); else SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0)); if (mr->flags & MEAS_REP_F_MS_L1) { SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9, mr->ms_l1.pwr)); SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10, mr->ms_l1.ta)); } SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr)); SCK_OK(st->db, sqlite3_reset(st->stmt_ins_mr)); rowid = sqlite3_last_insert_rowid(st->db); /* insert uplink measurement */ ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX, 1, &mr->ul); SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid)); /* insert downlink measurement, if present */ if (mr->flags & MEAS_REP_F_DL_VALID) { dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX, 0, &mr->dl); SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid)); } else SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2)); /* update meas_rep with the id's of the unidirectional * measurements */ SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 3, rowid)); SCK_DONE(st->db, sqlite3_step(st->stmt_upd_mr)); SCK_OK(st->db, sqlite3_reset(st->stmt_upd_mr)); return 0; err_io: return -EIO; } int meas_db_begin(struct meas_db_state *st) { SCK_OK(st->db, sqlite3_exec(st->db, "BEGIN", NULL, NULL, NULL)); return 0; err_io: return -EIO; } int meas_db_commit(struct meas_db_state *st) { SCK_OK(st->db, sqlite3_exec(st->db, "COMMIT", NULL, NULL, NULL)); return 0; err_io: return -EIO; } static const char *create_stmts[] = { "CREATE TABLE IF NOT EXISTS meas_rep (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "time TIMESTAMP," "imsi TEXT," "name TEXT," "scenario TEXT," "nr INTEGER," "bs_power INTEGER NOT NULL," "ms_timing_offset INTEGER," "fpc INTEGER NOT NULL DEFAULT 0," "ul_unidir INTEGER REFERENCES meas_rep_unidir(id)," "dl_unidir INTEGER REFERENCES meas_rep_unidir(id)," "ms_l1_pwr INTEGER," "ms_l1_ta INTEGER" ")", "CREATE TABLE IF NOT EXISTS meas_rep_unidir (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "meas_id INTEGER NOT NULL REFERENCES meas_rep(id)," "rx_lev_full INTEGER NOT NULL," "rx_lev_sub INTEGER NOT NULL," "rx_qual_full INTEGER NOT NULL," "rx_qual_sub INTEGER NOT NULL," "dtx BOOLEAN NOT NULL DEFAULT 0," "uplink BOOLEAN NOT NULL" ")", "CREATE VIEW IF NOT EXISTS path_loss AS " "SELECT " "meas_rep.id, " "datetime(time,'unixepoch') AS timestamp, " "imsi, " "name, " "scenario, " "ms_timing_offset, " "ms_l1_ta, " "fpc, " "ms_l1_pwr, " "ud_ul.rx_lev_full AS ul_rx_lev_full, " "ms_l1_pwr-ud_ul.rx_lev_full AS ul_path_loss_full, " "ud_ul.rx_lev_sub ul_rx_lev_sub, " "ms_l1_pwr-ud_ul.rx_lev_sub AS ul_path_loss_sub, " "ud_ul.rx_qual_full AS ul_rx_qual_full, " "ud_ul.rx_qual_sub AS ul_rx_qual_sub, " "bs_power, " "ud_dl.rx_lev_full AS dl_rx_lev_full, " "bs_power-ud_dl.rx_lev_full AS dl_path_loss_full, " "ud_dl.rx_lev_sub AS dl_rx_lev_sub, " "bs_power-ud_dl.rx_lev_sub AS dl_path_loss_sub, " "ud_dl.rx_qual_full AS dl_rx_qual_full, " "ud_dl.rx_qual_sub AS dl_rx_qual_sub " "FROM " "meas_rep, " "meas_rep_unidir AS ud_dl, " "meas_rep_unidir AS ud_ul " "WHERE " "ud_ul.id = meas_rep.ul_unidir AND " "ud_dl.id = meas_rep.dl_unidir", "CREATE VIEW IF NOT EXISTS overview AS " "SELECT " "id," "timestamp," "imsi," "name," "scenario," "ms_l1_pwr," "ul_rx_lev_full," "ul_path_loss_full," "ul_rx_qual_full," "bs_power," "dl_rx_lev_full," "dl_path_loss_full," "dl_rx_qual_full " "FROM path_loss", }; static int check_create_tbl(struct meas_db_state *st) { int i, rc; for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i], NULL, NULL, NULL)); } return 0; err_io: return -EIO; } #define PREP_CHK(db, stmt, ptr) \ do { \ int rc; \ rc = sqlite3_prepare_v2(db, stmt, strlen(stmt)+1, \ ptr, NULL); \ if (rc != SQLITE_OK) { \ fprintf(stderr, "Error during prepare of '%s': %s\n", \ stmt, sqlite3_errmsg(db)); \ goto err_io; \ } \ } while (0) struct meas_db_state *meas_db_open(void *ctx, const char *fname) { int rc; struct meas_db_state *st = talloc_zero(ctx, struct meas_db_state); if (!st) return NULL; rc = sqlite3_open_v2(fname, &st->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL); if (rc != SQLITE_OK) { fprintf(stderr, "Unable to open DB: %s\n", sqlite3_errmsg(st->db)); goto err_io; } rc = check_create_tbl(st); PREP_CHK(st->db, INS_MR, &st->stmt_ins_mr); PREP_CHK(st->db, INS_UD, &st->stmt_ins_ud); PREP_CHK(st->db, UPD_MR, &st->stmt_upd_mr); return st; err_io: talloc_free(st); return NULL; } void meas_db_close(struct meas_db_state *st) { if (sqlite3_finalize(st->stmt_ins_mr) != SQLITE_OK) fprintf(stderr, "DB insert measurement report finalize error: %s\n", sqlite3_errmsg(st->db)); if (sqlite3_finalize(st->stmt_ins_ud) != SQLITE_OK) fprintf(stderr, "DB insert unidir finalize error: %s\n", sqlite3_errmsg(st->db)); if (sqlite3_finalize(st->stmt_upd_mr) != SQLITE_OK) fprintf(stderr, "DB update measurement report finalize error: %s\n", sqlite3_errmsg(st->db)); if (sqlite3_close(st->db) != SQLITE_OK) fprintf(stderr, "Unable to close DB, abandoning.\n"); talloc_free(st); } osmo-bsc-1.3.0/src/utils/meas_db.h000066400000000000000000000007201332665256100167370ustar00rootroot00000000000000#ifndef OPENBSC_MEAS_DB_H #define OPENBSC_MEAS_DB_H struct meas_db_state; struct meas_db_state *meas_db_open(void *ctx, const char *fname); void meas_db_close(struct meas_db_state *st); int meas_db_begin(struct meas_db_state *st); int meas_db_commit(struct meas_db_state *st); int meas_db_insert(struct meas_db_state *st, const char *imsi, const char *name, unsigned long timestamp, const char *scenario, const struct gsm_meas_rep *mr); #endif osmo-bsc-1.3.0/src/utils/meas_json.c000066400000000000000000000110711332665256100173170ustar00rootroot00000000000000/* Convert measurement report feed into JSON feed printed to stdout. * Each measurement report is printed as a separae JSON root entry. * All measurement reports are separated by a new line. */ /* (C) 2015 by Alexander Chemeris * With parts of code adopted from different places in OpenBSC. * * 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 static void print_meas_rep_uni_json(struct gsm_meas_rep_unidir *mru) { printf("\"RXL-FULL\":%d, \"RXL-SUB\":%d, ", rxlev2dbm(mru->full.rx_lev), rxlev2dbm(mru->sub.rx_lev)); printf("\"RXQ-FULL\":%d, \"RXQ-SUB\":%d", mru->full.rx_qual, mru->sub.rx_qual); } static void print_meas_rep_json(struct gsm_meas_rep *mr) { int i; printf("\"NR\":%d", mr->nr); if (mr->flags & MEAS_REP_F_DL_DTX) printf(", \"DTXd\":true"); printf(", \"UL_MEAS\":{"); print_meas_rep_uni_json(&mr->ul); printf("}"); printf(", \"BS_POWER\":%d", mr->bs_power); if (mr->flags & MEAS_REP_F_MS_TO) printf(", \"MS_TO\":%d", mr->ms_timing_offset); if (mr->flags & MEAS_REP_F_MS_L1) { printf(", \"L1_MS_PWR\":%d", mr->ms_l1.pwr); printf(", \"L1_FPC\":%s", mr->flags & MEAS_REP_F_FPC ? "true" : "false"); printf(", \"L1_TA\":%u", mr->ms_l1.ta); } if (mr->flags & MEAS_REP_F_UL_DTX) printf(", \"DTXu\":true"); if (mr->flags & MEAS_REP_F_BA1) printf(", \"BA1\":true"); if (mr->flags & MEAS_REP_F_DL_VALID) { printf(", \"DL_MEAS\":{"); print_meas_rep_uni_json(&mr->dl); printf("}"); } if (mr->num_cell == 7) return; printf(", \"NUM_NEIGH\":%u, \"NEIGH\":[", mr->num_cell); for (i = 0; i < mr->num_cell; i++) { struct gsm_meas_rep_cell *mrc = &mr->cell[i]; if (i!=0) printf(", "); printf("{\"IDX\":%u, \"ARFCN\":%u, \"BSIC\":%u, \"POWER\":%d}", mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); } printf("]"); } static void print_chan_info_json(struct meas_feed_meas *mfm) { printf("\"lchan_type\":\"%s\", \"pchan_type\":\"%s\", " "\"bts_nr\":%d, \"trx_nr\":%d, \"ts_nr\":%d, \"ss_nr\":%d", gsm_lchant_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type), mfm->bts_nr, mfm->trx_nr, mfm->ts_nr, mfm->ss_nr); } static void print_meas_feed_json(struct meas_feed_meas *mfm) { time_t now = time(NULL); printf("{"); printf("\"time\":%ld, \"imsi\":\"%s\", \"name\":\"%s\", \"scenario\":\"%s\", ", now, mfm->imsi, mfm->name, mfm->scenario); switch (mfm->hdr.version) { case 1: printf("\"chan_info\":{"); print_chan_info_json(mfm); printf("}, "); /* no break, fall to version 0 */ case 0: printf("\"meas_rep\":{"); print_meas_rep_json(&mfm->mr); printf("}"); break; } printf("}\n"); } static int handle_meas(struct msgb *msg) { struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); print_meas_feed_json(mfm); return 0; } static int handle_msg(struct msgb *msg) { struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); if (mfh->version != MEAS_FEED_VERSION) return -EINVAL; switch (mfh->msg_type) { case MEAS_FEED_MEAS: handle_meas(msg); break; default: break; } return 0; } static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; if (what & BSC_FD_READ) { struct msgb *msg = msgb_alloc(1024, "UDP Rx"); rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); if (rc < 0) return rc; msgb_put(msg, rc); handle_msg(msg); msgb_free(msg); } return 0; } int main(int argc, char **argv) { int rc; struct osmo_fd udp_ofd; udp_ofd.cb = udp_fd_cb; rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); if (rc < 0) exit(1); while (1) { osmo_select_main(0); }; exit(0); } osmo-bsc-1.3.0/src/utils/meas_pcap2db.c000066400000000000000000000056261332665256100176720ustar00rootroot00000000000000/* read PCAP file with meas_feed data and write it to sqlite3 database */ /* (C) 2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 #include "meas_db.h" static struct meas_db_state *db; static void handle_mfm(const struct pcap_pkthdr *h, const struct meas_feed_meas *mfm) { const char *scenario; if (strlen(mfm->scenario)) scenario = mfm->scenario; else scenario = NULL; meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec, scenario, &mfm->mr); } static void pcap_cb(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { const char *cur = bytes; const struct iphdr *ip; const struct udphdr *udp; const struct meas_feed_meas *mfm; uint16_t udplen; if (h->caplen < 14+20+8) return; /* Check if there is IPv4 in the Ethernet */ if (cur[12] != 0x08 || cur[13] != 0x00) return; cur += 14; /* ethernet header */ ip = (struct iphdr *) cur; if (ip->version != 4) return; cur += ip->ihl * 4; if (ip->protocol != IPPROTO_UDP) return; udp = (struct udphdr *) cur; if (udp->dest != htons(8888)) return; udplen = ntohs(udp->len); if (udplen != sizeof(*udp) + sizeof(*mfm)) return; cur += sizeof(*udp); mfm = (const struct meas_feed_meas *) cur; handle_mfm(h, mfm); } int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE+1]; char *pcap_fname, *db_fname; pcap_t *pc; int rc; if (argc < 3) { fprintf(stderr, "You need to specify PCAP and database file\n"); exit(2); } pcap_fname = argv[1]; db_fname = argv[2]; pc = pcap_open_offline(pcap_fname, errbuf); if (!pc) { fprintf(stderr, "Cannot open %s: %s\n", pcap_fname, errbuf); exit(1); } db = meas_db_open(NULL, db_fname); if (!db) exit(0); rc = meas_db_begin(db); if (rc < 0) { fprintf(stderr, "Error during BEGIN\n"); exit(1); } pcap_loop(pc, 0 , pcap_cb, NULL); meas_db_commit(db); exit(0); } osmo-bsc-1.3.0/src/utils/meas_udp2db.c000066400000000000000000000053331332665256100175320ustar00rootroot00000000000000/* liesten to meas_feed on UDP and write it to sqlite3 database */ /* (C) 2012 by Harald Welte * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 "meas_db.h" static struct osmo_fd udp_ofd; static struct meas_db_state *db; static int handle_msg(struct msgb *msg) { struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); const char *scenario; time_t now = time(NULL); if (mfh->version != MEAS_FEED_VERSION) return -EINVAL; if (mfh->msg_type != MEAS_FEED_MEAS) return -EINVAL; if (strlen(mfm->scenario)) scenario = mfm->scenario; else scenario = NULL; meas_db_insert(db, mfm->imsi, mfm->name, now, scenario, &mfm->mr); return 0; } static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; if (what & BSC_FD_READ) { struct msgb *msg = msgb_alloc(1024, "UDP Rx"); rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); if (rc < 0) return rc; msgb_put(msg, rc); handle_msg(msg); msgb_free(msg); } return 0; } int main(int argc, char **argv) { char *db_fname; int rc; msgb_talloc_ctx_init(NULL, 0); if (argc < 2) { fprintf(stderr, "You have to specify the database file name\n"); exit(2); } db_fname = argv[1]; udp_ofd.cb = udp_fd_cb; rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); if (rc < 0) { fprintf(stderr, "Unable to create UDP listen socket\n"); exit(1); } db = meas_db_open(NULL, db_fname); if (!db) { fprintf(stderr, "Unable to open database\n"); exit(1); } /* FIXME: timer-based BEGIN/COMMIT */ while (1) { osmo_select_main(0); }; meas_db_close(db); exit(0); } osmo-bsc-1.3.0/src/utils/meas_vis.c000066400000000000000000000142001332665256100171440ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include struct ms_state_uni { CDKSLIDER *cdk; CDKLABEL *cdk_label; time_t last_update; char label[32]; char *_lbl[1]; }; struct ms_state { struct llist_head list; char name[31+1]; char imsi[15+1]; struct gsm_meas_rep mr; struct ms_state_uni ul; struct ms_state_uni dl; }; struct state { struct osmo_fd udp_ofd; struct llist_head ms_list; CDKSCREEN *cdkscreen; WINDOW *curses_win; CDKLABEL *cdk_title; char *title; CDKLABEL *cdk_header; char header[256]; }; static struct state g_st; struct ms_state *find_ms(const char *imsi) { struct ms_state *ms; llist_for_each_entry(ms, &g_st.ms_list, list) { if (!strcmp(ms->imsi, imsi)) return ms; } return NULL; } static struct ms_state *find_alloc_ms(const char *imsi) { struct ms_state *ms; ms = find_ms(imsi); if (!ms) { ms = talloc_zero(NULL, struct ms_state); osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi)); ms->ul._lbl[0] = ms->ul.label; ms->dl._lbl[0] = ms->dl.label; llist_add_tail(&ms->list, &g_st.ms_list); } return ms; } static int handle_meas(struct msgb *msg) { struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); struct ms_state *ms = find_alloc_ms(mfm->imsi); time_t now = time(NULL); osmo_strlcpy(ms->name, mfm->name, sizeof(ms->name)); memcpy(&ms->mr, &mfm->mr, sizeof(ms->mr)); ms->ul.last_update = now; if (ms->mr.flags & MEAS_REP_F_DL_VALID) ms->dl.last_update = now; /* move to head of list */ llist_del(&ms->list); llist_add(&ms->list, &g_st.ms_list); return 0; } static int handle_msg(struct msgb *msg) { struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); if (mfh->version != MEAS_FEED_VERSION) return -EINVAL; switch (mfh->msg_type) { case MEAS_FEED_MEAS: handle_meas(msg); break; default: break; } return 0; } static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) { int rc; if (what & BSC_FD_READ) { struct msgb *msg = msgb_alloc(1024, "UDP Rx"); rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); if (rc < 0) return rc; msgb_put(msg, rc); handle_msg(msg); msgb_free(msg); } return 0; } static void destroy_dir(struct ms_state_uni *uni) { if (uni->cdk) { destroyCDKSlider(uni->cdk); uni->cdk = NULL; } if (uni->cdk_label) { destroyCDKLabel(uni->cdk_label); uni->cdk_label = NULL; } } #define DIR_UL 0 #define DIR_DL 1 static const char *dir_str[2] = { [DIR_UL] = "UL", [DIR_DL] = "DL", }; static int colpair_by_qual(uint8_t rx_qual) { if (rx_qual == 0) return 24; else if (rx_qual <= 4) return 32; else return 16; } static int colpair_by_lev(int rx_lev) { if (rx_lev < -95) return 16; else if (rx_lev < -80) return 32; else return 24; } void write_uni(struct ms_state *ms, struct ms_state_uni *msu, struct gsm_rx_lev_qual *lq, int dir, int row) { char label[128]; time_t now = time(NULL); int qual_col = colpair_by_qual(lq->rx_qual); int lev_col = colpair_by_lev(rxlev2dbm(lq->rx_lev)); int color, pwr; if (dir == DIR_UL) { pwr = ms->mr.ms_l1.pwr; } else { pwr = ms->mr.bs_power; } color = A_REVERSE | COLOR_PAIR(lev_col) | ' '; snprintf(label, sizeof(label), "%s %s ", ms->imsi, dir_str[dir]); msu->cdk = newCDKSlider(g_st.cdkscreen, 0, row, NULL, label, color, COLS-40, rxlev2dbm(lq->rx_lev), -110, -47, 1, 2, FALSE, FALSE); //IsVisibleObj(ms->ul.cdk) = FALSE; snprintf(msu->label, sizeof(msu->label), "%1d %3d %2u %2d %4u", qual_col, lq->rx_qual, qual_col, pwr, ms->mr.ms_l1.ta, ms->mr.ms_timing_offset, now - msu->last_update); msu->cdk_label = newCDKLabel(g_st.cdkscreen, RIGHT, row, msu->_lbl, 1, FALSE, FALSE); } static void update_sliders(void) { int num_vis_sliders = 0; struct ms_state *ms; #define HEADER_LINES 2 /* remove all sliders */ llist_for_each_entry(ms, &g_st.ms_list, list) { destroy_dir(&ms->ul); destroy_dir(&ms->dl); } llist_for_each_entry(ms, &g_st.ms_list, list) { struct gsm_rx_lev_qual *lq; unsigned int row = HEADER_LINES + num_vis_sliders*3; if (ms->mr.flags & MEAS_REP_F_UL_DTX) lq = &ms->mr.ul.sub; else lq = &ms->mr.ul.full; write_uni(ms, &ms->ul, lq, DIR_UL, row); if (ms->mr.flags & MEAS_REP_F_DL_DTX) lq = &ms->mr.dl.sub; else lq = &ms->mr.dl.full; write_uni(ms, &ms->dl, lq, DIR_DL, row+1); num_vis_sliders++; if (num_vis_sliders >= LINES/3) break; } refreshCDKScreen(g_st.cdkscreen); } const struct value_string col_strs[] = { { COLOR_WHITE, "white" }, { COLOR_RED, "red" }, { COLOR_GREEN, "green" }, { COLOR_YELLOW, "yellow" }, { COLOR_BLUE, "blue" }, { COLOR_MAGENTA,"magenta" }, { COLOR_CYAN, "cyan" }, { COLOR_BLACK, "black" }, { 0, NULL } }; int main(int argc, char **argv) { int rc; char *header[1]; char *title[1]; msgb_talloc_ctx_init(NULL, 0); printf("sizeof(gsm_meas_rep)=%u\n", sizeof(struct gsm_meas_rep)); printf("sizeof(meas_feed_meas)=%u\n", sizeof(struct meas_feed_meas)); INIT_LLIST_HEAD(&g_st.ms_list); g_st.curses_win = initscr(); g_st.cdkscreen = initCDKScreen(g_st.curses_win); initCDKColor(); g_st.title = "OpenBSC link quality monitor"; title[0] = g_st.title; g_st.cdk_title = newCDKLabel(g_st.cdkscreen, CENTER, 0, title, 1, FALSE, FALSE); snprintf(g_st.header, sizeof(g_st.header), "Q Pwr TA TO Time"); header[0] = g_st.header; g_st.cdk_header = newCDKLabel(g_st.cdkscreen, RIGHT, 1, header, 1, FALSE, FALSE); #if 0 int i; for (i = 0; i < 64; i++) { short f, b; pair_content(i, &f, &b); attron(COLOR_PAIR(i)); printw("%u: %u (%s) ", i, f, get_value_string(col_strs, f)); printw("%u (%s)\n\r", b, get_value_string(col_strs, b)); } refresh(); getch(); exit(0); #endif g_st.udp_ofd.cb = udp_fd_cb; rc = osmo_sock_init_ofd(&g_st.udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); if (rc < 0) exit(1); while (1) { osmo_select_main(0); update_sliders(); }; exit(0); } osmo-bsc-1.3.0/src/utils/stubs.c000066400000000000000000000020111332665256100164730ustar00rootroot00000000000000/* Stubs required for linking */ /* (C) 2018 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 struct gsm_bts_trx_ts; struct msgb; bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { /* No TS init required here. */ return true; } int abis_rsl_rcvmsg(struct msgb *msg) { /* No RSL handling here */ return 0; } osmo-bsc-1.3.0/tests/000077500000000000000000000000001332665256100144105ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/Makefile.am000066400000000000000000000050171332665256100164470ustar00rootroot00000000000000SUBDIRS = \ bsc \ codec_pref \ gsm0408 \ abis \ subscr \ nanobts_omlattr \ handover \ $(NULL) # 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) \ vty_test_runner.py \ ctrl_test_runner.py \ handover_cfg.vty \ $(NULL) TESTSUITE = $(srcdir)/testsuite DISTCLEANFILES = \ atconfig \ $(NULL) if ENABLE_EXT_TESTS python-tests: $(BUILT_SOURCES) $(MAKE) vty-test osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v rm -f $(top_builddir)/sms.db $(top_builddir)/gsn_restart $(top_builddir)/gtphub_restart_count # To update the VTY script from current application behavior, # pass -u to vty_script_runner.py by doing: # make vty-test U=-u vty-test: osmo_verify_transcript_vty.py -v \ -n OsmoBSC -p 4242 \ -r "$(top_builddir)/src/osmo-bsc/osmo-bsc -c $(top_srcdir)/doc/examples/osmo-bsc/osmo-bsc-minimal.cfg" \ $(U) $(srcdir)/*.vty else python-tests: $(BUILT_SOURCES) echo "Not running python-based tests (determined at configure-time)" endif check-local: atconfig $(TESTSUITE) $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) $(MAKE) $(AM_MAKEFLAGS) python-tests 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 $@ osmo-bsc-1.3.0/tests/abis/000077500000000000000000000000001332665256100153265ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/abis/Makefile.am000066400000000000000000000010671332665256100173660ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) EXTRA_DIST = \ abis_test.ok \ $(NULL) noinst_PROGRAMS = \ abis_test \ $(NULL) abis_test_SOURCES = \ abis_test.c \ $(NULL) abis_test_LDADD = \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(NULL) osmo-bsc-1.3.0/tests/abis/abis_test.c000066400000000000000000000117131332665256100174520ustar00rootroot00000000000000/* * (C) 2012 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include static const uint8_t load_config[] = { 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, 0x30, 0x00, 0x42, 0x12, 0x00, 0x08, 0x31, 0x36, 0x38, 0x64, 0x34, 0x37, 0x32, 0x00, 0x13, 0x00, 0x0b, 0x76, 0x32, 0x30, 0x30, 0x62, 0x31, 0x34, 0x33, 0x64, 0x31, 0x00 }; static void test_sw_selection(void) { struct abis_nm_sw_desc descr[8], tmp; uint16_t len0, len1; int rc, pos; rc = abis_nm_get_sw_conf(load_config, ARRAY_SIZE(load_config), &descr[0], ARRAY_SIZE(descr)); if (rc != 2) { printf("%s(): FAILED to parse the File Id/File version: %d\n", __func__, rc); abort(); } len0 = abis_nm_sw_desc_len(&descr[0], true); printf("len: %u\n", len0); printf("file_id: %s\n", osmo_hexdump(descr[0].file_id, descr[0].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[0].file_version, descr[0].file_version_len)); len1 = abis_nm_sw_desc_len(&descr[1], true); printf("len: %u\n", len1); printf("file_id: %s\n", osmo_hexdump(descr[1].file_id, descr[1].file_id_len)); printf("file_ver: %s\n", osmo_hexdump(descr[1].file_version, descr[1].file_version_len)); /* start */ pos = abis_nm_select_newest_sw(descr, rc); if (pos != 1) { printf("Selected the wrong version: %d\n", pos); abort(); } printf("SELECTED: %d\n", pos); /* shuffle */ tmp = descr[0]; descr[0] = descr[1]; descr[1] = tmp; pos = abis_nm_select_newest_sw(descr, rc); if (pos != 0) { printf("Selected the wrong version: %d\n", pos); abort(); } printf("SELECTED: %d\n", pos); printf("%s(): OK\n", __func__); } struct test_abis_nm_ipaccess_cgi { struct osmo_plmn_id plmn; uint16_t lac; uint16_t cell_identity; const char *expect; }; static const struct test_abis_nm_ipaccess_cgi test_abis_nm_ipaccess_cgi_data[] = { { .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, .lac = 3, .cell_identity = 4, .expect = "00f120" "0003" "0004", }, { .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = true }, .lac = 3, .cell_identity = 4, .expect = "002100" "0003" "0004", }, { .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = false }, .lac = 0, .cell_identity = 0, .expect = "00f000" "0000" "0000", }, { .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = true }, .lac = 0, .cell_identity = 0, .expect = "000000" "0000" "0000", }, { .plmn = { .mcc = 999, .mnc = 999, .mnc_3_digits = false }, .lac = 65535, .cell_identity = 65535, .expect = "999999" "ffff" "ffff", }, { .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = false }, .lac = 0xabcd, .cell_identity = 0x2345, .expect = "09f909" "abcd" "2345", }, { .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = true }, .lac = 0xabcd, .cell_identity = 0x2345, .expect = "090990" "abcd" "2345", }, }; static void test_abis_nm_ipaccess_cgi() { int i; bool pass = true; for (i = 0; i < ARRAY_SIZE(test_abis_nm_ipaccess_cgi_data); i++) { struct gsm_network net; struct gsm_bts bts; const struct test_abis_nm_ipaccess_cgi *t = &test_abis_nm_ipaccess_cgi_data[i]; uint8_t result_buf[7] = {}; char *result; bool ok; net.plmn = t->plmn; bts.network = &net; bts.location_area_code = t->lac; bts.cell_identity = t->cell_identity; abis_nm_ipaccess_cgi(result_buf, &bts); result = osmo_hexdump_nospc(result_buf, sizeof(result_buf)); ok = (strcmp(result, t->expect) == 0); printf("%s[%d]: result=%s %s\n", __func__, i, result, ok ? "pass" : "FAIL"); pass = pass && ok; } OSMO_ASSERT(pass); } static const struct log_info_cat log_categories[] = { }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { osmo_init_logging2(NULL, &log_info); test_sw_selection(); test_abis_nm_ipaccess_cgi(); return EXIT_SUCCESS; } struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { OSMO_ASSERT(0); } bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; } osmo-bsc-1.3.0/tests/abis/abis_test.ok000066400000000000000000000011551332665256100176400ustar00rootroot00000000000000len: 26 file_id: 31 36 38 64 34 37 32 00 file_ver: 76 32 30 30 62 31 34 33 64 30 00 len: 26 file_id: 31 36 38 64 34 37 32 00 file_ver: 76 32 30 30 62 31 34 33 64 31 00 SELECTED: 1 SELECTED: 0 test_sw_selection(): OK test_abis_nm_ipaccess_cgi[0]: result=00f12000030004 pass test_abis_nm_ipaccess_cgi[1]: result=00210000030004 pass test_abis_nm_ipaccess_cgi[2]: result=00f00000000000 pass test_abis_nm_ipaccess_cgi[3]: result=00000000000000 pass test_abis_nm_ipaccess_cgi[4]: result=999999ffffffff pass test_abis_nm_ipaccess_cgi[5]: result=09f909abcd2345 pass test_abis_nm_ipaccess_cgi[6]: result=090990abcd2345 pass osmo-bsc-1.3.0/tests/atlocal.in000066400000000000000000000000001332665256100163450ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/bsc/000077500000000000000000000000001332665256100151575ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/bsc/Makefile.am000066400000000000000000000027261332665256100172220ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOLEGACYMGCP_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) EXTRA_DIST = \ bsc_test.ok \ $(NULL) noinst_PROGRAMS = \ bsc_test \ $(NULL) bsc_test_SOURCES = \ bsc_test.c \ $(NULL) bsc_test_LDADD = \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/abis_rsl.o \ $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \ $(top_builddir)/src/osmo-bsc/bsc_api.o \ $(top_builddir)/src/osmo-bsc/bsc_dyn_ts.o \ $(top_builddir)/src/osmo-bsc/osmo_bsc_filter.o \ $(top_builddir)/src/osmo-bsc/bsc_rll.o \ $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \ $(top_builddir)/src/osmo-bsc/chan_alloc.o \ $(top_builddir)/src/osmo-bsc/gsm_04_08_utils.o \ $(top_builddir)/src/osmo-bsc/gsm_04_80_utils.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/handover_cfg.o \ $(top_builddir)/src/osmo-bsc/handover_logic.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/paging.o \ $(top_builddir)/src/osmo-bsc/pcu_sock.o \ $(top_builddir)/src/osmo-bsc/rest_octets.o \ $(top_builddir)/src/osmo-bsc/system_information.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOLEGACYMGCP_LIBS) \ $(LIBRARY_GSM) \ -lrt \ $(NULL) osmo-bsc-1.3.0/tests/bsc/bsc_test.c000066400000000000000000000157051332665256100171410ustar00rootroot00000000000000/* * BSC Message filtering * * (C) 2013 by sysmocom s.f.m.c. GmbH * Written by Jacob Erlbeck * (C) 2010-2013 by Holger Hans Peter Freyther * (C) 2010-2013 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 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 void *ctx = NULL; enum test { TEST_SCAN_TO_BTS, }; /* GSM 04.08 MM INFORMATION test message */ static uint8_t gsm48_mm_info_nn_tzt[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x00, }; static uint8_t gsm48_mm_info_nn_tzt_out[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x1a, }; static uint8_t gsm48_mm_info_nn_tzt_dst[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x00, 0x49, 0x01, 0x00, }; static uint8_t gsm48_mm_info_nn_tzt_dst_out[] = { 0x05, 0x32, 0x45, 0x08, 0x80, 0x4f, 0x77, 0xeb, 0x1a, 0xb6, 0x97, 0xe7, 0x47, 0x31, 0x90, 0x61, 0x11, 0x02, 0x73, 0x1a, 0x49, 0x01, 0x02, }; struct test_definition { const uint8_t *data; const uint16_t length; const int dir; const int result; const uint8_t *out_data; const uint16_t out_length; const char* params; const int n_params; }; static int get_int(const char *params, size_t nmemb, const char *key, int def, int *is_set) { const char *kv = NULL; kv = strstr(params, key); if (kv) { kv += strlen(key) + 1; fprintf(stderr, "get_int(%s) -> %d\n", key, atoi(kv)); if (is_set) *is_set = 1; } return kv ? atoi(kv) : def; } static const struct test_definition test_scan_defs[] = { { .data = gsm48_mm_info_nn_tzt_dst, .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt), .dir = TEST_SCAN_TO_BTS, .result = 0, .out_data = gsm48_mm_info_nn_tzt_dst_out, .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_out), .params = "tz_hr=-5 tz_mn=15 tz_dst=2", .n_params = 3, }, { .data = gsm48_mm_info_nn_tzt_dst, .length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst), .dir = TEST_SCAN_TO_BTS, .result = 0, .out_data = gsm48_mm_info_nn_tzt_dst_out, .out_length = ARRAY_SIZE(gsm48_mm_info_nn_tzt_dst_out), .params = "tz_hr=-5 tz_mn=15 tz_dst=2", .n_params = 3, }, }; static void test_scan(void) { int i; struct gsm_network *net = gsm_network_init(ctx); struct gsm_bts *bts = gsm_bts_alloc(net, 0); struct bsc_msc_data *msc; struct gsm_subscriber_connection *conn; msc = talloc_zero(net, struct bsc_msc_data); conn = talloc_zero(net, struct gsm_subscriber_connection); bts->network = net; conn->sccp.msc = msc; conn->lchan = &bts->c0->ts[1].lchan[0]; /* start testing with proper messages */ printf("Testing BTS<->MSC message scan.\n"); for (i = 0; i < ARRAY_SIZE(test_scan_defs); ++i) { const struct test_definition *test_def = &test_scan_defs[i]; int result; struct msgb *msg = msgb_alloc(4096, "test-message"); int is_set = 0; net->tz.hr = get_int(test_def->params, test_def->n_params, "tz_hr", 0, &is_set); net->tz.mn = get_int(test_def->params, test_def->n_params, "tz_mn", 0, &is_set); net->tz.dst = get_int(test_def->params, test_def->n_params, "tz_dst", 0, &is_set); net->tz.override = 1; printf("Going to test item: %d\n", i); msg->l3h = msgb_put(msg, test_def->length); memcpy(msg->l3h, test_def->data, test_def->length); switch (test_def->dir) { case TEST_SCAN_TO_BTS: /* override timezone of msg coming from the MSC */ result = bsc_scan_msc_msg(conn, msg); break; default: abort(); break; } if (result != test_def->result) { printf("FAIL: Not the expected result, got: %d wanted: %d\n", result, test_def->result); goto out; } if (msgb_l3len(msg) != test_def->out_length) { printf("FAIL: Not the expected message size, got: %d wanted: %d\n", msgb_l3len(msg), test_def->out_length); goto out; } if (memcmp(msgb_l3(msg), test_def->out_data, test_def->out_length) != 0) { printf("FAIL: Not the expected message\n"); goto out; } out: msgb_free(msg); } talloc_free(net); } static const struct log_info_cat log_categories[] = { [DNM] = { .name = "DNM", .description = "A-bis Network Management / O&M (NM/OML)", .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_INFO, }, [DNAT] = { .name = "DNAT", .description = "GSM 08.08 NAT/Multiplexer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCTRL] = { .name = "DCTRL", .description = "Control interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DFILTER] = { .name = "DFILTER", .description = "BSC/NAT IMSI based filtering", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { ctx = talloc_named_const(NULL, 0, "bsc-test"); msgb_talloc_ctx_init(ctx, 0); osmo_init_logging2(ctx, &log_info); test_scan(); printf("Testing execution completed.\n"); talloc_free(ctx); return 0; } struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { OSMO_ASSERT(0); } void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) {} void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {} int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel) { return 0; } void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {} void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {} void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) {} int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { return 0; } void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) {} void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate) {} osmo-bsc-1.3.0/tests/bsc/bsc_test.ok000066400000000000000000000001511332665256100173150ustar00rootroot00000000000000Testing BTS<->MSC message scan. Going to test item: 0 Going to test item: 1 Testing execution completed. osmo-bsc-1.3.0/tests/codec_pref/000077500000000000000000000000001332665256100165015ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/codec_pref/Makefile.am000066400000000000000000000010101332665256100205250ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(NULL) EXTRA_DIST = \ codec_pref_test.ok \ $(NULL) noinst_PROGRAMS = \ codec_pref_test \ $(NULL) codec_pref_test_SOURCES = \ codec_pref_test.c \ $(NULL) codec_pref_test_LDADD = \ $(top_builddir)/src/osmo-bsc/codec_pref.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ -lrt \ $(NULL) osmo-bsc-1.3.0/tests/codec_pref/codec_pref_test.c000066400000000000000000000400601332665256100217750ustar00rootroot00000000000000/* * (C) 2018 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 void *ctx = NULL; #define MSC_AUDIO_SUPPORT_MAX 5 #define N_CONFIG_VARIANTS 9 /* Make sure that there is some memory to put our test configuration. */ static void init_msc_config(struct bsc_msc_data *msc) { unsigned int i; msc->audio_support = talloc_zero_array(ctx, struct gsm_audio_support *, MSC_AUDIO_SUPPORT_MAX); msc->audio_length = MSC_AUDIO_SUPPORT_MAX; for (i = 0; i < MSC_AUDIO_SUPPORT_MAX; i++) { msc->audio_support[i] = talloc_zero(msc->audio_support, struct gsm_audio_support); } } /* Free memory that we have used for the test configuration. */ static void free_msc_config(struct bsc_msc_data *msc) { talloc_free(msc->audio_support); } /* The speech codec list is sent by the MS and lists the voice codec settings * that the MS is able to support. The BSC must select one of this codecs * depending on what the MSC is able to support. The following function * generates some realistically made up speech codec lists. */ static void make_scl_config(struct gsm0808_speech_codec_list *scl, uint8_t config_no) { OSMO_ASSERT(config_no < N_CONFIG_VARIANTS); switch (config_no) { case 0: /* FR1 only */ scl->codec[0].type = GSM0808_SCT_FR1; scl->len = 1; break; case 1: /* HR1 only */ scl->codec[0].type = GSM0808_SCT_HR1; scl->len = 1; break; case 2: /* FR2 only */ scl->codec[0].type = GSM0808_SCT_FR2; scl->len = 1; break; case 3: /* FR3 only */ scl->codec[0].type = GSM0808_SCT_FR3; scl->len = 1; break; case 4: /* HR3 only */ scl->codec[0].type = GSM0808_SCT_HR3; scl->len = 1; break; case 5: /* FR1 and HR1 */ scl->codec[0].type = GSM0808_SCT_FR1; scl->codec[1].type = GSM0808_SCT_HR1; scl->len = 2; break; case 6: /* FR1, FR2 and HR1 */ scl->codec[0].type = GSM0808_SCT_FR1; scl->codec[1].type = GSM0808_SCT_FR2; scl->codec[2].type = GSM0808_SCT_HR1; scl->len = 3; break; case 7: /* FR1, FR3 and HR3 */ scl->codec[0].type = GSM0808_SCT_FR1; scl->codec[1].type = GSM0808_SCT_FR3; scl->codec[2].type = GSM0808_SCT_HR3; scl->len = 3; break; case 8: /* FR1, FR2, FR3, HR1 and HR3 */ scl->codec[0].type = GSM0808_SCT_FR1; scl->codec[1].type = GSM0808_SCT_FR2; scl->codec[2].type = GSM0808_SCT_FR3; scl->codec[3].type = GSM0808_SCT_HR1; scl->codec[4].type = GSM0808_SCT_HR3; scl->len = 5; break; } } /* The channel type element which is sent to the BSC by the MSC lists all the * codecs that the MSC is able to support. The following function generates * a realistic permitted speech settings */ static void make_ct_config(struct gsm0808_channel_type *ct, uint8_t config_no) { OSMO_ASSERT(config_no < N_CONFIG_VARIANTS); switch (config_no) { case 0: /* FR1 only */ ct->perm_spch[0] = GSM0808_PERM_FR1; ct->perm_spch_len = 1; break; case 1: /* HR1 only */ ct->perm_spch[0] = GSM0808_PERM_HR1; ct->perm_spch_len = 1; break; case 2: /* FR2 only */ ct->perm_spch[0] = GSM0808_PERM_FR2; ct->perm_spch_len = 1; break; case 3: /* FR3 only */ ct->perm_spch[0] = GSM0808_PERM_FR3; ct->perm_spch_len = 1; break; case 4: /* HR3 only */ ct->perm_spch[0] = GSM0808_PERM_HR3; ct->perm_spch_len = 1; break; case 5: /* FR1 and HR1 */ ct->perm_spch[0] = GSM0808_PERM_FR1; ct->perm_spch[1] = GSM0808_PERM_HR1; ct->perm_spch_len = 2; break; case 6: /* FR1, FR2 and HR1 */ ct->perm_spch[0] = GSM0808_PERM_FR1; ct->perm_spch[1] = GSM0808_PERM_FR2; ct->perm_spch[2] = GSM0808_PERM_HR1; ct->perm_spch_len = 3; break; case 7: /* FR1, FR3 and HR3 */ ct->perm_spch[0] = GSM0808_PERM_FR1; ct->perm_spch[1] = GSM0808_PERM_FR3; ct->perm_spch[2] = GSM0808_PERM_HR3; ct->perm_spch_len = 3; break; case 8: /* FR1, FR2, FR3, HR1 and HR3 */ ct->perm_spch[0] = GSM0808_PERM_FR1; ct->perm_spch[1] = GSM0808_PERM_FR2; ct->perm_spch[2] = GSM0808_PERM_FR3; ct->perm_spch[3] = GSM0808_PERM_HR1; ct->perm_spch[4] = GSM0808_PERM_HR3; ct->perm_spch_len = 5; break; } } /* Generate some realistic MSC configuration which one also could find in the * real world. This configuration acts as a filter. While the MSC could in * theory advertise codecs more codecs as we are able to support we have to * make sure that only the codecs we have support for are considered. */ static void make_msc_config(struct bsc_msc_data *msc, uint8_t config_no) { /* 1 = FR1/HR1 * 2 = FR2/HR2 * 3 = FR2/HR3 * Note: HR2 is deprecated */ OSMO_ASSERT(config_no < N_CONFIG_VARIANTS); switch (config_no) { case 0: /* FR1 only */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 0; msc->audio_length = 1; break; case 1: /* HR1 only */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 1; msc->audio_length = 1; break; case 2: /* FR2 only */ msc->audio_support[0]->ver = 2; msc->audio_support[0]->hr = 0; msc->audio_length = 1; break; case 3: /* FR3 only */ msc->audio_support[0]->ver = 3; msc->audio_support[0]->hr = 0; msc->audio_length = 1; break; case 4: /* HR3 only */ msc->audio_support[0]->ver = 3; msc->audio_support[0]->hr = 1; msc->audio_length = 1; break; case 5: /* FR1 and HR1 */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 0; msc->audio_support[1]->ver = 1; msc->audio_support[1]->hr = 1; msc->audio_length = 2; break; case 6: /* FR1, FR2 and HR1 */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 0; msc->audio_support[1]->ver = 2; msc->audio_support[1]->hr = 0; msc->audio_support[2]->ver = 1; msc->audio_support[2]->hr = 1; msc->audio_length = 3; break; case 7: /* FR1, FR3 and HR3 */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 0; msc->audio_support[1]->ver = 3; msc->audio_support[1]->hr = 0; msc->audio_support[2]->ver = 3; msc->audio_support[2]->hr = 1; msc->audio_length = 3; break; case 8: /* FR1, FR2, FR3, HR1 and HR3 */ msc->audio_support[0]->ver = 1; msc->audio_support[0]->hr = 0; msc->audio_support[1]->ver = 2; msc->audio_support[1]->hr = 0; msc->audio_support[2]->ver = 3; msc->audio_support[2]->hr = 0; msc->audio_support[3]->ver = 1; msc->audio_support[3]->hr = 1; msc->audio_support[4]->ver = 3; msc->audio_support[4]->hr = 1; msc->audio_length = 5; break; } } /* Generate a realitically looking bts codec configuration */ static void make_bts_config(struct gsm_bts *bts, uint8_t config_no) { /* Note: FR is supported by all BTSs, so there is no flag for it */ OSMO_ASSERT(config_no < N_CONFIG_VARIANTS); bts->codec.hr = 0; bts->codec.efr = 0; bts->codec.amr = 0; switch (config_no) { case 0: /* FR1 (implicit) only */ break; case 1: /* HR1 only (+FR implicit) */ bts->codec.hr = 1; break; case 2: /* FR2 only (+FR implicit) */ bts->codec.efr = 1; break; case 3: /* FR3 only (+FR implicit) */ bts->codec.amr = 1; break; case 4: /* HR3 only (+FR implicit) */ bts->codec.amr = 1; break; case 5: /* FR1 (implicit) and HR1 */ bts->codec.hr = 1; break; case 6: /* FR1 (implicit), FR2 and HR1 */ bts->codec.efr = 1; bts->codec.hr = 1; break; case 7: /* FR1 (implicit), FR3 and HR3 */ bts->codec.amr = 1; break; case 8: /* FR1 (implicit), FR2, FR3, HR1 and HR3 */ bts->codec.hr = 1; bts->codec.efr = 1; bts->codec.amr = 1; break; } } /* Try execute match_codec_pref(), display input and output parameters */ static int test_match_codec_pref(const struct gsm0808_channel_type *ct, const struct gsm0808_speech_codec_list *scl, const struct bsc_msc_data *msc, struct gsm_bts *bts) { int rc; unsigned int i; int full_rate; enum gsm48_chan_mode chan_mode; printf("Determining channel mode and rate:\n"); printf(" * MS: speech codec list (%u items):\n", scl->len); for (i = 0; i < scl->len; i++) printf(" codec[%u]->type=%s\n", i, gsm0808_speech_codec_type_name(scl->codec[i].type)); printf(" * MSC: channel type permitted speech (%u items):\n", ct->perm_spch_len); for (i = 0; i < ct->perm_spch_len; i++) printf(" perm_spch[%u]=%s\n", i, gsm0808_permitted_speech_name(ct->perm_spch[i])); printf(" * BSS: audio support settings (%u items):\n", msc->audio_length); for (i = 0; i < msc->audio_length; i++) if (msc->audio_support[i]->hr) printf(" audio_support[%u]=HR%u\n", i, msc->audio_support[i]->ver); else printf(" audio_support[%u]=FR%u\n", i, msc->audio_support[i]->ver); printf(" * BTS: audio support settings:\n"); printf(" (GSM-FR implicitly supported)\n"); printf(" codec->hr=%u\n", bts->codec.hr); printf(" codec->efr=%u\n", bts->codec.efr); printf(" codec->amr=%u\n", bts->codec.amr); rc = match_codec_pref(&full_rate, &chan_mode, ct, scl, msc, bts); printf(" * result: rc=%i, full_rate=%i, chan_mode=%s\n", rc, full_rate, gsm48_chan_mode_name(chan_mode)); printf("\n"); return rc; } /* MS, MSC and local MSC settings are the same */ static void test_one_to_one(void) { unsigned int i; struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_one_to_one ==============\n\n"); init_msc_config(&msc_local); for (i = 0; i < N_CONFIG_VARIANTS; i++) { make_msc_config(&msc_local, i); make_scl_config(&scl_ms, i); make_ct_config(&ct_msc, i); make_bts_config(&bts_local, i); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); } free_msc_config(&msc_local); } /* Network supports all combinations, MS varies */ static void test_ms(void) { unsigned int i; struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_ms ==============\n\n"); init_msc_config(&msc_local); make_msc_config(&msc_local, 8); make_ct_config(&ct_msc, 8); make_bts_config(&bts_local, 8); for (i = 0; i < N_CONFIG_VARIANTS; i++) { make_scl_config(&scl_ms, i); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); } free_msc_config(&msc_local); } /* BSS and MS support all combinations, MSC varies */ static void test_ct(void) { unsigned int i; struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_ct ==============\n\n"); init_msc_config(&msc_local); make_msc_config(&msc_local, 8); make_scl_config(&scl_ms, 8); make_bts_config(&bts_local, 8); for (i = 0; i < N_CONFIG_VARIANTS; i++) { make_ct_config(&ct_msc, i); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); } free_msc_config(&msc_local); } /* MSC and MS support all combinations, BSS varies */ static void test_msc(void) { unsigned int i; struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_msc ==============\n\n"); init_msc_config(&msc_local); make_ct_config(&ct_msc, 8); make_scl_config(&scl_ms, 8); make_bts_config(&bts_local, 8); for (i = 0; i < N_CONFIG_VARIANTS; i++) { make_msc_config(&msc_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); } free_msc_config(&msc_local); } /* Some mixed configurations that are supposed to work */ static void test_selected_working(void) { struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_selected_working ==============\n\n"); init_msc_config(&msc_local); make_scl_config(&scl_ms, 6); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); make_scl_config(&scl_ms, 0); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 6); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); make_scl_config(&scl_ms, 6); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 4); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); make_scl_config(&scl_ms, 0); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 2); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 6); make_bts_config(&bts_local, 1); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == 0); free_msc_config(&msc_local); } /* Some mixed configurations that can not work */ static void test_selected_non_working(void) { struct gsm0808_channel_type ct_msc; struct gsm0808_speech_codec_list scl_ms; struct bsc_msc_data msc_local; struct gsm_bts bts_local; int rc; printf("============== test_selected_non_working ==============\n\n"); init_msc_config(&msc_local); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 4); make_msc_config(&msc_local, 6); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 2); make_msc_config(&msc_local, 7); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); make_scl_config(&scl_ms, 1); make_ct_config(&ct_msc, 5); make_msc_config(&msc_local, 4); make_bts_config(&bts_local, 8); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); make_scl_config(&scl_ms, 8); make_ct_config(&ct_msc, 4); make_msc_config(&msc_local, 6); make_bts_config(&bts_local, 7); rc = test_match_codec_pref(&ct_msc, &scl_ms, &msc_local, &bts_local); OSMO_ASSERT(rc == -1); free_msc_config(&msc_local); } static const struct log_info_cat log_categories[] = { [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1,.loglevel = LOGL_NOTICE, }, }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { ctx = talloc_named_const(NULL, 0, "codec_pref_test"); msgb_talloc_ctx_init(ctx, 0); osmo_init_logging2(ctx, &log_info); test_one_to_one(); test_ms(); test_ct(); test_msc(); test_selected_working(); test_selected_non_working(); printf("Testing execution completed.\n"); talloc_free(ctx); return 0; } osmo-bsc-1.3.0/tests/codec_pref/codec_pref_test.ok000066400000000000000000000637131332665256100221760ustar00rootroot00000000000000============== test_one_to_one ============== Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR1 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR1 * BSS: audio support settings (1 items): audio_support[0]=FR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=0 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR1 * BSS: audio support settings (1 items): audio_support[0]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=0 codec->amr=0 * result: rc=0, full_rate=0, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR2 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR2 * BSS: audio support settings (1 items): audio_support[0]=FR2 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=1 codec->amr=0 * result: rc=0, full_rate=1, chan_mode=SPEECH_EFR Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR3 * BSS: audio support settings (1 items): audio_support[0]=FR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR3 * BSS: audio support settings (1 items): audio_support[0]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (2 items): codec[0]->type=FR1 codec[1]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (2 items): audio_support[0]=FR1 audio_support[1]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=0 codec->amr=0 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=HR1 * MSC: channel type permitted speech (3 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=0 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR3 codec[2]->type=HR3 * MSC: channel type permitted speech (3 items): perm_spch[0]=FR1 perm_spch[1]=FR3 perm_spch[2]=HR3 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 ============== test_ms ============== Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR1 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR2 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_EFR Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (2 items): codec[0]->type=FR1 codec[1]->type=HR1 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=HR1 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR3 codec[2]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 ============== test_ct ============== Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR1 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR1 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR2 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_EFR Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_AMR Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (3 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=HR1 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (3 items): perm_spch[0]=FR1 perm_spch[1]=FR3 perm_spch[2]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 ============== test_msc ============== Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (5 items): perm_spch[0]=FR1 perm_spch[1]=FR2 perm_spch[2]=FR3 perm_spch[3]=HR1 perm_spch[4]=HR3 * BSS: audio support settings (5 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=FR3 audio_support[3]=HR1 audio_support[4]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 ============== test_selected_working ============== Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=0, full_rate=0, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (3 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=1 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=FR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=1 codec->amr=0 * result: rc=0, full_rate=1, chan_mode=SPEECH_V1 Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=0 codec->amr=0 * result: rc=0, full_rate=0, chan_mode=SPEECH_V1 ============== test_selected_non_working ============== Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR3 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (1 items): perm_spch[0]=FR2 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR3 audio_support[2]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Determining channel mode and rate: * MS: speech codec list (1 items): codec[0]->type=HR1 * MSC: channel type permitted speech (2 items): perm_spch[0]=FR1 perm_spch[1]=HR1 * BSS: audio support settings (1 items): audio_support[0]=HR3 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=1 codec->efr=1 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Determining channel mode and rate: * MS: speech codec list (5 items): codec[0]->type=FR1 codec[1]->type=FR2 codec[2]->type=FR3 codec[3]->type=HR1 codec[4]->type=HR3 * MSC: channel type permitted speech (1 items): perm_spch[0]=HR3 * BSS: audio support settings (3 items): audio_support[0]=FR1 audio_support[1]=FR2 audio_support[2]=HR1 * BTS: audio support settings: (GSM-FR implicitly supported) codec->hr=0 codec->efr=0 codec->amr=1 * result: rc=-1, full_rate=-1, chan_mode=SIGNALLING Testing execution completed. osmo-bsc-1.3.0/tests/ctrl_test_runner.py000077500000000000000000000443771332665256100204000ustar00rootroot00000000000000#!/usr/bin/env python2 # (C) 2013 by Jacob Erlbeck # (C) 2014 by Holger Hans Peter Freyther # based on vty_test_runner.py: # (C) 2013 by Katerina Barone-Adesi # (C) 2013 by Holger Hans Peter Freyther # based on bsc_control.py. # 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 . import os import time import unittest import socket import sys import struct import osmopy.obscvty as obscvty import osmopy.osmoutil as osmoutil from osmopy.osmo_ipa import Ctrl, IPA # to be able to find $top_srcdir/doc/... confpath = os.path.join(sys.path[0], '..') verbose = False class TestCtrlBase(unittest.TestCase): def ctrl_command(self): raise Exception("Needs to be implemented by a subclass") def ctrl_app(self): raise Exception("Needs to be implemented by a subclass") def setUp(self): osmo_ctrl_cmd = self.ctrl_command()[:] config_index = osmo_ctrl_cmd.index('-c') if config_index: cfi = config_index + 1 osmo_ctrl_cmd[cfi] = os.path.join(confpath, osmo_ctrl_cmd[cfi]) try: self.proc = osmoutil.popen_devnull(osmo_ctrl_cmd) except OSError: print >> sys.stderr, "Current directory: %s" % os.getcwd() print >> sys.stderr, "Consider setting -b" time.sleep(2) appstring = self.ctrl_app()[2] appport = self.ctrl_app()[0] self.connect("127.0.0.1", appport) self.next_id = 1000 def tearDown(self): self.disconnect() osmoutil.end_proc(self.proc) def disconnect(self): if not (self.sock is None): self.sock.close() def connect(self, host, port): if verbose: print "Connecting to host %s:%i" % (host, port) retries = 30 while True: try: sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sck.setblocking(1) sck.connect((host, port)) except IOError: retries -= 1 if retries <= 0: raise time.sleep(.1) continue break self.sock = sck return sck def send(self, data): if verbose: print "Sending \"%s\"" %(data) data = Ctrl().add_header(data) return self.sock.send(data) == len(data) def send_set(self, var, value, id): setmsg = "SET %s %s %s" %(id, var, value) return self.send(setmsg) def send_get(self, var, id): getmsg = "GET %s %s" %(id, var) return self.send(getmsg) def do_set(self, var, value): id = self.next_id self.next_id += 1 self.send_set(var, value, id) return self.recv_msgs()[id] def do_get(self, var): id = self.next_id self.next_id += 1 self.send_get(var, id) return self.recv_msgs()[id] def recv_msgs(self): responses = {} data = self.sock.recv(4096) while (len(data)>0): (head, data) = IPA().split_combined(data) answer = Ctrl().rem_header(head) if verbose: print "Got message:", answer (mtype, id, msg) = answer.split(None, 2) id = int(id) rsp = {'mtype': mtype, 'id': id} if mtype == "ERROR": rsp['error'] = msg else: split = msg.split(None, 1) rsp['var'] = split[0] if len(split) > 1: rsp['value'] = split[1] else: rsp['value'] = None responses[id] = rsp if verbose: print "Decoded replies: ", responses return responses class TestCtrlBSC(TestCtrlBase): def tearDown(self): TestCtrlBase.tearDown(self) os.unlink("tmp_dummy_sock") def ctrl_command(self): return ["./src/osmo-bsc/osmo-bsc", "-r", "tmp_dummy_sock", "-c", "doc/examples/osmo-bsc/osmo-bsc.cfg"] def ctrl_app(self): return (4249, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") def testCtrlErrs(self): r = self.do_get('invalid') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Command not found') r = self.do_set('rf_locked', '999') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Value failed verification.') r = self.do_get('bts') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Error while parsing the index.') r = self.do_get('bts.999') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Error while resolving object') def testBtsLac(self): r = self.do_get('bts.0.location-area-code') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '1') r = self.do_set('bts.0.location-area-code', '23') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '23') r = self.do_get('bts.0.location-area-code') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.location-area-code') self.assertEquals(r['value'], '23') r = self.do_set('bts.0.location-area-code', '-1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testBtsCi(self): r = self.do_get('bts.0.cell-identity') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '0') r = self.do_set('bts.0.cell-identity', '23') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '23') r = self.do_get('bts.0.cell-identity') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.cell-identity') self.assertEquals(r['value'], '23') r = self.do_set('bts.0.cell-identity', '-1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testBtsGenerateSystemInformation(self): r = self.do_get('bts.0.send-new-system-informations') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Write Only attribute') # No RSL links so it will fail r = self.do_set('bts.0.send-new-system-informations', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Failed to generate SI') def testBtsChannelLoad(self): r = self.do_set('bts.0.channel-load', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Read Only attribute') # No RSL link so everything is 0 r = self.do_get('bts.0.channel-load') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['value'], 'CCCH+SDCCH4,0,0 TCH/F,0,0 TCH/H,0,0 SDCCH8,0,0' + ' TCH/F_PDCH,0,0 CCCH+SDCCH4+CBCH,0,0' + ' SDCCH8+CBCH,0,0 TCH/F_TCH/H_PDCH,0,0') def testBtsOmlConnectionState(self): """Check OML state. It will not be connected""" r = self.do_set('bts.0.oml-connection-state', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Read Only attribute') # No RSL link so everything is 0 r = self.do_get('bts.0.oml-connection-state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['value'], 'disconnected') def testTrxPowerRed(self): r = self.do_get('bts.0.trx.0.max-power-reduction') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '20') r = self.do_set('bts.0.trx.0.max-power-reduction', '22') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '22') r = self.do_get('bts.0.trx.0.max-power-reduction') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.max-power-reduction') self.assertEquals(r['value'], '22') r = self.do_set('bts.0.trx.0.max-power-reduction', '1') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Value must be even') def testTrxArfcn(self): r = self.do_get('bts.0.trx.0.arfcn') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '871') r = self.do_set('bts.0.trx.0.arfcn', '873') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '873') r = self.do_get('bts.0.trx.0.arfcn') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.trx.0.arfcn') self.assertEquals(r['value'], '873') r = self.do_set('bts.0.trx.0.arfcn', '2000') self.assertEquals(r['mtype'], 'ERROR') self.assertEquals(r['error'], 'Input not within the range') def testRfLock(self): r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,unlocked,on') r = self.do_set('rf_locked', '1') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], '1') time.sleep(1.5) r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,locked,off') r = self.do_get('rf_locked') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], 'state=off,policy=off') r = self.do_set('rf_locked', '0') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], '0') time.sleep(1.5) r = self.do_get('bts.0.rf_state') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'bts.0.rf_state') self.assertEquals(r['value'], 'inoperational,unlocked,on') r = self.do_get('rf_locked') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'rf_locked') self.assertEquals(r['value'], 'state=off,policy=on') def testTimezone(self): r = self.do_get('timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], 'off') r = self.do_set('timezone', '-2,15,2') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], '-2,15,2') r = self.do_get('timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], '-2,15,2') # Test invalid input r = self.do_set('timezone', '-2,15,2,5,6,7') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], '-2,15,2') r = self.do_set('timezone', '-2,15') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('timezone', '-2') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('timezone', '1') r = self.do_set('timezone', 'off') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], 'off') r = self.do_get('timezone') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'timezone') self.assertEquals(r['value'], 'off') def testMcc(self): r = self.do_set('mcc', '23') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '023') r = self.do_set('mcc', '023') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '023') def testMnc(self): r = self.do_set('mnc', '9') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '09') r = self.do_set('mnc', '09') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '09') r = self.do_set('mnc', '009') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '009') def testMccMncApply(self): # Test some invalid input r = self.do_set('mcc-mnc-apply', 'WRONG') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('mcc-mnc-apply', '1,') self.assertEquals(r['mtype'], 'ERROR') r = self.do_set('mcc-mnc-apply', '200,3') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Set it again r = self.do_set('mcc-mnc-apply', '200,3') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Nothing changed') # Change it r = self.do_set('mcc-mnc-apply', '200,4') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Change it r = self.do_set('mcc-mnc-apply', '201,4') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') # Verify r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '04') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '201') # Change it r = self.do_set('mcc-mnc-apply', '202,03') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '03') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '202') # Test MNC with 3 digits r = self.do_set('mcc-mnc-apply', '2,003') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Tried to drop the BTS') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '003') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '002') # Set same MNC with 3 digits r = self.do_set('mcc-mnc-apply', '2,003') self.assertEquals(r['mtype'], 'SET_REPLY') self.assertEquals(r['var'], 'mcc-mnc-apply') self.assertEquals(r['value'], 'Nothing changed') r = self.do_get('mnc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mnc') self.assertEquals(r['value'], '003') r = self.do_get('mcc') self.assertEquals(r['mtype'], 'GET_REPLY') self.assertEquals(r['var'], 'mcc') self.assertEquals(r['value'], '002') def add_bsc_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): print("Skipping the BSC test") return test = unittest.TestLoader().loadTestsFromTestCase(TestCtrlBSC) suite.addTest(test) if __name__ == '__main__': import argparse import sys workdir = '.' parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose mode") parser.add_argument("-p", "--pythonconfpath", dest="p", help="searchpath for config") parser.add_argument("-w", "--workdir", dest="w", help="Working directory") args = parser.parse_args() verbose_level = 1 if args.verbose: verbose_level = 2 verbose = True if args.w: workdir = args.w if args.p: confpath = args.p print "confpath %s, workdir %s" % (confpath, workdir) os.chdir(workdir) print "Running tests for specific control commands" suite = unittest.TestSuite() add_bsc_test(suite, workdir) res = unittest.TextTestRunner(verbosity=verbose_level).run(suite) sys.exit(len(res.errors) + len(res.failures)) osmo-bsc-1.3.0/tests/gsm0408/000077500000000000000000000000001332665256100155125ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/gsm0408/Makefile.am000066400000000000000000000012241332665256100175450ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) noinst_PROGRAMS = \ gsm0408_test \ $(NULL) EXTRA_DIST = \ gsm0408_test.ok \ $(NULL) gsm0408_test_SOURCES = \ gsm0408_test.c \ $(NULL) gsm0408_test_LDADD = \ $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/rest_octets.o \ $(top_builddir)/src/osmo-bsc/system_information.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(NULL) osmo-bsc-1.3.0/tests/gsm0408/gsm0408_test.c000066400000000000000000000560171332665256100200300ustar00rootroot00000000000000/* simple test for the gsm0408 formatting functions */ /* * (C) 2008 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define COMPARE(result, op, value) \ if (!((result) op (value))) {\ fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \ exit(-1); \ } #define COMPARE_STR(result, value) \ if (strcmp(result, value) != 0) { \ fprintf(stderr, "Compare failed. Was %s should be %s in %s:%d\n",result, value, __FILE__, __LINE__); \ exit(-1); \ } #define DBG(...) #define VERIFY(res, cmp, wanted) \ if (!(res cmp wanted)) { \ printf("ASSERT failed: %s:%d Wanted: %d %s %d\n", \ __FILE__, __LINE__, (int) res, # cmp, (int) wanted); \ } static inline void gen(struct gsm_bts *bts, const char *s) { int r; bts->si_valid = 0; bts->si_valid |= (1 << SYSINFO_TYPE_2quater); printf("generating SI2quater for %zu EARFCNs and %zu UARFCNs...\n", si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length); r = gsm_generate_si(bts, SYSINFO_TYPE_2quater); if (r > 0) for (bts->si2q_index = 0; bts->si2q_index < bts->si2q_count + 1; bts->si2q_index++) printf("generated %s SI2quater [%02u/%02u]: [%d] %s\n", GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater) ? "valid" : "invalid", bts->si2q_index, bts->si2q_count, r, osmo_hexdump((void *)GSM_BTS_SI2Q(bts, bts->si2q_index), GSM_MACBLOCK_LEN)); else printf("%s() failed to generate SI2quater: %s\n", s, strerror(-r)); } static inline void del_earfcn_b(struct gsm_bts *bts, uint16_t earfcn) { struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; int r = osmo_earfcn_del(e, earfcn); if (r) printf("failed to remove EARFCN %u: %s\n", earfcn, strerror(-r)); else printf("removed EARFCN %u - ", earfcn); gen(bts, __func__); } static inline void add_earfcn_b(struct gsm_bts *bts, uint16_t earfcn, uint8_t bw) { struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; int r = osmo_earfcn_add(e, earfcn, bw); if (r) printf("failed to add EARFCN %u: %s\n", earfcn, strerror(-r)); else printf("added EARFCN %u - ", earfcn); gen(bts, __func__); } static inline void _bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity) { int r; bts->u_offset = 0; r = bts_uarfcn_add(bts, arfcn, scramble, diversity); if (r < 0) printf("failed to add UARFCN to SI2quater: %s\n", strerror(-r)); else { bts->si2q_count = si2q_num(bts) - 1; gen(bts, __func__); } } #define bts_init(net) _bts_init(net, __func__) static inline struct gsm_bts *_bts_init(struct gsm_network *net, const char *msg) { struct gsm_bts *bts = gsm_bts_alloc(net, 0); if (!bts) { printf("BTS allocation failure in %s()\n", msg); exit(1); } printf("BTS allocation OK in %s()\n", msg); bts->network = net; return bts; } #define bts_del(bts) _bts_del(bts, __func__) static inline void _bts_del(struct gsm_bts *bts, const char *msg) { osmo_stat_item_group_free(bts->bts_statg); rate_ctr_group_free(bts->bts_ctrs); /* no need to llist_del(&bts->list), we never registered the bts there. */ talloc_free(bts); printf("BTS deallocated OK in %s()\n", msg); } static inline void test_si2q_segfault(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); printf("Test SI2quater UARFCN (same scrambling code and diversity):\n"); _bts_uarfcn_add(bts, 10564, 319, 0); _bts_uarfcn_add(bts, 10612, 319, 0); gen(bts, __func__); bts_del(bts); } static inline void test_si2q_mu(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); printf("Test SI2quater multiple UARFCNs:\n"); _bts_uarfcn_add(bts, 10564, 318, 0); _bts_uarfcn_add(bts, 10612, 319, 0); _bts_uarfcn_add(bts, 10612, 31, 0); _bts_uarfcn_add(bts, 10612, 19, 0); _bts_uarfcn_add(bts, 10613, 64, 0); _bts_uarfcn_add(bts, 10613, 164, 0); _bts_uarfcn_add(bts, 10613, 14, 0); bts_del(bts); } static inline void test_si2q_u(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); printf("Testing SYSINFO_TYPE_2quater UARFCN generation:\n"); /* first generate invalid SI as no UARFCN added */ gen(bts, __func__); /* subsequent calls should produce valid SI if there's enough memory */ _bts_uarfcn_add(bts, 1982, 13, 1); _bts_uarfcn_add(bts, 1982, 44, 0); _bts_uarfcn_add(bts, 1982, 61, 1); _bts_uarfcn_add(bts, 1982, 89, 1); _bts_uarfcn_add(bts, 1982, 113, 0); _bts_uarfcn_add(bts, 1982, 123, 0); _bts_uarfcn_add(bts, 1982, 56, 1); _bts_uarfcn_add(bts, 1982, 72, 1); _bts_uarfcn_add(bts, 1982, 223, 1); _bts_uarfcn_add(bts, 1982, 14, 0); _bts_uarfcn_add(bts, 1982, 88, 0); bts_del(bts); } static inline void test_si2q_e(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); printf("Testing SYSINFO_TYPE_2quater EARFCN generation:\n"); bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; bts->si_common.si2quater_neigh_list.thresh_hi = 5; osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); /* first generate invalid SI as no EARFCN added */ gen(bts, __func__); /* subsequent calls should produce valid SI if there's enough memory and EARFCNs */ add_earfcn_b(bts, 1917, 5); del_earfcn_b(bts, 1917); add_earfcn_b(bts, 1917, 1); add_earfcn_b(bts, 1932, OSMO_EARFCN_MEAS_INVALID); add_earfcn_b(bts, 1937, 2); add_earfcn_b(bts, 1945, OSMO_EARFCN_MEAS_INVALID); add_earfcn_b(bts, 1965, OSMO_EARFCN_MEAS_INVALID); add_earfcn_b(bts, 1967, 4); add_earfcn_b(bts, 1982, 3); bts_del(bts); } static inline void test_si2q_long(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); printf("Testing SYSINFO_TYPE_2quater combined EARFCN & UARFCN generation:\n"); bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; bts->si_common.si2quater_neigh_list.thresh_hi = 5; osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); bts_earfcn_add(bts, 1922, 11, 22, 8,32, 8); bts_earfcn_add(bts, 1922, 11, 22, 8, 32, 8); bts_earfcn_add(bts, 1924, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 1923, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 1925, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 2111, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 2112, 11, 12, 6, 11, 4); bts_earfcn_add(bts, 2113, 11, 12, 6, 11, 3); bts_earfcn_add(bts, 2114, 11, 12, 6, 11, 2); bts_earfcn_add(bts, 2131, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 2132, 11, 12, 6, 11, 4); bts_earfcn_add(bts, 2133, 11, 12, 6, 11, 3); bts_earfcn_add(bts, 2134, 11, 12, 6, 11, 2); bts_earfcn_add(bts, 2121, 11, 12, 6, 11, 5); bts_earfcn_add(bts, 2122, 11, 12, 6, 11, 4); bts_earfcn_add(bts, 2123, 11, 12, 6, 11, 3); bts_earfcn_add(bts, 2124, 11, 12, 6, 11, 2); _bts_uarfcn_add(bts, 1976, 13, 1); _bts_uarfcn_add(bts, 1976, 38, 1); _bts_uarfcn_add(bts, 1976, 44, 1); _bts_uarfcn_add(bts, 1976, 120, 1); _bts_uarfcn_add(bts, 1976, 140, 1); _bts_uarfcn_add(bts, 1976, 163, 1); _bts_uarfcn_add(bts, 1976, 166, 1); _bts_uarfcn_add(bts, 1976, 217, 1); _bts_uarfcn_add(bts, 1976, 224, 1); _bts_uarfcn_add(bts, 1976, 225, 1); _bts_uarfcn_add(bts, 1976, 226, 1); bts_del(bts); } static void test_mi_functionality(void) { const char *imsi_odd = "987654321098763"; const char *imsi_even = "9876543210987654"; const uint32_t tmsi = 0xfabeacd0; uint8_t mi[128]; unsigned int mi_len; char mi_parsed[GSM48_MI_SIZE]; printf("Testing parsing and generating TMSI/IMSI\n"); /* tmsi code */ mi_len = gsm48_generate_mid_from_tmsi(mi, tmsi); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len - 2); COMPARE((uint32_t)strtoul(mi_parsed, NULL, 10), ==, tmsi); /* imsi code */ mi_len = gsm48_generate_mid_from_imsi(mi, imsi_odd); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); printf("hex: %s\n", osmo_hexdump(mi, mi_len)); COMPARE_STR(mi_parsed, imsi_odd); mi_len = gsm48_generate_mid_from_imsi(mi, imsi_even); gsm48_mi_to_string(mi_parsed, sizeof(mi_parsed), mi + 2, mi_len -2); printf("hex: %s\n", osmo_hexdump(mi, mi_len)); COMPARE_STR(mi_parsed, imsi_even); } struct { int range; int arfcns_num; int arfcns[RANGE_ENC_MAX_ARFCNS]; } arfcn_test_ranges[] = { {ARFCN_RANGE_512, 12, { 1, 12, 31, 51, 57, 91, 97, 98, 113, 117, 120, 125 }}, {ARFCN_RANGE_512, 17, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }}, {ARFCN_RANGE_512, 18, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }}, {ARFCN_RANGE_512, 18, { 1, 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 417, 511 }}, {ARFCN_RANGE_512, 6, { 1, 17, 31, 45, 58, 79 }}, {ARFCN_RANGE_512, 6, { 10, 17, 31, 45, 58, 79 }}, {ARFCN_RANGE_1024, 17, { 0, 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 1023 }}, {ARFCN_RANGE_1024, 16, { 17, 31, 45, 58, 79, 81, 97, 113, 127, 213, 277, 287, 311, 331, 391, 1023 }}, {-1} }; static int test_single_range_encoding(int range, const int *orig_arfcns, int arfcns_num, int silent) { int arfcns[RANGE_ENC_MAX_ARFCNS]; int w[RANGE_ENC_MAX_ARFCNS]; int f0_included = 0; int rc, f0; uint8_t chan_list[16] = {0}; struct gsm_sysinfo_freq dec_freq[1024] = {{0}}; int dec_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; int dec_arfcns_count = 0; int arfcns_used = 0; int i; arfcns_used = arfcns_num; memmove(arfcns, orig_arfcns, sizeof(arfcns)); f0 = range == ARFCN_RANGE_1024 ? 0 : arfcns[0]; /* * Manipulate the ARFCN list according to the rules in J4 depending * on the selected range. */ arfcns_used = range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); memset(w, 0, sizeof(w)); range_enc_arfcns(range, arfcns, arfcns_used, w, 0); if (!silent) fprintf(stderr, "range=%d, arfcns_used=%d, f0=%d, f0_included=%d\n", range, arfcns_used, f0, f0_included); /* Select the range and the amount of bits needed */ switch (range) { case ARFCN_RANGE_128: range_enc_range128(chan_list, f0, w); break; case ARFCN_RANGE_256: range_enc_range256(chan_list, f0, w); break; case ARFCN_RANGE_512: range_enc_range512(chan_list, f0, w); break; case ARFCN_RANGE_1024: range_enc_range1024(chan_list, f0, f0_included, w); break; default: return 1; }; if (!silent) printf("chan_list = %s\n", osmo_hexdump(chan_list, sizeof(chan_list))); rc = gsm48_decode_freq_list(dec_freq, chan_list, sizeof(chan_list), 0xfe, 1); if (rc != 0) { printf("Cannot decode freq list, rc = %d\n", rc); return 1; } for (i = 0; i < ARRAY_SIZE(dec_freq); i++) { if (dec_freq[i].mask && dec_arfcns_count < ARRAY_SIZE(dec_arfcns)) dec_arfcns[dec_arfcns_count++] = i; } if (!silent) { printf("Decoded freqs %d (expected %d)\n", dec_arfcns_count, arfcns_num); printf("Decoded: "); for (i = 0; i < dec_arfcns_count; i++) { printf("%d ", dec_arfcns[i]); if (dec_arfcns[i] != orig_arfcns[i]) printf("(!= %d) ", orig_arfcns[i]); } printf("\n"); } if (dec_arfcns_count != arfcns_num) { printf("Wrong number of arfcns\n"); return 1; } if (memcmp(dec_arfcns, orig_arfcns, sizeof(dec_arfcns)) != 0) { printf("Decoding error, got wrong freqs\n"); fprintf(stderr, " w = "); for (i = 0; i < ARRAY_SIZE(w); i++) fprintf(stderr, "%d ", w[i]); fprintf(stderr, "\n"); return 1; } return 0; } static void test_random_range_encoding(int range, int max_arfcn_num) { int arfcns_num = 0; int test_idx; int rc, max_count; int num_tests = 1024; printf("Random range test: range %d, max num ARFCNs %d\n", range, max_arfcn_num); srandom(1); for (max_count = 1; max_count < max_arfcn_num; max_count++) { for (test_idx = 0; test_idx < num_tests; test_idx++) { int count; int i; int min_freq = 0; int rnd_arfcns[RANGE_ENC_MAX_ARFCNS] = {0}; char rnd_arfcns_set[1024] = {0}; if (range < ARFCN_RANGE_1024) min_freq = random() % (1023 - range); for (count = max_count; count; ) { int arfcn = min_freq + random() % (range + 1); OSMO_ASSERT(arfcn < ARRAY_SIZE(rnd_arfcns_set)); if (!rnd_arfcns_set[arfcn]) { rnd_arfcns_set[arfcn] = 1; count -= 1; } } arfcns_num = 0; for (i = 0; i < ARRAY_SIZE(rnd_arfcns_set); i++) if (rnd_arfcns_set[i]) rnd_arfcns[arfcns_num++] = i; rc = test_single_range_encoding(range, rnd_arfcns, arfcns_num, 1); if (rc != 0) { printf("Failed on test %d, range %d, num ARFCNs %d\n", test_idx, range, max_count); test_single_range_encoding(range, rnd_arfcns, arfcns_num, 0); return; } } } } static void test_range_encoding() { int *arfcns; int arfcns_num = 0; int test_idx; int range; for (test_idx = 0; arfcn_test_ranges[test_idx].arfcns_num > 0; test_idx++) { arfcns_num = arfcn_test_ranges[test_idx].arfcns_num; arfcns = &arfcn_test_ranges[test_idx].arfcns[0]; range = arfcn_test_ranges[test_idx].range; printf("Range test %d: range %d, num ARFCNs %d\n", test_idx, range, arfcns_num); test_single_range_encoding(range, arfcns, arfcns_num, 0); } test_random_range_encoding(ARFCN_RANGE_128, 29); test_random_range_encoding(ARFCN_RANGE_256, 22); test_random_range_encoding(ARFCN_RANGE_512, 18); test_random_range_encoding(ARFCN_RANGE_1024, 16); } static int freqs1[] = { 12, 70, 121, 190, 250, 320, 401, 475, 520, 574, 634, 700, 764, 830, 905, 980 }; static int freqs2[] = { 402, 460, 1, 67, 131, 197, 272, 347, }; static int freqs3[] = { 68, 128, 198, 279, 353, 398, 452, }; static int w_out[] = { 122, 2, 69, 204, 75, 66, 60, 70, 83, 3, 24, 67, 54, 64, 70, 9, }; static int range128[] = { 1, 1 + 127, }; static int range256[] = { 1, 1 + 128, }; static int range512[] = { 1, 1+ 511, }; static void test_arfcn_filter() { int arfcns[50], i, res, f0_included; for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = (i + 1) * 2; /* check that the arfcn is taken out. f0_included is only set for Range1024 */ f0_included = 24; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), arfcns[0], &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); VERIFY(f0_included, ==, 1); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, ((i+2) * 2) - (2+1)); /* check with range1024, ARFCN 0 is included */ for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = i * 2; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), 0, &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns) - 1); VERIFY(f0_included, ==, 1); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, (i + 1) * 2 - 1); /* check with range1024, ARFCN 0 not included */ for (i = 0; i < ARRAY_SIZE(arfcns); ++i) arfcns[i] = (i + 1) * 2; res = range_enc_filter_arfcns(arfcns, ARRAY_SIZE(arfcns), 0, &f0_included); VERIFY(res, ==, ARRAY_SIZE(arfcns)); VERIFY(f0_included, ==, 0); for (i = 0; i < res; ++i) VERIFY(arfcns[i], ==, ((i + 1) * 2) - 1); } static void test_print_encoding() { int rc; int w[17]; uint8_t chan_list[16]; memset(chan_list, 0x23, sizeof(chan_list)); for (rc = 0; rc < ARRAY_SIZE(w); ++rc) switch (rc % 3) { case 0: w[rc] = 0xAAAA; break; case 1: w[rc] = 0x5555; break; case 2: w[rc] = 0x9696; break; } range_enc_range512(chan_list, (1 << 9) | 0x96, w); printf("Range512: %s\n", osmo_hexdump(chan_list, ARRAY_SIZE(chan_list))); } static void test_si_range_helpers() { int ws[(sizeof(freqs1)/sizeof(freqs1[0]))]; int i, f0 = 0xFFFFFF; memset(&ws[0], 0x23, sizeof(ws)); i = range_enc_find_index(1023, freqs1, ARRAY_SIZE(freqs1)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs1[i] : -1); VERIFY(i, ==, 2); i = range_enc_find_index(511, freqs2, ARRAY_SIZE(freqs2)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs2[i] : -1); VERIFY(i, ==, 2); i = range_enc_find_index(511, freqs3, ARRAY_SIZE(freqs3)); printf("Element is: %d => freqs[i] = %d\n", i, i >= 0 ? freqs3[i] : -1); VERIFY(i, ==, 0); range_enc_arfcns(1023, freqs1, ARRAY_SIZE(freqs1), ws, 0); for (i = 0; i < sizeof(freqs1)/sizeof(freqs1[0]); ++i) { printf("w[%d]=%d\n", i, ws[i]); VERIFY(ws[i], ==, w_out[i]); } i = range_enc_determine_range(range128, ARRAY_SIZE(range128), &f0); VERIFY(i, ==, ARFCN_RANGE_128); VERIFY(f0, ==, 1); i = range_enc_determine_range(range256, ARRAY_SIZE(range256), &f0); VERIFY(i, ==, ARFCN_RANGE_256); VERIFY(f0, ==, 1); i = range_enc_determine_range(range512, ARRAY_SIZE(range512), &f0); VERIFY(i, ==, ARFCN_RANGE_512); VERIFY(f0, ==, 1); } static void test_si_ba_ind(struct gsm_network *net) { struct gsm_bts *bts = bts_init(net); const struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2); const struct gsm48_system_information_type_2bis *si2bis = (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, SYSINFO_TYPE_2bis); const struct gsm48_system_information_type_2ter *si2ter = (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, SYSINFO_TYPE_2ter); const struct gsm48_system_information_type_5 *si5 = (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5); const struct gsm48_system_information_type_5bis *si5bis = (struct gsm48_system_information_type_5bis *) GSM_BTS_SI(bts, SYSINFO_TYPE_5bis); const struct gsm48_system_information_type_5ter *si5ter = (struct gsm48_system_information_type_5ter *) GSM_BTS_SI(bts, SYSINFO_TYPE_5ter); int rc; bts->c0->arfcn = 23; printf("Testing if BA-IND is set as expected in SI2xxx and SI5xxx\n"); rc = gsm_generate_si(bts, SYSINFO_TYPE_2); OSMO_ASSERT(rc > 0); printf("SI2: %s\n", osmo_hexdump((uint8_t *)si2, rc)); /* Validate BA-IND == 0 */ OSMO_ASSERT(!(si2->bcch_frequency_list[0] & 0x10)); rc = gsm_generate_si(bts, SYSINFO_TYPE_2bis); OSMO_ASSERT(rc > 0); printf("SI2bis: %s\n", osmo_hexdump((uint8_t *)si2bis, rc)); /* Validate BA-IND == 0 */ OSMO_ASSERT(!(si2bis->bcch_frequency_list[0] & 0x10)); rc = gsm_generate_si(bts, SYSINFO_TYPE_2ter); OSMO_ASSERT(rc > 0); printf("SI2ter: %s\n", osmo_hexdump((uint8_t *)si2ter, rc)); /* Validate BA-IND == 0 */ OSMO_ASSERT(!(si2ter->ext_bcch_frequency_list[0] & 0x10)); rc = gsm_generate_si(bts, SYSINFO_TYPE_5); OSMO_ASSERT(rc > 0); printf("SI5: %s\n", osmo_hexdump((uint8_t *)si5, rc)); /* Validate BA-IND == 1 */ OSMO_ASSERT(si5->bcch_frequency_list[0] & 0x10); rc = gsm_generate_si(bts, SYSINFO_TYPE_5bis); OSMO_ASSERT(rc > 0); printf("SI5bis: %s\n", osmo_hexdump((uint8_t *)si5bis, rc)); /* Validate BA-IND == 1 */ OSMO_ASSERT(si5bis->bcch_frequency_list[0] & 0x10); rc = gsm_generate_si(bts, SYSINFO_TYPE_5ter); OSMO_ASSERT(rc > 0); printf("SI5ter: %s\n", osmo_hexdump((uint8_t *)si5ter, rc)); /* Validate BA-IND == 1 */ OSMO_ASSERT(si5ter->bcch_frequency_list[0] & 0x10); bts_del(bts); } struct test_gsm48_ra_id_by_bts { struct osmo_plmn_id plmn; uint16_t lac; uint8_t rac; struct gsm48_ra_id expect; }; static const struct test_gsm48_ra_id_by_bts test_gsm48_ra_id_by_bts_data[] = { { .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, .lac = 3, .rac = 4, .expect = { .digits = { 0x00, 0xf1, 0x20 }, .lac = 0x0300, /* network byte order of 3 */ .rac = 4, }, }, { .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = true }, .lac = 3, .rac = 4, .expect = { .digits = { 0x00, 0x21, 0x00 }, .lac = 0x0300, /* network byte order of 3 */ .rac = 4, }, }, { .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = false }, .lac = 0, .rac = 0, .expect = { .digits = { 0x00, 0xf0, 0x00 }, }, }, { .plmn = { .mcc = 0, .mnc = 0, .mnc_3_digits = true }, .lac = 0, .rac = 0, .expect = { .digits = {}, }, }, { .plmn = { .mcc = 999, .mnc = 999, .mnc_3_digits = false }, .lac = 65535, .rac = 255, .expect = { .digits = { 0x99, 0x99, 0x99 }, .lac = 0xffff, .rac = 0xff, }, }, { .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = false }, .lac = 0xabcd, .rac = 0xab, .expect = { .digits = { 0x09, 0xf9, 0x09 }, .lac = 0xcdab, .rac = 0xab, }, }, { .plmn = { .mcc = 909, .mnc = 90, .mnc_3_digits = true }, .lac = 0xabcd, .rac = 0xab, .expect = { .digits = { 0x09, 0x09, 0x90 }, .lac = 0xcdab, .rac = 0xab, }, }, }; static void test_gsm48_ra_id_by_bts() { int i; bool pass = true; for (i = 0; i < ARRAY_SIZE(test_gsm48_ra_id_by_bts_data); i++) { struct gsm_network net; struct gsm_bts bts; const struct test_gsm48_ra_id_by_bts *t = &test_gsm48_ra_id_by_bts_data[i]; struct gsm48_ra_id result = {}; bool ok; net.plmn = t->plmn; bts.network = &net; bts.location_area_code = t->lac; bts.gprs.rac = t->rac; gsm48_ra_id_by_bts(&result, &bts); ok = (t->expect.digits[0] == result.digits[0]) && (t->expect.digits[1] == result.digits[1]) && (t->expect.digits[2] == result.digits[2]) && (t->expect.lac == result.lac) && (t->expect.rac == result.rac); printf("%s[%d]: digits='%02x%02x%02x' lac=0x%04x=htons(%u) rac=0x%02x=%u %s\n", __func__, i, result.digits[0], result.digits[1], result.digits[2], result.lac, osmo_ntohs(result.lac), result.rac, result.rac, ok ? "pass" : "FAIL"); pass = pass && ok; } OSMO_ASSERT(pass); } static const struct log_info_cat log_categories[] = { }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { struct gsm_network *net; tall_bsc_ctx = talloc_named_const(NULL, 0, "gsm0408_test"); osmo_init_logging2(tall_bsc_ctx, &log_info); log_set_log_level(osmo_stderr_target, LOGL_INFO); net = gsm_network_init(tall_bsc_ctx); if (!net) { printf("Network init failure.\n"); return EXIT_FAILURE; } test_mi_functionality(); test_si_range_helpers(); test_arfcn_filter(); test_print_encoding(); test_range_encoding(); test_si2q_segfault(net); test_si2q_e(net); test_si2q_u(net); test_si2q_mu(net); test_si2q_long(net); test_si_ba_ind(net); test_gsm48_ra_id_by_bts(); printf("Done.\n"); return EXIT_SUCCESS; } struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { OSMO_ASSERT(0); } bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; } osmo-bsc-1.3.0/tests/gsm0408/gsm0408_test.ok000066400000000000000000000405151332665256100202130ustar00rootroot00000000000000Testing parsing and generating TMSI/IMSI hex: 17 08 99 78 56 34 12 90 78 36 hex: 17 09 91 78 56 34 12 90 78 56 f4 Element is: 2 => freqs[i] = 121 Element is: 2 => freqs[i] = 1 Element is: 0 => freqs[i] = 68 w[0]=122 w[1]=2 w[2]=69 w[3]=204 w[4]=75 w[5]=66 w[6]=60 w[7]=70 w[8]=83 w[9]=3 w[10]=24 w[11]=67 w[12]=54 w[13]=64 w[14]=70 w[15]=9 Range512: 89 4b 2a 95 65 95 55 2c a9 55 aa 55 6a 95 59 55 Range test 0: range 511, num ARFCNs 12 chan_list = 88 00 98 34 85 36 7c 50 22 dc 5e ec 00 00 00 00 Decoded freqs 12 (expected 12) Decoded: 1 12 31 51 57 91 97 98 113 117 120 125 Range test 1: range 511, num ARFCNs 17 chan_list = 88 00 82 7f 01 3f 7e 04 0b ff ff fc 10 41 07 e0 Decoded freqs 17 (expected 17) Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Range test 2: range 511, num ARFCNs 18 chan_list = 88 00 82 7f 01 7f 7e 04 0b ff ff fc 10 41 07 ff Decoded freqs 18 (expected 18) Decoded: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Range test 3: range 511, num ARFCNs 18 chan_list = 88 00 94 3a 44 32 d7 2a 43 2a 13 94 e5 38 39 f6 Decoded freqs 18 (expected 18) Decoded: 1 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 417 511 Range test 4: range 511, num ARFCNs 6 chan_list = 88 00 8b 3c 88 b9 6b 00 00 00 00 00 00 00 00 00 Decoded freqs 6 (expected 6) Decoded: 1 17 31 45 58 79 Range test 5: range 511, num ARFCNs 6 chan_list = 88 05 08 fc 88 b9 6b 00 00 00 00 00 00 00 00 00 Decoded freqs 6 (expected 6) Decoded: 10 17 31 45 58 79 Range test 6: range 1023, num ARFCNs 17 chan_list = 84 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f Decoded freqs 17 (expected 17) Decoded: 0 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 Range test 7: range 1023, num ARFCNs 16 chan_list = 80 71 e4 ab b9 58 05 cb 39 17 fd b0 75 62 0f 2f Decoded freqs 16 (expected 16) Decoded: 17 31 45 58 79 81 97 113 127 213 277 287 311 331 391 1023 Random range test: range 127, max num ARFCNs 29 Random range test: range 255, max num ARFCNs 22 Random range test: range 511, max num ARFCNs 18 Random range test: range 1023, max num ARFCNs 16 BTS allocation OK in test_si2q_segfault() Test SI2quater UARFCN (same scrambling code and diversity): generating SI2quater for 0 EARFCNs and 1 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 2 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7f 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 2 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7f 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b BTS deallocated OK in test_si2q_segfault() BTS allocation OK in test_si2q_e() Testing SYSINFO_TYPE_2quater EARFCN generation: generating SI2quater for 0 EARFCNs and 0 UARFCNs... generated invalid SI2quater [00/00]: [23] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 added EARFCN 1917 - generating SI2quater for 1 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be e8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b removed EARFCN 1917 - generating SI2quater for 0 EARFCNs and 0 UARFCNs... generated invalid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be e8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b added EARFCN 1917 - generating SI2quater for 1 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be c8 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b added EARFCN 1932 - generating SI2quater for 2 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 30 14 03 2b 2b 2b 2b 2b 2b 2b 2b added EARFCN 1937 - generating SI2quater for 3 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a0 a0 2b 2b 2b 2b 2b 2b added EARFCN 1945 - generating SI2quater for 4 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a8 3c c8 28 0b 2b 2b 2b added EARFCN 1965 - generating SI2quater for 5 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b added EARFCN 1967 - generating SI2quater for 6 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/01]: [23] 59 06 07 40 20 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b generated valid SI2quater [01/01]: [23] 59 06 07 42 20 04 86 59 83 d7 e0 50 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b added EARFCN 1982 - generating SI2quater for 7 EARFCNs and 0 UARFCNs... generated valid SI2quater [00/01]: [23] 59 06 07 40 20 04 86 59 83 be cc 1e 31 07 91 a8 3c ca 0f 5a 0a 03 2b generated valid SI2quater [01/01]: [23] 59 06 07 42 20 04 86 59 83 d7 e4 1e fa c2 80 2b 2b 2b 2b 2b 2b 2b 2b BTS deallocated OK in test_si2q_e() BTS allocation OK in test_si2q_u() Testing SYSINFO_TYPE_2quater UARFCN generation: generating SI2quater for 0 EARFCNs and 0 UARFCNs... generated invalid SI2quater [00/00]: [23] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 generating SI2quater for 0 EARFCNs and 1 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 0c 1a 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 2 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 14 1a 1f 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 3 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 1c 7b d0 f7 03 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 4 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 24 b3 e4 e9 68 03 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 5 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 2c 7a 34 0e 4e e9 83 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 6 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 34 7a 34 0e 4e e9 85 03 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 7 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 3c 70 39 02 ce f7 85 0e 03 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 8 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 44 7a 34 05 e4 72 05 08 d5 0b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 9 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 4c 7a 34 0e 64 77 85 43 55 c8 0b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 10 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 50 1c 3b 31 fa dd 88 85 7b c4 1c 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 11 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 0f 7c 58 1c 3b 25 7a ea 08 91 fb c4 1f b0 2b 2b 2b BTS deallocated OK in test_si2q_u() BTS allocation OK in test_si2q_mu() Test SI2quater multiple UARFCNs: generating SI2quater for 0 EARFCNs and 1 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7c 0b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 2 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 0a 7e 0b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 3 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 12 7e e0 0b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 4 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 03 2b 2b 2b 2b 2b 2b generating SI2quater for 0 EARFCNs and 5 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 82 20 03 2b 2b generating SI2quater for 0 EARFCNs and 6 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 84 52 67 03 2b generating SI2quater for 0 EARFCNs and 7 UARFCNs... generated valid SI2quater [00/00]: [23] 59 06 07 40 00 25 52 88 0a 7d 52 e8 18 3f f4 90 54 ba 86 20 73 8c 81 BTS deallocated OK in test_si2q_mu() BTS allocation OK in test_si2q_long() Testing SYSINFO_TYPE_2quater combined EARFCN & UARFCN generation: generating SI2quater for 17 EARFCNs and 1 UARFCNs... generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 0c 1a 10 99 66 0f 04 83 c1 1c bb 2b 03 2b 2b generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 2 UARFCNs... generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 14 4d e7 00 44 b3 07 82 41 e0 8e 5d 95 83 2b generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 3 UARFCNs... generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 1c 4d e7 03 04 86 59 83 c1 20 f0 47 2e ca c1 generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c2 6c 1e 0f 60 f0 bb 08 3f d7 2e ca c1 2b generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 20 64 21 06 e1 08 55 08 53 d7 2e ca c1 2b generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 2a 64 21 56 e1 0a d5 08 49 d7 2e ca c1 2b generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 25 64 21 2e e1 09 94 e5 d9 58 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 4 UARFCNs... generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 24 59 fa 26 73 84 86 59 83 c1 1c bb 2b 03 2b generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c1 20 f0 9b 07 83 d8 3c 2e b9 76 56 0b 2b generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 1f ec 21 03 21 08 37 08 42 a7 2e ca c1 2b generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 29 ec 21 53 21 0a b7 08 56 a7 2e ca c1 2b generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 24 ec 21 2b 21 09 77 08 4c a7 2e ca c1 2b generating SI2quater for 17 EARFCNs and 5 UARFCNs... generated valid SI2quater [00/04]: [23] 59 06 07 40 80 25 0f 70 2c 59 fa 30 73 f6 04 86 59 83 c1 1c bb 2b 03 generated valid SI2quater [01/04]: [23] 59 06 07 42 80 04 86 59 83 c1 20 f0 9b 07 83 d8 3c 2e b9 76 56 0b 2b generated valid SI2quater [02/04]: [23] 59 06 07 44 80 04 86 59 84 1f ec 21 03 21 08 37 08 42 a7 2e ca c1 2b generated valid SI2quater [03/04]: [23] 59 06 07 46 80 04 86 59 84 29 ec 21 53 21 0a b7 08 56 a7 2e ca c1 2b generated valid SI2quater [04/04]: [23] 59 06 07 48 80 04 86 59 84 24 ec 21 2b 21 09 77 08 4c a7 2e ca c1 2b generating SI2quater for 17 EARFCNs and 6 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 34 f1 ae 15 f3 f4 83 04 86 59 72 ec ac 0b 2b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 7 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 3c f1 ae 15 f3 f4 83 01 84 86 59 72 ec ac 0b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 8 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 45 19 a0 0d 7d 7e a6 19 e7 0b 2b 2b 2b 2b 2b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 9 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 4d 19 a0 26 fd 66 a6 03 e7 fa 0b 2b 2b 2b 2b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 10 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 55 47 89 1e fd 7c b0 00 e7 9b b0 2b 2b 2b 2b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b generating SI2quater for 17 EARFCNs and 11 UARFCNs... generated valid SI2quater [00/05]: [23] 59 06 07 40 a0 25 0f 70 5d 47 89 1e fd 7c b0 01 67 9b b3 f8 2b 2b 2b generated valid SI2quater [01/05]: [23] 59 06 07 42 a0 04 86 59 83 c1 20 f0 48 3c 26 c1 e0 f5 cb b2 b0 2b 2b generated valid SI2quater [02/05]: [23] 59 06 07 44 a0 04 86 59 83 c2 ec 20 ff 61 08 19 08 41 b7 2e ca c1 2b generated valid SI2quater [03/05]: [23] 59 06 07 46 a0 04 86 59 84 21 54 21 4f 61 0a 99 08 55 b7 2e ca c1 2b generated valid SI2quater [04/05]: [23] 59 06 07 48 a0 04 86 59 84 2b 54 21 27 61 09 59 08 4b b7 2e ca c1 2b generated valid SI2quater [05/05]: [23] 59 06 07 4a a0 04 86 59 84 26 53 97 65 60 2b 2b 2b 2b 2b 2b 2b 2b 2b BTS deallocated OK in test_si2q_long() BTS allocation OK in test_si_ba_ind() Testing if BA-IND is set as expected in SI2xxx and SI5xxx SI2: 59 06 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 SI2bis: 59 06 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e5 04 00 2b SI2ter: 59 06 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 2b 2b 2b SI5: 06 1d 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 SI5bis: 06 05 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 SI5ter: 06 06 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BTS deallocated OK in test_si_ba_ind() test_gsm48_ra_id_by_bts[0]: digits='00f120' lac=0x0300=htons(3) rac=0x04=4 pass test_gsm48_ra_id_by_bts[1]: digits='002100' lac=0x0300=htons(3) rac=0x04=4 pass test_gsm48_ra_id_by_bts[2]: digits='00f000' lac=0x0000=htons(0) rac=0x00=0 pass test_gsm48_ra_id_by_bts[3]: digits='000000' lac=0x0000=htons(0) rac=0x00=0 pass test_gsm48_ra_id_by_bts[4]: digits='999999' lac=0xffff=htons(65535) rac=0xff=255 pass test_gsm48_ra_id_by_bts[5]: digits='09f909' lac=0xcdab=htons(43981) rac=0xab=171 pass test_gsm48_ra_id_by_bts[6]: digits='090990' lac=0xcdab=htons(43981) rac=0xab=171 pass Done. osmo-bsc-1.3.0/tests/handover/000077500000000000000000000000001332665256100162165ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/handover/Makefile.am000066400000000000000000000040501332665256100202510ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMOMGCPCLIENT_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) EXTRA_DIST = \ handover_test.ok \ $(NULL) noinst_PROGRAMS = \ handover_test \ $(NULL) handover_test_SOURCES = \ handover_test.c \ $(NULL) handover_test_LDFLAGS =\ -Wl,--wrap=abis_rsl_sendmsg,--wrap=mgcp_conn_modify,--wrap=mgcp_conn_delete\ $(NULL) handover_test_LDADD = \ $(top_builddir)/src/osmo-bsc/a_reset.o \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/abis_rsl.o \ $(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \ $(top_builddir)/src/osmo-bsc/bsc_api.o \ $(top_builddir)/src/osmo-bsc/bsc_dyn_ts.o \ $(top_builddir)/src/osmo-bsc/bsc_init.o \ $(top_builddir)/src/osmo-bsc/bsc_rll.o \ $(top_builddir)/src/osmo-bsc/bsc_subscr_conn_fsm.o \ $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \ $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts.o \ $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \ $(top_builddir)/src/osmo-bsc/bts_sysmobts.o \ $(top_builddir)/src/osmo-bsc/chan_alloc.o \ $(top_builddir)/src/osmo-bsc/gsm_04_08_utils.o \ $(top_builddir)/src/osmo-bsc/gsm_04_80_utils.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(top_builddir)/src/osmo-bsc/handover_cfg.o \ $(top_builddir)/src/osmo-bsc/handover_decision.o \ $(top_builddir)/src/osmo-bsc/handover_decision_2.o \ $(top_builddir)/src/osmo-bsc/handover_logic.o \ $(top_builddir)/src/osmo-bsc/meas_rep.o \ $(top_builddir)/src/osmo-bsc/osmo_bsc_lcls.o \ $(top_builddir)/src/osmo-bsc/net_init.o \ $(top_builddir)/src/osmo-bsc/paging.o \ $(top_builddir)/src/osmo-bsc/pcu_sock.o \ $(top_builddir)/src/osmo-bsc/penalty_timers.o \ $(top_builddir)/src/osmo-bsc/rest_octets.o \ $(top_builddir)/src/osmo-bsc/system_information.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBOSMOMGCPCLIENT_LIBS) \ $(NULL) osmo-bsc-1.3.0/tests/handover/handover_test.c000066400000000000000000001443211332665256100212340ustar00rootroot00000000000000/* * (C) 2013 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 Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void *ctx; struct gsm_network *bsc_gsmnet; /* override, requires '-Wl,--wrap=mgcp_conn_modify'. * Catch modification of an MGCP connection. */ int __real_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); int __wrap_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) { /* CAUTION HACK: * * The pointer fi is misused to pass a reference to GSCON FSM ! * * This function is called from gscon_fsm_wait_ho_compl() from * bsc_subscr_conn_fsm.c when GSCON_EV_HO_COMPL is dispatched to the * GSCON FSM. By then, the GSCON FSM has already changed to the state * ST_WAIT_MDCX_BTS_HO (see gscon_fsm_wait_mdcx_bts_ho()) and waits for * GSCON_EV_MGW_MDCX_RESP_BTS. The signal GSCON_EV_MGW_MDCX_RESP_BTS * is sent to this function using the parameter parent_evt. So we * implicitly know the event that is needed to simulate a successful * MGW negotiation to the GSCON FSM. All we need to do is to dispatch * parent_evt back to the GSCON FSM in order to make it think that the * MGW negotiation is done. * * Unfortunately, there is a problem with this test implementation. * in order to simplfy the test we do not allocate any MGCP Client * FSM but the GSCON FSM will call this function with the fi pointer * pointing to the MGCP Client FSM. This means we get a nullpointer * here and there is no way to distinguish which GSCON FSM called * the function at all (normally we would know through the parent * pointer). * * To get around this problem we populate the fi pointer with the * reference to the GSCON FSM itsself, so we can know who called the * function. This is a misuse of the pointer since it normally would * hold an MGCP Client FSM instead of a GSCON FSM. * * See also note in function create_conn() */ osmo_fsm_inst_dispatch(fi, parent_evt, NULL); return 0; } /* override, requires '-Wl,--wrap=mgcp_conn_delete'. * Catch deletion of an MGCP connection. */ int __real_mgcp_conn_delete(struct osmo_fsm_inst *fi); int __wrap_mgcp_conn_delete(struct osmo_fsm_inst *fi) { /* Just do nothing and pretend that everything went well. * We never have allocatec any MGCP connections. */ return 0; } /* measurement report */ uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0; uint8_t meas_dl_rxlev = 0, meas_dl_rxqual = 0; uint8_t meas_ul_rxlev = 0, meas_ul_rxqual = 0; uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0, meas_ta_ms = 0; uint8_t meas_dtx_ms = 0, meas_dtx_bs = 0, meas_nr = 0; uint8_t meas_num_nc = 0, meas_rxlev_nc[6], meas_bsic_nc[6], meas_bcch_f_nc[6]; static void gen_meas_rep(struct gsm_lchan *lchan) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_dchan_hdr *dh; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t ulm[3], l1i[2], *buf; struct gsm48_hdr *gh; struct gsm48_meas_res *mr; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->c.msg_type = RSL_MT_MEAS_RES; dh->ie_chan = RSL_IE_CHAN_NR; dh->chan_nr = chan_nr; msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, meas_nr++); ulm[0] = meas_ul_rxlev | (meas_dtx_bs << 7); ulm[1] = meas_ul_rxlev; ulm[2] = (meas_ul_rxqual << 3) | meas_ul_rxqual; msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, sizeof(ulm), ulm); msgb_tv_put(msg, RSL_IE_BS_POWER, meas_tx_power_bs); l1i[0] = 0; l1i[1] = meas_ta_ms; msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(l1i), l1i); buf = msgb_put(msg, 3); buf[0] = RSL_IE_L3_INFO; buf[1] = (sizeof(*gh) + sizeof(*mr)) >> 8; buf[2] = (sizeof(*gh) + sizeof(*mr)) & 0xff; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); mr = (struct gsm48_meas_res *) msgb_put(msg, sizeof(*mr)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = GSM48_MT_RR_MEAS_REP; /* measurement results */ mr->rxlev_full = meas_dl_rxlev; mr->rxlev_sub = meas_dl_rxlev; mr->rxqual_full = meas_dl_rxqual; mr->rxqual_sub = meas_dl_rxqual; mr->dtx_used = meas_dtx_ms; mr->ba_used = meas_rep_ba; mr->meas_valid = !meas_valid; /* 0 = valid */ if (meas_rep_valid) { mr->no_nc_n_hi = meas_num_nc >> 2; mr->no_nc_n_lo = meas_num_nc & 3; } else { /* no results for serving cells */ mr->no_nc_n_hi = 1; mr->no_nc_n_lo = 3; } mr->rxlev_nc1 = meas_rxlev_nc[0]; mr->rxlev_nc2_hi = meas_rxlev_nc[1] >> 1; mr->rxlev_nc2_lo = meas_rxlev_nc[1] & 1; mr->rxlev_nc3_hi = meas_rxlev_nc[2] >> 2; mr->rxlev_nc3_lo = meas_rxlev_nc[2] & 3; mr->rxlev_nc4_hi = meas_rxlev_nc[3] >> 3; mr->rxlev_nc4_lo = meas_rxlev_nc[3] & 7; mr->rxlev_nc5_hi = meas_rxlev_nc[4] >> 4; mr->rxlev_nc5_lo = meas_rxlev_nc[4] & 15; mr->rxlev_nc6_hi = meas_rxlev_nc[5] >> 5; mr->rxlev_nc6_lo = meas_rxlev_nc[5] & 31; mr->bsic_nc1_hi = meas_bsic_nc[0] >> 3; mr->bsic_nc1_lo = meas_bsic_nc[0] & 7; mr->bsic_nc2_hi = meas_bsic_nc[1] >> 4; mr->bsic_nc2_lo = meas_bsic_nc[1] & 15; mr->bsic_nc3_hi = meas_bsic_nc[2] >> 5; mr->bsic_nc3_lo = meas_bsic_nc[2] & 31; mr->bsic_nc4 = meas_bsic_nc[3]; mr->bsic_nc5 = meas_bsic_nc[4]; mr->bsic_nc6 = meas_bsic_nc[5]; mr->bcch_f_nc1 = meas_bcch_f_nc[0]; mr->bcch_f_nc2 = meas_bcch_f_nc[1]; mr->bcch_f_nc3 = meas_bcch_f_nc[2]; mr->bcch_f_nc4 = meas_bcch_f_nc[3]; mr->bcch_f_nc5_hi = meas_bcch_f_nc[4] >> 1; mr->bcch_f_nc5_lo = meas_bcch_f_nc[4] & 1; mr->bcch_f_nc6_hi = meas_bcch_f_nc[5] >> 2; mr->bcch_f_nc6_lo = meas_bcch_f_nc[5] & 3; msg->dst = lchan->ts->trx->bts->c0->rsl_link; msg->l2h = (unsigned char *)dh; msg->l3h = (unsigned char *)gh; abis_rsl_rcvmsg(msg); } static struct gsm_bts *create_bts(int arfcn) { struct gsm_bts *bts; struct e1inp_sign_link *rsl_link; int i; bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_OSMOBTS, 0x3f); if (!bts) { printf("No resource for bts1\n"); return NULL; } bts->location_area_code = 23; bts->c0->arfcn = arfcn; bts->codec.efr = 1; bts->codec.hr = 1; bts->codec.amr = 1; rsl_link = talloc_zero(ctx, struct e1inp_sign_link); rsl_link->trx = bts->c0; bts->c0->rsl_link = rsl_link; bts->c0->mo.nm_state.operational = NM_OPSTATE_ENABLED; bts->c0->mo.nm_state.availability = NM_AVSTATE_OK; bts->c0->bb_transc.mo.nm_state.operational = NM_OPSTATE_ENABLED; bts->c0->bb_transc.mo.nm_state.availability = NM_AVSTATE_OK; /* 4 full rate and 4 half rate channels */ for (i = 1; i <= 6; i++) { bts->c0->ts[i].pchan = (i < 5) ? GSM_PCHAN_TCH_F : GSM_PCHAN_TCH_H; bts->c0->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED; bts->c0->ts[i].mo.nm_state.availability = NM_AVSTATE_OK; bts->c0->ts[i].lchan[0].type = GSM_LCHAN_NONE; bts->c0->ts[i].lchan[0].state = LCHAN_S_NONE; bts->c0->ts[i].lchan[1].type = GSM_LCHAN_NONE; bts->c0->ts[i].lchan[1].state = LCHAN_S_NONE; } return bts; } void create_conn(struct gsm_lchan *lchan) { static unsigned int next_imsi = 0; char imsi[sizeof(lchan->conn->bsub->imsi)]; struct gsm_network *net = lchan->ts->trx->bts->network; struct gsm_subscriber_connection *conn; conn = bsc_subscr_con_allocate(net); /* CAUTION HACK: When __real_mgcp_conn_modify() is called by the GSCON * FSM, then we need to know the reference to caller FSM (GSCON FSM). * Unfortunately the function __real_mgcp_conn_modify() is called with * fi_bts, which is unpopulated in this setup. The real function would * perform the communication with the MGW and then dispatch a signal * back to the parent FSM. Since we do not have all that in this setup * we populate the fi_bts pointer with a reference to the GSCON FSM in * order to have it available later in __real_mgcp_conn_modify(). */ conn->user_plane.fi_bts = conn->fi; lchan->conn = conn; conn->lchan = lchan; /* Make up a new IMSI for this test, for logging the subscriber */ next_imsi ++; snprintf(imsi, sizeof(imsi), "%06u", next_imsi); lchan->conn->bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, imsi); /* kick the FSM from INIT through to the ACTIVE state */ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, NULL); osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, NULL); } /* create lchan */ struct gsm_lchan *create_lchan(struct gsm_bts *bts, int full_rate, char *codec) { struct gsm_lchan *lchan; lchan = lchan_alloc(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H, 0); if (!lchan) { printf("No resource for lchan\n"); exit(EXIT_FAILURE); } lchan->state = LCHAN_S_ACTIVE; create_conn(lchan); if (!strcasecmp(codec, "FR") && full_rate) lchan->tch_mode = GSM48_CMODE_SPEECH_V1; else if (!strcasecmp(codec, "HR") && !full_rate) lchan->tch_mode = GSM48_CMODE_SPEECH_V1; else if (!strcasecmp(codec, "EFR") && full_rate) lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; else if (!strcasecmp(codec, "AMR")) lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; else { printf("Given codec unknown\n"); exit(EXIT_FAILURE); } lchan->conn->codec_list = (struct gsm0808_speech_codec_list){ .codec = { { .fi=true, .type=GSM0808_SCT_FR1, }, { .fi=true, .type=GSM0808_SCT_FR2, }, { .fi=true, .type=GSM0808_SCT_FR3, }, { .fi=true, .type=GSM0808_SCT_HR1, }, { .fi=true, .type=GSM0808_SCT_HR3, }, }, .len = 5, }; lchan->conn->codec_list_present = true; return lchan; } /* parse channel request */ static int got_chan_req = 0; static struct gsm_lchan *chan_req_lchan = NULL; static int parse_chan_act(struct gsm_lchan *lchan, uint8_t *data) { chan_req_lchan = lchan; return 0; } static int parse_chan_rel(struct gsm_lchan *lchan, uint8_t *data) { chan_req_lchan = lchan; return 0; } /* parse handover request */ static int got_ho_req = 0; static struct gsm_lchan *ho_req_lchan = NULL; static int parse_ho_command(struct gsm_lchan *lchan, uint8_t *data, int len) { struct gsm48_hdr *gh = (struct gsm48_hdr *) data; struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *) gh->data; int arfcn; struct gsm_bts *neigh; switch (gh->msg_type) { case GSM48_MT_RR_HANDO_CMD: arfcn = (ho->cell_desc.arfcn_hi << 8) | ho->cell_desc.arfcn_lo; /* look up trx. since every dummy bts uses different arfcn and * only one trx, it is simple */ llist_for_each_entry(neigh, &bsc_gsmnet->bts_list, list) { if (neigh->c0->arfcn != arfcn) continue; ho_req_lchan = lchan; return 0; } break; case GSM48_MT_RR_ASS_CMD: ho_req_lchan = lchan; return 0; break; default: fprintf(stderr, "Error, expecting HO or AS command\n"); return -EINVAL; } return -1; } /* send channel activation ack */ static void send_chan_act_ack(struct gsm_lchan *lchan, int act) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_dchan_hdr *dh; dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; dh->c.msg_type = (act) ? RSL_MT_CHAN_ACTIV_ACK : RSL_MT_RF_CHAN_REL_ACK; dh->ie_chan = RSL_IE_CHAN_NR; dh->chan_nr = gsm_lchan2chan_nr(lchan); msg->dst = lchan->ts->trx->bts->c0->rsl_link; msg->l2h = (unsigned char *)dh; abis_rsl_rcvmsg(msg); } /* send handover complete */ static void send_ho_complete(struct gsm_lchan *lchan, bool success) { struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL"); struct abis_rsl_rll_hdr *rh; uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t *buf; struct gsm48_hdr *gh; struct gsm48_ho_cpl *hc; rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh)); rh->c.msg_discr = ABIS_RSL_MDISC_RLL; rh->c.msg_type = RSL_MT_DATA_IND; rh->ie_chan = RSL_IE_CHAN_NR; rh->chan_nr = chan_nr; rh->ie_link_id = RSL_IE_LINK_IDENT; rh->link_id = 0x00; buf = msgb_put(msg, 3); buf[0] = RSL_IE_L3_INFO; buf[1] = (sizeof(*gh) + sizeof(*hc)) >> 8; buf[2] = (sizeof(*gh) + sizeof(*hc)) & 0xff; gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); hc = (struct gsm48_ho_cpl *) msgb_put(msg, sizeof(*hc)); gh->proto_discr = GSM48_PDISC_RR; gh->msg_type = success ? GSM48_MT_RR_HANDO_COMPL : GSM48_MT_RR_HANDO_FAIL; msg->dst = lchan->ts->trx->bts->c0->rsl_link; msg->l2h = (unsigned char *)rh; msg->l3h = (unsigned char *)gh; abis_rsl_rcvmsg(msg); } /* override, requires '-Wl,--wrap=abis_rsl_sendmsg'. * Catch RSL messages sent towards the BTS. */ int __real_abis_rsl_sendmsg(struct msgb *msg); int __wrap_abis_rsl_sendmsg(struct msgb *msg) { struct abis_rsl_dchan_hdr *dh = (struct abis_rsl_dchan_hdr *) msg->data; struct e1inp_sign_link *sign_link = msg->dst; int rc; struct gsm_lchan *lchan = rsl_lchan_lookup(sign_link->trx, dh->chan_nr, &rc); if (rc) { printf("rsl_lchan_lookup() failed\n"); exit(1); } switch (dh->c.msg_type) { case RSL_MT_CHAN_ACTIV: rc = parse_chan_act(lchan, dh->data); if (rc == 0) got_chan_req = 1; break; case RSL_MT_RF_CHAN_REL: rc = parse_chan_rel(lchan, dh->data); if (rc == 0) send_chan_act_ack(chan_req_lchan, 0); break; case RSL_MT_DATA_REQ: rc = parse_ho_command(lchan, msg->l3h, msgb_l3len(msg)); if (rc == 0) got_ho_req = 1; break; case RSL_MT_IPAC_CRCX: break; default: printf("unknown rsl message=0x%x\n", dh->c.msg_type); } return 0; } /* test cases */ static char *test_case_0[] = { "2", "Stay in better cell\n\n" "There are many neighbor cells, but only the current cell is the best\n" "cell, so no handover is performed\n", "create-bts", "7", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "30","0", "6","0","20","1","21","2","18","3","20","4","23","5","19", "expect-no-chan", NULL }; static char *test_case_1[] = { "2", "Handover to best better cell\n\n" "The best neighbor cell is selected\n", "create-bts", "7", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "10","0", "6","0","20","1","21","2","18","3","20","4","23","5","19", "expect-chan", "5", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_2[] = { "2", "Handover and Assignment must be enabled\n\n" "This test will start with disabled assignment and handover. A\n" "better neighbor cell (assignment enabled) will not be selected and \n" "also no assignment from TCH/H to TCH/F to improve quality. There\n" "will be no handover nor assignment. After enabling assignment on the\n" "current cell, the MS will assign to TCH/F. After enabling handover\n" "in the current cell, but disabling in the neighbor cell, handover\n" "will not be performed, until it is enabled in the neighbor cell too.\n", "create-bts", "2", "afs-rxlev-improve", "0", "5", "create-ms", "0", "TCH/H", "AMR", "as-enable", "0", "0", "ho-enable", "0", "0", "meas-rep", "0", "0","0", "1","0","30", "expect-no-chan", "as-enable", "0", "1", "meas-rep", "0", "0","0", "1","0","30", "expect-chan", "0", "1", "ack-chan", "expect-ho", "0", "5", "ho-complete", "ho-enable", "0", "1", "ho-enable", "1", "0", "meas-rep", "0", "0","0", "1","0","30", "expect-no-chan", "ho-enable", "1", "1", "meas-rep", "0", "0","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_3[] = { "2", "Penalty timer must not run\n\n" "The MS will try to handover to a better cell, but this will fail.\n" "Even though the cell is still better, handover will not be performed\n" "due to penalty timer after handover failure\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-failed", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", NULL }; static char *test_case_4[] = { "2", "TCH/H keeping with HR codec\n\n" "The MS is using half rate V1 codec, but the better cell is congested\n" "at TCH/H slots. As the congestion is removed, the handover takes\n" "place.\n", "create-bts", "2", "set-min-free", "1", "TCH/H", "4", "create-ms", "0", "TCH/H", "HR", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", "set-min-free", "1", "TCH/H", "3", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "5", "ack-chan", "expect-ho", "0", "5", "ho-complete", NULL }; static char *test_case_5[] = { "2", "TCH/F keeping with FR codec\n\n" "The MS is using full rate V1 codec, but the better cell is congested\n" "at TCH/F slots. As the congestion is removed, the handover takes\n" "place.\n", "create-bts", "2", "set-min-free", "1", "TCH/F", "4", "create-ms", "0", "TCH/F", "FR", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", "set-min-free", "1", "TCH/F", "3", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_6[] = { "2", "TCH/F keeping with EFR codec\n\n" "The MS is using full rate V2 codec, but the better cell is congested\n" "at TCH/F slots. As the congestion is removed, the handover takes\n" "place.\n", "create-bts", "2", "set-min-free", "1", "TCH/F", "4", "create-ms", "0", "TCH/F", "EFR", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", "set-min-free", "1", "TCH/F", "3", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_7[] = { "2", "TCH/F to TCH/H changing with AMR codec\n\n" "The MS is using AMR V3 codec, the better cell is congested at TCH/F\n" "slots. The handover is performed to non-congested TCH/H slots.\n", "create-bts", "2", "set-min-free", "1", "TCH/F", "4", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "5", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_8[] = { "2", "No handover to a cell with no slots available\n\n" "If no slot is available, no handover is performed\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/H", "AMR", "create-ms", "1", "TCH/H", "AMR", "create-ms", "1", "TCH/H", "AMR", "create-ms", "1", "TCH/H", "AMR", "meas-rep", "0", "0","0", "1","0","30", "expect-no-chan", NULL }; static char *test_case_9[] = { "2", "No more parallel handovers, if max_unsync_ho is defined\n\n" "There are tree mobiles that want to handover, but only two can do\n" "it at a time, because the maximum number is limited to two.\n", "create-bts", "2", "set-max-ho", "1", "2", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "0","0", "1","0","30", "expect-chan", "1", "1", "meas-rep", "1", "0","0", "1","0","30", "expect-chan", "1", "2", "meas-rep", "2", "0","0", "1","0","30", "expect-no-chan", NULL }; static char *test_case_10[] = { "2", "Hysteresis\n\n" "If neighbor cell is better, handover is only performed if the\n" "ammount of improvement is greater or equal hyteresis\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "27","0", "1","0","30", "expect-no-chan", "meas-rep", "0", "26","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_11[] = { "2", "No Hysteresis and minimum RX level\n\n" "If current cell's RX level is below mimium level, handover must be\n" "performed, no matter of the hysteresis. First do not perform\n" "handover to better neighbor cell, because the hysteresis is not\n" "met. Second do not perform handover because better neighbor cell is\n" "below minimum RX level. Third perform handover because current cell\n" "is below minimum RX level, even if the better neighbor cell (minimum\n" "RX level reached) does not meet the hysteresis.\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "10","0", "1","0","11", "expect-no-chan", "meas-rep", "0", "8","0", "1","0","9", "expect-no-chan", "meas-rep", "0", "9","0", "1","0","10", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_12[] = { "2", "No handover to congested cell\n\n" "The better neighbor cell is congested, so no handover is performed.\n" "After the congestion is over, handover will be performed.\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", "set-min-free", "1", "TCH/F", "3", "set-min-free", "1", "TCH/H", "3", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_13[] = { "2", "Handover to balance congestion\n\n" "The current and the better cell are congested, so no handover is\n" "performed. This is because handover would congest the neighbor cell\n" "more. After congestion raises in the current cell, the handover is\n" "performed to balance congestion\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "set-min-free", "0", "TCH/F", "4", "set-min-free", "0", "TCH/H", "4", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "20","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_14[] = { "2", "Handover to congested cell, if RX level is below minimum\n\n" "The better neighbor cell is congested, so no handover is performed.\n" "If the RX level of the current cell drops below minimum acceptable\n" "level, the handover is performed.\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", "meas-rep", "0", "10","0", "1","0","30", "expect-no-chan", "meas-rep", "0", "9","0", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_15[] = { "2", "Handover to cell with worse RXLEV, if RXQUAL is below minimum\n\n" "The neighbor cell has worse RXLEV, so no handover is performed.\n" "If the RXQUAL of the current cell drops below minimum acceptable\n" "level, the handover is performed. It is also required that 10\n" "reports are received, before RXQUAL is checked.\n", /* (See also test 28, which tests for RXQUAL triggering HO to congested cell.) */ /* TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference. * See Performence Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter * 2.1.1, "Interference" in the list of triggers on p.157. */ "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-no-chan", "meas-rep", "0", "40","6", "1","0","30", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_16[] = { "2", "Handover due to maximum TA exceeded\n\n" "The MS in the current (best) cell has reached maximum allowed timing\n" "advance. No handover is performed until the timing advance exceeds\n" "it. The originating cell is still the best, but no handover is\n" "performed back to that cell, because the penalty timer (due to\n" "maximum allowed timing advance) is running.\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "set-max-ta", "0", "5", /* of cell */ "set-ta", "0", "5", /* of ms */ "meas-rep", "0", "30","0", "1","0","20", "expect-no-chan", "set-ta", "0", "6", /* of ms */ "meas-rep", "0", "30","0", "1","0","20", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", "meas-rep", "0", "20","0", "1","0","30", "expect-no-chan", NULL }; static char *test_case_17[] = { "2", "Congestion check: No congestion\n\n" "Three cells have different number of used slots, but there is no\n" "congestion in any of these cells. No handover is performed.\n", "create-bts", "3", "set-min-free", "0", "TCH/F", "2", "set-min-free", "0", "TCH/H", "2", "set-min-free", "1", "TCH/F", "2", "set-min-free", "1", "TCH/H", "2", "set-min-free", "2", "TCH/F", "2", "set-min-free", "2", "TCH/H", "2", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/H", "AMR", "meas-rep", "0", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "1", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "2", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "3", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "4", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "5", "30","0", "2","0","20","1","20", "expect-no-chan", "congestion-check", "expect-no-chan", NULL }; static char *test_case_18[] = { "2", "Congestion check: One out of three cells is congested\n\n" "Three cells have different number of used slots, but there is\n" "congestion at TCH/F in the first cell. Handover is performed with\n" "the best candidate.\n", "create-bts", "3", "set-min-free", "0", "TCH/F", "2", "set-min-free", "0", "TCH/H", "2", "set-min-free", "1", "TCH/F", "2", "set-min-free", "1", "TCH/H", "2", "set-min-free", "2", "TCH/F", "2", "set-min-free", "2", "TCH/H", "2", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "1", "TCH/F", "AMR", "create-ms", "1", "TCH/H", "AMR", "meas-rep", "0", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "1", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "2", "30","0", "2","0","21","1","20", "expect-no-chan", "meas-rep", "3", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "4", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "5", "30","0", "2","0","20","1","20", "expect-no-chan", "meas-rep", "6", "30","0", "2","0","20","1","20", "expect-no-chan", "congestion-check", "expect-chan", "1", "2", "ack-chan", "expect-ho", "0", "3", /* best candidate is MS 2 at BTS 1, TS 3 */ "ho-complete", NULL }; static char *test_case_19[] = { "2", "Congestion check: Balancing over congested cells\n\n" "Two cells are congested, but the second cell is more congested.\n" "Handover is performed to solve the congestion.\n", "create-bts", "2", "set-min-free", "0", "TCH/F", "4", "set-min-free", "1", "TCH/F", "4", "create-ms", "0", "TCH/F", "FR", "create-ms", "0", "TCH/F", "FR", "create-ms", "0", "TCH/F", "FR", "create-ms", "1", "TCH/F", "FR", "meas-rep", "0", "30","0", "1","0","20", "expect-no-chan", "meas-rep", "1", "30","0", "1","0","21", "expect-no-chan", "meas-rep", "2", "30","0", "1","0","20", "expect-no-chan", "meas-rep", "3", "30","0", "1","0","20", "expect-no-chan", "congestion-check", "expect-chan", "1", "2", "ack-chan", "expect-ho", "0", "2", /* best candidate is MS 1 at BTS 0, TS 2 */ "ho-complete", NULL }; static char *test_case_20[] = { "2", "Congestion check: Solving congestion by handover TCH/F -> TCH/H\n\n" "Two BTS, one MS in the first congested BTS must handover to\n" "non-congested TCH/H of second BTS, in order to solve congestion\n", "create-bts", "2", "set-min-free", "0", "TCH/F", "4", "set-min-free", "0", "TCH/H", "4", "set-min-free", "1", "TCH/F", "4", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "30","0", "1","0","30", "expect-no-chan", "congestion-check", "expect-chan", "1", "5", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_21[] = { "2", "Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n" "Two BTS, one MS in the first congested BTS must handover to\n" "less-congested TCH/H of second BTS, in order to balance congestion\n", "create-bts", "2", "set-min-free", "0", "TCH/F", "4", "set-min-free", "0", "TCH/H", "4", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/F", "AMR", "create-ms", "0", "TCH/H", "AMR", "meas-rep", "0", "30","0", "1","0","30", "expect-no-chan", "congestion-check", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_22[] = { "2", "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" "There is only one BTS. The TCH/H slots are congested. Since\n" "assignment is performed to less-congested TCH/F, the candidate with\n" "the worst RX level is chosen.\n", "create-bts", "1", "set-min-free", "0", "TCH/F", "4", "set-min-free", "0", "TCH/H", "4", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "meas-rep", "0", "30","0", "0", "meas-rep", "1", "34","0", "0", "meas-rep", "2", "20","0", "0", "expect-no-chan", "congestion-check", "expect-chan", "0", "1", "ack-chan", "expect-ho", "0", "6", "ho-complete", NULL }; static char *test_case_23[] = { "2", "Story: 'A neighbor is your friend'\n", "create-bts", "3", "print", "Andreas is driving along the coast, on a sunny june afternoon.\n" "Suddenly he is getting a call from his friend and neighbor Axel.\n" "\n" "What happens: Two MS are created, #0 for Axel, #1 for Andreas.", /* Axel */ "create-ms", "2", "TCH/F", "AMR", /* andreas */ "create-ms", "0", "TCH/F", "AMR", "meas-rep", "1", "40","0", "1","0","30", "expect-no-chan", "print", "Axel asks Andreas if he would like to join them for a barbecue.\n" "Axel's house is right in the neighborhood and the weather is fine.\n" "Andreas agrees, so he drives to a close store to buy some barbecue\n" "skewers.\n" "\n" "What happens: While driving, a different cell (mounted atop the\n" "store) becomes better.", /* drive to bts 1 */ "meas-rep", "1", "20","0", "1","0","35", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", "print", "While Andreas is walking into the store, Axel asks, if he could also\n" "bring some beer. Andreas has problems understanding him: \"I have a\n" "bad reception here. The cell tower is right atop the store, but poor\n" "coverage inside. Can you repeat please?\"\n" "\n" "What happens: Inside the store the close cell is so bad, that\n" "handover back to the previous cell is required.", /* bts 1 becomes bad, so bts 0 helps out */ "meas-rep", "1", "5","0", "1","0","20", "expect-chan", "0", "1", "ack-chan", "expect-ho", "1", "1", "ho-complete", "print", "After Andreas bought skewers and beer, he leaves the store.\n" "\n" "What happens: Outside the store the close cell is better again, so\n" "handover back to the that cell is performed.", /* bts 1 becomes better again */ "meas-rep", "1", "20","0", "1","0","35", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", "print", /* bts 2 becomes better */ "Andreas drives down to the lake where Axel's house is.\n" "\n" "What happens: There is a small cell at Axel's house, which becomes\n" "better, because the current cell has no good comverage at the lake.", "meas-rep", "1", "14","0", "2","0","2","1","63", "expect-chan", "2", "2", "ack-chan", "expect-ho", "1", "1", "ho-complete", "print", "Andreas wonders why he still has good radio coverage: \"Last time it\n" "was so bad\". Axel says: \"I installed a pico cell in my house,\n" "now we can use our mobile phones down here at the lake.\"", NULL }; static char *test_case_24[] = { "2", "No (or not enough) measurements for handover\n\n" "Do not solve congestion in cell, because there is no measurement.\n" "As soon as enough measurments available (1 in our case), perform\n" "handover. Afterwards the old cell becomes congested and the new\n" "cell is not. Do not perform handover until new measurements are\n" "received.\n", /* two cells, first in congested, but no handover */ "create-bts", "2", "set-min-free", "0", "TCH/F", "4", "set-min-free", "0", "TCH/H", "4", "create-ms", "0", "TCH/F", "AMR", "congestion-check", "expect-no-chan", /* send measurement and trigger congestion check */ "meas-rep", "0", "20","0", "1","0","20", "expect-no-chan", "congestion-check", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", /* congest the first cell and remove congestion from second cell */ "set-min-free", "0", "TCH/F", "0", "set-min-free", "0", "TCH/H", "0", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", /* no handover until measurements applied */ "congestion-check", "expect-no-chan", "meas-rep", "0", "20","0", "1","0","20", "expect-no-chan", "congestion-check", "expect-chan", "0", "1", "ack-chan", "expect-ho", "1", "1", "ho-complete", NULL }; static char *test_case_25[] = { "1", "Stay in better cell\n\n" "There are many neighbor cells, but only the current cell is the best\n" "cell, so no handover is performed\n", "create-bts", "7", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "30","0", "6","0","20","1","21","2","18","3","20","4","23","5","19", "expect-no-chan", NULL }; static char *test_case_26[] = { "1", "Handover to best better cell\n\n" "The best neighbor cell is selected\n", "create-bts", "7", "create-ms", "0", "TCH/F", "AMR", "meas-rep", "0", "10","0", "6","0","20","1","21","2","18","3","20","4","23","5","19", "expect-chan", "5", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char *test_case_27[] = { "2", "Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n" "There is only one BTS. The TCH/H slots are congested. Since\n" "assignment is performed to less-congested TCH/F, the candidate with\n" "the worst RX level is chosen. (So far like test 22.)\n" "After that, trigger more congestion checks to ensure stability.\n", "create-bts", "1", "set-min-free", "0", "TCH/F", "2", "set-min-free", "0", "TCH/H", "4", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "create-ms", "0", "TCH/H", "AMR", "meas-rep", "0", "30","0", "0", "meas-rep", "1", "34","0", "0", "meas-rep", "2", "20","0", "0", "expect-no-chan", "congestion-check", "expect-chan", "0", "1", "ack-chan", "expect-ho", "0", "6", "ho-complete", "congestion-check", "expect-chan", "0", "2", "ack-chan", "expect-ho", "0", "5", "ho-complete", "congestion-check", "expect-no-chan", "congestion-check", "expect-no-chan", NULL }; static char *test_case_28[] = { "2", "Handover to congested cell, if RX quality is below minimum\n\n" "The better neighbor cell is congested, so no handover is performed.\n" "If the RX quality of the current cell drops below minimum acceptable\n" "level, the handover is performed. It is also required that 10\n" "resports are received, before RX quality is checked.\n", "create-bts", "2", "create-ms", "0", "TCH/F", "AMR", "set-min-free", "1", "TCH/F", "4", "set-min-free", "1", "TCH/H", "4", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-no-chan", "meas-rep", "0", "30","6", "1","0","40", "expect-chan", "1", "1", "ack-chan", "expect-ho", "0", "1", "ho-complete", NULL }; static char **test_cases[] = { test_case_0, test_case_1, test_case_2, test_case_3, test_case_4, test_case_5, test_case_6, test_case_7, test_case_8, test_case_9, test_case_10, test_case_11, test_case_12, test_case_13, test_case_14, test_case_15, test_case_16, test_case_17, test_case_18, test_case_19, test_case_20, test_case_21, test_case_22, test_case_23, test_case_24, test_case_25, test_case_26, test_case_27, test_case_28, }; static const struct log_info_cat log_categories[] = { [DHO] = { .name = "DHO", .description = "Hand-Over Process", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DHODEC] = { .name = "DHODEC", .description = "Hand-Over Decision", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DMEAS] = { .name = "DMEAS", .description = "Radio Measurement Processing", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DRSL] = { .name = "DRSL", .description = "A-bis Radio Signalling Link (RSL)", .color = "\033[1;35m", .enabled = 1, .loglevel = LOGL_DEBUG, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { char **test_case; struct gsm_bts *bts[256]; int bts_num = 0; struct gsm_lchan *lchan[256]; int lchan_num = 0; int i; int algorithm; int test_case_i; int last_test_i; ctx = talloc_named_const(NULL, 0, "handover_test"); msgb_talloc_ctx_init(ctx, 0); test_case_i = argc > 1? atoi(argv[1]) : -1; last_test_i = ARRAY_SIZE(test_cases) - 1; if (test_case_i < 0 || test_case_i > last_test_i) { for (i = 0; i <= last_test_i; i++) { printf("Test #%d (algorithm %s):\n%s\n", i, test_cases[i][0], test_cases[i][1]); } printf("\nPlease specify test case number 0..%d\n", last_test_i); return EXIT_FAILURE; } osmo_init_logging2(ctx, &log_info); log_set_print_category(osmo_stderr_target, 1); log_set_print_category_hex(osmo_stderr_target, 0); log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); bsc_network_alloc(); if (!bsc_gsmnet) exit(1); ho_set_algorithm(bsc_gsmnet->ho, 2); ho_set_ho_active(bsc_gsmnet->ho, true); ho_set_hodec2_as_active(bsc_gsmnet->ho, true); ho_set_hodec2_min_rxlev(bsc_gsmnet->ho, -100); ho_set_hodec2_rxlev_avg_win(bsc_gsmnet->ho, 1); ho_set_hodec2_rxlev_neigh_avg_win(bsc_gsmnet->ho, 1); ho_set_hodec2_rxqual_avg_win(bsc_gsmnet->ho, 10); ho_set_hodec2_pwr_hysteresis(bsc_gsmnet->ho, 3); ho_set_hodec2_pwr_interval(bsc_gsmnet->ho, 1); ho_set_hodec2_afs_bias_rxlev(bsc_gsmnet->ho, 0); ho_set_hodec2_min_rxqual(bsc_gsmnet->ho, 5); ho_set_hodec2_afs_bias_rxqual(bsc_gsmnet->ho, 0); ho_set_hodec2_max_distance(bsc_gsmnet->ho, 9999); ho_set_hodec2_ho_max(bsc_gsmnet->ho, 9999); ho_set_hodec2_penalty_max_dist(bsc_gsmnet->ho, 300); ho_set_hodec2_penalty_failed_ho(bsc_gsmnet->ho, 60); ho_set_hodec2_penalty_failed_as(bsc_gsmnet->ho, 60); bts_model_sysmobts_init(); test_case = test_cases[test_case_i]; fprintf(stderr, "--------------------\n"); fprintf(stderr, "Performing the following test %d (algorithm %s):\n%s", test_case_i, test_case[0], test_case[1]); algorithm = atoi(test_case[0]); test_case += 2; fprintf(stderr, "--------------------\n"); /* Disable the congestion check timer, we will trigger manually. */ bsc_gsmnet->hodec2.congestion_check_interval_s = 0; handover_decision_1_init(); hodec2_init(bsc_gsmnet); while (*test_case) { if (!strcmp(*test_case, "create-bts")) { static int arfcn = 870; int n = atoi(test_case[1]); fprintf(stderr, "- Creating %d BTS (one TRX each, " "TS(1-4) are TCH/F, TS(5-6) are TCH/H)\n", n); for (i = 0; i < n; i++) bts[bts_num + i] = create_bts(arfcn++); for (i = 0; i < n; i++) { if (gsm_generate_si(bts[bts_num + i], SYSINFO_TYPE_2)) fprintf(stderr, "Error generating SI2\n"); } bts_num += n; test_case += 2; } else if (!strcmp(*test_case, "as-enable")) { fprintf(stderr, "- Set assignment enable state at " "BTS %s to %s\n", test_case[1], test_case[2]); ho_set_hodec2_as_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "ho-enable")) { fprintf(stderr, "- Set handover enable state at " "BTS %s to %s\n", test_case[1], test_case[2]); ho_set_ho_active(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "afs-rxlev-improve")) { fprintf(stderr, "- Set afs RX level improvement at " "BTS %s to %s\n", test_case[1], test_case[2]); ho_set_hodec2_afs_bias_rxlev(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "afs-rxqual-improve")) { fprintf(stderr, "- Set afs RX quality improvement at " "BTS %s to %s\n", test_case[1], test_case[2]); ho_set_hodec2_afs_bias_rxqual(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "set-min-free")) { fprintf(stderr, "- Setting minimum required free %s " "slots at BTS %s to %s\n", test_case[2], test_case[1], test_case[3]); if (!strcmp(test_case[2], "TCH/F")) ho_set_hodec2_tchf_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); else ho_set_hodec2_tchh_min_slots(bts[atoi(test_case[1])]->ho, atoi(test_case[3])); test_case += 4; } else if (!strcmp(*test_case, "set-max-ho")) { fprintf(stderr, "- Setting maximum parallel handovers " "at BTS %s to %s\n", test_case[1], test_case[2]); ho_set_hodec2_ho_max( bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "set-max-ta")) { fprintf(stderr, "- Setting maximum timing advance " "at BTS %s to %s\n", test_case[1], test_case[2]); ho_set_hodec2_max_distance(bts[atoi(test_case[1])]->ho, atoi(test_case[2])); test_case += 3; } else if (!strcmp(*test_case, "create-ms")) { fprintf(stderr, "- Creating mobile #%d at BTS %s on " "%s with %s codec\n", lchan_num, test_case[1], test_case[2], test_case[3]); lchan[lchan_num] = create_lchan(bts[atoi(test_case[1])], !strcmp(test_case[2], "TCH/F"), test_case[3]); if (!lchan[lchan_num]) { printf("Failed to create lchan!\n"); return EXIT_FAILURE; } fprintf(stderr, " * New MS is at BTS %d TS %d\n", lchan[lchan_num]->ts->trx->bts->nr, lchan[lchan_num]->ts->nr); lchan_num++; test_case += 4; } else if (!strcmp(*test_case, "set-ta")) { fprintf(stderr, "- Setting maximum timing advance " "at MS %s to %s\n", test_case[1], test_case[2]); meas_ta_ms = atoi(test_case[2]); test_case += 3; } else if (!strcmp(*test_case, "meas-rep")) { /* meas-rep [ [...]] */ int n = atoi(test_case[4]); struct gsm_lchan *lc = lchan[atoi(test_case[1])]; fprintf(stderr, "- Sending measurement report from " "mobile #%s (rxlev=%s, rxqual=%s)\n", test_case[1], test_case[2], test_case[3]); meas_dl_rxlev = atoi(test_case[2]); meas_dl_rxqual = atoi(test_case[3]); meas_num_nc = n; test_case += 5; for (i = 0; i < n; i++) { int nr = atoi(test_case[0]); /* since our bts is not in the list of neighbor * cells, we need to shift */ if (nr >= lc->ts->trx->bts->nr) nr++; fprintf(stderr, " * Neighbor cell #%s, actual " "BTS %d (rxlev=%s)\n", test_case[0], nr, test_case[1]); meas_bcch_f_nc[i] = atoi(test_case[0]); /* bts number, not counting our own */ meas_rxlev_nc[i] = atoi(test_case[1]); meas_bsic_nc[i] = 0x3f; test_case += 2; } got_chan_req = 0; gen_meas_rep(lc); } else if (!strcmp(*test_case, "congestion-check")) { fprintf(stderr, "- Triggering congestion check\n"); got_chan_req = 0; if (algorithm == 2) hodec2_congestion_check(bsc_gsmnet); test_case += 1; } else if (!strcmp(*test_case, "expect-chan")) { fprintf(stderr, "- Expecting channel request at BTS %s " "TS %s\n", test_case[1], test_case[2]); if (!got_chan_req) { printf("Test failed, because no channel was " "requested\n"); return EXIT_FAILURE; } fprintf(stderr, " * Got channel request at BTS %d " "TS %d\n", chan_req_lchan->ts->trx->bts->nr, chan_req_lchan->ts->nr); if (chan_req_lchan->ts->trx->bts->nr != atoi(test_case[1])) { printf("Test failed, because channel was not " "requested on expected BTS\n"); return EXIT_FAILURE; } if (chan_req_lchan->ts->nr != atoi(test_case[2])) { printf("Test failed, because channel was not " "requested on expected TS\n"); return EXIT_FAILURE; } test_case += 3; } else if (!strcmp(*test_case, "expect-no-chan")) { fprintf(stderr, "- Expecting no channel request\n"); if (got_chan_req) { fprintf(stderr, " * Got channel request at " "BTS %d TS %d\n", chan_req_lchan->ts->trx->bts->nr, chan_req_lchan->ts->nr); printf("Test failed, because channel was " "requested\n"); return EXIT_FAILURE; } fprintf(stderr, " * Got no channel request\n"); test_case += 1; } else if (!strcmp(*test_case, "expect-ho")) { fprintf(stderr, "- Expecting handover/assignment " "request at BTS %s TS %s\n", test_case[1], test_case[2]); if (!got_ho_req) { printf("Test failed, because no handover was " "requested\n"); return EXIT_FAILURE; } fprintf(stderr, " * Got handover/assignment request at " "BTS %d TS %d\n", ho_req_lchan->ts->trx->bts->nr, ho_req_lchan->ts->nr); if (ho_req_lchan->ts->trx->bts->nr != atoi(test_case[1])) { printf("Test failed, because " "handover/assignment was not commanded " "at the expected BTS\n"); return EXIT_FAILURE; } if (ho_req_lchan->ts->nr != atoi(test_case[2])) { printf("Test failed, because " "handover/assignment was not commanded " "at the expected TS\n"); return EXIT_FAILURE; } test_case += 3; } else if (!strcmp(*test_case, "ack-chan")) { fprintf(stderr, "- Acknowledging channel request\n"); if (!got_chan_req) { printf("Cannot ack channel, because no " "request\n"); return EXIT_FAILURE; } test_case += 1; got_ho_req = 0; send_chan_act_ack(chan_req_lchan, 1); } else if (!strcmp(*test_case, "ho-complete")) { fprintf(stderr, "- Acknowledging handover/assignment " "request\n"); if (!got_chan_req) { printf("Cannot ack handover/assignment, " "because no chan request\n"); return EXIT_FAILURE; } if (!got_ho_req) { printf("Cannot ack handover/assignment, " "because no ho request\n"); return EXIT_FAILURE; } test_case += 1; got_chan_req = 0; got_ho_req = 0; /* switch lchan */ for (i = 0; i < lchan_num; i++) { if (lchan[i] == ho_req_lchan) { fprintf(stderr, " * MS %d changes from " "BTS=%d TS=%d to BTS=%d " "TS=%d\n", i, lchan[i]->ts->trx->bts->nr, lchan[i]->ts->nr, chan_req_lchan->ts->trx->bts->nr, chan_req_lchan->ts->nr); lchan[i] = chan_req_lchan; } } send_ho_complete(chan_req_lchan, true); } else if (!strcmp(*test_case, "ho-failed")) { fprintf(stderr, "- Making handover fail\n"); if (!got_chan_req) { printf("Cannot fail handover, because no chan " "request\n"); return EXIT_FAILURE; } test_case += 1; got_chan_req = 0; got_ho_req = 0; send_ho_complete(ho_req_lchan, false); } else if (!strcmp(*test_case, "print")) { fprintf(stderr, "\n%s\n\n", test_case[1]); test_case += 2; } else { printf("Unknown test command '%s', please fix!\n", *test_case); return EXIT_FAILURE; } } for (i = 0; i < lchan_num; i++) { struct gsm_subscriber_connection *conn = lchan[i]->conn; lchan[i]->conn = NULL; conn->lchan = NULL; osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REGULAR, NULL); lchan_free(lchan[i]); } fprintf(stderr, "--------------------\n"); printf("Test OK\n"); fprintf(stderr, "--------------------\n"); talloc_free(ctx); return EXIT_SUCCESS; } void rtp_socket_free() {} void rtp_send_frame() {} void rtp_socket_upstream() {} void rtp_socket_create() {} void rtp_socket_connect() {} void rtp_socket_proxy() {} void trau_mux_unmap() {} void trau_mux_map_lchan() {} void trau_recv_lchan() {} void trau_send_frame() {} int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; } void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) {} void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, struct msgb *msg, uint8_t chosen_encr) {} int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, uint16_t chosen_channel) { return 0; } void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) {} void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause) {} void bsc_assign_fail(struct gsm_subscriber_connection *conn, uint8_t cause, uint8_t *rr_cause) {} int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) { return 0; } void bsc_cm_update(struct gsm_subscriber_connection *conn, const uint8_t *cm2, uint8_t cm2_len, const uint8_t *cm3, uint8_t cm3_len) {} void bsc_mr_config(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan, int full_rate) {} osmo-bsc-1.3.0/tests/handover/handover_test.ok000066400000000000000000000000101332665256100214050ustar00rootroot00000000000000Test OK osmo-bsc-1.3.0/tests/handover_cfg.vty000066400000000000000000000574241332665256100176150ustar00rootroot00000000000000OsmoBSC> show network ... Handover: Off ... OsmoBSC> enable OsmoBSC# ### No handover config present OsmoBSC# show running-config ... !handover OsmoBSC# ### Toggling handover on network level affects 'show network': OsmoBSC# configure terminal OsmoBSC(config)# network OsmoBSC(config-net)# do show network ... Handover: Off ... OsmoBSC(config-net)# handover 1 OsmoBSC(config-net)# do show network ... Handover: On ... OsmoBSC(config-net)# ### If network level default is 'on', bts level can still override to 'off': OsmoBSC(config-net)# bts 0 OsmoBSC(config-net-bts)# handover 0 OsmoBSC(config-net-bts)# do show network ... Handover: Off ... OsmoBSC(config-net-bts)# exit OsmoBSC(config-net)# ### Create a *second* BTS that is not explicitly 'off': OsmoBSC(config-net)# bts 1 OsmoBSC(config-net-bts)# do show network ... Handover: On at 1 BTS, Off at 1 BTS ... OsmoBSC(config-net-bts)# ### Add arbitrary handover config item for bts 1: OsmoBSC(config-net-bts)# handover1 power budget interval 23 OsmoBSC(config-net-bts)# exit OsmoBSC(config-net)# ### HO is 'on' globally, bts 0 disables it, bts 1 tweaks a param: OsmoBSC(config-net)# show running-config ... network ... !handover handover 1 ... !handover bts 0 ... !handover handover 0 ... !handover bts 1 ... !handover handover1 power budget interval 23 ... !handover OsmoBSC(config-net)# ### Set global default to 'off', now bts 1 also uses the global default of 'off': OsmoBSC(config-net)# handover 0 OsmoBSC(config-net)# do show network ... Handover: Off ... OsmoBSC(config-net)# show running-config ... network ... !handover handover 0 ... !handover bts 0 ... !handover handover 0 ... !handover bts 1 ... !handover handover1 power budget interval 23 ... !handover OsmoBSC(config-net)# ### Remove the global setting, i.e. use the factory default net level, with same effect: OsmoBSC(config-net)# handover default % 'handover' setting removed, now is 0 OsmoBSC(config-net)# handover default % 'handover' already was unset, still is 0 OsmoBSC(config-net)# do show network ... Handover: Off ... OsmoBSC(config-net)# show running-config ... network ... !handover bts 0 ... !handover handover 0 ... !handover bts 1 ... !handover handover1 power budget interval 23 ... !handover OsmoBSC(config-net)# ### Re-enable net-level handover, but bts 0 remains disabled explicitly OsmoBSC(config-net)# handover 1 OsmoBSC(config-net)# do show network ... Handover: On at 1 BTS, Off at 1 BTS ... OsmoBSC(config-net)# show running-config ... network ... !handover handover 1 ... !handover bts 0 ... !handover handover 0 ... !handover bts 1 ... !handover handover1 power budget interval 23 ... !handover OsmoBSC(config-net)# ### Remove explicit setting of bts 0 to also use the global setting: OsmoBSC(config-net)# bts 0 OsmoBSC(config-net-bts)# handover default % 'handover' setting removed, now is 1 (set on higher level node) OsmoBSC(config-net-bts)# handover default % 'handover' already was unset, still is 1 (set on higher level node) OsmoBSC(config-net-bts)# do show network ... Handover: On ... OsmoBSC(config-net-bts)# show running-config ... network ... !handover handover 1 ... !handover bts 0 ... !handover bts 1 ... !handover handover1 power budget interval 23 ... !handover OsmoBSC(config-net-bts)# ### Verify that 'min rxlev' value range stops at -50 OsmoBSC(config-net-bts)# handover2 min rxlev ? <-110--50> minimum RxLev (dBm) default Use default (-100), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 min rxlev -111 % Unknown command. OsmoBSC(config-net-bts)# handover2 min rxlev -110 OsmoBSC(config-net-bts)# handover2 min rxlev -50 OsmoBSC(config-net-bts)# handover2 min rxlev -49 % Unknown command. OsmoBSC(config-net-bts)# handover2 min rxlev 50 % Unknown command. OsmoBSC(config-net-bts)# handover2 min rxlev default % 'handover2 min rxlev' setting removed, now is -100 OsmoBSC(config-net-bts)# ### Checking online help OsmoBSC(config-net-bts)# exit OsmoBSC(config-net)# list ... handover (0|1|default) handover algorithm (1|2|default) handover1 window rxlev averaging (<1-10>|default) handover1 window rxqual averaging (<1-10>|default) handover1 window rxlev neighbor averaging (<1-10>|default) handover1 power budget interval (<1-99>|default) handover1 power budget hysteresis (<0-999>|default) handover1 maximum distance (<0-9999>|default) handover2 window rxlev averaging (<1-10>|default) handover2 window rxqual averaging (<1-10>|default) handover2 window rxlev neighbor averaging (<1-10>|default) handover2 power budget interval (<1-99>|default) handover2 power budget hysteresis (<0-999>|default) handover2 maximum distance (<0-9999>|default) handover2 assignment (0|1|default) handover2 tdma-measurement (full|subset|default) handover2 min rxlev (<-110--50>|default) handover2 min rxqual (<0-7>|default) handover2 afs-bias rxlev (<0-20>|default) handover2 afs-bias rxqual (<0-7>|default) handover2 min-free-slots tch/f (<0-9999>|default) handover2 min-free-slots tch/h (<0-9999>|default) handover2 max-handovers (<1-9999>|default) handover2 penalty-time max-distance (<0-99999>|default) handover2 penalty-time failed-ho (<0-99999>|default) handover2 penalty-time failed-assignment (<0-99999>|default) handover2 retries (<0-9>|default) handover2 congestion-check (disabled|<1-999>|now) ... OsmoBSC(config-net)# handover? handover Handover general config OsmoBSC(config-net)# handover1? handover1 Handover options for handover decision algorithm 1 OsmoBSC(config-net)# handover2? handover2 Handover options for handover decision algorithm 2 OsmoBSC(config-net)# handover ? 0 Disable in-call handover 1 Enable in-call handover default Enable/disable handover: Use default (0), remove explicit setting on this node algorithm Choose algorithm for handover decision ... OsmoBSC(config-net)# handover1 ? window Measurement averaging settings power Neighbor cell power triggering maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net)# handover2 ? window Measurement averaging settings power Neighbor cell power triggering maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO assignment Enable or disable in-call channel re-assignment (HO algo 2 only) tdma-measurement Define measurement set of TDMA frames (HO algo 2 only) min Minimum Level/Quality thresholds before triggering HO (HO algo 2 only) afs-bias Configure bias to prefer AFS (AMR on TCH/F) over other codecs (HO algo 2 only) min-free-slots Minimum free TCH timeslots before cell is considered congested (HO algo 2 only) max-handovers Maximum number of concurrent handovers allowed per cell (HO algo 2 only) penalty-time Set penalty times to wait between repeated handovers (HO algo 2 only) retries Immediately retry on handover/assignment failure (HO algo 2 only) congestion-check Configure congestion check interval (HO algo 2 only) OsmoBSC(config-net)# handover algorithm ? 1 Algorithm 1: trigger handover based on comparing current cell and neighbor RxLev and RxQual, only. 2 Algorithm 2: trigger handover on RxLev/RxQual, and also to balance the load across several cells. Consider available codecs. Prevent repeated handover by penalty timers. default Use default (1), remove explicit setting on this node OsmoBSC(config-net)# handover1 window ? rxlev Received-Level averaging rxqual Received-Quality averaging OsmoBSC(config-net)# handover1 window rxlev ? averaging How many RxLev measurements are used for averaging neighbor How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net)# handover1 window rxlev averaging ? <1-10> RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net)# handover1 window rxlev neighbor ? averaging How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net)# handover1 window rxlev neighbor averaging ? <1-10> Neighbor RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net)# handover1 window rxqual ? averaging How many RxQual measurements are used for averaging OsmoBSC(config-net)# handover1 window rxqual averaging ? <1-10> RxQual averaging: Number of values to average over default Use default (1), remove explicit setting on this node OsmoBSC(config-net)# handover1 power ? budget Neighbor cell power triggering OsmoBSC(config-net)# handover1 power budget ? interval How often to check for a better cell (SACCH frames) hysteresis How many dB stronger must a neighbor be to become a HO candidate OsmoBSC(config-net)# handover1 power budget interval ? <1-99> Check for stronger neighbor every N number of SACCH frames default Use default (6), remove explicit setting on this node OsmoBSC(config-net)# handover1 power budget hysteresis ? <0-999> Neighbor's strength difference in dB default Use default (3), remove explicit setting on this node OsmoBSC(config-net)# handover1 maximum ? distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net)# handover1 maximum distance ? <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO default Use default (9999), remove explicit setting on this node OsmoBSC(config-net)# handover2 window ? rxlev Received-Level averaging rxqual Received-Quality averaging OsmoBSC(config-net)# handover2 window rxlev ? averaging How many RxLev measurements are used for averaging neighbor How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net)# handover2 window rxlev averaging ? <1-10> RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net)# handover2 window rxlev neighbor ? averaging How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net)# handover2 window rxlev neighbor averaging ? <1-10> Neighbor RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net)# handover2 window rxqual ? averaging How many RxQual measurements are used for averaging OsmoBSC(config-net)# handover2 window rxqual averaging ? <1-10> RxQual averaging: Number of values to average over default Use default (1), remove explicit setting on this node OsmoBSC(config-net)# handover2 power ? budget Neighbor cell power triggering OsmoBSC(config-net)# handover2 power budget ? interval How often to check for a better cell (SACCH frames) hysteresis How many dB stronger must a neighbor be to become a HO candidate OsmoBSC(config-net)# handover2 power budget interval ? <1-99> Check for stronger neighbor every N number of SACCH frames default Use default (6), remove explicit setting on this node OsmoBSC(config-net)# handover2 power budget hysteresis ? <0-999> Neighbor's strength difference in dB default Use default (3), remove explicit setting on this node OsmoBSC(config-net)# handover2 maximum ? distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net)# handover2 maximum distance ? <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO default Use default (9999), remove explicit setting on this node OsmoBSC(config-net)# handover2 assignment ? 0 Disable in-call assignment 1 Enable in-call assignment default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 tdma-measurement ? full Full set of 102/104 TDMA frames subset Sub set of 4 TDMA frames (SACCH) default Use default (subset), remove explicit setting on this node OsmoBSC(config-net)# handover2 min ? rxlev How weak may RxLev of an MS become before triggering HO rxqual How bad may RxQual of an MS become before triggering HO OsmoBSC(config-net)# handover2 min rxlev ? <-110--50> minimum RxLev (dBm) default Use default (-100), remove explicit setting on this node OsmoBSC(config-net)# handover2 min rxqual ? <0-7> minimum RxQual default Use default (5), remove explicit setting on this node OsmoBSC(config-net)# handover2 afs-bias ? rxlev RxLev improvement bias for AFS over other codecs rxqual RxQual improvement bias for AFS over other codecs OsmoBSC(config-net)# handover2 afs-bias rxlev ? <0-20> Virtual RxLev improvement (dB) default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 afs-bias rxqual ? <0-7> Virtual RxQual improvement default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 min-free-slots ? tch/f Minimum free TCH/F timeslots before cell is considered congested tch/h Minimum free TCH/H timeslots before cell is considered congested OsmoBSC(config-net)# handover2 min-free-slots tch/f ? <0-9999> Number of TCH/F slots default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 min-free-slots TCH/F ? % There is no matched command. OsmoBSC(config-net)# handover2 min-free-slots tch/h ? <0-9999> Number of TCH/H slots default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 max-handovers ? <1-9999> Number default Use default (9999), remove explicit setting on this node OsmoBSC(config-net)# handover2 penalty-time ? max-distance Time to suspend handovers after leaving this cell due to exceeding max distance failed-ho Time to suspend handovers after handover failure to this cell failed-assignment Time to suspend handovers after assignment failure in this cell OsmoBSC(config-net)# handover2 penalty-time max-distance ? <0-99999> Seconds default Use default (300), remove explicit setting on this node OsmoBSC(config-net)# handover2 penalty-time failed-ho ? <0-99999> Seconds default Use default (60), remove explicit setting on this node OsmoBSC(config-net)# handover2 penalty-time failed-assignment ? <0-99999> Seconds default Use default (60), remove explicit setting on this node OsmoBSC(config-net)# handover2 retries ? <0-9> Number of retries default Use default (0), remove explicit setting on this node OsmoBSC(config-net)# handover2 congestion-check ? disabled Disable congestion checking, do not handover based on cell overload <1-999> Congestion check interval in seconds (default 10) now Manually trigger a congestion check to run right now OsmoBSC(config-net)# ### Same on BTS level, except for the congestion-check OsmoBSC(config-net)# bts 0 OsmoBSC(config-net-bts)# handover? handover Handover general config OsmoBSC(config-net-bts)# handover1? handover1 Handover options for handover decision algorithm 1 OsmoBSC(config-net-bts)# handover2? handover2 Handover options for handover decision algorithm 2 OsmoBSC(config-net-bts)# handover ? 0 Disable in-call handover 1 Enable in-call handover default Enable/disable handover: Use default (0), remove explicit setting on this node algorithm Choose algorithm for handover decision ... OsmoBSC(config-net-bts)# handover1 ? window Measurement averaging settings power Neighbor cell power triggering maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net-bts)# handover2 ? window Measurement averaging settings power Neighbor cell power triggering maximum Maximum Timing-Advance value (i.e. MS distance) before triggering HO assignment Enable or disable in-call channel re-assignment (HO algo 2 only) tdma-measurement Define measurement set of TDMA frames (HO algo 2 only) min Minimum Level/Quality thresholds before triggering HO (HO algo 2 only) afs-bias Configure bias to prefer AFS (AMR on TCH/F) over other codecs (HO algo 2 only) min-free-slots Minimum free TCH timeslots before cell is considered congested (HO algo 2 only) max-handovers Maximum number of concurrent handovers allowed per cell (HO algo 2 only) penalty-time Set penalty times to wait between repeated handovers (HO algo 2 only) retries Immediately retry on handover/assignment failure (HO algo 2 only) OsmoBSC(config-net-bts)# handover algorithm ? 1 Algorithm 1: trigger handover based on comparing current cell and neighbor RxLev and RxQual, only. 2 Algorithm 2: trigger handover on RxLev/RxQual, and also to balance the load across several cells. Consider available codecs. Prevent repeated handover by penalty timers. default Use default (1), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 window ? rxlev Received-Level averaging rxqual Received-Quality averaging OsmoBSC(config-net-bts)# handover1 window rxlev ? averaging How many RxLev measurements are used for averaging neighbor How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net-bts)# handover1 window rxlev averaging ? <1-10> RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 window rxlev neighbor ? averaging How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net-bts)# handover1 window rxlev neighbor averaging ? <1-10> Neighbor RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 window rxqual ? averaging How many RxQual measurements are used for averaging OsmoBSC(config-net-bts)# handover1 window rxqual averaging ? <1-10> RxQual averaging: Number of values to average over default Use default (1), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 power ? budget Neighbor cell power triggering OsmoBSC(config-net-bts)# handover1 power budget ? interval How often to check for a better cell (SACCH frames) hysteresis How many dB stronger must a neighbor be to become a HO candidate OsmoBSC(config-net-bts)# handover1 power budget interval ? <1-99> Check for stronger neighbor every N number of SACCH frames default Use default (6), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 power budget hysteresis ? <0-999> Neighbor's strength difference in dB default Use default (3), remove explicit setting on this node OsmoBSC(config-net-bts)# handover1 maximum ? distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net-bts)# handover1 maximum distance ? <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO default Use default (9999), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 window ? rxlev Received-Level averaging rxqual Received-Quality averaging OsmoBSC(config-net-bts)# handover2 window rxlev ? averaging How many RxLev measurements are used for averaging neighbor How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net-bts)# handover2 window rxlev averaging ? <1-10> RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 window rxlev neighbor ? averaging How many Neighbor RxLev measurements are used for averaging OsmoBSC(config-net-bts)# handover2 window rxlev neighbor averaging ? <1-10> Neighbor RxLev averaging: Number of values to average over default Use default (10), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 window rxqual ? averaging How many RxQual measurements are used for averaging OsmoBSC(config-net-bts)# handover2 window rxqual averaging ? <1-10> RxQual averaging: Number of values to average over default Use default (1), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 power ? budget Neighbor cell power triggering OsmoBSC(config-net-bts)# handover2 power budget ? interval How often to check for a better cell (SACCH frames) hysteresis How many dB stronger must a neighbor be to become a HO candidate OsmoBSC(config-net-bts)# handover2 power budget interval ? <1-99> Check for stronger neighbor every N number of SACCH frames default Use default (6), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 power budget hysteresis ? <0-999> Neighbor's strength difference in dB default Use default (3), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 maximum ? distance Maximum Timing-Advance value (i.e. MS distance) before triggering HO OsmoBSC(config-net-bts)# handover2 maximum distance ? <0-9999> Maximum Timing-Advance value (i.e. MS distance) before triggering HO default Use default (9999), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 assignment ? 0 Disable in-call assignment 1 Enable in-call assignment default Use default (0), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 tdma-measurement ? full Full set of 102/104 TDMA frames subset Sub set of 4 TDMA frames (SACCH) default Use default (subset), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 min ? rxlev How weak may RxLev of an MS become before triggering HO rxqual How bad may RxQual of an MS become before triggering HO OsmoBSC(config-net-bts)# handover2 min rxlev ? <-110--50> minimum RxLev (dBm) default Use default (-100), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 min rxqual ? <0-7> minimum RxQual default Use default (5), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 afs-bias ? rxlev RxLev improvement bias for AFS over other codecs rxqual RxQual improvement bias for AFS over other codecs OsmoBSC(config-net-bts)# handover2 afs-bias rxlev ? <0-20> Virtual RxLev improvement (dB) default Use default (0), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 afs-bias rxqual ? <0-7> Virtual RxQual improvement default Use default (0), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 min-free-slots ? tch/f Minimum free TCH/F timeslots before cell is considered congested tch/h Minimum free TCH/H timeslots before cell is considered congested OsmoBSC(config-net-bts)# handover2 min-free-slots tch/f ? <0-9999> Number of TCH/F slots default Use default (0), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 min-free-slots TCH/F ? % There is no matched command. OsmoBSC(config-net-bts)# handover2 min-free-slots tch/h ? <0-9999> Number of TCH/H slots default Use default (0), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 max-handovers ? <1-9999> Number default Use default (9999), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 penalty-time ? max-distance Time to suspend handovers after leaving this cell due to exceeding max distance failed-ho Time to suspend handovers after handover failure to this cell failed-assignment Time to suspend handovers after assignment failure in this cell OsmoBSC(config-net-bts)# handover2 penalty-time max-distance ? <0-99999> Seconds default Use default (300), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 penalty-time failed-ho ? <0-99999> Seconds default Use default (60), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 penalty-time failed-assignment ? <0-99999> Seconds default Use default (60), remove explicit setting on this node OsmoBSC(config-net-bts)# handover2 retries ? <0-9> Number of retries default Use default (0), remove explicit setting on this node osmo-bsc-1.3.0/tests/nanobts_omlattr/000077500000000000000000000000001332665256100176165ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/nanobts_omlattr/Makefile.am000066400000000000000000000011421332665256100216500ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(NULL) noinst_PROGRAMS = \ nanobts_omlattr_test \ $(NULL) EXTRA_DIST = \ nanobts_omlattr_test.ok \ $(NULL) nanobts_omlattr_test_SOURCES = \ nanobts_omlattr_test.c \ $(NULL) nanobts_omlattr_test_LDADD = \ $(top_builddir)/src/osmo-bsc/abis_nm.o \ $(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \ $(top_builddir)/src/osmo-bsc/gsm_data.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(NULL) osmo-bsc-1.3.0/tests/nanobts_omlattr/nanobts_omlattr_test.c000066400000000000000000000233141332665256100242320ustar00rootroot00000000000000/* Test OML attribute generator */ /* (C) 2016 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 struct gsm_bts_model bts_model_nanobts = { .type = GSM_BTS_TYPE_NANOBTS, .name = "nanobts", .start = NULL, .oml_rcvmsg = NULL, .e1line_bind_ops = NULL, .nm_att_tlvdef = { .def = { /* ip.access specifics */ [NM_ATT_IPACC_DST_IP] = {TLV_TYPE_FIXED, 4}, [NM_ATT_IPACC_DST_IP_PORT] = {TLV_TYPE_FIXED, 2}, [NM_ATT_IPACC_STREAM_ID] = {TLV_TYPE_TV,}, [NM_ATT_IPACC_SEC_OML_CFG] = {TLV_TYPE_FIXED, 6}, [NM_ATT_IPACC_IP_IF_CFG] = {TLV_TYPE_FIXED, 8}, [NM_ATT_IPACC_IP_GW_CFG] = {TLV_TYPE_FIXED, 12}, [NM_ATT_IPACC_IN_SERV_TIME] = {TLV_TYPE_FIXED, 4}, [NM_ATT_IPACC_LOCATION] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_PAGING_CFG] = {TLV_TYPE_FIXED, 2}, [NM_ATT_IPACC_UNIT_ID] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_UNIT_NAME] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_SNMP_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_NV_FLAGS] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_FREQ_CTRL] = {TLV_TYPE_FIXED, 2}, [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_CUR_SW_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_TIMING_BUS] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_CGI] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_RAC] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_OBJ_VERSION] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_GPRS_PAGING_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_NSEI] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_BVCI] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_NSVCI] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_NS_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_BSSGP_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_NS_LINK_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_RLC_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_ALM_THRESH_LIST] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_MONIT_VAL_LIST] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_TIB_CONTROL] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_SUPP_FEATURES] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_CODING_SCHEMES] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_RLC_CFG_2] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_HEARTB_TOUT] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_UPTIME] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_RLC_CFG_3] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_SSL_CFG] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_SEC_POSSIBLE] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_IML_SSL_STATE] = {TLV_TYPE_TL16V}, [NM_ATT_IPACC_REVOC_DATE] = {TLV_TYPE_TL16V}, }, }, }; static void test_nanobts_attr_bts_get(struct gsm_bts *bts, uint8_t *expected) { struct msgb *msgb; printf("Testing nanobts_attr_bts_get()...\n"); msgb = nanobts_attr_bts_get(bts); printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); msgb_free(msgb); printf("ok.\n"); printf("\n"); } static void test_nanobts_attr_nse_get(struct gsm_bts *bts, uint8_t *expected) { struct msgb *msgb; printf("Testing nanobts_attr_nse_get()...\n"); msgb = nanobts_attr_nse_get(bts); printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); msgb_free(msgb); printf("ok.\n"); printf("\n"); } static void test_nanobts_attr_cell_get(struct gsm_bts *bts, uint8_t *expected) { struct msgb *msgb; printf("Testing nanobts_attr_cell_get()...\n"); msgb = nanobts_attr_cell_get(bts); printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); msgb_free(msgb); printf("ok.\n"); printf("\n"); } static void test_nanobts_attr_nscv_get(struct gsm_bts *bts, uint8_t *expected) { struct msgb *msgb; printf("Testing nanobts_attr_nscv_get()...\n"); msgb = nanobts_attr_nscv_get(bts); printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); msgb_free(msgb); printf("ok.\n"); printf("\n"); } static void test_nanobts_attr_radio_get(struct gsm_bts *bts, struct gsm_bts_trx *trx, uint8_t *expected) { struct msgb *msgb; printf("Testing nanobts_attr_nscv_get()...\n"); msgb = nanobts_attr_radio_get(bts, trx); printf("result= %s\n", osmo_hexdump_nospc(msgb->data, msgb->len)); printf("expected=%s\n", osmo_hexdump_nospc(expected, msgb->len)); OSMO_ASSERT(memcmp(msgb->data, expected, msgb->len) == 0); msgb_free(msgb); printf("ok.\n"); printf("\n"); } static const struct log_info_cat log_categories[] = { }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { void *ctx; struct gsm_bts *bts; struct gsm_network *net; struct gsm_bts_trx *trx; ctx = talloc_named_const(NULL, 0, "ctx"); osmo_init_logging2(ctx, &log_info); log_set_log_level(osmo_stderr_target, LOGL_INFO); /* Allocate environmental structs (bts, net, trx) */ net = talloc_zero(ctx, struct gsm_network); INIT_LLIST_HEAD(&net->bts_list); gsm_bts_model_register(&bts_model_nanobts); bts = gsm_bts_alloc_register(net, GSM_BTS_TYPE_NANOBTS, 63); OSMO_ASSERT(bts); bts->network = net; trx = talloc_zero(ctx, struct gsm_bts_trx); /* Parameters needed by nanobts_attr_bts_get() */ bts->rach_b_thresh = -1; bts->rach_ldavg_slots = -1; bts->c0->arfcn = 866; bts->cell_identity = 1337; bts->network->plmn = (struct osmo_plmn_id){ .mcc=1, .mnc=1 }; bts->location_area_code = 1; bts->gprs.rac = 0; uint8_t attr_bts_expected[] = { 0x19, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, 0x18, 0x06, 0x0e, 0x00, 0x02, 0x01, 0x20, 0x33, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, 0x1f, 0x3f, 0x25, 0x00, 0x01, 0x0a, 0x0c, 0x0a, 0x0b, 0x01, 0x2a, 0x5a, 0x2b, 0x03, 0xe8, 0x0a, 0x0d, 0x23, 0x0a, 0x08, 0x03, 0x62, 0x09, 0x3f, 0x99, 0x00, 0x07, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x05, 0x39 }; /* Parameters needed to test nanobts_attr_nse_get() */ bts->gprs.nse.nsei = 101; uint8_t attr_nse_expected[] = { 0x9d, 0x00, 0x02, 0x00, 0x65, 0xa0, 0x00, 0x07, 0x03, 0x03, 0x03, 0x03, 0x1e, 0x03, 0x0a, 0xa1, 0x00, 0x0b, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0a, 0x03, 0x0a, 0x03, 0x0a, 0x03 }; /* Parameters needed to test nanobts_attr_cell_get() */ bts->gprs.rac = 0x00; bts->gprs.cell.bvci = 2; bts->gprs.mode = BTS_GPRS_GPRS; uint8_t attr_cell_expected[] = { 0x9a, 0x00, 0x01, 0x00, 0x9c, 0x00, 0x02, 0x05, 0x03, 0x9e, 0x00, 0x02, 0x00, 0x02, 0xa3, 0x00, 0x09, 0x14, 0x05, 0x05, 0xa0, 0x05, 0x0a, 0x04, 0x08, 0x0f, 0xa8, 0x00, 0x02, 0x0f, 0x00, 0xa9, 0x00, 0x05, 0x00, 0xfa, 0x00, 0xfa, 0x02 }; /* Parameters needed to test nanobts_attr_nscv_get() */ bts->gprs.nsvc[0].nsvci = 0x65; bts->gprs.nsvc[0].remote_port = 0x59d8; bts->gprs.nsvc[0].remote_ip = 0x0a090165; bts->gprs.nsvc[0].local_port = 0x5a3c; uint8_t attr_nscv_expected[] = { 0x9f, 0x00, 0x02, 0x00, 0x65, 0xa2, 0x00, 0x08, 0x59, 0xd8, 0x0a, 0x09, 0x01, 0x65, 0x5a, 0x3c }; /* Parameters needed to test nanobts_attr_radio_get() */ trx->arfcn = 866; trx->max_power_red = 22; bts->c0->max_power_red = 22; uint8_t attr_radio_expected[] = { 0x2d, 0x0b, 0x05, 0x00, 0x02, 0x03, 0x62 }; /* Run tests */ test_nanobts_attr_bts_get(bts, attr_bts_expected); test_nanobts_attr_nse_get(bts, attr_nse_expected); test_nanobts_attr_cell_get(bts, attr_cell_expected); test_nanobts_attr_nscv_get(bts, attr_nscv_expected); test_nanobts_attr_radio_get(bts, trx, attr_radio_expected); printf("Done\n"); talloc_free(bts); talloc_free(net); talloc_free(trx); talloc_report_full(ctx, stderr); /* Expecting something like: * full talloc report on 'ctx' (total 813 bytes in 6 blocks) * logging contains 813 bytes in 5 blocks (ref 0) 0x60b0000000a0 * struct log_target contains 196 bytes in 2 blocks (ref 0) 0x6110000000a0 * struct log_category contains 36 bytes in 1 blocks (ref 0) 0x60d0000003e0 * struct log_info contains 616 bytes in 2 blocks (ref 0) 0x60d000000310 * struct log_info_cat contains 576 bytes in 1 blocks (ref 0) 0x6170000000e0 * That's the root ctx + 5x logging: */ OSMO_ASSERT(talloc_total_blocks(ctx) == 6); talloc_free(ctx); return 0; } /* stubs */ struct osmo_prim_hdr; int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { abort(); } struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) { OSMO_ASSERT(0); } bool on_gsm_ts_init(struct gsm_bts_trx_ts *ts) { return true; } osmo-bsc-1.3.0/tests/nanobts_omlattr/nanobts_omlattr_test.ok000066400000000000000000000015731332665256100244240ustar00rootroot00000000000000Testing nanobts_attr_bts_get()... result= 19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539 expected=19555b61676d7318060e00020120331e2424a83421a81f3f2500010a0c0a0b012a5a2b03e80a0d230a080362093f99000700f11000010539 ok. Testing nanobts_attr_nse_get()... result= 9d00020065a00007030303031e030aa1000b03030303030a030a030a03 expected=9d00020065a00007030303031e030aa1000b03030303030a030a030a03 ok. Testing nanobts_attr_cell_get()... result= 9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02 expected=9a0001009c000205039e00020002a30009140505a0050a04080fa800020f00a9000500fa00fa02 ok. Testing nanobts_attr_nscv_get()... result= 9f00020065a2000859d80a0901655a3c expected=9f00020065a2000859d80a0901655a3c ok. Testing nanobts_attr_nscv_get()... result= 2d0b0500020362 expected=2d0b0500020362 ok. Done osmo-bsc-1.3.0/tests/osmo-bsc.vty000066400000000000000000000006211332665256100166750ustar00rootroot00000000000000OsmoBSC> enable OsmoBSC# configure terminal OsmoBSC(config)# network OsmoBSC(config-net)# list ... meas-feed destination ADDR <0-65535> meas-feed scenario NAME ... OsmoBSC(config-net)# meas-feed destination 127.0.0.23 4223 OsmoBSC(config-net)# meas-feed scenario foo23 OsmoBSC(config-net)# show running-config ... network ... meas-feed destination 127.0.0.23 4223 meas-feed scenario foo23 ... osmo-bsc-1.3.0/tests/subscr/000077500000000000000000000000001332665256100157115ustar00rootroot00000000000000osmo-bsc-1.3.0/tests/subscr/Makefile.am000066400000000000000000000012171332665256100177460ustar00rootroot00000000000000AM_CPPFLAGS = \ $(all_includes) \ -I$(top_srcdir)/include \ $(NULL) AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ $(LIBSMPP34_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) AM_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ $(NULL) EXTRA_DIST = \ bsc_subscr_test.ok \ bsc_subscr_test.err \ $(NULL) noinst_PROGRAMS = \ bsc_subscr_test \ $(NULL) bsc_subscr_test_SOURCES = \ bsc_subscr_test.c \ $(NULL) bsc_subscr_test_LDADD = \ $(top_builddir)/src/osmo-bsc/bsc_subscriber.o \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOABIS_LIBS) \ $(LIBOSMOGSM_LIBS) \ $(LIBSMPP34_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(NULL) osmo-bsc-1.3.0/tests/subscr/bsc_subscr_test.c000066400000000000000000000104151332665256100212450ustar00rootroot00000000000000/* (C) 2008 by Jan Luebbe * (C) 2009 by Holger Hans Peter Freyther * (C) 2014 by Alexander Chemeris * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include struct llist_head *bsc_subscribers; #define VERBOSE_ASSERT(val, expect_op, fmt) \ do { \ printf(#val " == " fmt "\n", (val)); \ OSMO_ASSERT((val) expect_op); \ } while (0); static void assert_bsc_subscr(const struct bsc_subscr *bsub, const char *imsi) { struct bsc_subscr *sfound; OSMO_ASSERT(bsub); OSMO_ASSERT(strcmp(bsub->imsi, imsi) == 0); sfound = bsc_subscr_find_by_imsi(bsc_subscribers, imsi); OSMO_ASSERT(sfound == bsub); bsc_subscr_put(sfound); } static void test_bsc_subscr(void) { struct bsc_subscr *s1, *s2, *s3; const char *imsi1 = "1234567890"; const char *imsi2 = "9876543210"; const char *imsi3 = "5656565656"; printf("Test BSC subscriber allocation and deletion\n"); /* Check for emptiness */ VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d"); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL); /* Allocate entry 1 */ s1 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi1); VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); assert_bsc_subscr(s1, imsi1); VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); /* Allocate entry 2 */ s2 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi2); VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d"); /* Allocate entry 3 */ s3 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi3); VERBOSE_ASSERT(llist_count(bsc_subscribers), == 3, "%d"); /* Check entries */ assert_bsc_subscr(s1, imsi1); assert_bsc_subscr(s2, imsi2); assert_bsc_subscr(s3, imsi3); /* Free entry 1 */ bsc_subscr_put(s1); s1 = NULL; VERBOSE_ASSERT(llist_count(bsc_subscribers), == 2, "%d"); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); assert_bsc_subscr(s2, imsi2); assert_bsc_subscr(s3, imsi3); /* Free entry 2 */ bsc_subscr_put(s2); s2 = NULL; VERBOSE_ASSERT(llist_count(bsc_subscribers), == 1, "%d"); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi1) == NULL); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi2) == NULL); assert_bsc_subscr(s3, imsi3); /* Free entry 3 */ bsc_subscr_put(s3); s3 = NULL; VERBOSE_ASSERT(llist_count(bsc_subscribers), == 0, "%d"); OSMO_ASSERT(bsc_subscr_find_by_imsi(bsc_subscribers, imsi3) == NULL); OSMO_ASSERT(llist_empty(bsc_subscribers)); } static const struct log_info_cat log_categories[] = { [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; static const struct log_info log_info = { .cat = log_categories, .num_cat = ARRAY_SIZE(log_categories), }; int main() { void *ctx = talloc_named_const(NULL, 0, "bsc_subscr_test"); printf("Testing BSC subscriber core code.\n"); osmo_init_logging2(ctx, &log_info); log_set_print_filename(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0); log_set_print_category(osmo_stderr_target, 1); bsc_subscribers = talloc_zero(ctx, struct llist_head); INIT_LLIST_HEAD(bsc_subscribers); test_bsc_subscr(); printf("Done\n"); return 0; } osmo-bsc-1.3.0/tests/subscr/bsc_subscr_test.err000066400000000000000000000020701332665256100216110ustar00rootroot00000000000000DREF BSC subscr IMSI:1234567890 usage increases to: 1 DREF BSC subscr IMSI:1234567890 usage increases to: 2 DREF BSC subscr IMSI:1234567890 usage decreases to: 1 DREF BSC subscr IMSI:9876543210 usage increases to: 1 DREF BSC subscr IMSI:5656565656 usage increases to: 1 DREF BSC subscr IMSI:1234567890 usage increases to: 2 DREF BSC subscr IMSI:1234567890 usage decreases to: 1 DREF BSC subscr IMSI:9876543210 usage increases to: 2 DREF BSC subscr IMSI:9876543210 usage decreases to: 1 DREF BSC subscr IMSI:5656565656 usage increases to: 2 DREF BSC subscr IMSI:5656565656 usage decreases to: 1 DREF BSC subscr IMSI:1234567890 usage decreases to: 0 DREF BSC subscr IMSI:9876543210 usage increases to: 2 DREF BSC subscr IMSI:9876543210 usage decreases to: 1 DREF BSC subscr IMSI:5656565656 usage increases to: 2 DREF BSC subscr IMSI:5656565656 usage decreases to: 1 DREF BSC subscr IMSI:9876543210 usage decreases to: 0 DREF BSC subscr IMSI:5656565656 usage increases to: 2 DREF BSC subscr IMSI:5656565656 usage decreases to: 1 DREF BSC subscr IMSI:5656565656 usage decreases to: 0 osmo-bsc-1.3.0/tests/subscr/bsc_subscr_test.ok000066400000000000000000000005431332665256100214350ustar00rootroot00000000000000Testing BSC subscriber core code. Test BSC subscriber allocation and deletion llist_count(bsc_subscribers) == 0 llist_count(bsc_subscribers) == 1 llist_count(bsc_subscribers) == 1 llist_count(bsc_subscribers) == 2 llist_count(bsc_subscribers) == 3 llist_count(bsc_subscribers) == 2 llist_count(bsc_subscribers) == 1 llist_count(bsc_subscribers) == 0 Done osmo-bsc-1.3.0/tests/testsuite.at000066400000000000000000000156411332665256100167760ustar00rootroot00000000000000AT_INIT AT_BANNER([Regression tests.]) AT_SETUP([gsm0408]) AT_KEYWORDS([gsm0408]) cat $abs_srcdir/gsm0408/gsm0408_test.ok > expout AT_CHECK([$abs_top_builddir/tests/gsm0408/gsm0408_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([bsc_subscr]) AT_KEYWORDS([bsc_subscr]) cat $abs_srcdir/subscr/bsc_subscr_test.ok > expout cat $abs_srcdir/subscr/bsc_subscr_test.err > experr AT_CHECK([$abs_top_builddir/tests/subscr/bsc_subscr_test], [], [expout], [experr]) AT_CLEANUP AT_SETUP([abis]) AT_KEYWORDS([abis]) cat $abs_srcdir/abis/abis_test.ok > expout AT_CHECK([$abs_top_builddir/tests/abis/abis_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([bsc]) AT_KEYWORDS([bsc]) cat $abs_srcdir/bsc/bsc_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bsc/bsc_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([codec_pref]) AT_KEYWORDS([codec_pref]) cat $abs_srcdir/codec_pref/codec_pref_test.ok > expout AT_CHECK([$abs_top_builddir/tests/codec_pref/codec_pref_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([nanobts_omlattr]) AT_KEYWORDS([nanobts_omlattr]) cat $abs_srcdir/nanobts_omlattr/nanobts_omlattr_test.ok > expout AT_CHECK([$abs_top_builddir/tests/nanobts_omlattr/nanobts_omlattr_test], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 0]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 0], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 1]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 1], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 2]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 2], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 3]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 3], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 4]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 4], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 5]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 5], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 6]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 6], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 7]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 7], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 8]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 8], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 9]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 9], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 10]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 10], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 11]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 11], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 12]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 12], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 13]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 13], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 14]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 14], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 15]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 15], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 16]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 16], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 17]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 17], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 18]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 18], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 19]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 19], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 20]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 20], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 21]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 21], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 22]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 22], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 23]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 23], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 24]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 24], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 25]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 25], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 26]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 26], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 27]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 27], [], [expout], [ignore]) AT_CLEANUP AT_SETUP([handover test 28]) AT_KEYWORDS([handover]) cat $abs_srcdir/handover/handover_test.ok > expout AT_CHECK([$abs_top_builddir/tests/handover/handover_test 28], [], [expout], [ignore]) AT_CLEANUP osmo-bsc-1.3.0/tests/vty_test_runner.py000077500000000000000000000255611332665256100202500ustar00rootroot00000000000000#!/usr/bin/env python2 # (C) 2013 by Katerina Barone-Adesi # (C) 2013 by Holger Hans Peter Freyther # 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 . import os, sys import time import unittest import socket import subprocess import osmopy.obscvty as obscvty import osmopy.osmoutil as osmoutil from osmopy.osmo_ipa import IPA # to be able to find $top_srcdir/doc/... confpath = os.path.join(sys.path[0], '..') class TestVTYBase(unittest.TestCase): def checkForEndAndExit(self): res = self.vty.command("list") #print ('looking for "exit"\n') self.assert_(res.find(' exit\r') > 0) #print 'found "exit"\nlooking for "end"\n' self.assert_(res.find(' end\r') > 0) #print 'found "end"\n' def vty_command(self): raise Exception("Needs to be implemented by a subclass") def vty_app(self): raise Exception("Needs to be implemented by a subclass") def setUp(self): osmo_vty_cmd = self.vty_command()[:] config_index = osmo_vty_cmd.index('-c') if config_index: cfi = config_index + 1 osmo_vty_cmd[cfi] = os.path.join(confpath, osmo_vty_cmd[cfi]) try: self.proc = osmoutil.popen_devnull(osmo_vty_cmd) except OSError: print >> sys.stderr, "Current directory: %s" % os.getcwd() print >> sys.stderr, "Consider setting -b" appstring = self.vty_app()[2] appport = self.vty_app()[0] self.vty = obscvty.VTYInteract(appstring, "127.0.0.1", appport) def tearDown(self): if self.vty: self.vty._close_socket() self.vty = None osmoutil.end_proc(self.proc) class TestVTYGenericBSC(TestVTYBase): def _testConfigNetworkTree(self, include_bsc_items=True): self.vty.enable() self.assertTrue(self.vty.verify("configure terminal",[''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify("network",[''])) self.assertEquals(self.vty.node(), 'config-net') self.checkForEndAndExit() self.assertTrue(self.vty.verify("bts 0",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.checkForEndAndExit() self.assertTrue(self.vty.verify("trx 0",[''])) self.assertEquals(self.vty.node(), 'config-net-bts-trx') self.checkForEndAndExit() self.vty.command("write terminal") self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.assertTrue(self.vty.verify("exit",[''])) self.assertTrue(self.vty.verify("bts 1",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.checkForEndAndExit() self.assertTrue(self.vty.verify("trx 1",[''])) self.assertEquals(self.vty.node(), 'config-net-bts-trx') self.checkForEndAndExit() self.vty.command("write terminal") self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net-bts') self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config-net') self.assertTrue(self.vty.verify("exit",[''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("exit",[''])) self.assertTrue(self.vty.node() is None) class TestVTYBSC(TestVTYGenericBSC): def vty_command(self): return ["./src/osmo-bsc/osmo-bsc", "-c", "doc/examples/osmo-bsc/osmo-bsc.cfg"] def vty_app(self): return (4242, "./src/osmo-bsc/osmo-bsc", "OsmoBSC", "bsc") def testConfigNetworkTree(self): self._testConfigNetworkTree() def testVtyTree(self): self.vty.enable() self.assertTrue(self.vty.verify("configure terminal", [''])) self.assertEquals(self.vty.node(), 'config') self.checkForEndAndExit() self.assertTrue(self.vty.verify("msc 0", [''])) self.assertEquals(self.vty.node(), 'config-msc') self.checkForEndAndExit() self.assertTrue(self.vty.verify("exit", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("bsc", [''])) self.assertEquals(self.vty.node(), 'config-bsc') self.checkForEndAndExit() self.assertTrue(self.vty.verify("exit", [''])) self.assertEquals(self.vty.node(), 'config') self.assertTrue(self.vty.verify("exit", [''])) self.assertTrue(self.vty.node() is None) def testUssdNotificationsMsc(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("msc") # Test invalid input self.vty.verify("bsc-msc-lost-text", ['% Command incomplete.']) self.vty.verify("bsc-welcome-text", ['% Command incomplete.']) self.vty.verify("bsc-grace-text", ['% Command incomplete.']) # Enable USSD notifications self.vty.verify("bsc-msc-lost-text MSC disconnected", ['']) self.vty.verify("bsc-welcome-text Hello MS", ['']) self.vty.verify("bsc-grace-text In grace period", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('bsc-msc-lost-text MSC disconnected') > 0) self.assertEquals(res.find('no bsc-msc-lost-text'), -1) self.assert_(res.find('bsc-welcome-text Hello MS') > 0) self.assertEquals(res.find('no bsc-welcome-text'), -1) self.assert_(res.find('bsc-grace-text In grace period') > 0) self.assertEquals(res.find('no bsc-grace-text'), -1) # Now disable it.. self.vty.verify("no bsc-msc-lost-text", ['']) self.vty.verify("no bsc-welcome-text", ['']) self.vty.verify("no bsc-grace-text", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find('bsc-msc-lost-text MSC disconnected'), -1) self.assert_(res.find('no bsc-msc-lost-text') > 0) self.assertEquals(res.find('bsc-welcome-text Hello MS'), -1) self.assert_(res.find('no bsc-welcome-text') > 0) self.assertEquals(res.find('bsc-grace-text In grace period'), -1) self.assert_(res.find('no bsc-grace-text') > 0) def testUssdNotificationsBsc(self): self.vty.enable() self.vty.command("configure terminal") self.vty.command("bsc") # Test invalid input self.vty.verify("missing-msc-text", ['% Command incomplete.']) # Enable USSD notifications self.vty.verify("missing-msc-text No MSC found", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('missing-msc-text No MSC found') > 0) self.assertEquals(res.find('no missing-msc-text'), -1) # Now disable it.. self.vty.verify("no missing-msc-text", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find('missing-msc-text No MSC found'), -1) self.assert_(res.find('no missing-msc-text') > 0) def testNetworkTimezone(self): self.vty.enable() self.vty.verify("configure terminal", ['']) self.vty.verify("network", ['']) # Test invalid input self.vty.verify("timezone", ['% Command incomplete.']) self.vty.verify("timezone 20 0", ['% Unknown command.']) self.vty.verify("timezone 0 11", ['% Unknown command.']) self.vty.verify("timezone 0 0 99", ['% Unknown command.']) # Set time zone without DST self.vty.verify("timezone 2 30", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('timezone 2 30') > 0) self.assertEquals(res.find('timezone 2 30 '), -1) # Set time zone with DST self.vty.verify("timezone 2 30 1", ['']) # Verify settings res = self.vty.command("write terminal") self.assert_(res.find('timezone 2 30 1') > 0) # Now disable it.. self.vty.verify("no timezone", ['']) # Verify settings res = self.vty.command("write terminal") self.assertEquals(res.find(' timezone'), -1) def testShowNetwork(self): res = self.vty.command("show network") self.assert_(res.startswith('BSC is on Country Code') >= 0) def testMscDataCoreLACCI(self): self.vty.enable() res = self.vty.command("show running-config") self.assertEquals(res.find("core-location-area-code"), -1) self.assertEquals(res.find("core-cell-identity"), -1) self.vty.command("configure terminal") self.vty.command("msc 0") self.vty.command("core-location-area-code 666") self.vty.command("core-cell-identity 333") res = self.vty.command("show running-config") self.assert_(res.find("core-location-area-code 666") > 0) self.assert_(res.find("core-cell-identity 333") > 0) def add_bsc_test(suite, workdir): if not os.path.isfile(os.path.join(workdir, "src/osmo-bsc/osmo-bsc")): print("Skipping the BSC test") return test = unittest.TestLoader().loadTestsFromTestCase(TestVTYBSC) suite.addTest(test) if __name__ == '__main__': import argparse import sys workdir = '.' parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="verbose mode") parser.add_argument("-p", "--pythonconfpath", dest="p", help="searchpath for config") parser.add_argument("-w", "--workdir", dest="w", help="Working directory") parser.add_argument("test_name", nargs="*", help="(parts of) test names to run, case-insensitive") args = parser.parse_args() verbose_level = 1 if args.verbose: verbose_level = 2 if args.w: workdir = args.w if args.p: confpath = args.p print "confpath %s, workdir %s" % (confpath, workdir) os.chdir(workdir) print "Running tests for specific VTY commands" suite = unittest.TestSuite() add_bsc_test(suite, workdir) if args.test_name: osmoutil.pick_tests(suite, *args.test_name) res = unittest.TextTestRunner(verbosity=verbose_level, stream=sys.stdout).run(suite) sys.exit(len(res.errors) + len(res.failures)) # vim: shiftwidth=4 expandtab nocin ai